Path: blob/master/editor/debugger/editor_debugger_node.cpp
9903 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/object/undo_redo.h"33#include "editor/debugger/editor_debugger_plugin.h"34#include "editor/debugger/editor_debugger_tree.h"35#include "editor/debugger/script_editor_debugger.h"36#include "editor/docks/inspector_dock.h"37#include "editor/docks/scene_tree_dock.h"38#include "editor/editor_log.h"39#include "editor/editor_node.h"40#include "editor/editor_string_names.h"41#include "editor/editor_undo_redo_manager.h"42#include "editor/gui/editor_bottom_panel.h"43#include "editor/run/editor_run_bar.h"44#include "editor/script/script_editor_plugin.h"45#include "editor/settings/editor_settings.h"46#include "editor/themes/editor_theme_manager.h"47#include "scene/gui/menu_button.h"48#include "scene/gui/tab_container.h"49#include "scene/resources/packed_scene.h"5051template <typename Func>52void _for_all(TabContainer *p_node, const Func &p_func) {53for (int i = 0; i < p_node->get_tab_count(); i++) {54ScriptEditorDebugger *dbg = Object::cast_to<ScriptEditorDebugger>(p_node->get_tab_control(i));55ERR_FAIL_NULL(dbg);56p_func(dbg);57}58}5960EditorDebuggerNode *EditorDebuggerNode::singleton = nullptr;6162EditorDebuggerNode::EditorDebuggerNode() {63if (!singleton) {64singleton = this;65}6667add_theme_constant_override("margin_left", -EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles))->get_margin(SIDE_LEFT));68add_theme_constant_override("margin_right", -EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles))->get_margin(SIDE_RIGHT));69add_theme_constant_override("margin_bottom", -EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles))->get_margin(SIDE_BOTTOM));7071tabs = memnew(TabContainer);72tabs->set_tabs_visible(false);73tabs->connect("tab_changed", callable_mp(this, &EditorDebuggerNode::_debugger_changed));74add_child(tabs);7576Ref<StyleBoxEmpty> empty;77empty.instantiate();78tabs->add_theme_style_override(SceneStringName(panel), empty);7980auto_switch_remote_scene_tree = EDITOR_GET("debugger/auto_switch_to_remote_scene_tree");81_add_debugger();8283// Remote scene tree84remote_scene_tree = memnew(EditorDebuggerTree);85remote_scene_tree->connect("objects_selected", callable_mp(this, &EditorDebuggerNode::_remote_objects_requested));86remote_scene_tree->connect("selection_cleared", callable_mp(this, &EditorDebuggerNode::_remote_selection_cleared));87remote_scene_tree->connect("save_node", callable_mp(this, &EditorDebuggerNode::_save_node_requested));88remote_scene_tree->connect("button_clicked", callable_mp(this, &EditorDebuggerNode::_remote_tree_button_pressed));89SceneTreeDock::get_singleton()->add_remote_tree_editor(remote_scene_tree);90SceneTreeDock::get_singleton()->connect("remote_tree_selected", callable_mp(this, &EditorDebuggerNode::request_remote_tree));9192remote_scene_tree_timeout = EDITOR_GET("debugger/remote_scene_tree_refresh_interval");93inspect_edited_object_timeout = EDITOR_GET("debugger/remote_inspect_refresh_interval");9495if (Engine::get_singleton()->is_recovery_mode_hint()) {96return;97}9899EditorRunBar::get_singleton()->get_pause_button()->connect(SceneStringName(pressed), callable_mp(this, &EditorDebuggerNode::_paused));100}101102ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() {103ScriptEditorDebugger *node = memnew(ScriptEditorDebugger);104105int id = tabs->get_tab_count();106node->connect("stop_requested", callable_mp(this, &EditorDebuggerNode::_debugger_wants_stop).bind(id));107node->connect("stopped", callable_mp(this, &EditorDebuggerNode::_debugger_stopped).bind(id));108node->connect("stack_frame_selected", callable_mp(this, &EditorDebuggerNode::_stack_frame_selected).bind(id));109node->connect("error_selected", callable_mp(this, &EditorDebuggerNode::_error_selected).bind(id));110node->connect("breakpoint_selected", callable_mp(this, &EditorDebuggerNode::_error_selected).bind(id));111node->connect("clear_execution", callable_mp(this, &EditorDebuggerNode::_clear_execution));112node->connect("breaked", callable_mp(this, &EditorDebuggerNode::_breaked).bind(id));113node->connect("debug_data", callable_mp(this, &EditorDebuggerNode::_debug_data).bind(id));114node->connect("remote_tree_select_requested", callable_mp(this, &EditorDebuggerNode::_remote_tree_select_requested).bind(id));115node->connect("remote_tree_clear_selection_requested", callable_mp(this, &EditorDebuggerNode::_remote_tree_clear_selection_requested).bind(id));116node->connect("remote_tree_updated", callable_mp(this, &EditorDebuggerNode::_remote_tree_updated).bind(id));117node->connect("remote_objects_updated", callable_mp(this, &EditorDebuggerNode::_remote_objects_updated).bind(id));118node->connect("remote_object_property_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_property_updated).bind(id));119node->connect("remote_objects_requested", callable_mp(this, &EditorDebuggerNode::_remote_objects_requested).bind(id));120node->connect("set_breakpoint", callable_mp(this, &EditorDebuggerNode::_breakpoint_set_in_tree).bind(id));121node->connect("clear_breakpoints", callable_mp(this, &EditorDebuggerNode::_breakpoints_cleared_in_tree).bind(id));122node->connect("errors_cleared", callable_mp(this, &EditorDebuggerNode::_update_errors));123124if (tabs->get_tab_count() > 0) {125get_debugger(0)->clear_style();126}127128tabs->add_child(node);129130node->set_name(vformat(TTR("Session %d"), tabs->get_tab_count()));131if (tabs->get_tab_count() > 1) {132node->clear_style();133tabs->set_tabs_visible(true);134tabs->add_theme_style_override(SceneStringName(panel), EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("DebuggerPanel"), EditorStringName(EditorStyles)));135}136137if (!debugger_plugins.is_empty()) {138for (Ref<EditorDebuggerPlugin> plugin : debugger_plugins) {139plugin->create_session(node);140}141}142143return node;144}145146void EditorDebuggerNode::_stack_frame_selected(int p_debugger) {147const ScriptEditorDebugger *dbg = get_debugger(p_debugger);148ERR_FAIL_NULL(dbg);149if (dbg != get_current_debugger()) {150return;151}152_text_editor_stack_goto(dbg);153}154155void EditorDebuggerNode::_error_selected(const String &p_file, int p_line, int p_debugger) {156Ref<Script> s = ResourceLoader::load(p_file);157emit_signal(SNAME("goto_script_line"), s, p_line - 1);158}159160void EditorDebuggerNode::_text_editor_stack_goto(const ScriptEditorDebugger *p_debugger) {161String file = p_debugger->get_stack_script_file();162if (file.is_empty()) {163return;164}165if (file.is_resource_file()) {166stack_script = ResourceLoader::load(file);167} else {168// If the script is built-in, it can be opened only if the scene is loaded in memory.169int i = file.find("::");170int j = file.rfind_char('(', i);171if (j > -1) { // If the script is named, the string is "name (file)", so we need to extract the path.172file = file.substr(j + 1, file.find_char(')', i) - j - 1);173}174Ref<PackedScene> ps = ResourceLoader::load(file.get_slice("::", 0));175stack_script = ResourceLoader::load(file);176}177const int line = p_debugger->get_stack_script_line() - 1;178emit_signal(SNAME("goto_script_line"), stack_script, line);179emit_signal(SNAME("set_execution"), stack_script, line);180stack_script.unref(); // Why?!?181}182183void EditorDebuggerNode::_text_editor_stack_clear(const ScriptEditorDebugger *p_debugger) {184String file = p_debugger->get_stack_script_file();185if (file.is_empty()) {186return;187}188if (file.is_resource_file()) {189stack_script = ResourceLoader::load(file);190} else {191// If the script is built-in, it can be opened only if the scene is loaded in memory.192int i = file.find("::");193int j = file.rfind_char('(', i);194if (j > -1) { // If the script is named, the string is "name (file)", so we need to extract the path.195file = file.substr(j + 1, file.find_char(')', i) - j - 1);196}197Ref<PackedScene> ps = ResourceLoader::load(file.get_slice("::", 0));198stack_script = ResourceLoader::load(file);199}200emit_signal(SNAME("clear_execution"), stack_script);201stack_script.unref(); // Why?!?202}203204void EditorDebuggerNode::_bind_methods() {205// LiveDebug.206ClassDB::bind_method("live_debug_create_node", &EditorDebuggerNode::live_debug_create_node);207ClassDB::bind_method("live_debug_instantiate_node", &EditorDebuggerNode::live_debug_instantiate_node);208ClassDB::bind_method("live_debug_remove_node", &EditorDebuggerNode::live_debug_remove_node);209ClassDB::bind_method("live_debug_remove_and_keep_node", &EditorDebuggerNode::live_debug_remove_and_keep_node);210ClassDB::bind_method("live_debug_restore_node", &EditorDebuggerNode::live_debug_restore_node);211ClassDB::bind_method("live_debug_duplicate_node", &EditorDebuggerNode::live_debug_duplicate_node);212ClassDB::bind_method("live_debug_reparent_node", &EditorDebuggerNode::live_debug_reparent_node);213214ADD_SIGNAL(MethodInfo("goto_script_line"));215ADD_SIGNAL(MethodInfo("set_execution", PropertyInfo("script"), PropertyInfo(Variant::INT, "line")));216ADD_SIGNAL(MethodInfo("clear_execution", PropertyInfo("script")));217ADD_SIGNAL(MethodInfo("breaked", PropertyInfo(Variant::BOOL, "reallydid"), PropertyInfo(Variant::BOOL, "can_debug")));218ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::BOOL, "enabled")));219ADD_SIGNAL(MethodInfo("breakpoint_set_in_tree", PropertyInfo("script"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::BOOL, "enabled"), PropertyInfo(Variant::INT, "debugger")));220ADD_SIGNAL(MethodInfo("breakpoints_cleared_in_tree", PropertyInfo(Variant::INT, "debugger")));221}222223void EditorDebuggerNode::register_undo_redo(UndoRedo *p_undo_redo) {224p_undo_redo->set_method_notify_callback(_methods_changed, this);225p_undo_redo->set_property_notify_callback(_properties_changed, this);226}227228ScriptEditorDebugger *EditorDebuggerNode::get_debugger(int p_id) const {229return Object::cast_to<ScriptEditorDebugger>(tabs->get_tab_control(p_id));230}231232ScriptEditorDebugger *EditorDebuggerNode::get_previous_debugger() const {233return Object::cast_to<ScriptEditorDebugger>(tabs->get_tab_control(tabs->get_previous_tab()));234}235236ScriptEditorDebugger *EditorDebuggerNode::get_current_debugger() const {237return Object::cast_to<ScriptEditorDebugger>(tabs->get_tab_control(tabs->get_current_tab()));238}239240ScriptEditorDebugger *EditorDebuggerNode::get_default_debugger() const {241return Object::cast_to<ScriptEditorDebugger>(tabs->get_tab_control(0));242}243244String EditorDebuggerNode::get_server_uri() const {245ERR_FAIL_COND_V(server.is_null(), "");246return server->get_uri();247}248249void EditorDebuggerNode::set_keep_open(bool p_keep_open) {250keep_open = p_keep_open;251if (keep_open) {252if (server.is_null() || !server->is_active()) {253start();254}255} else {256bool found = false;257_for_all(tabs, [&](ScriptEditorDebugger *p_debugger) {258if (p_debugger->is_session_active()) {259found = true;260}261});262if (!found) {263stop();264}265}266}267268Error EditorDebuggerNode::start(const String &p_uri) {269if (Engine::get_singleton()->is_recovery_mode_hint()) {270return ERR_UNAVAILABLE;271}272273ERR_FAIL_COND_V(!p_uri.contains("://"), ERR_INVALID_PARAMETER);274if (keep_open && current_uri == p_uri && server.is_valid()) {275return OK;276}277stop(true);278current_uri = p_uri;279280server = Ref<EditorDebuggerServer>(EditorDebuggerServer::create(p_uri.substr(0, p_uri.find("://") + 3)));281const Error err = server->start(p_uri);282if (err != OK) {283return err;284}285set_process(true);286EditorNode::get_log()->add_message("--- Debugging process started ---", EditorLog::MSG_TYPE_EDITOR);287return OK;288}289290void EditorDebuggerNode::stop(bool p_force) {291if (keep_open && !p_force) {292return;293}294295remote_scene_tree_wait = false;296inspect_edited_object_wait = false;297298current_uri.clear();299if (server.is_valid()) {300server->stop();301EditorNode::get_log()->add_message("--- Debugging process stopped ---", EditorLog::MSG_TYPE_EDITOR);302303if (EditorRunBar::get_singleton()->is_movie_maker_enabled()) {304// Request attention in case the user was doing something else when movie recording is finished.305DisplayServer::get_singleton()->window_request_attention();306}307308server.unref();309}310311// Also close all debugging sessions.312_for_all(tabs, [&](ScriptEditorDebugger *dbg) {313if (dbg->is_session_active()) {314dbg->_stop_and_notify();315}316});317_break_state_changed();318breakpoints.clear();319EditorUndoRedoManager::get_singleton()->clear_history(EditorUndoRedoManager::REMOTE_HISTORY, false);320set_process(false);321}322323void EditorDebuggerNode::_notification(int p_what) {324switch (p_what) {325case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {326if (!EditorThemeManager::is_generated_theme_outdated()) {327return;328}329330if (tabs->get_tab_count() > 1) {331tabs->add_theme_style_override(SceneStringName(panel), EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("DebuggerPanel"), EditorStringName(EditorStyles)));332}333334add_theme_constant_override("margin_left", -EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles))->get_margin(SIDE_LEFT));335add_theme_constant_override("margin_right", -EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles))->get_margin(SIDE_RIGHT));336add_theme_constant_override("margin_bottom", -EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles))->get_margin(SIDE_BOTTOM));337338remote_scene_tree->update_icon_max_width();339} break;340341case NOTIFICATION_READY: {342_update_debug_options();343initializing = false;344} break;345346case NOTIFICATION_PROCESS: {347if (server.is_null()) {348return;349}350351if (!server->is_active()) {352stop();353return;354}355server->poll();356357_update_errors();358359// Remote scene tree update.360if (!remote_scene_tree_wait) {361remote_scene_tree_timeout -= get_process_delta_time();362if (remote_scene_tree_timeout < 0) {363remote_scene_tree_timeout = EDITOR_GET("debugger/remote_scene_tree_refresh_interval");364365if (remote_scene_tree->is_visible_in_tree()) {366remote_scene_tree_wait = true;367get_current_debugger()->request_remote_tree();368}369}370}371372// Remote inspector update.373if (!inspect_edited_object_wait) {374inspect_edited_object_timeout -= get_process_delta_time();375if (inspect_edited_object_timeout < 0) {376inspect_edited_object_timeout = EDITOR_GET("debugger/remote_inspect_refresh_interval");377378if (EditorDebuggerRemoteObjects *robjs = Object::cast_to<EditorDebuggerRemoteObjects>(InspectorDock::get_inspector_singleton()->get_edited_object())) {379inspect_edited_object_wait = true;380get_current_debugger()->request_remote_objects(robjs->remote_object_ids, false);381}382}383}384385// Take connections.386if (server->is_connection_available()) {387ScriptEditorDebugger *debugger = nullptr;388_for_all(tabs, [&](ScriptEditorDebugger *dbg) {389if (debugger || dbg->is_session_active()) {390return;391}392debugger = dbg;393});394if (debugger == nullptr) {395if (tabs->get_tab_count() <= 4) { // Max 4 debugging sessions active.396debugger = _add_debugger();397} else {398// We already have too many sessions, disconnecting new clients to prevent them from hanging.399server->take_connection()->close();400return; // Can't add, stop here.401}402}403404EditorRunBar::get_singleton()->get_pause_button()->set_disabled(false);405// Switch to remote tree view if so desired.406auto_switch_remote_scene_tree = (bool)EDITOR_GET("debugger/auto_switch_to_remote_scene_tree");407if (auto_switch_remote_scene_tree) {408SceneTreeDock::get_singleton()->show_remote_tree();409}410// Good to go.411SceneTreeDock::get_singleton()->show_tab_buttons();412debugger->set_editor_remote_tree(remote_scene_tree);413debugger->start(server->take_connection());414// Send breakpoints.415for (const KeyValue<Breakpoint, bool> &E : breakpoints) {416const Breakpoint &bp = E.key;417debugger->set_breakpoint(bp.source, bp.line, E.value);418} // Will arrive too late, how does the regular run work?419420debugger->update_live_edit_root();421}422} break;423}424}425426void EditorDebuggerNode::_update_errors() {427int error_count = 0;428int warning_count = 0;429_for_all(tabs, [&](ScriptEditorDebugger *dbg) {430error_count += dbg->get_error_count();431warning_count += dbg->get_warning_count();432});433434if (error_count != last_error_count || warning_count != last_warning_count) {435_for_all(tabs, [&](ScriptEditorDebugger *dbg) {436dbg->update_tabs();437});438439if (error_count == 0 && warning_count == 0) {440debugger_button->set_text(TTR("Debugger"));441debugger_button->remove_theme_color_override(SceneStringName(font_color));442debugger_button->set_button_icon(Ref<Texture2D>());443} else {444debugger_button->set_text(TTR("Debugger") + " (" + itos(error_count + warning_count) + ")");445if (error_count >= 1 && warning_count >= 1) {446debugger_button->set_button_icon(get_editor_theme_icon(SNAME("ErrorWarning")));447// Use error color to represent the highest level of severity reported.448debugger_button->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));449} else if (error_count >= 1) {450debugger_button->set_button_icon(get_editor_theme_icon(SNAME("Error")));451debugger_button->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));452} else {453debugger_button->set_button_icon(get_editor_theme_icon(SNAME("Warning")));454debugger_button->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));455}456}457last_error_count = error_count;458last_warning_count = warning_count;459}460}461462void EditorDebuggerNode::_debugger_stopped(int p_id) {463ScriptEditorDebugger *dbg = get_debugger(p_id);464ERR_FAIL_NULL(dbg);465466bool found = false;467_for_all(tabs, [&](ScriptEditorDebugger *p_debugger) {468if (p_debugger->is_session_active()) {469found = true;470}471});472if (!found) {473EditorRunBar::get_singleton()->get_pause_button()->set_pressed(false);474EditorRunBar::get_singleton()->get_pause_button()->set_disabled(true);475SceneTreeDock *dock = SceneTreeDock::get_singleton();476if (dock->is_inside_tree()) {477dock->hide_remote_tree();478dock->hide_tab_buttons();479}480EditorNode::get_singleton()->notify_all_debug_sessions_exited();481}482}483484void EditorDebuggerNode::_debugger_wants_stop(int p_id) {485// Ask editor to kill PID.486if (int pid = get_debugger(p_id)->get_remote_pid()) {487callable_mp(EditorNode::get_singleton(), &EditorNode::stop_child_process).call_deferred(pid);488}489}490491void EditorDebuggerNode::_debugger_changed(int p_tab) {492remote_scene_tree_wait = false;493inspect_edited_object_wait = false;494495if (Object *robjs = InspectorDock::get_inspector_singleton()->get_edited_object()) {496if (Object::cast_to<EditorDebuggerRemoteObjects>(robjs)) {497// Clear inspected object, you can only inspect objects in selected debugger.498// Hopefully, in the future, we will have one inspector per debugger.499EditorNode::get_singleton()->push_item(nullptr);500}501}502503if (ScriptEditorDebugger *prev_debug = get_previous_debugger()) {504prev_debug->clear_inspector();505_text_editor_stack_clear(prev_debug);506}507if (remote_scene_tree->is_visible_in_tree()) {508get_current_debugger()->request_remote_tree();509}510if (get_current_debugger()->is_breaked()) {511_text_editor_stack_goto(get_current_debugger());512}513514_break_state_changed();515}516517void EditorDebuggerNode::_debug_data(const String &p_msg, const Array &p_data, int p_debugger) {518if (p_debugger != tabs->get_current_tab()) {519return;520}521522if (p_msg == "scene:scene_tree") {523remote_scene_tree_wait = false;524} else if (p_msg == "scene:inspect_objects") {525inspect_edited_object_wait = false;526}527}528529void EditorDebuggerNode::set_script_debug_button(MenuButton *p_button) {530script_menu = p_button;531script_menu->set_text(TTRC("Debug"));532script_menu->set_switch_on_hover(true);533PopupMenu *p = script_menu->get_popup();534p->add_shortcut(ED_GET_SHORTCUT("debugger/step_into"), DEBUG_STEP);535p->add_shortcut(ED_GET_SHORTCUT("debugger/step_over"), DEBUG_NEXT);536p->add_separator();537p->add_shortcut(ED_GET_SHORTCUT("debugger/break"), DEBUG_BREAK);538p->add_shortcut(ED_GET_SHORTCUT("debugger/continue"), DEBUG_CONTINUE);539p->add_separator();540p->add_check_shortcut(ED_GET_SHORTCUT("debugger/debug_with_external_editor"), DEBUG_WITH_EXTERNAL_EDITOR);541p->connect(SceneStringName(id_pressed), callable_mp(this, &EditorDebuggerNode::_menu_option));542543_break_state_changed();544script_menu->show();545}546547void EditorDebuggerNode::_break_state_changed() {548const bool breaked = get_current_debugger()->is_breaked();549const bool can_debug = get_current_debugger()->is_debuggable();550if (breaked) { // Show debugger.551EditorNode::get_bottom_panel()->make_item_visible(this);552}553554// Update script menu.555if (!script_menu) {556return;557}558PopupMenu *p = script_menu->get_popup();559p->set_item_disabled(p->get_item_index(DEBUG_NEXT), !(breaked && can_debug));560p->set_item_disabled(p->get_item_index(DEBUG_STEP), !(breaked && can_debug));561p->set_item_disabled(p->get_item_index(DEBUG_BREAK), breaked);562p->set_item_disabled(p->get_item_index(DEBUG_CONTINUE), !breaked);563}564565void EditorDebuggerNode::_menu_option(int p_id) {566switch (p_id) {567case DEBUG_NEXT: {568debug_next();569} break;570case DEBUG_STEP: {571debug_step();572} break;573case DEBUG_BREAK: {574debug_break();575} break;576case DEBUG_CONTINUE: {577debug_continue();578} break;579case DEBUG_WITH_EXTERNAL_EDITOR: {580bool ischecked = script_menu->get_popup()->is_item_checked(script_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR));581debug_with_external_editor = !ischecked;582script_menu->get_popup()->set_item_checked(script_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR), !ischecked);583if (!initializing) {584EditorSettings::get_singleton()->set_project_metadata("debug_options", "debug_with_external_editor", !ischecked);585}586} break;587}588}589590void EditorDebuggerNode::_update_debug_options() {591if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "debug_with_external_editor", false).operator bool()) {592_menu_option(DEBUG_WITH_EXTERNAL_EDITOR);593}594}595596void EditorDebuggerNode::_paused() {597const bool paused = EditorRunBar::get_singleton()->get_pause_button()->is_pressed();598_for_all(tabs, [&](ScriptEditorDebugger *dbg) {599if (paused && !dbg->is_breaked()) {600dbg->debug_break();601} else if (!paused && dbg->is_breaked()) {602dbg->debug_continue();603}604});605}606607void EditorDebuggerNode::_breaked(bool p_breaked, bool p_can_debug, const String &p_message, bool p_has_stackdump, int p_debugger) {608if (get_current_debugger() != get_debugger(p_debugger)) {609if (!p_breaked) {610return;611}612tabs->set_current_tab(p_debugger);613}614_break_state_changed();615EditorRunBar::get_singleton()->get_pause_button()->set_pressed(p_breaked);616emit_signal(SNAME("breaked"), p_breaked, p_can_debug);617}618619bool EditorDebuggerNode::is_skip_breakpoints() const {620return get_current_debugger()->is_skip_breakpoints();621}622623bool EditorDebuggerNode::is_ignore_error_breaks() const {624return get_default_debugger()->is_ignore_error_breaks();625}626627void EditorDebuggerNode::set_breakpoint(const String &p_path, int p_line, bool p_enabled) {628breakpoints[Breakpoint(p_path, p_line)] = p_enabled;629_for_all(tabs, [&](ScriptEditorDebugger *dbg) {630dbg->set_breakpoint(p_path, p_line, p_enabled);631});632633emit_signal(SNAME("breakpoint_toggled"), p_path, p_line, p_enabled);634}635636void EditorDebuggerNode::set_breakpoints(const String &p_path, const Array &p_lines) {637for (int i = 0; i < p_lines.size(); i++) {638set_breakpoint(p_path, p_lines[i], true);639}640641for (const KeyValue<Breakpoint, bool> &E : breakpoints) {642Breakpoint b = E.key;643if (b.source == p_path && !p_lines.has(b.line)) {644set_breakpoint(p_path, b.line, false);645}646}647}648649void EditorDebuggerNode::reload_all_scripts() {650_for_all(tabs, [&](ScriptEditorDebugger *dbg) {651dbg->reload_all_scripts();652});653}654655void EditorDebuggerNode::reload_scripts(const Vector<String> &p_script_paths) {656_for_all(tabs, [&](ScriptEditorDebugger *dbg) {657dbg->reload_scripts(p_script_paths);658});659}660661void EditorDebuggerNode::debug_next() {662get_current_debugger()->debug_next();663}664665void EditorDebuggerNode::debug_step() {666get_current_debugger()->debug_step();667}668669void EditorDebuggerNode::debug_break() {670get_current_debugger()->debug_break();671}672673void EditorDebuggerNode::debug_continue() {674get_current_debugger()->debug_continue();675}676677String EditorDebuggerNode::get_var_value(const String &p_var) const {678return get_current_debugger()->get_var_value(p_var);679}680681// LiveEdit/Inspector682void EditorDebuggerNode::request_remote_tree() {683get_current_debugger()->request_remote_tree();684}685686void EditorDebuggerNode::set_remote_selection(const TypedArray<int64_t> &p_ids) {687stop_waiting_inspection();688get_current_debugger()->request_remote_objects(p_ids);689}690691void EditorDebuggerNode::clear_remote_tree_selection() {692remote_scene_tree->clear_selection();693get_current_debugger()->clear_inspector(remote_scene_tree_clear_msg);694}695696void EditorDebuggerNode::stop_waiting_inspection() {697inspect_edited_object_timeout = EDITOR_GET("debugger/remote_inspect_refresh_interval");698inspect_edited_object_wait = false;699}700701bool EditorDebuggerNode::match_remote_selection(const TypedArray<uint64_t> &p_ids) const {702return p_ids == remote_scene_tree->get_selection();703}704705void EditorDebuggerNode::_remote_tree_select_requested(const TypedArray<int64_t> &p_ids, int p_debugger) {706if (p_debugger == tabs->get_current_tab()) {707remote_scene_tree->select_nodes(p_ids);708}709}710711void EditorDebuggerNode::_remote_tree_clear_selection_requested(int p_debugger) {712if (p_debugger != tabs->get_current_tab()) {713return;714}715remote_scene_tree->clear_selection();716remote_scene_tree_clear_msg = false;717get_current_debugger()->clear_inspector(false);718remote_scene_tree_clear_msg = true;719}720721void EditorDebuggerNode::_remote_tree_updated(int p_debugger) {722if (p_debugger != tabs->get_current_tab()) {723return;724}725remote_scene_tree->clear();726remote_scene_tree->update_scene_tree(get_current_debugger()->get_remote_tree(), p_debugger);727}728729void EditorDebuggerNode::_remote_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) {730if (p_button != MouseButton::LEFT) {731return;732}733734TreeItem *item = Object::cast_to<TreeItem>(p_item);735ERR_FAIL_NULL(item);736737if (p_id == EditorDebuggerTree::BUTTON_SUBSCENE) {738remote_scene_tree->emit_signal(SNAME("open"), item->get_meta("scene_file_path"));739} else if (p_id == EditorDebuggerTree::BUTTON_VISIBILITY) {740ObjectID obj_id = item->get_metadata(0);741ERR_FAIL_COND(obj_id.is_null());742get_current_debugger()->update_remote_object(obj_id, "visible", !item->get_meta("visible"));743get_current_debugger()->request_remote_tree();744}745}746747void EditorDebuggerNode::_remote_objects_updated(EditorDebuggerRemoteObjects *p_objs, int p_debugger) {748if (p_debugger == tabs->get_current_tab() && p_objs != InspectorDock::get_inspector_singleton()->get_edited_object()) {749EditorNode::get_singleton()->push_item(p_objs);750}751}752753void EditorDebuggerNode::_remote_object_property_updated(ObjectID p_id, const String &p_property, int p_debugger) {754if (p_debugger != tabs->get_current_tab()) {755return;756}757758Object *obj = InspectorDock::get_inspector_singleton()->get_edited_object();759if (obj && obj->get_instance_id() == p_id) {760InspectorDock::get_inspector_singleton()->update_property(p_property);761}762}763764void EditorDebuggerNode::_remote_objects_requested(const TypedArray<uint64_t> &p_ids, int p_debugger) {765if (p_debugger != tabs->get_current_tab()) {766return;767}768stop_waiting_inspection();769get_current_debugger()->request_remote_objects(p_ids);770}771772void EditorDebuggerNode::_remote_selection_cleared(int p_debugger) {773if (p_debugger != tabs->get_current_tab()) {774return;775}776stop_waiting_inspection();777get_current_debugger()->clear_inspector();778}779780void EditorDebuggerNode::_save_node_requested(ObjectID p_id, const String &p_file, int p_debugger) {781if (p_debugger != tabs->get_current_tab()) {782return;783}784get_current_debugger()->save_node(p_id, p_file);785}786787void EditorDebuggerNode::_breakpoint_set_in_tree(Ref<RefCounted> p_script, int p_line, bool p_enabled, int p_debugger) {788if (p_debugger != tabs->get_current_tab()) {789return;790}791792emit_signal(SNAME("breakpoint_set_in_tree"), p_script, p_line, p_enabled);793}794795void EditorDebuggerNode::_breakpoints_cleared_in_tree(int p_debugger) {796if (p_debugger != tabs->get_current_tab()) {797return;798}799800emit_signal(SNAME("breakpoints_cleared_in_tree"));801}802803// Remote inspector/edit.804void EditorDebuggerNode::_methods_changed(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount) {805if (!singleton) {806return;807}808_for_all(singleton->tabs, [&](ScriptEditorDebugger *dbg) {809dbg->_method_changed(p_base, p_name, p_args, p_argcount);810});811}812813void EditorDebuggerNode::_properties_changed(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value) {814if (!singleton) {815return;816}817_for_all(singleton->tabs, [&](ScriptEditorDebugger *dbg) {818dbg->_property_changed(p_base, p_property, p_value);819});820}821822// LiveDebug823void EditorDebuggerNode::set_live_debugging(bool p_enabled) {824_for_all(tabs, [&](ScriptEditorDebugger *dbg) {825dbg->set_live_debugging(p_enabled);826});827}828829void EditorDebuggerNode::update_live_edit_root() {830_for_all(tabs, [&](ScriptEditorDebugger *dbg) {831dbg->update_live_edit_root();832});833}834835void EditorDebuggerNode::live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name) {836_for_all(tabs, [&](ScriptEditorDebugger *dbg) {837dbg->live_debug_create_node(p_parent, p_type, p_name);838});839}840841void EditorDebuggerNode::live_debug_instantiate_node(const NodePath &p_parent, const String &p_path, const String &p_name) {842_for_all(tabs, [&](ScriptEditorDebugger *dbg) {843dbg->live_debug_instantiate_node(p_parent, p_path, p_name);844});845}846847void EditorDebuggerNode::live_debug_remove_node(const NodePath &p_at) {848_for_all(tabs, [&](ScriptEditorDebugger *dbg) {849dbg->live_debug_remove_node(p_at);850});851}852853void EditorDebuggerNode::live_debug_remove_and_keep_node(const NodePath &p_at, ObjectID p_keep_id) {854_for_all(tabs, [&](ScriptEditorDebugger *dbg) {855dbg->live_debug_remove_and_keep_node(p_at, p_keep_id);856});857}858859void EditorDebuggerNode::live_debug_restore_node(ObjectID p_id, const NodePath &p_at, int p_at_pos) {860_for_all(tabs, [&](ScriptEditorDebugger *dbg) {861dbg->live_debug_restore_node(p_id, p_at, p_at_pos);862});863}864865void EditorDebuggerNode::live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name) {866_for_all(tabs, [&](ScriptEditorDebugger *dbg) {867dbg->live_debug_duplicate_node(p_at, p_new_name);868});869}870871void EditorDebuggerNode::live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos) {872_for_all(tabs, [&](ScriptEditorDebugger *dbg) {873dbg->live_debug_reparent_node(p_at, p_new_place, p_new_name, p_at_pos);874});875}876877void EditorDebuggerNode::set_debug_mute_audio(bool p_mute) {878_for_all(tabs, [&](ScriptEditorDebugger *dbg) {879dbg->set_debug_mute_audio(p_mute);880});881debug_mute_audio = p_mute;882}883884bool EditorDebuggerNode::get_debug_mute_audio() const {885return debug_mute_audio;886}887888void EditorDebuggerNode::set_camera_override(CameraOverride p_override) {889_for_all(tabs, [&](ScriptEditorDebugger *dbg) {890dbg->set_camera_override(p_override);891});892camera_override = p_override;893}894895EditorDebuggerNode::CameraOverride EditorDebuggerNode::get_camera_override() {896return camera_override;897}898899void EditorDebuggerNode::add_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin) {900ERR_FAIL_COND_MSG(p_plugin.is_null(), "Debugger plugin is null.");901ERR_FAIL_COND_MSG(debugger_plugins.has(p_plugin), "Debugger plugin already exists.");902debugger_plugins.insert(p_plugin);903904Ref<EditorDebuggerPlugin> plugin = p_plugin;905for (int i = 0; get_debugger(i); i++) {906plugin->create_session(get_debugger(i));907}908}909910void EditorDebuggerNode::remove_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin) {911ERR_FAIL_COND_MSG(p_plugin.is_null(), "Debugger plugin is null.");912ERR_FAIL_COND_MSG(!debugger_plugins.has(p_plugin), "Debugger plugin doesn't exists.");913debugger_plugins.erase(p_plugin);914Ref<EditorDebuggerPlugin>(p_plugin)->clear();915}916917bool EditorDebuggerNode::plugins_capture(ScriptEditorDebugger *p_debugger, const String &p_message, const Array &p_data) {918int session_index = tabs->get_tab_idx_from_control(p_debugger);919ERR_FAIL_COND_V(session_index < 0, false);920int colon_index = p_message.find_char(':');921ERR_FAIL_COND_V_MSG(colon_index < 1, false, "Invalid message received.");922923const String cap = p_message.substr(0, colon_index);924bool parsed = false;925for (Ref<EditorDebuggerPlugin> plugin : debugger_plugins) {926if (plugin->has_capture(cap)) {927parsed |= plugin->capture(p_message, p_data, session_index);928}929}930return parsed;931}932933934