Skip to content

Commit

Permalink
GH-285 Add EditorDebugger Integration
Browse files Browse the repository at this point in the history
  • Loading branch information
Naros committed Jun 11, 2024
1 parent 975b5e1 commit f1a29a2
Show file tree
Hide file tree
Showing 35 changed files with 1,238 additions and 52 deletions.
30 changes: 30 additions & 0 deletions src/common/signal_utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// This file is part of the Godot Orchestrator project.
//
// Copyright (c) 2023-present Vahera Studios LLC and its contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "common/signal_utils.h"

namespace SignalUtils
{
bool disconnect_if(Object* p_object, const StringName& p_signal, const Callable& p_callable)
{
if (p_object->is_connected(p_signal, p_callable))
{
p_object->disconnect(p_signal, p_callable);
return true;
}
return false;
}
}
34 changes: 34 additions & 0 deletions src/common/signal_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// This file is part of the Godot Orchestrator project.
//
// Copyright (c) 2023-present Vahera Studios LLC and its contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef ORCHESTRATOR_SIGNAL_UTILS_H
#define ORCHESTRATOR_SIGNAL_UTILS_H

#include <godot_cpp/classes/object.hpp>

using namespace godot;

namespace SignalUtils
{
/// Disconnects the signal from the object if the callable is connected
/// @param p_object the object to disconnect the signal from
/// @param p_signal the signal name to disconnect
/// @param p_callable the callable to disconnect
/// @return true if the signal was disconnected, false otherwise
bool disconnect_if(Object* p_object, const StringName& p_signal, const Callable& p_callable);
}

#endif // ORCHESTRATOR_SIGNAL_UTILS_H
2 changes: 0 additions & 2 deletions src/editor/graph/graph_edit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,6 @@ void OrchestratorGraphEdit::_notification(int p_what)
_grid_pattern->connect("item_selected", callable_mp(this, &OrchestratorGraphEdit::_on_grid_style_selected));
get_menu_hbox()->add_child(_grid_pattern);
get_menu_hbox()->move_child(_grid_pattern, 5);
get_menu_hbox()->get_child(4)->connect("toggled", callable_mp(this, &OrchestratorGraphEdit::_on_show_grid));

set_grid_pattern(GRID_PATTERN_LINES);
#endif

Expand Down
109 changes: 103 additions & 6 deletions src/editor/graph/graph_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
#include "common/logger.h"
#include "common/scene_utils.h"
#include "common/settings.h"
#include "editor/plugins/orchestrator_editor_debugger_plugin.h"
#include "editor/plugins/orchestrator_editor_plugin.h"
#include "editor/script_editor_cache.h"
#include "graph_edit.h"
#include "graph_node_pin.h"
#include "script/nodes/editable_pin_node.h"
Expand All @@ -32,7 +34,6 @@
#include <godot_cpp/classes/input.hpp>
#include <godot_cpp/classes/input_event_action.hpp>
#include <godot_cpp/classes/input_event_mouse_button.hpp>
#include <godot_cpp/classes/label.hpp>
#include <godot_cpp/classes/margin_container.hpp>
#include <godot_cpp/classes/script_editor_base.hpp>
#include <godot_cpp/classes/style_box_flat.hpp>
Expand All @@ -49,6 +50,19 @@ OrchestratorGraphNode::OrchestratorGraphNode(OrchestratorGraphEdit* p_graph, con
set_v_size_flags(SIZE_EXPAND_FILL);
set_meta("__script_node", p_node);

Ref<OrchestratorScriptEditorCache> cache = OrchestratorPlugin::get_singleton()->get_script_editor_cache();
if (cache.is_valid())
{
#if GODOT_VERSION >= 0x040300
if (cache->is_node_disabled_breakpoint(_node->get_orchestration()->get_self()->get_path(), _node->get_id()))
_set_breakpoint_state(OScriptNode::BreakpointFlags::BREAKPOINT_DISABLED);
else if (cache->is_node_breakpoint(_node->get_orchestration()->get_self()->get_path(), _node->get_id()))
_set_breakpoint_state(OScriptNode::BreakpointFlags::BREAKPOINT_ENABLED);
else
_set_breakpoint_state(OScriptNode::BreakpointFlags::BREAKPOINT_NONE);
#endif
}

_update_tooltip();
}

Expand Down Expand Up @@ -276,6 +290,26 @@ void OrchestratorGraphNode::_update_indicators()
notification->set_tooltip_text("Node is experimental and behavior may change without notice.");
_indicators->add_child(notification);
}

#if GODOT_VERSION >= 0x040300
if (_node->has_breakpoint())
{
TextureRect* breakpoint = memnew(TextureRect);
breakpoint->set_custom_minimum_size(Vector2(0, 24));
breakpoint->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
if (!_node->has_disabled_breakpoint())
{
breakpoint->set_texture(SceneUtils::get_editor_icon("DebugSkipBreakpointsOff"));
breakpoint->set_tooltip_text("Debugger will break when processing this node");
}
else
{
breakpoint->set_texture(SceneUtils::get_editor_icon("DebugSkipBreakpointsOn"));
breakpoint->set_tooltip_text("Debugger will skip the breakpoint when processing this node");
}
_indicators->add_child(breakpoint);
}
#endif
}

void OrchestratorGraphNode::_update_titlebar()
Expand Down Expand Up @@ -445,11 +479,18 @@ void OrchestratorGraphNode::_show_context_menu(const Vector2& p_position)
if (!call_script_function.is_valid())
_context_menu->set_item_disabled(_context_menu->get_item_index(CM_EXPAND_NODE), true);


// todo: support breakpoints (See Trello)
// _context_menu->add_separator("Breakpoints");
// _context_menu->add_item("Toggle Breakpoint", CM_TOGGLE_BREAKPOINT, KEY_F9);
// _context_menu->add_item("Add Breakpoint", CM_ADD_BREAKPOINT);
#if GODOT_VERSION >= 0x040300
_context_menu->add_separator("Breakpoints");
_context_menu->add_item("Toggle Breakpoint", CM_TOGGLE_BREAKPOINT, KEY_F9);
if (_node->has_breakpoint())
{
_context_menu->add_item("Remove breakpoint", CM_REMOVE_BREAKPOINT);
if (_node->has_disabled_breakpoint())
_context_menu->add_item("Enable breakpoint", CM_ADD_BREAKPOINT);
else
_context_menu->add_item("Disable breakpoint", CM_DISABLE_BREAKPOINT);
}
#endif

_context_menu->add_separator("Documentation");
_context_menu->add_icon_item(SceneUtils::get_editor_icon("Help"), "View Documentation", CM_VIEW_DOCUMENTATION);
Expand All @@ -469,6 +510,29 @@ void OrchestratorGraphNode::_simulate_action_pressed(const String& p_action_name
get_graph()->execute_action(p_action_name);
}

#if GODOT_VERSION >= 0x040300
void OrchestratorGraphNode::_set_breakpoint_state(OScriptNode::BreakpointFlags p_flag)
{
_node->set_breakpoint_flag(p_flag);

OrchestratorEditorDebuggerPlugin* debugger = OrchestratorEditorDebuggerPlugin::get_singleton();
if (!debugger)
return;

const int node_id = _node->get_id();
const String path = _node->get_orchestration()->get_self()->get_path();

debugger->set_breakpoint(path, node_id, p_flag != OScriptNode::BreakpointFlags::BREAKPOINT_NONE);

Ref<OrchestratorScriptEditorCache> cache = OrchestratorPlugin::get_singleton()->get_script_editor_cache();
if (cache.is_valid())
{
cache->set_breakpoint(path, node_id, p_flag == OScriptNode::BreakpointFlags::BREAKPOINT_ENABLED);
cache->set_disabled_breakpoint(path, node_id, p_flag != OScriptNode::BreakpointFlags::BREAKPOINT_DISABLED);
}
}
#endif

void OrchestratorGraphNode::_on_changed()
{
// Notifications can bubble up to the OrchestratorGraphNode from either the OrchestratorGraphNodePin
Expand Down Expand Up @@ -671,6 +735,39 @@ void OrchestratorGraphNode::_on_context_menu_selection(int p_id)
get_graph()->emit_signal("expand_node", _node->get_id());
break;
}
#if GODOT_VERSION >= 0x040300
case CM_TOGGLE_BREAKPOINT:
{
// An OScriptNode registers the breakpoint data from the current open session.
// This data is transient and is lost across restarts, although GDScript persists
// this and we should implement this too.
//
// What we need to integrate with is EditorDebugNode. It is what will be responsible
// for coordinating the breakpoint communication with the EngineDebugger that runs
// in the separate prcoess that runs the game in F5. It uses Local/Remove debuggers.
if (_node->has_breakpoint())
_set_breakpoint_state(OScriptNode::BreakpointFlags::BREAKPOINT_NONE);
else
_set_breakpoint_state(OScriptNode::BreakpointFlags::BREAKPOINT_ENABLED);
break;
}
case CM_ADD_BREAKPOINT:
case CM_ENABLE_BREAKPOINT:
{
_set_breakpoint_state(OScriptNode::BreakpointFlags::BREAKPOINT_ENABLED);
break;
}
case CM_REMOVE_BREAKPOINT:
{
_set_breakpoint_state(OScriptNode::BreakpointFlags::BREAKPOINT_NONE);
break;
}
case CM_DISABLE_BREAKPOINT:
{
_set_breakpoint_state(OScriptNode::BreakpointFlags::BREAKPOINT_DISABLED);
break;
}
#endif
#ifdef _DEBUG
case CM_SHOW_DETAILS:
{
Expand Down
9 changes: 9 additions & 0 deletions src/editor/graph/graph_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#ifndef ORCHESTRATOR_GRAPH_NODE_H
#define ORCHESTRATOR_GRAPH_NODE_H

#include "common/version.h"
#include "script/node.h"

#include <godot_cpp/classes/graph_node.hpp>
Expand Down Expand Up @@ -57,6 +58,9 @@ class OrchestratorGraphNode : public GraphNode
CM_RENAME,
CM_TOGGLE_BREAKPOINT,
CM_ADD_BREAKPOINT,
CM_ENABLE_BREAKPOINT,
CM_REMOVE_BREAKPOINT,
CM_DISABLE_BREAKPOINT,
CM_VIEW_DOCUMENTATION,
CM_COLLAPSE_FUNCTION,
CM_EXPAND_NODE,
Expand Down Expand Up @@ -207,6 +211,11 @@ class OrchestratorGraphNode : public GraphNode
/// @param p_action_name the action to simulate
void _simulate_action_pressed(const String& p_action_name);

#if GODOT_VERSION >= 0x040300
/// Set the breakpoint state
void _set_breakpoint_state(OScriptNode::BreakpointFlags p_flag);
#endif

private:
/// Called when the graph node is moved
/// @param p_old_pos old position
Expand Down
89 changes: 77 additions & 12 deletions src/editor/main_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
//
#include "main_view.h"

#include "common/settings.h"
#include "common/scene_utils.h"
#include "common/version.h"
#include "common/settings.h"
#include "common/signal_utils.h"
#include "editor/about_dialog.h"
#include "editor/graph/graph_edit.h"
#include "editor/plugins/orchestrator_editor_plugin.h"
Expand Down Expand Up @@ -289,6 +289,13 @@ void OrchestratorMainView::_notification(int p_what)
{
if (Node* scene_tabs = editor_node->find_child("*EditorSceneTabs*", true, false))
scene_tabs->connect("tab_changed", callable_mp(this, &OrchestratorMainView::_on_scene_tab_changed));

#if GODOT_VERSION >= 0x040300
OrchestratorEditorDebuggerPlugin* debugger = OrchestratorEditorDebuggerPlugin::get_singleton();
debugger->connect("goto_script_line", callable_mp(this, &OrchestratorMainView::_on_goto_script_line));
debugger->connect("breakpoints_cleared_in_tree", callable_mp(this, &OrchestratorMainView::_clear_all_breakpoints));
debugger->connect("breakpoint_set_in_tree", callable_mp(this, &OrchestratorMainView::_set_breakpoint));
#endif
}

FileSystemDock* dock = _plugin->get_editor_interface()->get_file_system_dock();
Expand All @@ -299,20 +306,21 @@ void OrchestratorMainView::_notification(int p_what)
else if (p_what == NOTIFICATION_EXIT_TREE)
{
FileSystemDock* dock = _plugin->get_editor_interface()->get_file_system_dock();
if (dock->is_connected("file_removed", callable_mp(this, &OrchestratorMainView::_on_file_removed)))
dock->disconnect("file_removed", callable_mp(this, &OrchestratorMainView::_on_file_removed));
if (dock->is_connected("folder_removed", callable_mp(this, &OrchestratorMainView::_on_folder_removed)))
dock->disconnect("folder_removed", callable_mp(this, &OrchestratorMainView::_on_folder_removed));
if (dock->is_connected("files_moved", callable_mp(this, &OrchestratorMainView::_on_files_moved)))
dock->disconnect("files_moved", callable_mp(this, &OrchestratorMainView::_on_files_moved));
SignalUtils::disconnect_if(dock, "file_removed", callable_mp(this, &OrchestratorMainView::_on_file_removed));
SignalUtils::disconnect_if(dock, "folder_removed", callable_mp(this, &OrchestratorMainView::_on_folder_removed));
SignalUtils::disconnect_if(dock, "files_moved", callable_mp(this, &OrchestratorMainView::_on_files_moved));

#if GODOT_VERSION >= 0x040300
OrchestratorEditorDebuggerPlugin* debugger = OrchestratorEditorDebuggerPlugin::get_singleton();
SignalUtils::disconnect_if(debugger, "goto_script_line", callable_mp(this, &OrchestratorMainView::_on_goto_script_line));
SignalUtils::disconnect_if(debugger, "breakpoints_cleared_in_tree", callable_mp(this, &OrchestratorMainView::_clear_all_breakpoints));
SignalUtils::disconnect_if(debugger, "breakpoint_set_in_tree", callable_mp(this, &OrchestratorMainView::_set_breakpoint));
#endif

if (Node* editor_node = get_tree()->get_root()->get_child(0))
{
if (Node* scene_tabs = editor_node->find_child("*EditorSceneTabs*", true, false))
{
if (scene_tabs->is_connected("tab_changed", callable_mp(this, &OrchestratorMainView::_on_scene_tab_changed)))
scene_tabs->disconnect("tab_changed", callable_mp(this, &OrchestratorMainView::_on_scene_tab_changed));
}
SignalUtils::disconnect_if(scene_tabs, "tab_changed", callable_mp(this, &OrchestratorMainView::_on_scene_tab_changed));
}
}
}
Expand Down Expand Up @@ -368,6 +376,16 @@ void OrchestratorMainView::edit_script(const Ref<OScript>& p_script)
call_deferred("emit_signal", "toggle_component_panel", _right_panel_visible);
}

PackedStringArray OrchestratorMainView::get_breakpoints() const
{
PackedStringArray breakpoints;
#if GODOT_VERSION >= 0x040300
for (const ScriptFile& file : _script_files)
breakpoints.append_array(file.editor->get_breakpoints());
#endif
return breakpoints;
}

void OrchestratorMainView::apply_changes()
{
for (const ScriptFile& file : _script_files)
Expand Down Expand Up @@ -969,3 +987,50 @@ void OrchestratorMainView::_on_recent_history_selected(int p_index)
_update_files_list();
}
}

#if GODOT_VERSION >= 0x040300
void OrchestratorMainView::_on_goto_script_line(const Ref<Script>& p_script, int p_line)
{
if (p_script.is_valid())
{
for (const ScriptFile& file : _script_files)
{
if (file.file_name == p_script->get_path())
{
// Make plugin active
_plugin->make_active();

// Show the script editor view
_show_script_editor_view(file.file_name);

// Navigate to the node in the view
file.editor->goto_node(p_line + 1);

break;
}
}
}
}

void OrchestratorMainView::_clear_all_breakpoints()
{
// Clear all breakpoints in the script editors
for (const ScriptFile& file : _script_files)
file.editor->clear_breakpoints();

// Clear cache breakpoints
_plugin->get_script_editor_cache()->clear_all_breakpoints();
}

void OrchestratorMainView::_set_breakpoint(const Ref<Script>& p_script, int p_line, bool p_enabled)
{
_plugin->get_script_editor_cache()->set_breakpoint(p_script->get_path(), p_line + 1, p_enabled);
_plugin->get_script_editor_cache()->set_disabled_breakpoint(p_script->get_path(), p_line + 1, true);

for (const ScriptFile& file : _script_files)
{
if (file.editor->is_same_script(p_script))
file.editor->set_breakpoint(p_line + 1, p_enabled);
}
}
#endif
Loading

0 comments on commit f1a29a2

Please sign in to comment.