Merge pull request #119047 from jinyangcruise/fix_near_jump_issue

Fix jumping to definition does not set proper return point if the definition is very close
This commit is contained in:
Thaddeus Crews
2026-06-18 13:15:31 -05:00
14 changed files with 633 additions and 174 deletions
+2 -2
View File
@@ -68,13 +68,13 @@
</signal>
<signal name="request_save_history">
<description>
Emitted when the user contextual goto and the item is in the same script.
Emitted when the editor requests to save a new history navigation point.
</description>
</signal>
<signal name="request_save_previous_state">
<param index="0" name="state" type="Dictionary" />
<description>
Emitted when the user changes current script or moves caret by 10 or more columns within the same script.
Emitted when the editor requests an update to its navigation point.
</description>
</signal>
<signal name="search_in_files_requested">
+62 -10
View File
@@ -54,6 +54,7 @@
#include "editor/file_system/editor_paths.h"
#include "editor/gui/editor_toaster.h"
#include "editor/inspector/editor_property_name_processor.h"
#include "editor/script/script_editor_navigation_marker.h"
#include "editor/script/script_editor_plugin.h"
#include "editor/script/syntax_highlighters.h"
#include "editor/settings/editor_settings.h"
@@ -270,13 +271,14 @@ void EditorHelp::_search(bool p_search_previous) {
void EditorHelp::_class_desc_finished() {
if (scroll_to >= 0) {
class_desc->connect(SceneStringName(draw), callable_mp(class_desc, &RichTextLabel::scroll_to_paragraph).bind(scroll_to), CONNECT_ONE_SHOT | CONNECT_DEFERRED);
class_desc->connect(SceneStringName(draw), callable_mp(this, &EditorHelp::_class_desc_scroll_to_paragraph).bind(scroll_to, need_save_new_history), CONNECT_ONE_SHOT | CONNECT_DEFERRED);
}
scroll_to = -1;
need_save_new_history = false;
}
void EditorHelp::_class_list_select(const String &p_select) {
_goto_desc(p_select);
_goto_desc(p_select, true);
}
void EditorHelp::_class_desc_select(const String &p_select) {
@@ -344,12 +346,14 @@ void EditorHelp::_class_desc_select(const String &p_select) {
// Case order is important here to correctly handle edge cases like `Variant.Type` in `@GlobalScope`.
if (table->has(link)) {
// Found in the current page.
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
if (class_desc->is_finished()) {
emit_signal(SNAME("request_save_history"));
class_desc->scroll_to_paragraph((*table)[link]);
_class_desc_scroll_to_paragraph((*table)[link], _need_save_new_history());
} else {
scroll_to = (*table)[link];
need_save_new_history = _need_save_new_history();
}
ScriptEditorNavigationMarker::get_singleton()->locate_end();
} else {
// Look for link in `@GlobalScope`.
if (topic == "class_enum") {
@@ -726,7 +730,7 @@ void EditorHelp::_pop_code_font() {
class_desc->pop(); // font
}
Error EditorHelp::_goto_desc(const String &p_class) {
Error EditorHelp::_goto_desc(const String &p_class, bool p_can_trigger_save_history) {
if (!doc->class_list.has(p_class)) {
return ERR_DOES_NOT_EXIST;
}
@@ -738,11 +742,17 @@ Error EditorHelp::_goto_desc(const String &p_class) {
description_line = 0;
if (p_class == edited_class) {
if (p_can_trigger_save_history) {
trigger_history_save_on_navigate();
}
return OK; // Already there.
}
edited_class = p_class;
_update_doc();
if (p_can_trigger_save_history) {
trigger_history_save_on_navigate();
}
return OK;
}
@@ -2344,7 +2354,7 @@ void EditorHelp::_update_doc() {
}
void EditorHelp::_request_help(const String &p_string) {
Error err = _goto_desc(p_string);
Error err = _goto_desc(p_string, false);
if (err == OK) {
EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT);
}
@@ -2432,12 +2442,36 @@ void EditorHelp::_help_callback(const String &p_topic) {
}
if (class_desc->is_finished()) {
class_desc->scroll_to_paragraph(line);
_class_desc_scroll_to_paragraph(line, _need_save_new_history());
} else {
scroll_to = line;
need_save_new_history = _need_save_new_history();
}
}
void EditorHelp::_class_desc_scroll_to_paragraph(int p_line, bool p_save_history) {
// Save history before scrolling.
if (p_save_history) {
Dictionary state = get_state();
// Row 0 is not a state worth saving as a previous state to history.
if (int(state["row"]) > 0) {
emit_signal(SNAME("_request_save_new_history"), state);
}
}
class_desc->scroll_to_paragraph(p_line);
// Save history after scrolling.
if (p_save_history) {
emit_signal(SNAME("_request_save_new_history"), get_state());
if (ScriptEditorNavigationMarker::get_singleton()->is_locating()) {
ScriptEditorNavigationMarker::get_singleton()->locate_end();
}
}
}
bool EditorHelp::_need_save_new_history() const {
return !ScriptEditorNavigationMarker::get_singleton()->is_initializing() && ScriptEditorNavigationMarker::get_singleton()->is_locating();
}
static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt, const Control *p_owner_node, const String &p_class) {
bool is_native = false;
{
@@ -3352,7 +3386,7 @@ void EditorHelp::go_to_help(const String &p_help) {
void EditorHelp::go_to_class(const String &p_class) {
_wait_for_thread();
_goto_desc(p_class);
_goto_desc(p_class, true);
}
void EditorHelp::update_doc() {
@@ -3362,6 +3396,21 @@ void EditorHelp::update_doc() {
_update_doc();
}
void EditorHelp::trigger_history_save_on_navigate() {
if (_need_save_new_history()) {
emit_signal(SNAME("_request_save_new_history"), get_state());
if (ScriptEditorNavigationMarker::get_singleton()->is_locating()) {
ScriptEditorNavigationMarker::get_singleton()->locate_end();
}
}
}
Dictionary EditorHelp::get_state() {
Dictionary state;
state["row"] = get_scroll(); // Use "row" to simplify the logic when processing history list.
return state;
}
void EditorHelp::cleanup_doc() {
_wait_for_thread();
memdelete(doc);
@@ -3380,12 +3429,15 @@ Vector<Pair<String, int>> EditorHelp::get_sections() {
void EditorHelp::scroll_to_section(int p_section_index) {
_wait_for_thread();
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
int line = section_line[p_section_index].second;
if (class_desc->is_finished()) {
class_desc->scroll_to_paragraph(line);
_class_desc_scroll_to_paragraph(line, _need_save_new_history());
} else {
scroll_to = line;
need_save_new_history = _need_save_new_history();
}
ScriptEditorNavigationMarker::get_singleton()->locate_end();
}
void EditorHelp::popup_search() {
@@ -3425,7 +3477,7 @@ void EditorHelp::_bind_methods() {
ClassDB::bind_method("_help_callback", &EditorHelp::_help_callback);
ADD_SIGNAL(MethodInfo("go_to_help"));
ADD_SIGNAL(MethodInfo("request_save_history"));
ADD_SIGNAL(MethodInfo("_request_save_new_history", PropertyInfo(Variant::DICTIONARY, "state")));
}
void EditorHelp::init_gdext_pointers() {
+6 -1
View File
@@ -148,8 +148,11 @@ class EditorHelp : public VBoxContainer {
} theme_cache;
int scroll_to = -1;
bool need_save_new_history = false;
void _help_callback(const String &p_topic);
void _class_desc_scroll_to_paragraph(int p_line, bool p_save_history);
bool _need_save_new_history() const;
void _add_text(const String &p_bbcode);
bool scroll_locked = false;
@@ -175,7 +178,7 @@ class EditorHelp : public VBoxContainer {
void _class_desc_resized(bool p_force_update_theme);
int display_margin = 0;
Error _goto_desc(const String &p_class);
Error _goto_desc(const String &p_class, bool p_can_trigger_save_history);
//void _update_history_buttons();
void _update_method_list(MethodType p_method_type, const Vector<DocData::MethodDoc> &p_methods);
void _update_method_descriptions(const DocData::ClassDoc &p_classdoc, MethodType p_method_type, const Vector<DocData::MethodDoc> &p_methods);
@@ -249,6 +252,8 @@ public:
void go_to_help(const String &p_help);
void go_to_class(const String &p_class);
void update_doc();
void trigger_history_save_on_navigate();
Dictionary get_state();
Vector<Pair<String, int>> get_sections();
void scroll_to_section(int p_section_index);
+45 -13
View File
@@ -37,6 +37,7 @@
#include "core/string/string_builder.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/script/script_editor_navigation_marker.h"
#include "editor/script/syntax_highlighters.h"
#include "editor/settings/editor_settings.h"
#include "editor/themes/editor_scale.h"
@@ -88,7 +89,9 @@ void GotoLinePopup::_goto_line() {
}
void GotoLinePopup::_submit() {
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
_goto_line();
ScriptEditorNavigationMarker::get_singleton()->locate_end();
hide();
}
@@ -1387,12 +1390,21 @@ void CodeTextEditor::center_viewport_to_caret_if_line_invisible(int p_line) {
text_editor->call_on_all_layout_pending_finished(callable_mp(this, &CodeTextEditor::center_viewport_to_caret_if_line_invisible).bind(0));
return;
}
if (!text_editor->is_line_in_viewport(p_line)) {
if (!text_editor->is_line_in_viewport(CLAMP(p_line, 0, text_editor->get_line_count() - 1))) {
text_editor->center_viewport_to_caret();
}
}
void CodeTextEditor::goto_line(int p_line, int p_column) {
void CodeTextEditor::trigger_history_save_on_navigate() {
if (!ScriptEditorNavigationMarker::get_singleton()->is_initializing() && ScriptEditorNavigationMarker::get_singleton()->is_locating()) {
call_on_all_layout_pending_finished(callable_mp(this, &CodeTextEditor::_emit_request_save_new_history));
if (ScriptEditorNavigationMarker::get_singleton()->is_locating()) {
ScriptEditorNavigationMarker::get_singleton()->locate_end();
}
}
}
void CodeTextEditor::goto_line_without_history(int p_line, int p_column) {
text_editor->remove_secondary_carets();
text_editor->deselect();
text_editor->unfold_line(CLAMP(p_line, 0, text_editor->get_line_count() - 1));
@@ -1403,6 +1415,11 @@ void CodeTextEditor::goto_line(int p_line, int p_column) {
adjust_viewport_to_caret();
}
void CodeTextEditor::goto_line(int p_line, int p_column) {
goto_line_without_history(p_line, p_column);
trigger_history_save_on_navigate();
}
void CodeTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) {
text_editor->remove_secondary_carets();
text_editor->unfold_line(CLAMP(p_line, 0, text_editor->get_line_count() - 1));
@@ -1410,6 +1427,7 @@ void CodeTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) {
text_editor->set_code_hint("");
text_editor->cancel_code_completion();
adjust_viewport_to_caret();
trigger_history_save_on_navigate();
}
void CodeTextEditor::goto_line_centered(int p_line, int p_column) {
@@ -1421,6 +1439,15 @@ void CodeTextEditor::goto_line_centered(int p_line, int p_column) {
text_editor->set_code_hint("");
text_editor->cancel_code_completion();
center_viewport_to_caret();
trigger_history_save_on_navigate();
}
void CodeTextEditor::goto_line_and_center_if_necessary(int p_line, int p_column) {
if (!text_editor->is_line_in_viewport(CLAMP(p_line, 0, text_editor->get_line_count() - 1))) {
goto_line_centered(p_line, p_column);
} else {
goto_line(p_line, p_column);
}
}
void CodeTextEditor::set_executing_line(int p_line) {
@@ -1431,7 +1458,7 @@ void CodeTextEditor::clear_executing_line() {
text_editor->clear_executing_lines();
}
Variant CodeTextEditor::get_edit_state() {
Dictionary CodeTextEditor::get_edit_state() {
Dictionary state;
state.merge(get_navigation_state());
@@ -1445,7 +1472,7 @@ Variant CodeTextEditor::get_edit_state() {
return state;
}
Variant CodeTextEditor::get_previous_state() {
Dictionary CodeTextEditor::get_previous_state() {
return previous_state;
}
@@ -1467,7 +1494,7 @@ void CodeTextEditor::set_preview_navigation_change(bool p_preview) {
}
}
void CodeTextEditor::set_edit_state(const Variant &p_state) {
void CodeTextEditor::set_edit_state(const Dictionary &p_state) {
Dictionary state = p_state;
/* update the row first as it sets the column to 0 */
@@ -1478,17 +1505,17 @@ void CodeTextEditor::set_edit_state(const Variant &p_state) {
} else {
text_editor->set_caret_line(state["row"]);
text_editor->set_caret_column(state["column"]);
if (int(state["scroll_position"]) == -1) {
// Special case for previous state.
center_viewport_to_caret();
} else {
text_editor->set_v_scroll(state["scroll_position"]);
}
text_editor->set_v_scroll(state["scroll_position"]);
text_editor->set_h_scroll(state["h_scroll_position"]);
}
if (state.get("selection", false)) {
text_editor->select(state["selection_from_line"], state["selection_from_column"], state["selection_to_line"], state["selection_to_column"]);
// Selection can be set in two ways, from->to or to->from, so we need to check the order to set it correctly.
if (state["row"] < state["selection_to_line"] || state["column"] < state["selection_to_column"]) {
text_editor->select(state["selection_to_line"], state["selection_to_column"], state["selection_from_line"], state["selection_from_column"]);
} else {
text_editor->select(state["selection_from_line"], state["selection_from_column"], state["selection_to_line"], state["selection_to_column"]);
}
} else {
text_editor->deselect();
}
@@ -1525,7 +1552,7 @@ void CodeTextEditor::set_edit_state(const Variant &p_state) {
}
}
Variant CodeTextEditor::get_navigation_state() {
Dictionary CodeTextEditor::get_navigation_state() {
Dictionary state;
state["scroll_position"] = text_editor->get_v_scroll();
@@ -1915,6 +1942,10 @@ void CodeTextEditor::_show_goto_popup_request() {
emit_signal("show_goto_popup");
}
void CodeTextEditor::_emit_request_save_new_history() {
emit_signal(SNAME("_request_save_new_history"));
}
void CodeTextEditor::_bind_methods() {
ADD_SIGNAL(MethodInfo("validate_script"));
ADD_SIGNAL(MethodInfo("load_theme_settings"));
@@ -1923,6 +1954,7 @@ void CodeTextEditor::_bind_methods() {
ADD_SIGNAL(MethodInfo("show_goto_popup"));
ADD_SIGNAL(MethodInfo("navigation_preview_ended"));
ADD_SIGNAL(MethodInfo("zoomed", PropertyInfo(Variant::FLOAT, "zoom_factor")));
ADD_SIGNAL(MethodInfo("_request_save_new_history", PropertyInfo(Variant::DICTIONARY, "state")));
}
void CodeTextEditor::set_code_complete_func(CodeTextEditorCodeCompleteFunc p_code_complete_func, void *p_ud) {
+8 -4
View File
@@ -211,6 +211,7 @@ class CodeTextEditor : public VBoxContainer {
void _zoom_to(float p_zoom_factor);
void _show_goto_popup_request();
void _emit_request_save_new_history();
void _update_error_content_height();
@@ -260,17 +261,20 @@ public:
void adjust_viewport_to_caret();
void center_viewport_to_caret();
void center_viewport_to_caret_if_line_invisible(int p_line);
void trigger_history_save_on_navigate();
void goto_line_without_history(int p_line, int p_column = 0);
void goto_line(int p_line, int p_column = 0);
void goto_line_selection(int p_line, int p_begin, int p_end);
void goto_line_centered(int p_line, int p_column = 0);
void goto_line_and_center_if_necessary(int p_line, int p_column = 0);
void set_executing_line(int p_line);
void clear_executing_line();
Variant get_edit_state();
void set_edit_state(const Variant &p_state);
Variant get_navigation_state();
Variant get_previous_state();
Dictionary get_edit_state();
void set_edit_state(const Dictionary &p_state);
Dictionary get_navigation_state();
Dictionary get_previous_state();
void store_previous_state();
bool is_previewing_navigation_change() const;
+27 -1
View File
@@ -34,6 +34,7 @@
#include "core/object/callable_mp.h"
#include "core/object/class_db.h"
#include "editor/editor_node.h"
#include "editor/script/script_editor_navigation_marker.h"
#include "editor/script/script_editor_plugin.h"
#include "editor/script/syntax_highlighters.h"
#include "editor/settings/editor_settings.h"
@@ -53,10 +54,11 @@ void ScriptEditorBase::_bind_methods() {
// First use in ScriptTextEditor.
ADD_SIGNAL(MethodInfo("request_save_history"));
ADD_SIGNAL(MethodInfo("_request_save_new_history", PropertyInfo(Variant::DICTIONARY, "state")));
ADD_SIGNAL(MethodInfo("request_save_previous_state", PropertyInfo(Variant::DICTIONARY, "state")));
ADD_SIGNAL(MethodInfo("request_help", PropertyInfo(Variant::STRING, "topic")));
ADD_SIGNAL(MethodInfo("request_open_script_at_line", PropertyInfo(Variant::OBJECT, "script"), PropertyInfo(Variant::INT, "line")));
ADD_SIGNAL(MethodInfo("go_to_help", PropertyInfo(Variant::STRING, "what")));
ADD_SIGNAL(MethodInfo("request_save_previous_state", PropertyInfo(Variant::DICTIONARY, "state")));
ADD_SIGNAL(MethodInfo("replace_in_files_requested", PropertyInfo(Variant::STRING, "text")));
ADD_SIGNAL(MethodInfo("go_to_method", PropertyInfo(Variant::OBJECT, "script"), PropertyInfo(Variant::STRING, "method")));
}
@@ -174,7 +176,9 @@ void TextEditorBase::EditMenus::_bookmark_item_pressed(int p_idx) {
if (p_idx < 4) { // Any item before the separator.
script_text_editor->_edit_option(bookmarks_menu->get_item_id(p_idx));
} else {
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
script_text_editor->code_editor->goto_line_centered(bookmarks_menu->get_item_metadata(p_idx));
ScriptEditorNavigationMarker::get_singleton()->locate_end();
}
}
@@ -488,10 +492,14 @@ bool TextEditorBase::_edit_option(int p_op) {
code_editor->toggle_bookmark();
} break;
case BOOKMARK_GOTO_NEXT: {
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
code_editor->goto_next_bookmark();
ScriptEditorNavigationMarker::get_singleton()->locate_end();
} break;
case BOOKMARK_GOTO_PREV: {
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
code_editor->goto_prev_bookmark();
ScriptEditorNavigationMarker::get_singleton()->locate_end();
} break;
case BOOKMARK_REMOVE_ALL: {
code_editor->remove_all_bookmarks();
@@ -524,6 +532,20 @@ void TextEditorBase::_validate_script() {
emit_signal(SNAME("edited_script_changed"));
}
void TextEditorBase::_emit_request_save_new_history() {
Dictionary state = get_edit_state();
state["ensure_caret_visible"] = true;
previous_history_line = state["row"];
emit_signal(SNAME("_request_save_new_history"), state);
}
void TextEditorBase::_emit_request_save_previous_state() {
Dictionary state = get_edit_state();
state["ensure_caret_visible"] = true;
previous_history_line = state["row"];
emit_signal(SNAME("request_save_previous_state"), state);
}
void TextEditorBase::add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) {
ERR_FAIL_COND(p_highlighter.is_null());
@@ -631,6 +653,9 @@ void TextEditorBase::set_edit_state(const Variant &p_state) {
code_editor->set_edit_state(p_state);
Dictionary state = p_state;
if (state.has("row")) {
previous_history_line = state["row"];
}
if (state.has("syntax_highlighter")) {
for (const Ref<EditorSyntaxHighlighter> &highlighter : highlighters) {
if (highlighter->_get_name() == String(state["syntax_highlighter"])) {
@@ -653,6 +678,7 @@ TextEditorBase::TextEditorBase() {
code_editor->connect("validate_script", callable_mp(this, &TextEditorBase::_validate_script));
code_editor->connect("load_theme_settings", callable_mp(this, &TextEditorBase::_load_theme_settings));
code_editor->connect("show_goto_popup", callable_mp(this, &TextEditorBase::_edit_option).bind(SEARCH_GOTO_LINE));
code_editor->connect("_request_save_new_history", callable_mp(this, &TextEditorBase::_emit_request_save_new_history));
context_menu = memnew(PopupMenu);
context_menu->connect(SceneStringName(id_pressed), callable_mp(this, &TextEditorBase::_edit_option));
+7
View File
@@ -182,6 +182,10 @@ protected:
void _convert_case(CodeTextEditor::CaseStyle p_case) { code_editor->convert_case(p_case); }
int previous_history_line = -1;
void _emit_request_save_new_history();
void _emit_request_save_previous_state();
public:
virtual void add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) override;
virtual void set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter);
@@ -208,9 +212,12 @@ public:
virtual void trim_final_newlines() { code_editor->trim_final_newlines(); }
virtual void insert_final_newline() { code_editor->insert_final_newline(); }
virtual void goto_line_without_history(int p_line, int p_column = 0) { code_editor->goto_line_without_history(p_line, p_column); }
virtual void goto_line(int p_line, int p_column = 0) { code_editor->goto_line(p_line, p_column); }
virtual void goto_line_selection(int p_line, int p_begin, int p_end) { code_editor->goto_line_selection(p_line, p_begin, p_end); }
virtual void goto_line_centered(int p_line, int p_column = 0) { code_editor->goto_line_centered(p_line, p_column); }
virtual void goto_line_and_center_if_necessary(int p_line, int p_column = 0) { code_editor->goto_line_and_center_if_necessary(p_line, p_column); }
virtual void trigger_history_save_on_navigate() { code_editor->trigger_history_save_on_navigate(); }
virtual void set_executing_line(int p_line) { code_editor->set_executing_line(p_line); }
virtual void clear_executing_line() { code_editor->clear_executing_line(); }
@@ -0,0 +1,105 @@
/**************************************************************************/
/* script_editor_navigation_marker.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "script_editor_navigation_marker.h"
#include "core/config/engine.h"
#include "core/os/memory.h"
ScriptEditorNavigationMarker *ScriptEditorNavigationMarker::get_singleton() {
if (!singleton) {
singleton = memnew(ScriptEditorNavigationMarker);
}
return singleton;
}
void ScriptEditorNavigationMarker::release_singleton() {
if (!singleton) {
return;
}
memdelete(singleton);
singleton = nullptr;
}
void ScriptEditorNavigationMarker::init_begin() {
init_in_progress = true;
}
void ScriptEditorNavigationMarker::init_end() {
init_in_progress = false;
}
void ScriptEditorNavigationMarker::locate_begin() {
locate_in_progress = true;
}
void ScriptEditorNavigationMarker::locate_end() {
locate_in_progress = false;
locate_end_physics_frame = Engine::get_singleton()->get_physics_frames();
locate_end_process_frame = Engine::get_singleton()->get_process_frames();
}
void ScriptEditorNavigationMarker::traverse_begin() {
traverse_in_progress = true;
}
void ScriptEditorNavigationMarker::traverse_end() {
traverse_in_progress = false;
traverse_end_physics_frame = Engine::get_singleton()->get_physics_frames();
traverse_end_process_frame = Engine::get_singleton()->get_process_frames();
}
bool ScriptEditorNavigationMarker::is_initializing() const {
return init_in_progress;
}
bool ScriptEditorNavigationMarker::is_locating() const {
return locate_in_progress;
}
bool ScriptEditorNavigationMarker::is_traversing() const {
return traverse_in_progress;
}
bool ScriptEditorNavigationMarker::is_locate_just_occured() const {
if (Engine::get_singleton()->is_in_physics_frame()) {
return locate_end_physics_frame == Engine::get_singleton()->get_physics_frames() || locate_end_physics_frame == Engine::get_singleton()->get_physics_frames() - 1;
} else {
return locate_end_process_frame == Engine::get_singleton()->get_process_frames();
}
}
bool ScriptEditorNavigationMarker::is_traverse_just_occured() const {
if (Engine::get_singleton()->is_in_physics_frame()) {
return traverse_end_physics_frame == Engine::get_singleton()->get_physics_frames() || traverse_end_physics_frame == Engine::get_singleton()->get_physics_frames() - 1;
} else {
return traverse_end_process_frame == Engine::get_singleton()->get_process_frames();
}
}
@@ -0,0 +1,66 @@
/**************************************************************************/
/* script_editor_navigation_marker.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include <cstdint>
class ScriptEditorNavigationMarker {
static inline ScriptEditorNavigationMarker *singleton = nullptr;
private:
bool init_in_progress = false;
bool locate_in_progress = false;
uint64_t locate_end_physics_frame = 0;
uint64_t locate_end_process_frame = 0;
bool traverse_in_progress = false;
uint64_t traverse_end_physics_frame = 0;
uint64_t traverse_end_process_frame = 0;
public:
static ScriptEditorNavigationMarker *get_singleton();
static void release_singleton();
void init_begin();
void init_end();
void locate_begin();
void locate_end();
void traverse_begin();
void traverse_end();
bool is_initializing() const;
bool is_locating() const;
bool is_traversing() const;
bool is_locate_just_occured() const;
bool is_traverse_just_occured() const;
};
+230 -114
View File
@@ -65,6 +65,7 @@
#include "editor/run/editor_run_bar.h"
#include "editor/scene/editor_scene_tabs.h"
#include "editor/script/find_in_files.h"
#include "editor/script/script_editor_navigation_marker.h"
#include "editor/script/script_text_editor.h"
#include "editor/script/syntax_highlighters.h"
#include "editor/script/text_editor.h"
@@ -196,7 +197,9 @@ void ScriptEditor::_script_created(Ref<Script> p_script) {
void ScriptEditor::_goto_script_line2(int p_line) {
if (TextEditorBase *current = Object::cast_to<TextEditorBase>(_get_current_editor())) {
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
current->goto_line(p_line);
ScriptEditorNavigationMarker::get_singleton()->locate_end();
}
}
@@ -307,57 +310,139 @@ void ScriptEditor::_update_history_arrows() {
script_forward->set_disabled(history_pos >= history.size() - 1);
}
void ScriptEditor::_save_history() {
if (history_pos >= 0 && history_pos < history.size() && history[history_pos].control == tab_container->get_current_tab_control()) {
Node *n = tab_container->get_current_tab_control();
if (Object::cast_to<TextEditorBase>(n)) {
Dictionary nav_state = Object::cast_to<TextEditorBase>(n)->get_navigation_state();
nav_state["ensure_caret_visible"] = true;
history.write[history_pos].state = nav_state;
}
if (Object::cast_to<EditorHelp>(n)) {
history.write[history_pos].state = Object::cast_to<EditorHelp>(n)->get_scroll();
}
// For compatibility with the legacy exposed signal request_save_history.
void ScriptEditor::_save_history(Control *p_control) {
Dictionary nav_state;
if (Object::cast_to<TextEditorBase>(p_control)) {
nav_state.merge(Object::cast_to<TextEditorBase>(p_control)->get_navigation_state());
nav_state["ensure_caret_visible"] = true;
} else if (Object::cast_to<EditorHelp>(p_control)) {
nav_state.merge(Object::cast_to<EditorHelp>(p_control)->get_state());
}
history.resize(history_pos + 1);
ScriptHistory sh;
sh.control = tab_container->get_current_tab_control();
sh.state = Variant();
history.push_back(sh);
history_pos++;
_update_history_arrows();
if (nav_state.is_empty() || !nav_state.has("row")) {
return;
}
_save_new_history(nav_state, p_control);
}
void ScriptEditor::_save_previous_state(Dictionary p_state) {
if (lock_history) {
// Done as a result of a deferred call triggered by set_edit_state().
void ScriptEditor::_save_new_history(const Dictionary &p_state, Control *p_control) {
if (restoring_layout) {
return;
}
if (history_pos >= 0 && history_pos < history.size() && history[history_pos].control == tab_container->get_current_tab_control()) {
Node *n = tab_container->get_current_tab_control();
if (Object::cast_to<ScriptTextEditor>(n)) {
history.write[history_pos].state = p_state;
if (history_pos >= 0 && history_pos < history.size()) {
if (history[history_pos].control == p_control) {
if (history[history_pos].state == p_state) {
return;
}
if (p_state["row"] == history[history_pos].state["row"]) {
_save_previous_state(p_state, p_control);
return;
}
}
}
history.resize(history_pos + 1);
ScriptHistory sh;
sh.control = tab_container->get_current_tab_control();
sh.state = Variant();
sh.control = p_control;
sh.state = p_state;
history.resize(history_pos + 1);
history.push_back(sh);
history_pos++;
_compress_history_patterns(true);
for (int i = history.size() - 1; i >= 0; i--) {
if (history[i].control == tab_container->get_current_tab_control()) {
history_pos = i;
break;
}
}
_update_history_arrows();
}
void ScriptEditor::_go_to_tab(int p_idx) {
void ScriptEditor::_save_previous_state(const Dictionary &p_state, Control *p_control) {
if (history_pos < 0 || history_pos >= history.size()) {
return;
}
ScriptHistory sh = history[history_pos];
if (sh.control != p_control) {
return;
}
history.write[history_pos].state = p_state;
}
// Compress the history and remove duplicate patterns.
// Example 1: If the history is ...ABAB..., it will be compressed to ...AB....
// Example 2: If the history is ...ABCABC..., it will be compressed to ...ABC....
void ScriptEditor::_compress_history_patterns(bool p_once) {
bool stop = false;
bool changed = true;
int iterations = 0;
while (!stop && changed && history.size() > 1 && iterations++ < 100) {
changed = false;
for (int end_idx = history.size() - 1; end_idx >= 1; end_idx--) {
bool found_duplicate = false;
int max_possible_len = (end_idx + 1) / 2;
for (int len = 1; len <= max_possible_len; len++) {
// Compare [first_start, first_start + len) and [second_start, second_start + len)
int second_start = end_idx - len + 1;
int first_start = second_start - len;
if (first_start < 0) {
continue;
}
bool is_match = true;
for (int k = 0; k < len; k++) {
const ScriptHistory &h1 = history[first_start + k];
const ScriptHistory &h2 = history[second_start + k];
if (h1.control != h2.control || h1.state["row"] != h2.state["row"]) {
is_match = false;
break;
}
}
if (is_match) {
for (int r = 0; r < len; r++) {
history.remove_at(first_start);
}
if (history_pos >= second_start) {
history_pos -= len;
} else if (first_start <= history_pos && history_pos < second_start) {
history_pos = first_start;
}
found_duplicate = true;
changed = true;
stop = p_once;
break;
}
}
if (found_duplicate) {
break;
}
}
}
if (history.is_empty()) {
history_pos = -1;
} else {
if (history_pos >= history.size()) {
history_pos = history.size() - 1;
}
if (history_pos < -1) {
history_pos = -1;
}
}
}
void ScriptEditor::_go_to_tab(int p_idx, bool p_save_history) {
if (ScriptEditorBase *current = _get_current_editor()) {
if (current->is_unsaved()) {
current->apply_code();
@@ -369,29 +454,6 @@ void ScriptEditor::_go_to_tab(int p_idx) {
return;
}
if (history_pos >= 0 && history_pos < history.size() && history[history_pos].control == tab_container->get_current_tab_control()) {
Node *n = tab_container->get_current_tab_control();
if (Object::cast_to<TextEditorBase>(n)) {
Dictionary nav_state = Object::cast_to<TextEditorBase>(n)->get_navigation_state();
nav_state["ensure_caret_visible"] = true;
history.write[history_pos].state = nav_state;
}
if (Object::cast_to<EditorHelp>(n)) {
history.write[history_pos].state = Object::cast_to<EditorHelp>(n)->get_scroll();
}
}
history.resize(history_pos + 1);
ScriptHistory sh;
sh.control = c;
sh.state = Variant();
if (!lock_history && (history.is_empty() || history[history.size() - 1].control != sh.control)) {
history.push_back(sh);
history_pos++;
}
tab_container->set_current_tab(p_idx);
c = tab_container->get_current_tab_control();
@@ -402,6 +464,12 @@ void ScriptEditor::_go_to_tab(int p_idx) {
teb->ensure_focus();
}
if (p_save_history) {
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
teb->trigger_history_save_on_navigate();
ScriptEditorNavigationMarker::get_singleton()->locate_end();
}
Ref<Script> scr = seb->get_edited_resource();
if (scr.is_valid()) {
notify_script_changed(scr);
@@ -417,6 +485,12 @@ void ScriptEditor::_go_to_tab(int p_idx) {
if (is_visible_in_tree()) {
eh->set_focused();
}
if (p_save_history) {
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
eh->trigger_history_save_on_navigate();
ScriptEditorNavigationMarker::get_singleton()->locate_end();
}
}
c->set_meta("__editor_pass", ++edit_pass);
@@ -521,7 +595,7 @@ void ScriptEditor::_show_error_dialog(const String &p_path) {
error_dialog->popup_centered();
}
void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
void ScriptEditor::_close_tab(int p_idx, bool p_save) {
int selected = p_idx;
if (selected < 0 || selected >= tab_container->get_tab_count()) {
return;
@@ -551,14 +625,10 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
}
}
// roll back to previous tab
if (p_history_back) {
_history_back();
}
//remove from history
history.resize(history_pos + 1);
// Roll back to previous tab instead of just previous history state
_roll_back_to_pre_tab();
// Remove all history states related to the closed tab.
for (int i = 0; i < history.size(); i++) {
if (history[i].control == tselected) {
history.remove_at(i);
@@ -567,9 +637,7 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
}
}
if (history_pos >= history.size()) {
history_pos = history.size() - 1;
}
_compress_history_patterns(false);
int idx = tab_container->get_current_tab();
if (TextEditorBase *current = Object::cast_to<TextEditorBase>(tselected)) {
@@ -585,7 +653,7 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
if (history_pos >= 0) {
idx = tab_container->get_tab_idx_from_control(history[history_pos].control);
}
_go_to_tab(idx);
_go_to_tab(idx, true);
} else {
_update_selected_editor_menu();
_update_online_doc();
@@ -600,8 +668,8 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
}
}
void ScriptEditor::_close_current_tab(bool p_save, bool p_history_back) {
_close_tab(tab_container->get_current_tab(), p_save, p_history_back);
void ScriptEditor::_close_current_tab(bool p_save) {
_close_tab(tab_container->get_current_tab(), p_save);
}
void ScriptEditor::_close_discard_current_tab(const String &p_str) {
@@ -617,7 +685,7 @@ void ScriptEditor::_close_docs_tab() {
int child_count = tab_container->get_tab_count();
for (int i = child_count - 1; i >= 0; i--) {
if (Object::cast_to<EditorHelp>(tab_container->get_tab_control(i))) {
_close_tab(i, true, false);
_close_tab(i, true);
}
}
}
@@ -652,7 +720,7 @@ void ScriptEditor::_close_tabs_below() {
for (int i = tab_container->get_tab_count() - 1; i > current_idx; i--) {
script_close_queue.push_back(i);
}
_go_to_tab(current_idx);
_go_to_tab(current_idx, true);
_queue_close_tabs();
}
@@ -679,7 +747,7 @@ void ScriptEditor::_queue_close_tabs() {
}
}
_close_current_tab(false, false);
_close_current_tab(false);
}
_update_find_replace_bar();
}
@@ -1663,7 +1731,7 @@ void ScriptEditor::_help_overview_selected(int p_idx) {
void ScriptEditor::_script_selected(int p_idx) {
grab_focus_block = !Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT); //amazing hack, simply amazing
_go_to_tab(script_list->get_item_metadata(p_idx));
_go_to_tab(script_list->get_item_metadata(p_idx), true);
script_name_button->set_text(script_list->get_item_text(p_idx));
_calculate_script_name_button_size();
grab_focus_block = false;
@@ -2037,10 +2105,8 @@ void ScriptEditor::_update_script_names() {
sedata.set(i, sd);
}
lock_history = true;
_go_to_tab(new_prev_tab);
_go_to_tab(new_cur_tab);
lock_history = false;
_sort_list_on_update = false;
}
@@ -2249,11 +2315,15 @@ bool ScriptEditor::edit(const Ref<Resource> &p_resource, int p_line, int p_col,
teb->ensure_focus();
}
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
if (p_line >= 0) {
teb->goto_line_centered(p_line, p_col);
teb->goto_line_and_center_if_necessary(p_line, p_col);
} else {
teb->trigger_history_save_on_navigate();
}
ScriptEditorNavigationMarker::get_singleton()->locate_end();
} else if (tab_container->get_current_tab() != i) {
_go_to_tab(i);
_go_to_tab(i, true);
}
}
_update_script_names();
@@ -2346,8 +2416,9 @@ bool ScriptEditor::edit(const Ref<Resource> &p_resource, int p_line, int p_col,
teb->connect("request_help", callable_mp(this, &ScriptEditor::_help_search));
teb->connect("request_open_script_at_line", callable_mp(this, &ScriptEditor::_goto_script_line));
teb->connect("go_to_help", callable_mp(this, &ScriptEditor::_help_class_goto));
teb->connect("request_save_history", callable_mp(this, &ScriptEditor::_save_history));
teb->connect("request_save_previous_state", callable_mp(this, &ScriptEditor::_save_previous_state));
teb->connect("request_save_history", callable_mp(this, &ScriptEditor::_save_history).bind(teb));
teb->connect("_request_save_new_history", callable_mp(this, &ScriptEditor::_save_new_history).bind(teb));
teb->connect("request_save_previous_state", callable_mp(this, &ScriptEditor::_save_previous_state).bind(teb));
teb->connect("search_in_files_requested", callable_mp(this, &ScriptEditor::open_find_in_files_dialog).bind(false));
teb->connect("replace_in_files_requested", callable_mp(this, &ScriptEditor::open_find_in_files_dialog).bind(true));
teb->connect("go_to_method", callable_mp(this, &ScriptEditor::script_goto_method));
@@ -2376,9 +2447,13 @@ bool ScriptEditor::edit(const Ref<Resource> &p_resource, int p_line, int p_col,
_update_modified_scripts_for_external_editor(p_resource);
if (TextEditorBase *teb = Object::cast_to<TextEditorBase>(seb)) {
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
if (p_line >= 0) {
teb->goto_line_centered(p_line, p_col);
teb->goto_line_and_center_if_necessary(p_line, p_col);
} else {
teb->trigger_history_save_on_navigate();
}
ScriptEditorNavigationMarker::get_singleton()->locate_end();
}
notify_script_changed(p_resource);
@@ -2650,7 +2725,7 @@ Error ScriptEditor::close_file(const String &p_file) {
if (seb->is_unsaved()) {
seb->get_edited_resource()->reload_from_file();
}
_close_tab(i, false, _get_current_editor() == seb);
_close_tab(i, false);
return OK;
}
}
@@ -2676,7 +2751,7 @@ void ScriptEditor::_add_callback(Object *p_obj, const String &p_function, const
ste->add_callback(p_function, p_args);
_go_to_tab(i);
_go_to_tab(i, true);
script_list->select(script_list->find_metadata(i));
@@ -2788,7 +2863,7 @@ void ScriptEditor::_file_removed(const String &p_removed_file) {
ScriptEditorBase *seb = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i));
if (seb && seb->edited_file_data.path == p_removed_file) {
// The script is deleted with no undo, so just close the tab.
_close_tab(i, false, false);
_close_tab(i, false);
}
}
@@ -3078,7 +3153,7 @@ void ScriptEditor::shortcut_input(const Ref<InputEvent> &p_event) {
if (script_list->get_item_count() > 1) {
int next_tab = script_list->get_current() + 1;
next_tab %= script_list->get_item_count();
_go_to_tab(script_list->get_item_metadata(next_tab));
_go_to_tab(script_list->get_item_metadata(next_tab), true);
_update_script_names();
}
accept_event();
@@ -3087,7 +3162,7 @@ void ScriptEditor::shortcut_input(const Ref<InputEvent> &p_event) {
if (script_list->get_item_count() > 1) {
int next_tab = script_list->get_current() - 1;
next_tab = next_tab >= 0 ? next_tab : script_list->get_item_count() - 1;
_go_to_tab(script_list->get_item_metadata(next_tab));
_go_to_tab(script_list->get_item_metadata(next_tab), true);
_update_script_names();
}
accept_event();
@@ -3211,6 +3286,7 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
HashSet<String> loaded_scripts;
List<String> extensions = _get_recognized_extensions();
ScriptEditorNavigationMarker::get_singleton()->init_begin();
for (const Variant &v : scripts) {
String path = v;
@@ -3311,18 +3387,36 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
restoring_layout = false;
_update_script_names();
ScriptEditorNavigationMarker::get_singleton()->init_end();
bool selected_saved_history = false;
if (p_layout->has_section_key("ScriptEditor", "selected_script")) {
String selected_script = p_layout->get_value("ScriptEditor", "selected_script");
// If the selected script is not in the list of open scripts, select nothing.
for (int i = 0; i < tab_container->get_tab_count(); i++) {
for (int i = 0; i < tab_container->get_tab_count() && !selected_script.is_empty(); i++) {
ScriptEditorBase *seb = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i));
if (seb && seb->get_edited_resource()->get_path() == selected_script) {
_go_to_tab(i);
_go_to_tab(i, true);
selected_saved_history = true;
break;
}
}
}
if (!selected_saved_history && tab_container->get_child_count() > 0) {
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
Control *tselected = tab_container->get_current_tab_control();
TextEditorBase *teb = Object::cast_to<TextEditorBase>(tselected);
if (teb && teb->get_code_editor()) {
teb->get_code_editor()->trigger_history_save_on_navigate();
}
EditorHelp *eh = Object::cast_to<EditorHelp>(tselected);
if (eh) {
eh->trigger_history_save_on_navigate();
}
ScriptEditorNavigationMarker::get_singleton()->locate_end();
}
}
void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) {
@@ -3369,7 +3463,7 @@ void ScriptEditor::_help_class_open(const String &p_class) {
EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_tab_control(i));
if (eh && eh->get_class() == p_class) {
_go_to_tab(i);
_go_to_tab(i, true);
_update_script_names();
return;
}
@@ -3378,11 +3472,13 @@ void ScriptEditor::_help_class_open(const String &p_class) {
EditorHelp *eh = memnew(EditorHelp);
eh->set_name(p_class);
eh->connect("go_to_help", callable_mp(this, &ScriptEditor::_help_class_goto));
eh->connect("_request_save_new_history", callable_mp(this, &ScriptEditor::_save_new_history).bind(eh));
tab_container->add_child(eh);
_go_to_tab(tab_container->get_tab_count() - 1);
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
eh->go_to_class(p_class);
eh->connect("go_to_help", callable_mp(this, &ScriptEditor::_help_class_goto));
eh->connect("request_save_history", callable_mp(this, &ScriptEditor::_save_history));
ScriptEditorNavigationMarker::get_singleton()->locate_end();
_add_recent_script(p_class);
_sort_list_on_update = true;
_update_script_names();
@@ -3399,10 +3495,13 @@ void ScriptEditor::_help_class_goto(const String &p_desc) {
EditorHelp *eh = memnew(EditorHelp);
eh->set_name(cname);
eh->connect("go_to_help", callable_mp(this, &ScriptEditor::_help_class_goto));
eh->connect("_request_save_new_history", callable_mp(this, &ScriptEditor::_save_new_history).bind(eh));
tab_container->add_child(eh);
_go_to_tab(tab_container->get_tab_count() - 1);
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
eh->go_to_help(p_desc);
eh->connect("go_to_help", callable_mp(this, &ScriptEditor::_help_class_goto));
ScriptEditorNavigationMarker::get_singleton()->locate_end();
_add_recent_script(eh->get_class());
_sort_list_on_update = true;
_update_script_names();
@@ -3415,7 +3514,9 @@ bool ScriptEditor::_help_tab_goto(const String &p_name, const String &p_desc) {
if (eh && eh->get_class() == p_name) {
_go_to_tab(i);
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
eh->go_to_help(p_desc);
ScriptEditorNavigationMarker::get_singleton()->locate_end();
_update_script_names();
return true;
}
@@ -3479,22 +3580,8 @@ void ScriptEditor::_update_selected_editor_menu() {
}
}
void ScriptEditor::_unlock_history() {
lock_history = false;
}
void ScriptEditor::_update_history_pos(int p_new_pos) {
Node *n = tab_container->get_current_tab_control();
if (Object::cast_to<TextEditorBase>(n)) {
Dictionary nav_state = Object::cast_to<TextEditorBase>(n)->get_navigation_state();
nav_state["ensure_caret_visible"] = true;
history.write[history_pos].state = nav_state;
}
if (Object::cast_to<EditorHelp>(n)) {
history.write[history_pos].state = Object::cast_to<EditorHelp>(n)->get_scroll();
}
history_pos = p_new_pos;
tab_container->set_current_tab(tab_container->get_tab_idx_from_control(history[history_pos].control));
@@ -3503,12 +3590,7 @@ void ScriptEditor::_update_history_pos(int p_new_pos) {
ScriptEditorBase *seb = Object::cast_to<ScriptEditorBase>(n);
if (seb) {
if (TextEditorBase *teb = Object::cast_to<TextEditorBase>(n)) {
lock_history = true;
teb->set_edit_state(history[history_pos].state);
// `set_edit_state()` can modify the caret position which might trigger a
// request to save the history. Since `TextEdit::caret_changed` is emitted
// deferred, we need to defer unlocking of the history as well.
callable_mp(this, &ScriptEditor::_unlock_history).call_deferred();
teb->ensure_focus();
}
@@ -3521,7 +3603,7 @@ void ScriptEditor::_update_history_pos(int p_new_pos) {
}
if (EditorHelp *eh = Object::cast_to<EditorHelp>(n)) {
eh->set_scroll(history[history_pos].state);
eh->set_scroll(history[history_pos].state["row"]);
eh->set_focused();
}
@@ -3533,13 +3615,40 @@ void ScriptEditor::_update_history_pos(int p_new_pos) {
void ScriptEditor::_history_forward() {
if (history_pos < history.size() - 1) {
ScriptEditorNavigationMarker::get_singleton()->traverse_begin();
_update_history_pos(history_pos + 1);
ScriptEditorNavigationMarker::get_singleton()->traverse_end();
}
}
void ScriptEditor::_history_back() {
if (history_pos > 0) {
ScriptEditorNavigationMarker::get_singleton()->traverse_begin();
_update_history_pos(history_pos - 1);
ScriptEditorNavigationMarker::get_singleton()->traverse_end();
}
}
void ScriptEditor::_roll_back_to_pre_tab() {
Control *tselected = tab_container->get_current_tab_control();
if (history[history_pos].control != tselected) {
return;
}
int pos = -1;
for (int i = history_pos - 1; i >= 0; i--) {
if (history[i].control != tselected) {
pos = i;
break;
}
}
if (pos == -1) {
history_pos = -1;
history.clear();
} else {
_update_history_pos(pos);
history.resize(history_pos + 1);
}
}
@@ -3659,6 +3768,7 @@ void ScriptEditor::_script_changed() {
}
void ScriptEditor::_on_find_in_files_result_selected(const String &fpath, int line_number, int begin, int end) {
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
if (ResourceLoader::exists(fpath)) {
Ref<Resource> res = ResourceLoader::load(fpath);
@@ -3670,10 +3780,12 @@ void ScriptEditor::_on_find_in_files_result_selected(const String &fpath, int li
if (text_shader_editor) {
text_shader_editor->goto_line_selection(line_number - 1, begin, end);
}
ScriptEditorNavigationMarker::get_singleton()->locate_end();
return;
} else if (fpath.has_extension("tscn")) {
const PackedStringArray lines = FileAccess::get_file_as_string(fpath).split("\n");
if (line_number > lines.size()) {
ScriptEditorNavigationMarker::get_singleton()->locate_end();
return;
}
@@ -3714,6 +3826,7 @@ void ScriptEditor::_on_find_in_files_result_selected(const String &fpath, int li
}
}
ScriptEditorNavigationMarker::get_singleton()->locate_end();
return;
} else {
Ref<Script> scr = res;
@@ -3726,6 +3839,7 @@ void ScriptEditor::_on_find_in_files_result_selected(const String &fpath, int li
EditorInterface::get_singleton()->set_main_screen_editor("Script");
ste->goto_line_selection(line_number - 1, begin, end);
}
ScriptEditorNavigationMarker::get_singleton()->locate_end();
return;
}
}
@@ -3742,6 +3856,7 @@ void ScriptEditor::_on_find_in_files_result_selected(const String &fpath, int li
te->goto_line_selection(line_number - 1, begin, end);
}
}
ScriptEditorNavigationMarker::get_singleton()->locate_end();
}
void ScriptEditor::_on_find_in_files_modified_files() {
@@ -4183,6 +4298,7 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
ScriptEditor::~ScriptEditor() {
memdelete(find_in_files);
ScriptEditorNavigationMarker::release_singleton();
}
void ScriptEditorPlugin::_focus_another_editor() {
+9 -8
View File
@@ -216,7 +216,7 @@ class ScriptEditor : public PanelContainer {
struct ScriptHistory {
Control *control = nullptr;
Variant state;
Dictionary state;
};
Vector<ScriptHistory> history;
@@ -252,10 +252,10 @@ class ScriptEditor : public PanelContainer {
void _show_error_dialog(const String &p_path);
void _close_tab(int p_idx, bool p_save = true, bool p_history_back = true);
void _close_tab(int p_idx, bool p_save = true);
void _update_find_replace_bar();
void _close_current_tab(bool p_save = true, bool p_history_back = true);
void _close_current_tab(bool p_save = true);
void _close_discard_current_tab(const String &p_str);
void _close_docs_tab();
void _close_other_tabs();
@@ -355,18 +355,19 @@ class ScriptEditor : public PanelContainer {
void _history_forward();
void _history_back();
void _roll_back_to_pre_tab();
bool waiting_update_names;
bool lock_history = false;
void _unlock_history();
void _help_class_open(const String &p_class);
void _help_class_goto(const String &p_desc);
bool _help_tab_goto(const String &p_name, const String &p_desc);
void _update_history_arrows();
void _save_history();
void _save_previous_state(Dictionary p_state);
void _go_to_tab(int p_idx);
void _save_history(Control *p_control);
void _save_new_history(const Dictionary &p_state, Control *p_control);
void _save_previous_state(const Dictionary &p_state, Control *p_control);
void _compress_history_patterns(bool p_once);
void _go_to_tab(int p_idx, bool p_save_history = false);
void _update_history_pos(int p_new_pos);
void _update_script_colors();
void _update_modified_scripts_for_external_editor(Ref<Script> p_for_script = Ref<Script>());
+27 -16
View File
@@ -49,6 +49,7 @@
#include "editor/inspector/editor_context_menu_plugin.h"
#include "editor/inspector/editor_inspector.h"
#include "editor/inspector/multi_node_edit.h"
#include "editor/script/script_editor_navigation_marker.h"
#include "editor/script/syntax_highlighters.h"
#include "editor/settings/editor_command_palette.h"
#include "editor/settings/editor_settings.h"
@@ -177,7 +178,9 @@ void ScriptTextEditor::EditMenusSTE::_breakpoint_item_pressed(int p_idx) {
if (p_idx < 4) { // Any item before the separator.
_edit_option(breakpoints_menu->get_item_id(p_idx));
} else {
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
script_text_editor->get_code_editor()->goto_line_centered(breakpoints_menu->get_item_metadata(p_idx));
ScriptEditorNavigationMarker::get_singleton()->locate_end();
}
}
@@ -460,6 +463,7 @@ void ScriptTextEditor::add_callback(const String &p_function, const PackedString
code_editor->get_text_editor()->set_caret_line(pos, true, true, -1);
code_editor->get_text_editor()->set_caret_column(indent_column);
code_editor->get_text_editor()->end_complex_operation();
code_editor->center_viewport_to_caret();
}
bool ScriptTextEditor::_is_valid_color_info(const Dictionary &p_info) {
@@ -775,6 +779,9 @@ void ScriptTextEditor::set_edit_state(const Variant &p_state) {
}
Dictionary state = p_state;
if (state.has("row")) {
previous_history_line = state["row"];
}
if (state.has("syntax_highlighter")) {
for (const Ref<EditorSyntaxHighlighter> &highlighter : highlighters) {
if (highlighter->_get_name() == String(state["syntax_highlighter"])) {
@@ -1166,6 +1173,9 @@ void ScriptTextEditor::_breakpoint_toggled(int p_row) {
}
void ScriptTextEditor::_on_caret_moved() {
if (ScriptEditorNavigationMarker::get_singleton()->is_locate_just_occured() || ScriptEditorNavigationMarker::get_singleton()->is_traverse_just_occured() || ScriptEditorNavigationMarker::get_singleton()->is_locating() || ScriptEditorNavigationMarker::get_singleton()->is_traversing()) {
return;
}
if (code_editor->is_previewing_navigation_change()) {
return;
}
@@ -1173,21 +1183,17 @@ void ScriptTextEditor::_on_caret_moved() {
call_on_all_layout_pending_finished(callable_mp(this, &ScriptTextEditor::_on_caret_moved));
return;
}
// When previous_line < 0, it means the user has just switched to this editor from a different one
// (which already saved a state in the history). In this case, we should not save this editor's previous state.
int current_line = code_editor->get_text_editor()->get_caret_line();
if (previous_line >= 0 && Math::abs(current_line - previous_line) >= 10) {
Dictionary nav_state = get_navigation_state();
nav_state["row"] = previous_line;
nav_state["scroll_position"] = -1;
nav_state["ensure_caret_visible"] = true;
emit_signal(SNAME("request_save_previous_state"), nav_state);
if (Math::abs(current_line - previous_history_line) >= 10) {
_emit_request_save_new_history();
store_previous_state();
} else {
_emit_request_save_previous_state();
}
previous_line = current_line;
}
void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_column) {
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
Ref<Script> script = edited_res;
Node *base = get_tree()->get_edited_scene_root();
if (base) {
@@ -1206,7 +1212,7 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
EditorNode::get_singleton()->load_scene_or_resource(p_symbol);
}
} else if (lc_error == OK) {
_goto_line(p_row);
goto_line_without_history(p_row, p_column);
if (!result.class_name.is_empty() && EditorHelp::get_doc_data()->class_list.has(result.class_name) && !EditorHelp::get_doc_data()->class_list[result.class_name].is_script_doc) {
switch (result.type) {
@@ -1285,7 +1291,6 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
if (result.script.is_valid()) {
emit_signal(SNAME("request_open_script_at_line"), result.script, result.location - 1);
} else {
emit_signal(SNAME("request_save_history"));
goto_line_centered(result.location - 1);
}
}
@@ -1302,6 +1307,7 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
EditorNode::get_singleton()->load_scene_or_resource(path);
}
}
ScriptEditorNavigationMarker::get_singleton()->locate_end();
}
void ScriptTextEditor::_validate_symbol(const String &p_symbol) {
@@ -1460,6 +1466,12 @@ String ScriptTextEditor::_get_absolute_path(const String &rel_path) {
return path.replace("///", "//").simplify_path();
}
void ScriptTextEditor::_goto_line(int p_line) {
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
goto_line(p_line);
ScriptEditorNavigationMarker::get_singleton()->locate_end();
}
void ScriptTextEditor::_update_connected_methods() {
CodeEdit *text_edit = code_editor->get_text_editor();
text_edit->set_gutter_width(connection_gutter, text_edit->get_line_height());
@@ -1807,7 +1819,9 @@ bool ScriptTextEditor::_edit_option(int p_op) {
bpoint_idx++;
}
}
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
code_editor->goto_line_centered(bpoints[bpoint_idx]);
ScriptEditorNavigationMarker::get_singleton()->locate_end();
} break;
case DEBUG_GOTO_PREV_BREAKPOINT: {
PackedInt32Array bpoints = tx->get_breakpointed_lines();
@@ -1822,7 +1836,9 @@ bool ScriptTextEditor::_edit_option(int p_op) {
bpoint_idx--;
}
}
ScriptEditorNavigationMarker::get_singleton()->locate_begin();
code_editor->goto_line_centered(bpoints[bpoint_idx]);
ScriptEditorNavigationMarker::get_singleton()->locate_end();
} break;
case SHOW_TOOLTIP_AT_CARET: {
_show_symbol_tooltip(tx->get_word_under_caret(), tx->get_caret_line(), tx->get_caret_column(), true);
@@ -1903,11 +1919,6 @@ void ScriptTextEditor::_notification(int p_what) {
case NOTIFICATION_DRAG_END: {
drag_info_label->hide();
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
if (!is_visible()) {
previous_line = -1;
}
} break;
}
}
+1 -2
View File
@@ -93,7 +93,6 @@ class ScriptTextEditor : public CodeEditorBase {
Color marked_line_color = Color(1, 1, 1);
Color warning_line_color = Color(1, 1, 1);
Color folded_code_region_color = Color(1, 1, 1);
int previous_line = -1; // Previous caret line number when user continuously operates in this editor. Affects history state. Reset to -1 on editor switch.
PopupPanel *color_panel = nullptr;
ColorPicker *color_picker = nullptr;
@@ -203,7 +202,7 @@ protected:
String _get_absolute_path(const String &rel_path);
void _goto_line(int p_line) { goto_line(p_line); }
void _goto_line(int p_line);
void _make_context_menu(bool p_selection, bool p_color, bool p_foldable, bool p_open_docs, bool p_goto_definition, const Vector2 &p_pos);
+38 -3
View File
@@ -56,6 +56,36 @@ STATIC_ASSERT_INCOMPLETE_TYPE(class, RenderingServer);
#include "editor/scene/gui/control_editor_plugin.h"
#endif // TOOLS_ENABLED
class LayoutRecheckCallable : public CallableCustom {
private:
ObjectID owner_id;
Callable callback;
uint32_t h;
static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) { return p_a == p_b; }
static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b) { return p_a < p_b; }
public:
virtual bool is_valid() const override { return callback.is_valid(); }
virtual uint32_t hash() const override { return h; }
virtual String get_as_text() const override { return "LayoutRecheckCallable"; }
virtual CompareEqualFunc get_compare_equal_func() const override { return compare_equal; }
virtual CompareLessFunc get_compare_less_func() const override { return compare_less; }
virtual ObjectID get_object() const override { return ObjectID(); }
virtual StringName get_method() const override { return StringName(); }
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override {
Control *owner = Object::cast_to<Control>(ObjectDB::get_instance(owner_id));
if (owner && !owner->is_queued_for_deletion()) {
owner->call_on_all_layout_pending_finished(callback);
}
}
LayoutRecheckCallable(ObjectID p_owner_id, const Callable &p_callback) :
owner_id(p_owner_id), callback(p_callback) {
h = (uint32_t)hash_murmur3_one_64((uint64_t)this);
}
};
// Editor plugin interoperability.
// TODO: Decouple controls from their editor plugin and get rid of this.
@@ -2116,11 +2146,16 @@ Control *Control::get_layout_pending_control_in_tree() const {
void Control::call_on_all_layout_pending_finished(const Callable &p_callable) {
Control *pending_control = get_layout_pending_control_in_tree();
if (pending_control != nullptr) {
Callable recheck = callable_mp(this, &Control::call_on_all_layout_pending_finished).bind(p_callable);
pending_control->connect(SNAME("_layout_pending_finished"), recheck, CONNECT_ONE_SHOT | CONNECT_REFERENCE_COUNTED);
} else {
Callable recheck(memnew(LayoutRecheckCallable(get_instance_id(), p_callable)));
pending_control->connect(SNAME("_layout_pending_finished"), recheck, CONNECT_ONE_SHOT);
} else if (p_callable.is_valid()) {
p_callable.call();
}
#ifdef DEBUG_ENABLED
else {
ERR_PRINT(vformat("Callable \"%s\" is invalid.", p_callable));
}
#endif // DEBUG_ENABLED
}
void Control::_update_minimum_size_cache() const {