Skip to content

Commit

Permalink
GH-285 Add EditorDebugger support (Requires Godot 4.3)
Browse files Browse the repository at this point in the history
  • Loading branch information
Naros committed Jun 30, 2024
1 parent cc8e799 commit 6834c63
Show file tree
Hide file tree
Showing 26 changed files with 1,168 additions and 17 deletions.
133 changes: 133 additions & 0 deletions src/editor/editor_cache.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// 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 "editor/editor_cache.h"
#include "plugins/orchestrator_editor_debugger_plugin.h"
#include "plugins/orchestrator_editor_plugin.h"

#include <godot_cpp/classes/editor_paths.hpp>

#if GODOT_VERSION >= 0x040300
PackedInt64Array OrchestratorEditorCache::_get_breakpoints_for_path(const String& p_path, bool p_disabled) const
{
return _cache->get_value(p_path, p_disabled ? "disabled_breakpoints" : "breakpoints", PackedInt64Array());
}
#endif

Error OrchestratorEditorCache::load()
{
const EditorInterface* ei = OrchestratorPlugin::get_singleton()->get_editor_interface();

_cache = Ref<ConfigFile>(memnew(ConfigFile));

Error result = _cache->load(ei->get_editor_paths()->get_project_settings_dir().path_join(CACHE_FILE));
if (result != OK)
return result;

#if GODOT_VERSION >= 0x040300
if (OrchestratorEditorDebuggerPlugin* debugger = OrchestratorEditorDebuggerPlugin::get_singleton())
{
for (const String& section : _cache->get_sections())
{
if (_cache->has_section_key(section, "breakpoints"))
{
PackedInt64Array breakpoints = _get_breakpoints_for_path(section, false);
for (int i = 0; i < breakpoints.size(); i++)
debugger->set_breakpoint(section, breakpoints[i], true);
}
if (_cache->has_section_key(section, "disabled_breakpoints"))
{
PackedInt64Array disabled_breakpoints = _get_breakpoints_for_path(section, true);
for (int i = 0; i < disabled_breakpoints.size(); i++)
debugger->set_breakpoint(section, disabled_breakpoints[i], true);
}
}
}
#endif

return OK;
}

Error OrchestratorEditorCache::save()
{
if (_cache.is_valid())
{
const EditorInterface* ei = OrchestratorPlugin::get_singleton()->get_editor_interface();
return _cache->save(ei->get_editor_paths()->get_project_settings_dir().path_join(CACHE_FILE));
}

return ERR_FILE_CANT_WRITE;
}

#if GODOT_VERSION >= 0x040300
void OrchestratorEditorCache::clear_all_breakpoints()
{
for (const String& section : _cache->get_sections())
{
PackedInt64Array breakpoints = _get_breakpoints_for_path(section, false);
if (!breakpoints.is_empty())
_cache->set_value(section, "breakpoints", Variant());

PackedInt64Array disabled_breakpoints = _get_breakpoints_for_path(section, true);
if (!disabled_breakpoints.is_empty())
_cache->set_value(section, "disabled_breakpoints", Variant());
}
}

bool OrchestratorEditorCache::is_node_breakpoint(const String& p_path, int p_node_id) const
{
return _get_breakpoints_for_path(p_path, false).has(p_node_id);
}

bool OrchestratorEditorCache::is_node_disabled_breakpoint(const String& p_path, int p_node_id) const
{
return _get_breakpoints_for_path(p_path, true).has(p_node_id);
}

void OrchestratorEditorCache::set_breakpoint(const String& p_path, int p_node_id, bool p_enabled)
{
PackedInt64Array breakpoints = _get_breakpoints_for_path(p_path, false);
if (p_enabled && !breakpoints.has(p_node_id))
{
breakpoints.push_back(p_node_id);
_cache->set_value(p_path, "breakpoints", breakpoints);
}
else if (!p_enabled && breakpoints.has(p_node_id))
{
breakpoints.remove_at(breakpoints.find(p_node_id));
_cache->set_value(p_path, "breakpoints", breakpoints);
}
}

void OrchestratorEditorCache::set_disabled_breakpoint(const String& p_path, int p_node_id, bool p_remove)
{
PackedInt64Array breakpoints = _get_breakpoints_for_path(p_path, true);
if (p_remove && breakpoints.has(p_node_id))
{
breakpoints.remove_at(breakpoints.find(p_node_id));
_cache->set_value(p_path, "disabled_breakpoints", breakpoints);
}
else if (!p_remove && !breakpoints.has(p_node_id))
{
breakpoints.push_back(p_node_id);
_cache->set_value(p_path, "disabled_breakpoints", breakpoints);
}
}
#endif

void OrchestratorEditorCache::_bind_methods()
{
}
85 changes: 85 additions & 0 deletions src/editor/editor_cache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// 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_EDITOR_CACHE_H
#define ORCHESTRATOR_EDITOR_CACHE_H

#include "common/version.h"

#include <godot_cpp/classes/config_file.hpp>
#include <godot_cpp/classes/ref_counted.hpp>

using namespace godot;

/// A simple cache that maintains editor state details for Orchestrator.
class OrchestratorEditorCache : public RefCounted
{
GDCLASS(OrchestratorEditorCache, RefCounted);
static void _bind_methods();

protected:
const String CACHE_FILE = "orchestrator_editor_cache.cfg"; //! The cache file name
Ref<ConfigFile> _cache; //! Cache file

#if GODOT_VERSION >= 0x040300
/// Get breakpoints for the specified path
/// @param p_path the path
/// @param p_disabled when true, returns disabled breakpoints, false returns enabled breakpoints
/// @return array of breakpoints
PackedInt64Array _get_breakpoints_for_path(const String& p_path, bool p_disabled) const;
#endif

public:
/// Loads the script editor cache from disk.
/// @return the error code based on the load operation
Error load();

/// Saves the script editor cache to disk.
/// @return the error code based on the save operation
Error save();

#if GODOT_VERSION >= 0x040300
/// Clears all breakpoints
void clear_all_breakpoints();

/// Check whether the node in a script is a breakpoint
/// @param p_path the script path
/// @param p_node_id the node id
/// @return true if the node is a breakpoint, false otherwise
bool is_node_breakpoint(const String& p_path, int p_node_id) const;

/// Check whether the node in a script is a breakpoint and is disabled
/// @param p_path the script path
/// @param p_node_id the node id
/// @return true if the node is a breakpoint that is disabled, false otherwise
bool is_node_disabled_breakpoint(const String& p_path, int p_node_id) const;

/// Set whether a breakpoint is enabled
/// @param p_path the script path
/// @param p_node_id the node id
/// @param p_enabled whether the breakpoint is enabled or disabled
void set_breakpoint(const String& p_path, int p_node_id, bool p_enabled);

/// Set the breakpoint as disabled
/// @param p_path the script path
/// @param p_node_id the node id
/// @param p_remove whether to remove the disabled entry
void set_disabled_breakpoint(const String& p_path, int p_node_id, bool p_remove = false);
#endif

};

#endif // ORCHESTRATOR_EDITOR_CACHE_H
78 changes: 78 additions & 0 deletions src/editor/editor_panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,31 @@ void OrchestratorEditorPanel::_update_scene_tab_signals(bool p_connect)
if (p_connect)
{
OCONNECT(scene_tabs, "tab_changed", callable_mp(this, &OrchestratorEditorPanel::_scene_tab_changed));

#if GODOT_VERSION >= 0x040300
OrchestratorEditorDebuggerPlugin* debugger = OrchestratorEditorDebuggerPlugin::get_singleton();
if (debugger)
{
OCONNECT(debugger, "goto_script_line", callable_mp(this, &OrchestratorEditorPanel::_goto_script_line));
OCONNECT(debugger, "breakpoints_cleared_in_tree", callable_mp(this, &OrchestratorEditorPanel::_clear_all_breakpoints));
OCONNECT(debugger, "breakpoint_set_in_tree", callable_mp(this, &OrchestratorEditorPanel::_set_breakpoint));
}
#endif

return;
}

ODISCONNECT(scene_tabs, "tab_changed", callable_mp(this, &OrchestratorEditorPanel::_scene_tab_changed));

#if GODOT_VERSION >= 0x040300
OrchestratorEditorDebuggerPlugin* debugger = OrchestratorEditorDebuggerPlugin::get_singleton();
if (debugger)
{
ODISCONNECT(debugger, "goto_script_line", callable_mp(this, &OrchestratorEditorPanel::_goto_script_line));
ODISCONNECT(debugger, "breakpoints_cleared_in_tree", callable_mp(this, &OrchestratorEditorPanel::_clear_all_breakpoints));
ODISCONNECT(debugger, "breakpoint_set_in_tree", callable_mp(this, &OrchestratorEditorPanel::_set_breakpoint));
}
#endif
}

void OrchestratorEditorPanel::_update_file_system_dock_signals(bool p_connect)
Expand Down Expand Up @@ -649,6 +670,53 @@ void OrchestratorEditorPanel::_folder_removed(const String& p_folder_name)
}
}

#if GODOT_VERSION >= 0x040300
void OrchestratorEditorPanel::_goto_script_line(const Ref<Script>& p_script, int p_line)
{
if (p_script.is_valid())
{
for (const OrchestrationFile& file : _files_context.open_files)
{
if (file.file_name == p_script->get_path())
{
// Make plugin active
OrchestratorPlugin::get_singleton()->make_active();
// Show viewport
_show_editor_viewport(file.file_name);
// Goto node
file.viewport->goto_node(p_line + 1);
break;
}
}
}
}

void OrchestratorEditorPanel::_clear_all_breakpoints()
{
// Clear all breakpoints in the script editors
for (const OrchestrationFile& file : _files_context.open_files)
file.viewport->clear_breakpoints();

// Clear cache breakpoints
OrchestratorPlugin::get_singleton()->get_editor_cache()->clear_all_breakpoints();
}

void OrchestratorEditorPanel::_set_breakpoint(const Ref<Script>& p_script, int p_line, bool p_enabled)
{
const int node_id = p_line + 1;

Ref<OrchestratorEditorCache> cache = OrchestratorPlugin::get_singleton()->get_editor_cache();
cache->set_breakpoint(p_script->get_path(), node_id, p_enabled);
cache->set_disabled_breakpoint(p_script->get_path(), node_id, true); // todo: is this right?

for (const OrchestrationFile& file : _files_context.open_files)
{
if (file.viewport->is_same_script(p_script))
file.viewport->set_breakpoint(node_id, p_enabled);
}
}
#endif

void OrchestratorEditorPanel::_window_changed(bool p_visible)
{
_select_separator->set_visible(!p_visible);
Expand Down Expand Up @@ -775,6 +843,16 @@ void OrchestratorEditorPanel::set_window_layout(const Ref<ConfigFile>& p_configu
}
}

#if GODOT_VERSION >= 0x040300
PackedStringArray OrchestratorEditorPanel::get_breakpoints() const
{
PackedStringArray breakpoints;
for (const OrchestrationFile& file : _files_context.open_files)
breakpoints.append_array(file.viewport->get_breakpoints());
return breakpoints;
}
#endif

void OrchestratorEditorPanel::_notification(int p_what)
{
switch(p_what)
Expand Down
14 changes: 14 additions & 0 deletions src/editor/editor_panel.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#ifndef ORCHESTRATOR_EDITOR_PANEL_H
#define ORCHESTRATOR_EDITOR_PANEL_H

#include "common/version.h"

#include <godot_cpp/classes/config_file.hpp>
#include <godot_cpp/classes/file_dialog.hpp>
#include <godot_cpp/classes/file_system_dock.hpp>
Expand Down Expand Up @@ -179,6 +181,12 @@ class OrchestratorEditorPanel : public PanelContainer
void _file_moved(const String& p_old_file_name, const String& p_new_file_name);
void _folder_removed(const String& p_folder_name);

#if GODOT_VERSION >= 0x040300
void _goto_script_line(const Ref<Script>& p_script, int p_line);
void _clear_all_breakpoints();
void _set_breakpoint(const Ref<Script>& p_script, int p_line, bool p_enabled);
#endif

/// Constructor, intentially protected
OrchestratorEditorPanel() = default;

Expand Down Expand Up @@ -206,6 +214,12 @@ class OrchestratorEditorPanel : public PanelContainer
/// @param p_config the configuration to read from
void set_window_layout(const Ref<ConfigFile>& p_config);

#if GODOT_VERSION >= 0x040300
/// Get all active, defined breakpoints
/// @return list of breakpoints
PackedStringArray get_breakpoints() const;
#endif

/// Constructs the editor panel
/// @param p_window_wrapper
explicit OrchestratorEditorPanel(OrchestratorWindowWrapper* p_window_wrapper);
Expand Down
Loading

0 comments on commit 6834c63

Please sign in to comment.