diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index eb1dac3158f..0398f65e437 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. @@ -746,6 +750,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 +951,70 @@ void GDScriptSyntaxHighlighter::_update_cache() { } } +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. + LocalVector 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 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"; + } 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)); + struct_indent = indent; + } + } + 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..061b1400079 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,6 +117,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); + LocalVector _get_local_struct_names(); + public: virtual void _update_cache() override; virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override;