diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index 79ac8f12cbb..a01c3dfdbed 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -200,20 +200,30 @@ void EditorLog::_load_state() { } void EditorLog::_meta_clicked(const String &p_meta) { - Ref uri_match = RegEx(R"(^([a-zA-Z][a-zA-Z0-9+.-]*):(?://)?(.+?)(?::([0-9]+))?$)").search(p_meta); - if (uri_match.is_null()) { + if (!p_meta.contains_char(':')) { return; } + const PackedStringArray parts = p_meta.rsplit(":", true, 1); + String path = parts[0]; + const int line = parts[1].to_int() - 1; - String scheme = uri_match->get_string(1); - if (scheme == "res") { - String file = uri_match->get_string(2); - int line = (int)uri_match->get_string(3).to_int(); - if (ResourceLoader::exists(file)) { - Ref res = ResourceLoader::load(file); - ScriptEditor::get_singleton()->edit(res, line - 1, 0); + if (path.begins_with("res://")) { + if (ResourceLoader::exists(path)) { + const Ref res = ResourceLoader::load(path); + ScriptEditor::get_singleton()->edit(res, line, 0); InspectorDock::get_singleton()->edit_resource(res); } + } else if (path.has_extension("cpp") || path.has_extension("h") || path.has_extension("mm") || path.has_extension("hpp")) { + // Godot source file. Try to open it in external editor. + if (path.begins_with("./") || path.begins_with(".\\")) { + // Relative path. Convert to absolute, using executable path as reference. + path = path.trim_prefix("./").trim_prefix(".\\"); + path = OS::get_singleton()->get_executable_path().get_base_dir().get_base_dir().path_join(path); + } + + if (!ScriptEditorPlugin::open_in_external_editor(path, line, -1, true)) { + OS::get_singleton()->shell_open(path); + } } else { OS::get_singleton()->shell_open(p_meta); } diff --git a/editor/script/script_editor_plugin.cpp b/editor/script/script_editor_plugin.cpp index 0cd7df012a0..a42b539eda7 100644 --- a/editor/script/script_editor_plugin.cpp +++ b/editor/script/script_editor_plugin.cpp @@ -2554,63 +2554,11 @@ bool ScriptEditor::edit(const Ref &p_resource, int p_line, int p_col, if (use_external_editor && (EditorDebuggerNode::get_singleton()->get_dump_stack_script() != p_resource || EditorDebuggerNode::get_singleton()->get_debug_with_external_editor()) && p_resource->get_path().is_resource_file()) { - String path = EDITOR_GET("text_editor/external/exec_path"); - String flags = EDITOR_GET("text_editor/external/exec_flags"); - - List args; - bool has_file_flag = false; - String script_path = ProjectSettings::get_singleton()->globalize_path(p_resource->get_path()); - - if (flags.size()) { - String project_path = ProjectSettings::get_singleton()->get_resource_path(); - - flags = flags.replacen("{line}", itos(MAX(p_line + 1, 1))); - flags = flags.replacen("{col}", itos(p_col + 1)); - flags = flags.strip_edges().replace("\\\\", "\\"); - - int from = 0; - int num_chars = 0; - bool inside_quotes = false; - - for (int i = 0; i < flags.size(); i++) { - if (flags[i] == '"' && (!i || flags[i - 1] != '\\')) { - if (!inside_quotes) { - from++; - } - inside_quotes = !inside_quotes; - - } else if (flags[i] == '\0' || (!inside_quotes && flags[i] == ' ')) { - String arg = flags.substr(from, num_chars); - if (arg.contains("{file}")) { - has_file_flag = true; - } - - // do path replacement here, else there will be issues with spaces and quotes - arg = arg.replacen("{project}", project_path); - arg = arg.replacen("{file}", script_path); - args.push_back(arg); - - from = i + 1; - num_chars = 0; - } else { - num_chars++; - } - } + if (ScriptEditorPlugin::open_in_external_editor(ProjectSettings::get_singleton()->globalize_path(p_resource->get_path()), p_line, p_col)) { + return false; + } else { + ERR_PRINT("Couldn't open external text editor, falling back to the internal editor. Review your `text_editor/external/` editor settings."); } - - // Default to passing script path if no {file} flag is specified. - if (!has_file_flag) { - args.push_back(script_path); - } - - if (!path.is_empty()) { - Error err = OS::get_singleton()->create_process(path, args); - if (err == OK) { - return false; - } - } - - ERR_PRINT("Couldn't open external text editor, falling back to the internal editor. Review your `text_editor/external/` editor settings."); } for (int i = 0; i < tab_container->get_tab_count(); i++) { @@ -4629,6 +4577,63 @@ void ScriptEditorPlugin::_notification(int p_what) { } } +bool ScriptEditorPlugin::open_in_external_editor(const String &p_path, int p_line, int p_col, bool p_ignore_project) { + const String path = EDITOR_GET("text_editor/external/exec_path"); + if (path.is_empty()) { + return false; + } + + String flags = EDITOR_GET("text_editor/external/exec_flags"); + + List args; + bool has_file_flag = false; + + if (!flags.is_empty()) { + flags = flags.replacen("{line}", itos(MAX(p_line + 1, 1))); + flags = flags.replacen("{col}", itos(p_col + 1)); + flags = flags.strip_edges().replace("\\\\", "\\"); + + int from = 0; + int num_chars = 0; + bool inside_quotes = false; + + for (int i = 0; i < flags.size(); i++) { + if (flags[i] == '"' && (!i || flags[i - 1] != '\\')) { + if (!inside_quotes) { + from++; + } + inside_quotes = !inside_quotes; + + } else if (flags[i] == '\0' || (!inside_quotes && flags[i] == ' ')) { + String arg = flags.substr(from, num_chars); + if (arg.contains("{file}")) { + has_file_flag = true; + } + + // Do path replacement here, else there will be issues with spaces and quotes + if (p_ignore_project) { + arg = arg.replacen("{project}", String()); + } else { + arg = arg.replacen("{project}", ProjectSettings::get_singleton()->get_resource_path()); + } + arg = arg.replacen("{file}", p_path); + args.push_back(arg); + + from = i + 1; + num_chars = 0; + } else { + num_chars++; + } + } + } + + // Default to passing script path if no {file} flag is specified. + if (!has_file_flag) { + args.push_back(p_path); + } + return OS::get_singleton()->create_process(path, args) == OK; +} + void ScriptEditorPlugin::edit(Object *p_object) { if (Object::cast_to