Path: blob/master/editor/debugger/editor_debugger_node.cpp
20957 views
/**************************************************************************/1/* editor_debugger_node.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "editor_debugger_node.h"3132#include "core/io/resource_loader.h"33#include "core/object/undo_redo.h"34#include "editor/debugger/editor_debugger_plugin.h"35#include "editor/debugger/editor_debugger_tree.h"36#include "editor/debugger/script_editor_debugger.h"37#include "editor/docks/editor_dock_manager.h"38#include "editor/docks/inspector_dock.h"39#include "editor/docks/scene_tree_dock.h"40#include "editor/editor_log.h"41#include "editor/editor_node.h"42#include "editor/editor_string_names.h"43#include "editor/editor_undo_redo_manager.h"44#include "editor/run/editor_run_bar.h"45#include "editor/script/script_editor_plugin.h"46#include "editor/settings/editor_command_palette.h"47#include "editor/settings/editor_settings.h"48#include "editor/themes/editor_theme_manager.h"49#include "scene/gui/menu_button.h"50#include "scene/gui/tab_container.h"51#include "scene/resources/packed_scene.h"5253template <typename Func>54void _for_all(TabContainer *p_node, const Func &p_func) {55for (int i = 0; i < p_node->get_tab_count(); i++) {56ScriptEditorDebugger *dbg = Object::cast_to<ScriptEditorDebugger>(p_node->get_tab_control(i));57ERR_FAIL_NULL(dbg);58p_func(dbg);59}60}6162EditorDebuggerNode *EditorDebuggerNode::singleton = nullptr;6364EditorDebuggerNode::EditorDebuggerNode() {65set_name(TTRC("Debugger"));66set_icon_name("Debug");67set_layout_key("Debugger");68set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_debugger_bottom_panel", TTRC("Toggle Debugger Dock"), KeyModifierMask::ALT | Key::D));69set_default_slot(EditorDock::DOCK_SLOT_BOTTOM);70set_available_layouts(EditorDock::DOCK_LAYOUT_HORIZONTAL);71set_global(false);72set_transient(true);7374_update_margins();7576if (!singleton) {77singleton = this;78}7980tabs = memnew(TabContainer);81tabs->set_tabs_visible(false);82tabs->connect("tab_changed", callable_mp(this, &EditorDebuggerNode::_debugger_changed));83add_child(tabs);8485Ref<StyleBoxEmpty> empty;86empty.instantiate();87tabs->add_theme_style_override(SceneStringName(panel), empty);8889auto_switch_remote_scene_tree = EDITOR_GET("debugger/auto_switch_to_remote_scene_tree");90_add_debugger();9192// Remote scene tree93remote_scene_tree = memnew(EditorDebuggerTree);94remote_scene_tree->connect("objects_selected", callable_mp(this, &EditorDebuggerNode::_remote_objects_requested));95remote_scene_tree->connect("selection_cleared", callable_mp(this, &EditorDebuggerNode::_remote_selection_cleared));96remote_scene_tree->connect("save_node", callable_mp(this, &EditorDebuggerNode::_save_node_requested));97remote_scene_tree->connect("button_clicked", callable_mp(this, &EditorDebuggerNode::_remote_tree_button_pressed));98SceneTreeDock::get_singleton()->add_remote_tree_editor(remote_scene_tree);99SceneTreeDock::get_singleton()->connect("remote_tree_selected", callable_mp(this, &EditorDebuggerNode::request_remote_tree));100101remote_scene_tree_timeout = EDITOR_GET("debugger/remote_scene_tree_refresh_interval");102inspect_edited_object_timeout = EDITOR_GET("debugger/remote_inspect_refresh_interval");103104if (Engine::get_singleton()->is_recovery_mode_hint()) {105return;106}107108EditorRunBar::get_singleton()->get_pause_button()->connect(SceneStringName(pressed), callable_mp(this, &EditorDebuggerNode::_paused));109}110111ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() {112ScriptEditorDebugger *node = memnew(ScriptEditorDebugger);113114int id = tabs->get_tab_count();115node->connect("stop_requested", callable_mp(this, &EditorDebuggerNode::_debugger_wants_stop).bind(id));116node->connect("stopped", callable_mp(this, &EditorDebuggerNode::_debugger_stopped).bind(id));117node->connect("stack_frame_selected", callable_mp(this, &EditorDebuggerNode::_stack_frame_selected).bind(id));118node->connect("error_selected", callable_mp(this, &EditorDebuggerNode::_error_selected).bind(id));119node->connect("breakpoint_selected", callable_mp(this, &EditorDebuggerNode::_error_selected).bind(id));120node->connect("clear_execution", callable_mp(this, &EditorDebuggerNode::_clear_execution));121node->connect("breaked", callable_mp(this, &EditorDebuggerNode::_breaked).bind(id));122node->connect("debug_data", callable_mp(this, &EditorDebuggerNode::_debug_data).bind(id));123node->connect("remote_tree_select_requested", callable_mp(this, &EditorDebuggerNode::_remote_tree_select_requested).bind(id));124node->connect("remote_tree_clear_selection_requested", callable_mp(this, &EditorDebuggerNode::_remote_tree_clear_selection_requested).bind(id));125node->connect("remote_tree_updated", callable_mp(this, &EditorDebuggerNode::_remote_tree_updated).bind(id));126node->connect("remote_objects_updated", callable_mp(this, &EditorDebuggerNode::_remote_objects_updated).bind(id));127node->connect("remote_object_property_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_property_updated).bind(id));128node->connect("remote_objects_requested", callable_mp(this, &EditorDebuggerNode::_remote_objects_requested).bind(id));129node->connect("set_breakpoint", callable_mp(this, &EditorDebuggerNode::_breakpoint_set_in_tree).bind(id));130node->connect("clear_breakpoints", callable_mp(this, &EditorDebuggerNode::_breakpoints_cleared_in_tree).bind(id));131node->connect("errors_cleared", callable_mp(this, &EditorDebuggerNode::_update_errors));132133if (tabs->get_tab_count() > 0) {134get_debugger(0)->clear_style();135}136137tabs->add_child(node);138139node->set_name(vformat(TTR("Session %d"), tabs->get_tab_count()));140if (tabs->get_tab_count() > 1) {141node->clear_style();142tabs->set_tabs_visible(true);143tabs->add_theme_style_override(SceneStringName(panel), EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("DebuggerPanel"), EditorStringName(EditorStyles)));144}145146if (!debugger_plugins.is_empty()) {147for (Ref<EditorDebuggerPlugin> plugin : debugger_plugins) {148plugin->create_session(node);149}150}151152return node;153}154155void EditorDebuggerNode::_stack_frame_selected(int p_debugger) {156const ScriptEditorDebugger *dbg = get_debugger(p_debugger);157ERR_FAIL_NULL(dbg);158if (dbg != get_current_debugger()) {159return;160}161_text_editor_stack_goto(dbg);162}163164void EditorDebuggerNode::_error_selected(const String &p_file, int p_line, int p_debugger) {165if (!p_file.is_resource_file() && !ResourceCache::has(p_file)) {166// If it's a built-in script, make sure the scene is opened first.167EditorNode::get_singleton()->load_scene(p_file.get_slice("::", 0));168}169Ref<Script> s = ResourceLoader::load(p_file);170emit_signal(SNAME("goto_script_line"), s, p_line - 1);171}172173void EditorDebuggerNode::_text_editor_stack_goto(const ScriptEditorDebugger *p_debugger) {174String file = p_debugger->get_stack_script_file();175if (file.is_empty()) {176return;177}178if (file.is_resource_file()) {179stack_script = ResourceLoader::load(file);180} else {181// If the script is built-in, it can be opened only if the scene is loaded in memory.182int i = file.find("::");183int j = file.rfind_char('(', i);184if (j > -1) { // If the script is named, the string is "name (file)", so we need to extract the path.185file = file.substr(j + 1, file.find_char(')', i) - j - 1);186}187Ref<PackedScene> ps = ResourceLoader::load(file.get_slice("::", 0));188stack_script = ResourceLoader::load(file);189}190const int line = p_debugger->get_stack_script_line() - 1;191emit_signal(SNAME("goto_script_line"), stack_script, line);192emit_signal(SNAME("set_execution"), stack_script, line);193stack_script.unref(); // Why?!?194}195196void EditorDebuggerNode::_text_editor_stack_clear(const ScriptEditorDebugger *p_debugger) {197String file = p_debugger->get_stack_script_file();198if (file.is_empty()) {199return;200}201if (file.is_resource_file()) {202stack_script = ResourceLoader::load(file);203} else {204// If the script is built-in, it can be opened only if the scene is loaded in memory.205int i = file.find("::");206int j = file.rfind_char('(', i);207if (j > -1) { // If the script is named, the string is "name (file)", so we need to extract the path.208file = file.substr(j + 1, file.find_char(')', i) - j - 1);209}210Ref<PackedScene> ps = ResourceLoader::load(file.get_slice("::", 0));211stack_script = ResourceLoader::load(file);212}213emit_signal(SNAME("clear_execution"), stack_script);214stack_script.unref(); // Why?!?215}216217void EditorDebuggerNode::_bind_methods() {218// LiveDebug.219ClassDB::bind_method("live_debug_create_node", &EditorDebuggerNode::live_debug_create_node);220ClassDB::bind_method("live_debug_instantiate_node", &EditorDebuggerNode::live_debug_instantiate_node);221ClassDB::bind_method("live_debug_remove_node", &EditorDebuggerNode::live_debug_remove_node);222ClassDB::bind_method("live_debug_remove_and_keep_node", &EditorDebuggerNode::live_debug_remove_and_keep_node);223ClassDB::bind_method("live_debug_restore_node", &EditorDebuggerNode::live_debug_restore_node);224ClassDB::bind_method("live_debug_duplicate_node", &EditorDebuggerNode::live_debug_duplicate_node);225ClassDB::bind_method("live_debug_reparent_node", &EditorDebuggerNode::live_debug_reparent_node);226227ADD_SIGNAL(MethodInfo("goto_script_line"));228ADD_SIGNAL(MethodInfo("set_execution", PropertyInfo("script"), PropertyInfo(Variant::INT, "line")));229ADD_SIGNAL(MethodInfo("clear_execution", PropertyInfo("script")));230ADD_SIGNAL(MethodInfo("breaked", PropertyInfo(Variant::BOOL, "reallydid"), PropertyInfo(Variant::BOOL, "can_debug")));231ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::BOOL, "enabled")));232ADD_SIGNAL(MethodInfo("breakpoint_set_in_tree", PropertyInfo("script"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::BOOL, "enabled"), PropertyInfo(Variant::INT, "debugger")));233ADD_SIGNAL(MethodInfo("breakpoints_cleared_in_tree", PropertyInfo(Variant::INT, "debugger")));234}235236void EditorDebuggerNode::register_undo_redo(UndoRedo *p_undo_redo) {237p_undo_redo->set_method_notify_callback(_methods_changed, this);238p_undo_redo->set_property_notify_callback(_properties_changed, this);239}240241ScriptEditorDebugger *EditorDebuggerNode::get_debugger(int p_id) const {242return Object::cast_to<ScriptEditorDebugger>(tabs->get_tab_control(p_id));243}244245ScriptEditorDebugger *EditorDebuggerNode::get_previous_debugger() const {246return Object::cast_to<ScriptEditorDebugger>(tabs->get_tab_control(tabs->get_previous_tab()));247}248249ScriptEditorDebugger *EditorDebuggerNode::get_current_debugger() const {250return Object::cast_to<ScriptEditorDebugger>(tabs->get_tab_control(tabs->get_current_tab()));251}252253ScriptEditorDebugger *EditorDebuggerNode::get_default_debugger() const {254return Object::cast_to<ScriptEditorDebugger>(tabs->get_tab_control(0));255}256257String EditorDebuggerNode::get_server_uri() const {258return server.is_valid() ? server->get_uri() : "";259}260261void EditorDebuggerNode::set_keep_open(bool p_keep_open) {262keep_open = p_keep_open;263if (keep_open) {264if (server.is_null() || !server->is_active()) {265start();266}267} else {268bool found = false;269_for_all(tabs, [&](ScriptEditorDebugger *p_debugger) {270if (p_debugger->is_session_active()) {271found = true;272}273});274if (!found) {275stop();276}277}278}279280Error EditorDebuggerNode::start(const String &p_uri) {281if (Engine::get_singleton()->is_recovery_mode_hint()) {282return ERR_UNAVAILABLE;283}284285ERR_FAIL_COND_V(!p_uri.contains("://"), ERR_INVALID_PARAMETER);286if (keep_open && current_uri == p_uri && server.is_valid()) {287return OK;288}289stop(true);290current_uri = p_uri;291292server = Ref<EditorDebuggerServer>(EditorDebuggerServer::create(p_uri.substr(0, p_uri.find("://") + 3)));293const Error err = server->start(p_uri);294if (err != OK) {295return err;296}297set_process(true);298EditorNode::get_log()->add_message("--- Debugging process started ---", EditorLog::MSG_TYPE_EDITOR);299return OK;300}301302void EditorDebuggerNode::stop(bool p_force) {303if (keep_open && !p_force) {304return;305}306307remote_scene_tree_wait = false;308inspect_edited_object_wait = false;309310current_uri.clear();311if (server.is_valid()) {312server->stop();313EditorNode::get_log()->add_message("--- Debugging process stopped ---", EditorLog::MSG_TYPE_EDITOR);314315if (EditorRunBar::get_singleton()->is_movie_maker_enabled()) {316// Request attention in case the user was doing something else when movie recording is finished.317DisplayServer::get_singleton()->window_request_attention();318}319320server.unref();321}322323// Also close all debugging sessions.324_for_all(tabs, [&](ScriptEditorDebugger *dbg) {325if (dbg->is_session_active()) {326dbg->_stop_and_notify();327}328});329_break_state_changed();330breakpoints.clear();331EditorUndoRedoManager::get_singleton()->clear_history(EditorUndoRedoManager::REMOTE_HISTORY, false);332set_process(false);333}334335void EditorDebuggerNode::_notification(int p_what) {336switch (p_what) {337case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {338if (!EditorThemeManager::is_generated_theme_outdated()) {339return;340}341342if (tabs->get_tab_count() > 1) {343tabs->add_theme_style_override(SceneStringName(panel), EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("DebuggerPanel"), EditorStringName(EditorStyles)));344}345_update_margins();346347remote_scene_tree->update_icon_max_width();348} break;349350case NOTIFICATION_READY: {351_update_debug_options();352initializing = false;353} break;354355case NOTIFICATION_PROCESS: {356if (server.is_null()) {357return;358}359360if (!server->is_active()) {361stop();362return;363}364server->poll();365366_update_errors();367368// Remote scene tree update.369if (!remote_scene_tree_wait) {370remote_scene_tree_timeout -= get_process_delta_time();371if (remote_scene_tree_timeout < 0) {372remote_scene_tree_timeout = EDITOR_GET("debugger/remote_scene_tree_refresh_interval");373374if (remote_scene_tree->is_visible_in_tree()) {375remote_scene_tree_wait = true;376get_current_debugger()->request_remote_tree();377}378}379}380381// Remote inspector update.382if (!inspect_edited_object_wait) {383inspect_edited_object_timeout -= get_process_delta_time();384if (inspect_edited_object_timeout < 0) {385inspect_edited_object_timeout = EDITOR_GET("debugger/remote_inspect_refresh_interval");386387if (EditorDebuggerRemoteObjects *robjs = Object::cast_to<EditorDebuggerRemoteObjects>(InspectorDock::get_inspector_singleton()->get_edited_object())) {388inspect_edited_object_wait = true;389get_current_debugger()->request_remote_objects(robjs->remote_object_ids, false);390}391}392}393394// Take connections.395if (server->is_connection_available()) {396ScriptEditorDebugger *debugger = nullptr;397_for_all(tabs, [&](ScriptEditorDebugger *dbg) {398if (debugger || dbg->is_session_active()) {399return;400}401debugger = dbg;402});403if (debugger == nullptr) {404if (tabs->get_tab_count() <= 4) { // Max 4 debugging sessions active.405debugger = _add_debugger();406} else {407// We already have too many sessions, disconnecting new clients to prevent them from hanging.408server->take_connection()->close();409return; // Can't add, stop here.410}411}412413EditorRunBar::get_singleton()->get_pause_button()->set_disabled(false);414// Switch to remote tree view if so desired.415remote_scene_tree->set_new_session();416auto_switch_remote_scene_tree = EDITOR_GET("debugger/auto_switch_to_remote_scene_tree");417if (auto_switch_remote_scene_tree) {418SceneTreeDock::get_singleton()->show_remote_tree();419}420// Good to go.421SceneTreeDock::get_singleton()->show_tab_buttons();422debugger->set_editor_remote_tree(remote_scene_tree);423debugger->start(server->take_connection());424// Send breakpoints.425for (const KeyValue<Breakpoint, bool> &E : breakpoints) {426const Breakpoint &bp = E.key;427debugger->set_breakpoint(bp.source, bp.line, E.value);428} // Will arrive too late, how does the regular run work?429430debugger->update_live_edit_root();431}432} break;433}434}435436void EditorDebuggerNode::_update_errors() {437int error_count = 0;438int warning_count = 0;439_for_all(tabs, [&](ScriptEditorDebugger *dbg) {440error_count += dbg->get_error_count();441warning_count += dbg->get_warning_count();442});443444if (error_count != last_error_count || warning_count != last_warning_count) {445_for_all(tabs, [&](ScriptEditorDebugger *dbg) {446dbg->update_tabs();447});448449last_error_count = error_count;450last_warning_count = warning_count;451452if (error_count == 0 && warning_count == 0) {453set_title("");454set_dock_icon(Ref<Texture2D>());455set_title_color(Color(0, 0, 0, 0));456set_force_show_icon(false);457} else {458set_title(TTR("Debugger") + " (" + itos(error_count + warning_count) + ")");459if (error_count >= 1 && warning_count >= 1) {460set_dock_icon(get_editor_theme_icon(SNAME("ErrorWarning")));461// Use error color to represent the highest level of severity reported.462set_title_color(get_theme_color(SNAME("error_color"), EditorStringName(Editor)));463} else if (error_count >= 1) {464set_dock_icon(get_editor_theme_icon(SNAME("Error")));465set_title_color(get_theme_color(SNAME("error_color"), EditorStringName(Editor)));466} else {467set_dock_icon(get_editor_theme_icon(SNAME("Warning")));468set_title_color(get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));469}470set_force_show_icon(true);471}472}473}474475void EditorDebuggerNode::_update_margins() {476Ref<StyleBox> bottom_panel_margins = EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles));477add_theme_constant_override("margin_top", -bottom_panel_margins->get_margin(SIDE_TOP));478add_theme_constant_override("margin_left", -bottom_panel_margins->get_margin(SIDE_LEFT));479add_theme_constant_override("margin_right", -bottom_panel_margins->get_margin(SIDE_RIGHT));480add_theme_constant_override("margin_bottom", -bottom_panel_margins->get_margin(SIDE_BOTTOM));481}482483void EditorDebuggerNode::_debugger_stopped(int p_id) {484ScriptEditorDebugger *dbg = get_debugger(p_id);485ERR_FAIL_NULL(dbg);486487bool found = false;488_for_all(tabs, [&](ScriptEditorDebugger *p_debugger) {489if (p_debugger->is_session_active()) {490found = true;491}492});493if (!found) {494EditorRunBar::get_singleton()->get_pause_button()->set_pressed(false);495EditorRunBar::get_singleton()->get_pause_button()->set_disabled(true);496SceneTreeDock *dock = SceneTreeDock::get_singleton();497if (dock->is_inside_tree()) {498dock->hide_remote_tree();499dock->hide_tab_buttons();500}501EditorNode::get_singleton()->notify_all_debug_sessions_exited();502}503}504505void EditorDebuggerNode::_debugger_wants_stop(int p_id) {506// Ask editor to kill PID.507if (int pid = get_debugger(p_id)->get_remote_pid()) {508callable_mp(EditorNode::get_singleton(), &EditorNode::stop_child_process).call_deferred(pid);509}510}511512void EditorDebuggerNode::_debugger_changed(int p_tab) {513remote_scene_tree_wait = false;514inspect_edited_object_wait = false;515516if (Object *robjs = InspectorDock::get_inspector_singleton()->get_edited_object()) {517if (Object::cast_to<EditorDebuggerRemoteObjects>(robjs)) {518// Clear inspected object, you can only inspect objects in selected debugger.519// Hopefully, in the future, we will have one inspector per debugger.520EditorNode::get_singleton()->push_item(nullptr);521}522}523524if (ScriptEditorDebugger *prev_debug = get_previous_debugger()) {525prev_debug->clear_inspector();526_text_editor_stack_clear(prev_debug);527}528if (remote_scene_tree->is_visible_in_tree()) {529get_current_debugger()->request_remote_tree();530}531if (get_current_debugger()->is_breaked()) {532_text_editor_stack_goto(get_current_debugger());533}534535_break_state_changed();536}537538void EditorDebuggerNode::_debug_data(const String &p_msg, const Array &p_data, int p_debugger) {539if (p_debugger != tabs->get_current_tab()) {540return;541}542543if (p_msg == "scene:scene_tree") {544remote_scene_tree_wait = false;545} else if (p_msg == "scene:inspect_objects") {546inspect_edited_object_wait = false;547}548}549550void EditorDebuggerNode::set_script_debug_button(MenuButton *p_button) {551script_menu = p_button;552script_menu->set_text(TTRC("Debug"));553script_menu->set_switch_on_hover(true);554PopupMenu *p = script_menu->get_popup();555p->add_shortcut(ED_GET_SHORTCUT("debugger/step_into"), DEBUG_STEP);556p->add_shortcut(ED_GET_SHORTCUT("debugger/step_over"), DEBUG_NEXT);557p->add_separator();558p->add_shortcut(ED_GET_SHORTCUT("debugger/break"), DEBUG_BREAK);559p->add_shortcut(ED_GET_SHORTCUT("debugger/continue"), DEBUG_CONTINUE);560p->add_separator();561p->add_check_shortcut(ED_GET_SHORTCUT("debugger/debug_with_external_editor"), DEBUG_WITH_EXTERNAL_EDITOR);562p->connect(SceneStringName(id_pressed), callable_mp(this, &EditorDebuggerNode::_menu_option));563564_break_state_changed();565script_menu->show();566}567568void EditorDebuggerNode::_break_state_changed() {569const bool breaked = get_current_debugger()->is_breaked();570const bool can_debug = get_current_debugger()->is_debuggable();571if (breaked) { // Show debugger.572EditorDockManager::get_singleton()->focus_dock(this);573}574575// Update script menu.576if (!script_menu) {577return;578}579PopupMenu *p = script_menu->get_popup();580p->set_item_disabled(p->get_item_index(DEBUG_NEXT), !(breaked && can_debug));581p->set_item_disabled(p->get_item_index(DEBUG_STEP), !(breaked && can_debug));582p->set_item_disabled(p->get_item_index(DEBUG_BREAK), breaked);583p->set_item_disabled(p->get_item_index(DEBUG_CONTINUE), !breaked);584}585586void EditorDebuggerNode::_menu_option(int p_id) {587switch (p_id) {588case DEBUG_NEXT: {589debug_next();590} break;591case DEBUG_STEP: {592debug_step();593} break;594case DEBUG_BREAK: {595debug_break();596} break;597case DEBUG_CONTINUE: {598debug_continue();599} break;600case DEBUG_WITH_EXTERNAL_EDITOR: {601bool ischecked = script_menu->get_popup()->is_item_checked(script_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR));602debug_with_external_editor = !ischecked;603script_menu->get_popup()->set_item_checked(script_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR), !ischecked);604if (!initializing) {605EditorSettings::get_singleton()->set_project_metadata("debug_options", "debug_with_external_editor", !ischecked);606}607} break;608}609}610611void EditorDebuggerNode::_update_debug_options() {612if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "debug_with_external_editor", false).operator bool()) {613_menu_option(DEBUG_WITH_EXTERNAL_EDITOR);614}615}616617void EditorDebuggerNode::_paused() {618const bool paused = EditorRunBar::get_singleton()->get_pause_button()->is_pressed();619_for_all(tabs, [&](ScriptEditorDebugger *dbg) {620if (paused && !dbg->is_breaked()) {621dbg->debug_break();622} else if (!paused && dbg->is_breaked()) {623dbg->debug_continue();624}625});626}627628void EditorDebuggerNode::_breaked(bool p_breaked, bool p_can_debug, const String &p_message, bool p_has_stackdump, int p_debugger) {629if (get_current_debugger() != get_debugger(p_debugger)) {630if (!p_breaked) {631return;632}633tabs->set_current_tab(p_debugger);634}635_break_state_changed();636EditorRunBar::get_singleton()->get_pause_button()->set_pressed(p_breaked);637emit_signal(SNAME("breaked"), p_breaked, p_can_debug);638}639640bool EditorDebuggerNode::is_skip_breakpoints() const {641return get_current_debugger()->is_skip_breakpoints();642}643644bool EditorDebuggerNode::is_ignore_error_breaks() const {645return get_default_debugger()->is_ignore_error_breaks();646}647648void EditorDebuggerNode::set_breakpoint(const String &p_path, int p_line, bool p_enabled) {649breakpoints[Breakpoint(p_path, p_line)] = p_enabled;650_for_all(tabs, [&](ScriptEditorDebugger *dbg) {651dbg->set_breakpoint(p_path, p_line, p_enabled);652});653654emit_signal(SNAME("breakpoint_toggled"), p_path, p_line, p_enabled);655}656657void EditorDebuggerNode::set_breakpoints(const String &p_path, const Array &p_lines) {658for (int i = 0; i < p_lines.size(); i++) {659set_breakpoint(p_path, p_lines[i], true);660}661662for (const KeyValue<Breakpoint, bool> &E : breakpoints) {663Breakpoint b = E.key;664if (b.source == p_path && !p_lines.has(b.line)) {665set_breakpoint(p_path, b.line, false);666}667}668}669670void EditorDebuggerNode::reload_all_scripts() {671_for_all(tabs, [&](ScriptEditorDebugger *dbg) {672dbg->reload_all_scripts();673});674}675676void EditorDebuggerNode::reload_scripts(const Vector<String> &p_script_paths) {677_for_all(tabs, [&](ScriptEditorDebugger *dbg) {678dbg->reload_scripts(p_script_paths);679});680}681682void EditorDebuggerNode::debug_next() {683get_current_debugger()->debug_next();684}685686void EditorDebuggerNode::debug_step() {687get_current_debugger()->debug_step();688}689690void EditorDebuggerNode::debug_break() {691get_current_debugger()->debug_break();692}693694void EditorDebuggerNode::debug_continue() {695get_current_debugger()->debug_continue();696}697698String EditorDebuggerNode::get_var_value(const String &p_var) const {699return get_current_debugger()->get_var_value(p_var);700}701702// LiveEdit/Inspector703void EditorDebuggerNode::request_remote_tree() {704get_current_debugger()->request_remote_tree();705}706707void EditorDebuggerNode::set_remote_selection(const TypedArray<int64_t> &p_ids) {708stop_waiting_inspection();709get_current_debugger()->request_remote_objects(p_ids);710}711712void EditorDebuggerNode::clear_remote_tree_selection() {713remote_scene_tree->clear_selection();714get_current_debugger()->clear_inspector(remote_scene_tree_clear_msg);715}716717void EditorDebuggerNode::stop_waiting_inspection() {718inspect_edited_object_timeout = EDITOR_GET("debugger/remote_inspect_refresh_interval");719inspect_edited_object_wait = false;720}721722bool EditorDebuggerNode::match_remote_selection(const TypedArray<uint64_t> &p_ids) const {723return p_ids == remote_scene_tree->get_selection();724}725726void EditorDebuggerNode::_remote_tree_select_requested(const TypedArray<int64_t> &p_ids, int p_debugger) {727if (p_debugger == tabs->get_current_tab()) {728remote_scene_tree->select_nodes(p_ids);729}730}731732void EditorDebuggerNode::_remote_tree_clear_selection_requested(int p_debugger) {733if (p_debugger != tabs->get_current_tab()) {734return;735}736remote_scene_tree->clear_selection();737remote_scene_tree_clear_msg = false;738get_current_debugger()->clear_inspector(false);739remote_scene_tree_clear_msg = true;740}741742void EditorDebuggerNode::_remote_tree_updated(int p_debugger) {743if (p_debugger != tabs->get_current_tab()) {744return;745}746remote_scene_tree->clear();747remote_scene_tree->update_scene_tree(get_current_debugger()->get_remote_tree(), p_debugger);748}749750void EditorDebuggerNode::_remote_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) {751if (p_button != MouseButton::LEFT) {752return;753}754755TreeItem *item = Object::cast_to<TreeItem>(p_item);756ERR_FAIL_NULL(item);757758if (p_id == EditorDebuggerTree::BUTTON_SUBSCENE) {759remote_scene_tree->emit_signal(SNAME("open"), item->get_meta("scene_file_path"));760} else if (p_id == EditorDebuggerTree::BUTTON_VISIBILITY) {761ObjectID obj_id = item->get_metadata(0);762ERR_FAIL_COND(obj_id.is_null());763get_current_debugger()->update_remote_object(obj_id, "visible", !item->get_meta("visible"));764get_current_debugger()->request_remote_tree();765}766}767768void EditorDebuggerNode::_remote_objects_updated(EditorDebuggerRemoteObjects *p_objs, int p_debugger) {769if (p_debugger == tabs->get_current_tab() && p_objs != InspectorDock::get_inspector_singleton()->get_edited_object()) {770EditorNode::get_singleton()->push_item(p_objs);771}772}773774void EditorDebuggerNode::_remote_object_property_updated(ObjectID p_id, const String &p_property, int p_debugger) {775if (p_debugger != tabs->get_current_tab()) {776return;777}778779Object *obj = InspectorDock::get_inspector_singleton()->get_edited_object();780if (obj && obj->get_instance_id() == p_id) {781InspectorDock::get_inspector_singleton()->update_property(p_property);782}783}784785void EditorDebuggerNode::_remote_objects_requested(const TypedArray<uint64_t> &p_ids, int p_debugger) {786if (p_debugger != tabs->get_current_tab()) {787return;788}789stop_waiting_inspection();790get_current_debugger()->request_remote_objects(p_ids);791}792793void EditorDebuggerNode::_remote_selection_cleared(int p_debugger) {794if (p_debugger != tabs->get_current_tab()) {795return;796}797stop_waiting_inspection();798get_current_debugger()->clear_inspector();799}800801void EditorDebuggerNode::_save_node_requested(ObjectID p_id, const String &p_file, int p_debugger) {802if (p_debugger != tabs->get_current_tab()) {803return;804}805get_current_debugger()->save_node(p_id, p_file);806}807808void EditorDebuggerNode::_breakpoint_set_in_tree(Ref<RefCounted> p_script, int p_line, bool p_enabled, int p_debugger) {809if (p_debugger != tabs->get_current_tab()) {810return;811}812813emit_signal(SNAME("breakpoint_set_in_tree"), p_script, p_line, p_enabled);814}815816void EditorDebuggerNode::_breakpoints_cleared_in_tree(int p_debugger) {817if (p_debugger != tabs->get_current_tab()) {818return;819}820821emit_signal(SNAME("breakpoints_cleared_in_tree"));822}823824// Remote inspector/edit.825void EditorDebuggerNode::_methods_changed(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount) {826if (!singleton) {827return;828}829_for_all(singleton->tabs, [&](ScriptEditorDebugger *dbg) {830dbg->_method_changed(p_base, p_name, p_args, p_argcount);831});832}833834void EditorDebuggerNode::_properties_changed(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value) {835if (!singleton) {836return;837}838_for_all(singleton->tabs, [&](ScriptEditorDebugger *dbg) {839dbg->_property_changed(p_base, p_property, p_value);840});841}842843// LiveDebug844void EditorDebuggerNode::set_live_debugging(bool p_enabled) {845_for_all(tabs, [&](ScriptEditorDebugger *dbg) {846dbg->set_live_debugging(p_enabled);847});848}849850void EditorDebuggerNode::update_live_edit_root() {851_for_all(tabs, [&](ScriptEditorDebugger *dbg) {852dbg->update_live_edit_root();853});854}855856void EditorDebuggerNode::live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name) {857_for_all(tabs, [&](ScriptEditorDebugger *dbg) {858dbg->live_debug_create_node(p_parent, p_type, p_name);859});860}861862void EditorDebuggerNode::live_debug_instantiate_node(const NodePath &p_parent, const String &p_path, const String &p_name) {863_for_all(tabs, [&](ScriptEditorDebugger *dbg) {864dbg->live_debug_instantiate_node(p_parent, p_path, p_name);865});866}867868void EditorDebuggerNode::live_debug_remove_node(const NodePath &p_at) {869_for_all(tabs, [&](ScriptEditorDebugger *dbg) {870dbg->live_debug_remove_node(p_at);871});872}873874void EditorDebuggerNode::live_debug_remove_and_keep_node(const NodePath &p_at, ObjectID p_keep_id) {875_for_all(tabs, [&](ScriptEditorDebugger *dbg) {876dbg->live_debug_remove_and_keep_node(p_at, p_keep_id);877});878}879880void EditorDebuggerNode::live_debug_restore_node(ObjectID p_id, const NodePath &p_at, int p_at_pos) {881_for_all(tabs, [&](ScriptEditorDebugger *dbg) {882dbg->live_debug_restore_node(p_id, p_at, p_at_pos);883});884}885886void EditorDebuggerNode::live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name) {887_for_all(tabs, [&](ScriptEditorDebugger *dbg) {888dbg->live_debug_duplicate_node(p_at, p_new_name);889});890}891892void EditorDebuggerNode::live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos) {893_for_all(tabs, [&](ScriptEditorDebugger *dbg) {894dbg->live_debug_reparent_node(p_at, p_new_place, p_new_name, p_at_pos);895});896}897898void EditorDebuggerNode::set_debug_mute_audio(bool p_mute) {899_for_all(tabs, [&](ScriptEditorDebugger *dbg) {900dbg->set_debug_mute_audio(p_mute);901});902debug_mute_audio = p_mute;903}904905bool EditorDebuggerNode::get_debug_mute_audio() const {906return debug_mute_audio;907}908909void EditorDebuggerNode::set_camera_override(CameraOverride p_override) {910_for_all(tabs, [&](ScriptEditorDebugger *dbg) {911dbg->set_camera_override(p_override);912});913camera_override = p_override;914}915916EditorDebuggerNode::CameraOverride EditorDebuggerNode::get_camera_override() {917return camera_override;918}919920void EditorDebuggerNode::add_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin) {921ERR_FAIL_COND_MSG(p_plugin.is_null(), "Debugger plugin is null.");922ERR_FAIL_COND_MSG(debugger_plugins.has(p_plugin), "Debugger plugin already exists.");923debugger_plugins.insert(p_plugin);924925Ref<EditorDebuggerPlugin> plugin = p_plugin;926for (int i = 0; get_debugger(i); i++) {927plugin->create_session(get_debugger(i));928}929}930931void EditorDebuggerNode::remove_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin) {932ERR_FAIL_COND_MSG(p_plugin.is_null(), "Debugger plugin is null.");933ERR_FAIL_COND_MSG(!debugger_plugins.has(p_plugin), "Debugger plugin doesn't exists.");934debugger_plugins.erase(p_plugin);935Ref<EditorDebuggerPlugin>(p_plugin)->clear();936}937938bool EditorDebuggerNode::plugins_capture(ScriptEditorDebugger *p_debugger, const String &p_message, const Array &p_data) {939int session_index = tabs->get_tab_idx_from_control(p_debugger);940ERR_FAIL_COND_V(session_index < 0, false);941int colon_index = p_message.find_char(':');942ERR_FAIL_COND_V_MSG(colon_index < 1, false, "Invalid message received.");943944const String cap = p_message.substr(0, colon_index);945bool parsed = false;946for (Ref<EditorDebuggerPlugin> plugin : debugger_plugins) {947if (plugin->has_capture(cap)) {948parsed |= plugin->capture(p_message, p_data, session_index);949}950}951return parsed;952}953954955