From 77e6d92357153cd58068c8c2faff7333e2550723 Mon Sep 17 00:00:00 2001 From: Jorge Korgut Junior Date: Tue, 20 May 2025 10:03:39 +0200 Subject: [PATCH] Fix highlighting warning and error issues related to Godot editor. Fix Issue #104413 : Add a limit of 20 lines of warning highlight otherwise paint the background of the first line only. Fix Issue #106278 : Extract the logic from clearing and setting background lines from _update_errors() and _update_warnings() to _update_background_text(). Fix Issue #83979 : Added signal for folding/unfolding lines (fold_lines_updated). Feature: Highlight the folded line using signals connected to _update_background_text() to keep the editor visuals up to date. The background is set in the following priority order: Error, Warning, then Highlight background. --- editor/plugins/script_text_editor.cpp | 76 +++++++++++++++++---------- editor/plugins/script_text_editor.h | 1 + editor/plugins/text_editor.cpp | 2 - scene/gui/code_edit.cpp | 66 +++++++++++++++++++---- scene/gui/code_edit.h | 3 ++ 5 files changed, 109 insertions(+), 39 deletions(-) diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index a5e6d43779b..180952211b8 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -605,6 +605,50 @@ void ScriptTextEditor::_update_color_constructor_options() { } } +void ScriptTextEditor::_update_background_color() { + // Clear background lines. + CodeEdit *te = code_editor->get_text_editor(); + for (int i = 0; i < te->get_line_count(); i++) { + bool is_folded_code_region = te->is_line_code_region_start(i) && te->is_line_folded(i); + te->set_line_background_color(i, is_folded_code_region ? folded_code_region_color : Color(0, 0, 0, 0)); + } + + // Set the warning background. + if (warning_line_color.a != 0.0) { + for (const ScriptLanguage::Warning &warning : warnings) { + int folder_line_header = te->get_folded_line_header(warning.end_line - 2); + bool is_folded = folder_line_header != (warning.end_line - 2); + + if (is_folded) { + te->set_line_background_color(folder_line_header, warning_line_color); + } else if (warning.end_line - warning.start_line > 0 && warning.end_line - warning.start_line < 20) { + // If the warning spans below 20 lines (arbitrary), set the background color for all lines. + // (W.end_line - W.start_line > 0) ensures that we set the background for single line warnings. + for (int i = warning.start_line - 1; i < warning.end_line - 1; i++) { + te->set_line_background_color(i, warning_line_color); + } + } else { + // Otherwise, just set the background color for the start line of the warning. + te->set_line_background_color(warning.start_line - 1, warning_line_color); + } + } + } + + // Set the error background. + if (marked_line_color.a != 0.0) { + for (const ScriptLanguage::ScriptError &error : errors) { + int folder_line_header = te->get_folded_line_header(error.line - 1); + bool is_folded_code = folder_line_header != (error.line - 1); + + if (is_folded_code) { + te->set_line_background_color(folder_line_header, marked_line_color); + } else { + te->set_line_background_color(error.line - 1, marked_line_color); + } + } + } +} + void ScriptTextEditor::_update_color_text() { if (inline_color_line < 0) { return; @@ -806,6 +850,7 @@ void ScriptTextEditor::_validate_script() { _update_connected_methods(); _update_warnings(); _update_errors(); + _update_background_color(); emit_signal(SNAME("name_changed")); emit_signal(SNAME("edited_script_changed")); @@ -874,17 +919,6 @@ void ScriptTextEditor::_update_warnings() { warnings_panel->pop(); // Cell. } warnings_panel->pop(); // Table. - if (warning_line_color.a != 0.0) { - CodeEdit *te = code_editor->get_text_editor(); - for (int i = 0; i < te->get_line_count(); i++) { - for (const ScriptLanguage::Warning &W : warnings) { - if (i >= W.start_line - 1 && i < W.end_line) { - te->set_line_background_color(i, warning_line_color); - break; - } - } - } - } } void ScriptTextEditor::_update_errors() { @@ -947,23 +981,11 @@ void ScriptTextEditor::_update_errors() { errors_panel->pop(); // Indent. } - CodeEdit *te = code_editor->get_text_editor(); bool highlight_safe = EDITOR_GET("text_editor/appearance/gutters/highlight_type_safe_lines"); bool last_is_safe = false; - for (int i = 0; i < te->get_line_count(); i++) { - if (errors.is_empty()) { - bool is_folded_code_region = te->is_line_code_region_start(i) && te->is_line_folded(i); - te->set_line_background_color(i, is_folded_code_region ? folded_code_region_color : Color(0, 0, 0, 0)); - } else if (marked_line_color.a != 0) { - for (const ScriptLanguage::ScriptError &E : errors) { - bool error_line = i == E.line - 1; - te->set_line_background_color(i, error_line ? marked_line_color : Color(0, 0, 0, 0)); - if (error_line) { - break; - } - } - } + CodeEdit *te = code_editor->get_text_editor(); + for (int i = 0; i < te->get_line_count(); i++) { if (highlight_safe) { if (safe_lines.has(i + 1)) { te->set_line_gutter_item_color(i, line_number_gutter, safe_line_number_color); @@ -1731,11 +1753,9 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case EDIT_FOLD_ALL_LINES: { tx->fold_all_lines(); - tx->queue_redraw(); } break; case EDIT_UNFOLD_ALL_LINES: { tx->unfold_all_lines(); - tx->queue_redraw(); } break; case EDIT_CREATE_CODE_REGION: { tx->create_code_region(); @@ -2037,6 +2057,7 @@ void ScriptTextEditor::_notification(int p_what) { if (is_visible_in_tree()) { _update_warnings(); _update_errors(); + _update_background_color(); } [[fallthrough]]; case NOTIFICATION_ENTER_TREE: { @@ -2594,6 +2615,7 @@ void ScriptTextEditor::_enable_code_editor() { code_editor->get_text_editor()->connect("gutter_added", callable_mp(this, &ScriptTextEditor::_update_gutter_indexes)); code_editor->get_text_editor()->connect("gutter_removed", callable_mp(this, &ScriptTextEditor::_update_gutter_indexes)); code_editor->get_text_editor()->connect("gutter_clicked", callable_mp(this, &ScriptTextEditor::_gutter_clicked)); + code_editor->get_text_editor()->connect("_fold_line_updated", callable_mp(this, &ScriptTextEditor::_update_background_color)); code_editor->get_text_editor()->connect(SceneStringName(gui_input), callable_mp(this, &ScriptTextEditor::_text_edit_gui_input)); code_editor->show_toggle_files_button(); _update_gutter_indexes(); diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index 8ce4b9aa715..270c324402b 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -211,6 +211,7 @@ protected: String _picker_color_stringify(const Color &p_color, COLOR_MODE p_mode); void _picker_color_changed(const Color &p_color); void _update_color_constructor_options(); + void _update_background_color(); void _update_color_text(); void _notification(int p_what); diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index c00c7e744c7..5c904696249 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -410,11 +410,9 @@ void TextEditor::_edit_option(int p_op) { } break; case EDIT_FOLD_ALL_LINES: { tx->fold_all_lines(); - tx->queue_redraw(); } break; case EDIT_UNFOLD_ALL_LINES: { tx->unfold_all_lines(); - tx->queue_redraw(); } break; case EDIT_TRIM_TRAILING_WHITESAPCE: { trim_trailing_whitespace(); diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 9458f4c6577..9784a90b868 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -1672,10 +1672,10 @@ bool CodeEdit::can_fold_line(int p_line) const { return false; } -void CodeEdit::fold_line(int p_line) { - ERR_FAIL_INDEX(p_line, get_line_count()); +bool CodeEdit::_fold_line(int p_line) { + ERR_FAIL_INDEX_V(p_line, get_line_count(), false); if (!is_line_folding_enabled() || !can_fold_line(p_line)) { - return; + return false; } /* Find the last line to be hidden. */ @@ -1741,12 +1741,14 @@ void CodeEdit::fold_line(int p_line) { // Collapse any carets in the hidden area. collapse_carets(p_line, get_line(p_line).length(), end_line, get_line(end_line).length(), true); + + return true; } -void CodeEdit::unfold_line(int p_line) { - ERR_FAIL_INDEX(p_line, get_line_count()); +bool CodeEdit::_unfold_line(int p_line) { + ERR_FAIL_INDEX_V(p_line, get_line_count(), false); if (!is_line_folded(p_line) && !_is_line_hidden(p_line)) { - return; + return false; } int fold_start = p_line; @@ -1766,18 +1768,47 @@ void CodeEdit::unfold_line(int p_line) { set_line_background_color(i - 1, Color(0.0, 0.0, 0.0, 0.0)); } } - queue_redraw(); + return true; } void CodeEdit::fold_all_lines() { + bool any_line_folded = false; + for (int i = 0; i < get_line_count(); i++) { - fold_line(i); + any_line_folded |= _fold_line(i); + } + + if (any_line_folded) { + emit_signal(SNAME("_fold_line_updated")); + } +} + +void CodeEdit::fold_line(int p_line) { + bool line_folded = _fold_line(p_line); + + if (line_folded) { + emit_signal(SNAME("_fold_line_updated")); } - queue_redraw(); } void CodeEdit::unfold_all_lines() { - _unhide_all_lines(); + bool any_line_unfolded = false; + + for (int i = 0; i < get_line_count(); i++) { + any_line_unfolded |= _unfold_line(i); + } + + if (any_line_unfolded) { + emit_signal(SNAME("_fold_line_updated")); + } +} + +void CodeEdit::unfold_line(int p_line) { + bool line_unfolded = _unfold_line(p_line); + + if (line_unfolded) { + emit_signal(SNAME("_fold_line_updated")); + } } void CodeEdit::toggle_foldable_line(int p_line) { @@ -1806,6 +1837,18 @@ void CodeEdit::toggle_foldable_lines_at_carets() { end_multicaret_edit(); } +int CodeEdit::get_folded_line_header(int p_line) const { + ERR_FAIL_INDEX_V(p_line, get_line_count(), 0); + // Search for the first non hidden line. + while (p_line > 0) { + if (!_is_line_hidden(p_line)) { + break; + } + p_line--; + } + return p_line; +} + bool CodeEdit::is_line_folded(int p_line) const { ERR_FAIL_INDEX_V(p_line, get_line_count(), false); return p_line + 1 < get_line_count() && !_is_line_hidden(p_line) && _is_line_hidden(p_line + 1); @@ -3820,6 +3863,9 @@ CodeEdit::CodeEdit() { symbol_tooltip_timer->connect("timeout", callable_mp(this, &CodeEdit::_on_symbol_tooltip_timer_timeout)); add_child(symbol_tooltip_timer, false, INTERNAL_MODE_FRONT); + /* Fold Lines Private signal */ + add_user_signal(MethodInfo("_fold_line_updated")); + connect("lines_edited_from", callable_mp(this, &CodeEdit::_lines_edited_from)); connect("text_set", callable_mp(this, &CodeEdit::_text_set)); connect(SceneStringName(text_changed), callable_mp(this, &CodeEdit::_text_changed)); diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index fb53bd4ccf4..af9e82150c8 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -132,6 +132,8 @@ private: String code_region_start_tag = "region"; String code_region_end_tag = "endregion"; void _update_code_region_tags(); + bool _fold_line(int p_line); + bool _unfold_line(int p_line); /* Delimiters */ enum DelimiterType { @@ -426,6 +428,7 @@ public: void toggle_foldable_line(int p_line); void toggle_foldable_lines_at_carets(); + int get_folded_line_header(int p_line) const; bool is_line_folded(int p_line) const; TypedArray get_folded_lines() const;