Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions modules/gdscript/editor/gdscript_highlighter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -746,6 +750,18 @@ void GDScriptSyntaxHighlighter::_update_cache() {
class_names[class_name] = usertype_color;
}

// Global structs (declared with `struct_name`).
List<StringName> 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;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

/* Autoloads. */
for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {
const ProjectSettings::AutoloadInfo &info = E.value;
Expand Down Expand Up @@ -935,6 +951,70 @@ void GDScriptSyntaxHighlighter::_update_cache() {
}
}

LocalVector<StringName> 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<StringName> 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.");
Expand Down
3 changes: 3 additions & 0 deletions modules/gdscript/editor/gdscript_highlighter.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class GDScriptSyntaxHighlighter : public EditorSyntaxHighlighter {
HashMap<StringName, Color> reserved_keywords;
HashMap<StringName, Color> member_keywords;
HashSet<StringName> global_functions;
HashSet<int> struct_body_lines; ///< Lines inside a local struct body, for member-coloring its fields.

enum Type {
NONE,
Expand Down Expand Up @@ -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<StringName> _get_local_struct_names();

public:
virtual void _update_cache() override;
virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override;
Expand Down
Loading