From 40de66d6e96c085597b443edb41c9762ddaa3213 Mon Sep 17 00:00:00 2001
From: AdvanceControl <163237388+AdvanceControl@users.noreply.github.com>
Date: Sun, 2 Nov 2025 19:41:36 +0800
Subject: [PATCH] Add bracket pair colorization for GDScript
---
doc/classes/EditorSettings.xml | 3 +
editor/settings/editor_settings.cpp | 8 ++
.../gdscript/editor/gdscript_highlighter.cpp | 88 +++++++++++++++++++
.../gdscript/editor/gdscript_highlighter.h | 13 +++
4 files changed, 112 insertions(+)
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 47584dea959d..6c472103a519 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -1596,6 +1596,9 @@
The GDScript syntax highlighter text color for annotations (e.g. [code]@export[/code]).
+
+ The list of colors to use to color each pair of brackets. The color for a bracket pair is determined by its nesting level. The first color in the array is used for the outermost level, the second for the first nested level, and so on. If the nesting level exceeds the number of colors in the array, the colors will repeat from the beginning. An empty array disables this feature.
+
The GDScript syntax highlighter text color for function definitions (e.g. the [code]_ready[/code] in [code]func _ready():[/code]).
diff --git a/editor/settings/editor_settings.cpp b/editor/settings/editor_settings.cpp
index 3f78ac9c520e..96ee0419e9cc 100644
--- a/editor/settings/editor_settings.cpp
+++ b/editor/settings/editor_settings.cpp
@@ -748,6 +748,14 @@ void EditorSettings::_load_defaults(Ref p_extra_config) {
_initial_set("text_editor/theme/highlighting/comment_markers/warning_list", "BUG,DEPRECATED,FIXME,HACK,TASK,TBD,TODO,WARNING");
_initial_set("text_editor/theme/highlighting/comment_markers/notice_list", "INFO,NOTE,NOTICE,TEST,TESTING");
+ PackedColorArray bracket_pair_colors_defaults;
+ bracket_pair_colors_defaults.push_back(Color(0.4, 0.9, 1.0));
+ bracket_pair_colors_defaults.push_back(Color(0.8, 0.4, 0.8));
+ bracket_pair_colors_defaults.push_back(Color(0.3, 0.7, 0.9));
+ bracket_pair_colors_defaults.push_back(Color(0.9, 0.5, 0.2));
+
+ EDITOR_SETTING_BASIC(Variant::PACKED_COLOR_ARRAY, PROPERTY_HINT_NONE, "text_editor/theme/highlighting/gdscript/bracket_pair_colors", bracket_pair_colors_defaults, "")
+
// Appearance
EDITOR_SETTING_BASIC(Variant::BOOL, PROPERTY_HINT_NONE, "text_editor/appearance/enable_inline_color_picker", true, "");
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index ec5a23033399..d4d5fa308b62 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -76,6 +76,10 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
int in_declaration_param_dicts = 0; // The number of opened `{` inside func params.
int in_type_params = 0; // The number of opened `[` after type name.
+ int bracket_level_round = 0; // for ().
+ int bracket_level_curly = 0; // for {}.
+ int bracket_level_square = 0; // for [].
+
Color keyword_color;
Color color;
@@ -93,6 +97,24 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
get_line_syntax_highlighting(p_line - 1);
}
in_region = color_region_cache[p_line - 1];
+
+ if (bracket_pair_colors.size() > 0) {
+ if (!bracket_level_cache.has(p_line - 1)) {
+ int prev_line = p_line - 1;
+ while (prev_line > 0 && !bracket_level_cache.has(prev_line)) {
+ prev_line--;
+ }
+
+ for (int i = prev_line; i < p_line; i++) {
+ get_line_syntax_highlighting(i);
+ }
+ }
+
+ Vector prev_levels = bracket_level_cache[p_line - 1];
+ bracket_level_square = prev_levels[SQUARE];
+ bracket_level_round = prev_levels[ROUND];
+ bracket_level_curly = prev_levels[CURLY];
+ }
}
const String &str = text_edit->get_line_with_ime(p_line);
@@ -655,6 +677,59 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
next_type = IDENTIFIER;
}
+ // Bracket pair colorization.
+ if (bracket_pair_colors.size() > 0) {
+ bool is_bracket = false;
+ Color bracket_color;
+
+ int *level_ptr = nullptr;
+ bool is_opening_bracket = false;
+
+ switch (str[j]) {
+ case '(':
+ level_ptr = &bracket_level_round;
+ is_opening_bracket = true;
+ break;
+ case ')':
+ level_ptr = &bracket_level_round;
+ is_opening_bracket = false;
+ break;
+
+ case '[':
+ level_ptr = &bracket_level_square;
+ is_opening_bracket = true;
+ break;
+ case ']':
+ level_ptr = &bracket_level_square;
+ is_opening_bracket = false;
+ break;
+
+ case '{':
+ level_ptr = &bracket_level_curly;
+ is_opening_bracket = true;
+ break;
+ case '}':
+ level_ptr = &bracket_level_curly;
+ is_opening_bracket = false;
+ break;
+ }
+
+ if (level_ptr != nullptr) {
+ if (is_opening_bracket) {
+ bracket_color = bracket_pair_colors[*level_ptr % bracket_pair_colors.size()];
+ (*level_ptr)++;
+ } else {
+ *level_ptr = MAX(0, *level_ptr - 1);
+ bracket_color = bracket_pair_colors[*level_ptr % bracket_pair_colors.size()];
+ }
+ is_bracket = true;
+ }
+
+ if (is_bracket) {
+ color = bracket_color;
+ }
+ }
+
if (next_type != current_type) {
if (current_type == NONE) {
current_type = next_type;
@@ -688,6 +763,14 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
color_map[j] = highlighter_info;
}
}
+
+ Vector final_levels;
+ final_levels.resize(3);
+ final_levels.set(SQUARE, bracket_level_square);
+ final_levels.set(ROUND, bracket_level_round);
+ final_levels.set(CURLY, bracket_level_curly);
+ bracket_level_cache[p_line] = final_levels;
+
return color_map;
}
@@ -910,6 +993,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
annotation_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/annotation_color");
string_name_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/string_name_color");
type_color = EDITOR_GET("text_editor/theme/highlighting/base_type_color");
+ bracket_pair_colors = EDITOR_GET("text_editor/theme/highlighting/gdscript/bracket_pair_colors");
comment_marker_colors[COMMENT_MARKER_CRITICAL] = EDITOR_GET("text_editor/theme/highlighting/comment_markers/critical_color");
comment_marker_colors[COMMENT_MARKER_WARNING] = EDITOR_GET("text_editor/theme/highlighting/comment_markers/warning_color");
comment_marker_colors[COMMENT_MARKER_NOTICE] = EDITOR_GET("text_editor/theme/highlighting/comment_markers/notice_color");
@@ -960,6 +1044,10 @@ void GDScriptSyntaxHighlighter::add_color_region(ColorRegion::Type p_type, const
clear_highlighting_cache();
}
+void GDScriptSyntaxHighlighter::_clear_highlighting_cache() {
+ bracket_level_cache.clear();
+}
+
Ref GDScriptSyntaxHighlighter::_create() const {
Ref syntax_highlighter;
syntax_highlighter.instantiate();
diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
index 263c2a023777..a94480d5c4de 100644
--- a/modules/gdscript/editor/gdscript_highlighter.h
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -79,6 +79,12 @@ class GDScriptSyntaxHighlighter : public EditorSyntaxHighlighter {
TYPE,
};
+ enum BracketType {
+ ROUND, // for ().
+ CURLY, // for {}.
+ SQUARE, // for [].
+ };
+
// Colors.
Color font_color;
Color symbol_color;
@@ -95,6 +101,8 @@ class GDScriptSyntaxHighlighter : public EditorSyntaxHighlighter {
Color string_name_color;
Color type_color;
+ PackedColorArray bracket_pair_colors;
+
enum CommentMarkerLevel {
COMMENT_MARKER_CRITICAL,
COMMENT_MARKER_WARNING,
@@ -104,8 +112,13 @@ class GDScriptSyntaxHighlighter : public EditorSyntaxHighlighter {
Color comment_marker_colors[COMMENT_MARKER_MAX];
HashMap comment_markers;
+ HashMap> bracket_level_cache;
+
void add_color_region(ColorRegion::Type p_type, const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only = false, bool p_r_prefix = false);
+protected:
+ virtual void _clear_highlighting_cache() override;
+
public:
virtual void _update_cache() override;
virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override;