From 114e33148c5eaecd951d43bf2b8e59112f56b653 Mon Sep 17 00:00:00 2001 From: Dubhghlas McLaughlin <103212704+mcdubhghlas@users.noreply.github.com> Date: Tue, 22 Sep 2026 07:28:36 -0500 Subject: [PATCH 1/3] Fix missing snytax highlights for structs. --- .../gdscript/editor/gdscript_highlighter.cpp | 50 +++++++++++++++++++ .../gdscript/editor/gdscript_highlighter.h | 2 + 2 files changed, 52 insertions(+) diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index eb1dac3158f..30d241cb2c6 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -746,6 +746,18 @@ void GDScriptSyntaxHighlighter::_update_cache() { class_names[class_name] = usertype_color; } + // Global structs (declared with `struct_name`). + List global_structs; + ScriptServer::get_global_struct_list(&global_structs); + for (const StringName &struct_name : global_structs) { + class_names[struct_name] = usertype_color; + } + + // Local structs declared in the edited script (not registered globally). + for (const StringName &struct_name : _get_local_struct_names()) { + class_names[struct_name] = usertype_color; + } + /* Autoloads. */ for (const KeyValue &E : ProjectSettings::get_singleton()->get_autoload_list()) { const ProjectSettings::AutoloadInfo &info = E.value; @@ -935,6 +947,44 @@ void GDScriptSyntaxHighlighter::_update_cache() { } } +Vector GDScriptSyntaxHighlighter::_get_local_struct_names() const { + // Structs are declared at file scope with `struct Name:` or `struct_name Name:`. + // They aren't exposed on the compiled script, so scan the source for their names + // to highlight the type name at its declaration and at every use. + Vector names; + if (text_edit == nullptr) { + return names; + } + const int line_count = text_edit->get_line_count(); + for (int i = 0; i < line_count; i++) { + const String line = text_edit->get_line(i).strip_edges(); + String keyword; + if (line.begins_with("struct_name")) { + keyword = "struct_name"; + } else if (line.begins_with("struct")) { + keyword = "struct"; + } else { + continue; + } + int pos = keyword.length(); + if (pos >= line.length() || !is_whitespace(line[pos])) { + continue; // Part of a longer identifier, not the keyword. + } + while (pos < line.length() && is_whitespace(line[pos])) { + pos++; + } + const int start = pos; + if (pos < line.length() && is_unicode_identifier_start(line[pos])) { + pos++; + while (pos < line.length() && is_unicode_identifier_continue(line[pos])) { + pos++; + } + names.push_back(line.substr(start, pos - start)); + } + } + return names; +} + void GDScriptSyntaxHighlighter::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, bool p_r_prefix) { ERR_FAIL_COND_MSG(p_start_key.is_empty(), "Color region start key cannot be empty."); ERR_FAIL_COND_MSG(!is_symbol(p_start_key[0]), "Color region start key must start with a symbol."); diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h index 18da179443e..56c7679d402 100644 --- a/modules/gdscript/editor/gdscript_highlighter.h +++ b/modules/gdscript/editor/gdscript_highlighter.h @@ -116,6 +116,8 @@ class GDScriptSyntaxHighlighter : public EditorSyntaxHighlighter { 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); + Vector _get_local_struct_names() const; + public: virtual void _update_cache() override; virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override; From 5d0f424064a52ae595f857062cc32fe61d32eeb4 Mon Sep 17 00:00:00 2001 From: Dubhghlas McLaughlin <103212704+mcdubhghlas@users.noreply.github.com> Date: Tue, 22 Sep 2026 20:43:46 -0500 Subject: [PATCH 2/3] Track struct-body lines: Colour the field name. --- .../gdscript/editor/gdscript_highlighter.cpp | 36 +++++++++++++++++-- .../gdscript/editor/gdscript_highlighter.h | 3 +- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index 30d241cb2c6..fb008ee46d7 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -480,6 +480,10 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l } } else if (prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::VAR) || prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FOR) || prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::TK_CONST)) { in_var_const_declaration = true; + // A `var` field inside a struct body reads as a member variable. + if (prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::VAR) && struct_body_lines.has(p_line)) { + in_member_variable = true; + } } // Check for lambda. @@ -947,17 +951,42 @@ void GDScriptSyntaxHighlighter::_update_cache() { } } -Vector GDScriptSyntaxHighlighter::_get_local_struct_names() const { +Vector GDScriptSyntaxHighlighter::_get_local_struct_names() { // Structs are declared at file scope with `struct Name:` or `struct_name Name:`. // They aren't exposed on the compiled script, so scan the source for their names - // to highlight the type name at its declaration and at every use. + // to highlight the type name at its declaration and at every use. Also record the + // lines that make up each struct body so their `var` fields read as members. Vector names; + struct_body_lines.clear(); if (text_edit == nullptr) { return names; } const int line_count = text_edit->get_line_count(); + int struct_indent = -1; // Indentation of the enclosing struct header, or -1 when outside a struct. for (int i = 0; i < line_count; i++) { - const String line = text_edit->get_line(i).strip_edges(); + const String raw = text_edit->get_line(i); + int indent = 0; + while (indent < raw.length() && is_whitespace(raw[indent])) { + indent++; + } + const String line = raw.substr(indent).strip_edges(); + + if (line.is_empty() || line.begins_with("#")) { + // Blank and comment lines don't end a struct body. + if (struct_indent >= 0) { + struct_body_lines.insert(i); + } + continue; + } + + if (struct_indent >= 0) { + if (indent > struct_indent) { + struct_body_lines.insert(i); + continue; + } + struct_indent = -1; // Dedent closes the struct body. + } + String keyword; if (line.begins_with("struct_name")) { keyword = "struct_name"; @@ -980,6 +1009,7 @@ Vector GDScriptSyntaxHighlighter::_get_local_struct_names() const { pos++; } names.push_back(line.substr(start, pos - start)); + struct_indent = indent; } } return names; diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h index 56c7679d402..ca6960aaa4c 100644 --- a/modules/gdscript/editor/gdscript_highlighter.h +++ b/modules/gdscript/editor/gdscript_highlighter.h @@ -69,6 +69,7 @@ class GDScriptSyntaxHighlighter : public EditorSyntaxHighlighter { HashMap reserved_keywords; HashMap member_keywords; HashSet global_functions; + HashSet struct_body_lines; ///< Lines inside a local struct body, for member-coloring its fields. enum Type { NONE, @@ -116,7 +117,7 @@ class GDScriptSyntaxHighlighter : public EditorSyntaxHighlighter { 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); - Vector _get_local_struct_names() const; + Vector _get_local_struct_names(); public: virtual void _update_cache() override; From 307e69ef1de179bacf92378da5c489645fa823d1 Mon Sep 17 00:00:00 2001 From: Dubhghlas McLaughlin <103212704+mcdubhghlas@users.noreply.github.com> Date: Tue, 22 Sep 2026 20:46:27 -0500 Subject: [PATCH 3/3] jon was right it IS a good candidate for LocalVector instead of Vector. --- modules/gdscript/editor/gdscript_highlighter.cpp | 4 ++-- modules/gdscript/editor/gdscript_highlighter.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index fb008ee46d7..0398f65e437 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -951,12 +951,12 @@ void GDScriptSyntaxHighlighter::_update_cache() { } } -Vector GDScriptSyntaxHighlighter::_get_local_struct_names() { +LocalVector GDScriptSyntaxHighlighter::_get_local_struct_names() { // Structs are declared at file scope with `struct Name:` or `struct_name Name:`. // They aren't exposed on the compiled script, so scan the source for their names // to highlight the type name at its declaration and at every use. Also record the // lines that make up each struct body so their `var` fields read as members. - Vector names; + LocalVector names; struct_body_lines.clear(); if (text_edit == nullptr) { return names; diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h index ca6960aaa4c..061b1400079 100644 --- a/modules/gdscript/editor/gdscript_highlighter.h +++ b/modules/gdscript/editor/gdscript_highlighter.h @@ -117,7 +117,7 @@ class GDScriptSyntaxHighlighter : public EditorSyntaxHighlighter { 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); - Vector _get_local_struct_names(); + LocalVector _get_local_struct_names(); public: virtual void _update_cache() override;