summaryrefslogtreecommitdiffstats
path: root/modules/gdscript/editor
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/editor')
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp131
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.h11
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.cpp104
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.h6
4 files changed, 136 insertions, 116 deletions
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index 1f07def21c..426565bb68 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -63,13 +63,15 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
bool in_member_variable = false;
bool in_lambda = false;
- bool in_function_name = false;
- bool in_variable_declaration = false;
+ bool in_function_name = false; // Any call.
+ bool in_function_declaration = false; // Only declaration.
+ bool in_var_const_declaration = false;
bool in_signal_declaration = false;
bool expect_type = false;
- int in_function_args = 0;
- int in_function_arg_dicts = 0;
+ int in_declaration_params = 0; // The number of opened `(` after func/signal name.
+ 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.
Color keyword_color;
Color color;
@@ -150,7 +152,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
// Check if it's the whole line.
if (end_key_length == 0 || color_regions[c].line_only || from + end_key_length > line_length) {
// Don't skip comments, for highlighting markers.
- if (color_regions[in_region].start_key.begins_with("#")) {
+ if (color_regions[in_region].type == ColorRegion::TYPE_COMMENT) {
break;
}
if (from + end_key_length > line_length) {
@@ -172,7 +174,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
// Don't skip comments, for highlighting markers.
- if (j == line_length && !color_regions[in_region].start_key.begins_with("#")) {
+ if (j == line_length && color_regions[in_region].type != ColorRegion::TYPE_COMMENT) {
continue;
}
}
@@ -180,13 +182,13 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
// If we are in one, find the end key.
if (in_region != -1) {
Color region_color = color_regions[in_region].color;
- if (in_node_path && (color_regions[in_region].start_key == "\"" || color_regions[in_region].start_key == "\'")) {
+ if (in_node_path && color_regions[in_region].type == ColorRegion::TYPE_STRING) {
region_color = node_path_color;
}
- if (in_node_ref && (color_regions[in_region].start_key == "\"" || color_regions[in_region].start_key == "\'")) {
+ if (in_node_ref && color_regions[in_region].type == ColorRegion::TYPE_STRING) {
region_color = node_ref_color;
}
- if (in_string_name && (color_regions[in_region].start_key == "\"" || color_regions[in_region].start_key == "\'")) {
+ if (in_string_name && color_regions[in_region].type == ColorRegion::TYPE_STRING) {
region_color = string_name_color;
}
@@ -194,7 +196,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
highlighter_info["color"] = region_color;
color_map[j] = highlighter_info;
- if (color_regions[in_region].start_key.begins_with("#")) {
+ if (color_regions[in_region].type == ColorRegion::TYPE_COMMENT) {
int marker_start_pos = from;
int marker_len = 0;
while (from <= line_length) {
@@ -445,12 +447,15 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
if (str[k] == '(') {
in_function_name = true;
- } else if (prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::VAR) || prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FOR)) {
- in_variable_declaration = true;
+ if (prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FUNC)) {
+ in_function_declaration = true;
+ }
+ } 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::CONST)) {
+ in_var_const_declaration = true;
}
// Check for lambda.
- if (in_function_name && prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FUNC)) {
+ if (in_function_declaration) {
k = j - 1;
while (k > 0 && is_whitespace(str[k])) {
k--;
@@ -475,48 +480,60 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
if (is_a_symbol) {
- if (in_function_args > 0) {
+ if (in_declaration_params > 0) {
switch (str[j]) {
case '(':
- in_function_args += 1;
+ in_declaration_params += 1;
break;
case ')':
- in_function_args -= 1;
+ in_declaration_params -= 1;
break;
case '{':
- in_function_arg_dicts += 1;
+ in_declaration_param_dicts += 1;
break;
case '}':
- in_function_arg_dicts -= 1;
+ in_declaration_param_dicts -= 1;
break;
}
- } else if (in_function_name && str[j] == '(') {
- in_function_args = 1;
- in_function_arg_dicts = 0;
- }
-
- if (expect_type && (prev_is_char || str[j] == '=') && str[j] != '[' && str[j] != ',' && str[j] != '.') {
- expect_type = false;
+ } else if ((in_function_declaration || in_signal_declaration || prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FUNC)) && str[j] == '(') {
+ in_declaration_params = 1;
+ in_declaration_param_dicts = 0;
}
- if (j > 0 && str[j - 1] == '-' && str[j] == '>') {
- expect_type = true;
- }
-
- if (in_variable_declaration || in_function_args > 0) {
- int k = j;
- // Skip space.
- while (k < line_length && is_whitespace(str[k])) {
- k++;
+ if (expect_type) {
+ switch (str[j]) {
+ case '[':
+ in_type_params += 1;
+ break;
+ case ']':
+ in_type_params -= 1;
+ break;
+ case ',':
+ if (in_type_params <= 0) {
+ expect_type = false;
+ }
+ break;
+ case ' ':
+ case '\t':
+ case '.':
+ break;
+ default:
+ expect_type = false;
+ break;
}
-
- if (str[k] == ':' && in_function_arg_dicts == 0) {
- // Has type hint.
+ } else {
+ if (j > 0 && str[j - 1] == '-' && str[j] == '>') {
expect_type = true;
+ in_type_params = 0;
+ }
+ if ((in_var_const_declaration || (in_declaration_params == 1 && in_declaration_param_dicts == 0)) && str[j] == ':') {
+ expect_type = true;
+ in_type_params = 0;
}
}
- in_variable_declaration = false;
+ in_function_declaration = false;
+ in_var_const_declaration = false;
in_signal_declaration = false;
in_function_name = false;
in_lambda = false;
@@ -582,7 +599,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
color = member_color;
} else if (in_function_name) {
next_type = FUNCTION;
- if (!in_lambda && prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FUNC)) {
+ if (!in_lambda && in_function_declaration) {
color = function_definition_color;
} else {
color = function_color;
@@ -738,7 +755,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
for (const String &comment : comments) {
String beg = comment.get_slice(" ", 0);
String end = comment.get_slice_count(" ") > 1 ? comment.get_slice(" ", 1) : String();
- add_color_region(beg, end, comment_color, end.is_empty());
+ add_color_region(ColorRegion::TYPE_COMMENT, beg, end, comment_color, end.is_empty());
}
/* Doc comments */
@@ -748,18 +765,20 @@ void GDScriptSyntaxHighlighter::_update_cache() {
for (const String &doc_comment : doc_comments) {
String beg = doc_comment.get_slice(" ", 0);
String end = doc_comment.get_slice_count(" ") > 1 ? doc_comment.get_slice(" ", 1) : String();
- add_color_region(beg, end, doc_comment_color, end.is_empty());
+ add_color_region(ColorRegion::TYPE_COMMENT, beg, end, doc_comment_color, end.is_empty());
}
+ /* Code regions */
+ const Color code_region_color = Color(EDITOR_GET("text_editor/theme/highlighting/folded_code_region_color").operator Color(), 1.0);
+ add_color_region(ColorRegion::TYPE_CODE_REGION, "#region", "", code_region_color, true);
+ add_color_region(ColorRegion::TYPE_CODE_REGION, "#endregion", "", code_region_color, true);
+
/* Strings */
string_color = EDITOR_GET("text_editor/theme/highlighting/string_color");
- List<String> strings;
- gdscript->get_string_delimiters(&strings);
- for (const String &string : strings) {
- String beg = string.get_slice(" ", 0);
- String end = string.get_slice_count(" ") > 1 ? string.get_slice(" ", 1) : String();
- add_color_region(beg, end, string_color, end.is_empty());
- }
+ add_color_region(ColorRegion::TYPE_STRING, "\"", "\"", string_color);
+ add_color_region(ColorRegion::TYPE_STRING, "'", "'", string_color);
+ add_color_region(ColorRegion::TYPE_MULTILINE_STRING, "\"\"\"", "\"\"\"", string_color);
+ add_color_region(ColorRegion::TYPE_MULTILINE_STRING, "'''", "'''", string_color);
const Ref<Script> scr = _get_edited_resource();
if (scr.is_valid()) {
@@ -892,20 +911,17 @@ void GDScriptSyntaxHighlighter::_update_cache() {
}
}
-void GDScriptSyntaxHighlighter::add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only) {
- for (int i = 0; i < p_start_key.length(); i++) {
- ERR_FAIL_COND_MSG(!is_symbol(p_start_key[i]), "color regions must start with a symbol");
- }
+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) {
+ 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.");
- if (p_end_key.length() > 0) {
- for (int i = 0; i < p_end_key.length(); i++) {
- ERR_FAIL_COND_MSG(!is_symbol(p_end_key[i]), "color regions must end with a symbol");
- }
+ if (!p_end_key.is_empty()) {
+ ERR_FAIL_COND_MSG(!is_symbol(p_end_key[0]), "Color region end key must start with a symbol.");
}
int at = 0;
for (int i = 0; i < color_regions.size(); i++) {
- ERR_FAIL_COND_MSG(color_regions[i].start_key == p_start_key, "color region with start key '" + p_start_key + "' already exists.");
+ ERR_FAIL_COND_MSG(color_regions[i].start_key == p_start_key, "Color region with start key '" + p_start_key + "' already exists.");
if (p_start_key.length() < color_regions[i].start_key.length()) {
at++;
} else {
@@ -914,6 +930,7 @@ void GDScriptSyntaxHighlighter::add_color_region(const String &p_start_key, cons
}
ColorRegion color_region;
+ color_region.type = p_type;
color_region.color = p_color;
color_region.start_key = p_start_key;
color_region.end_key = p_end_key;
diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
index 090857f397..eb7bb7d801 100644
--- a/modules/gdscript/editor/gdscript_highlighter.h
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -39,6 +39,15 @@ class GDScriptSyntaxHighlighter : public EditorSyntaxHighlighter {
private:
struct ColorRegion {
+ enum Type {
+ TYPE_NONE,
+ TYPE_STRING, // `"` and `'`, optional prefix `&`, `^`, or `r`.
+ TYPE_MULTILINE_STRING, // `"""` and `'''`, optional prefix `r`.
+ TYPE_COMMENT, // `#` and `##`.
+ TYPE_CODE_REGION, // `#region` and `#endregion`.
+ };
+
+ Type type = TYPE_NONE;
Color color;
String start_key;
String end_key;
@@ -94,7 +103,7 @@ private:
Color comment_marker_colors[COMMENT_MARKER_MAX];
HashMap<String, CommentMarkerLevel> comment_markers;
- void add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only = false);
+ 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);
public:
virtual void _update_cache() override;
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
index f55b00ebe1..316281209a 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
@@ -47,10 +47,7 @@ Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Ve
Error err;
Ref<Resource> loaded_res = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
- if (err) {
- ERR_PRINT("Failed to load " + p_path);
- return err;
- }
+ ERR_FAIL_COND_V_MSG(err, err, "Failed to load " + p_path);
ids = r_ids;
ids_ctx_plural = r_ids_ctx_plural;
@@ -59,11 +56,11 @@ Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Ve
GDScriptParser parser;
err = parser.parse(source_code, p_path, false);
- ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to parse GDScript with GDScriptParser.");
+ ERR_FAIL_COND_V_MSG(err, err, "Failed to parse GDScript with GDScriptParser.");
GDScriptAnalyzer analyzer(&parser);
err = analyzer.analyze();
- ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to analyze GDScript with GDScriptAnalyzer.");
+ ERR_FAIL_COND_V_MSG(err, err, "Failed to analyze GDScript with GDScriptAnalyzer.");
// Traverse through the parsed tree from GDScriptParser.
GDScriptParser::ClassNode *c = parser.get_tree();
@@ -197,11 +194,7 @@ void GDScriptEditorTranslationParserPlugin::_assess_expression(const GDScriptPar
_assess_expression(binary_op_node->right_operand);
} break;
case GDScriptParser::Node::CALL: {
- const GDScriptParser::CallNode *call_node = static_cast<const GDScriptParser::CallNode *>(p_expression);
- _extract_from_call(call_node);
- for (int i = 0; i < call_node->arguments.size(); i++) {
- _assess_expression(call_node->arguments[i]);
- }
+ _assess_call(static_cast<const GDScriptParser::CallNode *>(p_expression));
} break;
case GDScriptParser::Node::CAST: {
_assess_expression(static_cast<const GDScriptParser::CastNode *>(p_expression)->operand);
@@ -241,6 +234,9 @@ void GDScriptEditorTranslationParserPlugin::_assess_expression(const GDScriptPar
}
void GDScriptEditorTranslationParserPlugin::_assess_assignment(const GDScriptParser::AssignmentNode *p_assignment) {
+ _assess_expression(p_assignment->assignee);
+ _assess_expression(p_assignment->assigned_value);
+
// Extract the translatable strings coming from assignments. For example, get_node("Label").text = "____"
StringName assignee_name;
@@ -258,26 +254,18 @@ void GDScriptEditorTranslationParserPlugin::_assess_assignment(const GDScriptPar
if (assignee_name != StringName() && assignment_patterns.has(assignee_name) && _is_constant_string(p_assignment->assigned_value)) {
// If the assignment is towards one of the extract patterns (text, tooltip_text etc.), and the value is a constant string, we collect the string.
ids->push_back(p_assignment->assigned_value->reduced_value);
- } else if (assignee_name == fd_filters && p_assignment->assigned_value->type == GDScriptParser::Node::CALL) {
- // FileDialog.filters accepts assignment in the form of PackedStringArray. For example,
- // get_node("FileDialog").filters = PackedStringArray(["*.png ; PNG Images","*.gd ; GDScript Files"]).
-
- const GDScriptParser::CallNode *call_node = static_cast<const GDScriptParser::CallNode *>(p_assignment->assigned_value);
- if (!call_node->arguments.is_empty() && call_node->arguments[0]->type == GDScriptParser::Node::ARRAY) {
- const GDScriptParser::ArrayNode *array_node = static_cast<const GDScriptParser::ArrayNode *>(call_node->arguments[0]);
-
- // Extract the name in "extension ; name" of PackedStringArray.
- for (int i = 0; i < array_node->elements.size(); i++) {
- _extract_fd_constant_strings(array_node->elements[i]);
- }
- }
- } else {
- // If the assignee is not in extract patterns or the assigned_value is not a constant string, try to see if the assigned_value contains tr().
- _assess_expression(p_assignment->assigned_value);
+ } else if (assignee_name == fd_filters) {
+ // Extract from `get_node("FileDialog").filters = <filter array>`.
+ _extract_fd_filter_array(p_assignment->assigned_value);
}
}
-void GDScriptEditorTranslationParserPlugin::_extract_from_call(const GDScriptParser::CallNode *p_call) {
+void GDScriptEditorTranslationParserPlugin::_assess_call(const GDScriptParser::CallNode *p_call) {
+ _assess_expression(p_call->callee);
+ for (int i = 0; i < p_call->arguments.size(); i++) {
+ _assess_expression(p_call->arguments[i]);
+ }
+
// Extract the translatable strings coming from function calls. For example:
// tr("___"), get_node("Label").set_text("____"), get_node("LineEdit").set_placeholder("____").
@@ -322,52 +310,56 @@ void GDScriptEditorTranslationParserPlugin::_extract_from_call(const GDScriptPar
ids_ctx_plural->push_back(id_ctx_plural);
}
} else if (first_arg_patterns.has(function_name)) {
- if (_is_constant_string(p_call->arguments[0])) {
+ if (!p_call->arguments.is_empty() && _is_constant_string(p_call->arguments[0])) {
ids->push_back(p_call->arguments[0]->reduced_value);
}
} else if (second_arg_patterns.has(function_name)) {
- if (_is_constant_string(p_call->arguments[1])) {
+ if (p_call->arguments.size() > 1 && _is_constant_string(p_call->arguments[1])) {
ids->push_back(p_call->arguments[1]->reduced_value);
}
} else if (function_name == fd_add_filter) {
// Extract the 'JPE Images' in this example - get_node("FileDialog").add_filter("*.jpg; JPE Images").
- _extract_fd_constant_strings(p_call->arguments[0]);
- } else if (function_name == fd_set_filter && p_call->arguments[0]->type == GDScriptParser::Node::CALL) {
- // FileDialog.set_filters() accepts assignment in the form of PackedStringArray. For example,
- // get_node("FileDialog").set_filters( PackedStringArray(["*.png ; PNG Images","*.gd ; GDScript Files"])).
-
- const GDScriptParser::CallNode *call_node = static_cast<const GDScriptParser::CallNode *>(p_call->arguments[0]);
- if (call_node->arguments[0]->type == GDScriptParser::Node::ARRAY) {
- const GDScriptParser::ArrayNode *array_node = static_cast<const GDScriptParser::ArrayNode *>(call_node->arguments[0]);
- for (int i = 0; i < array_node->elements.size(); i++) {
- _extract_fd_constant_strings(array_node->elements[i]);
- }
+ if (!p_call->arguments.is_empty()) {
+ _extract_fd_filter_string(p_call->arguments[0]);
}
- }
-
- if (p_call->callee && p_call->callee->type == GDScriptParser::Node::SUBSCRIPT) {
- const GDScriptParser::SubscriptNode *subscript_node = static_cast<const GDScriptParser::SubscriptNode *>(p_call->callee);
- if (subscript_node->base && subscript_node->base->type == GDScriptParser::Node::CALL) {
- const GDScriptParser::CallNode *call_node = static_cast<const GDScriptParser::CallNode *>(subscript_node->base);
- _extract_from_call(call_node);
+ } else if (function_name == fd_set_filter) {
+ // Extract from `get_node("FileDialog").set_filters(<filter array>)`.
+ if (!p_call->arguments.is_empty()) {
+ _extract_fd_filter_array(p_call->arguments[0]);
}
}
}
-void GDScriptEditorTranslationParserPlugin::_extract_fd_constant_strings(const GDScriptParser::ExpressionNode *p_expression) {
+void GDScriptEditorTranslationParserPlugin::_extract_fd_filter_string(const GDScriptParser::ExpressionNode *p_expression) {
// Extract the name in "extension ; name".
-
if (_is_constant_string(p_expression)) {
- String arg_val = p_expression->reduced_value;
- PackedStringArray arr = arg_val.split(";", true);
- if (arr.size() != 2) {
- ERR_PRINT("Argument for setting FileDialog has bad format.");
- return;
- }
+ PackedStringArray arr = p_expression->reduced_value.operator String().split(";", true);
+ ERR_FAIL_COND_MSG(arr.size() != 2, "Argument for setting FileDialog has bad format.");
ids->push_back(arr[1].strip_edges());
}
}
+void GDScriptEditorTranslationParserPlugin::_extract_fd_filter_array(const GDScriptParser::ExpressionNode *p_expression) {
+ const GDScriptParser::ArrayNode *array_node = nullptr;
+
+ if (p_expression->type == GDScriptParser::Node::ARRAY) {
+ // Extract from `["*.png ; PNG Images","*.gd ; GDScript Files"]` (implicit cast to `PackedStringArray`).
+ array_node = static_cast<const GDScriptParser::ArrayNode *>(p_expression);
+ } else if (p_expression->type == GDScriptParser::Node::CALL) {
+ // Extract from `PackedStringArray(["*.png ; PNG Images","*.gd ; GDScript Files"])`.
+ const GDScriptParser::CallNode *call_node = static_cast<const GDScriptParser::CallNode *>(p_expression);
+ if (call_node->get_callee_type() == GDScriptParser::Node::IDENTIFIER && call_node->function_name == SNAME("PackedStringArray") && !call_node->arguments.is_empty() && call_node->arguments[0]->type == GDScriptParser::Node::ARRAY) {
+ array_node = static_cast<const GDScriptParser::ArrayNode *>(call_node->arguments[0]);
+ }
+ }
+
+ if (array_node) {
+ for (int i = 0; i < array_node->elements.size(); i++) {
+ _extract_fd_filter_string(array_node->elements[i]);
+ }
+ }
+}
+
GDScriptEditorTranslationParserPlugin::GDScriptEditorTranslationParserPlugin() {
assignment_patterns.insert("text");
assignment_patterns.insert("placeholder_text");
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.h b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
index fab79a925f..fe876134c2 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.h
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
@@ -61,8 +61,10 @@ class GDScriptEditorTranslationParserPlugin : public EditorTranslationParserPlug
void _assess_expression(const GDScriptParser::ExpressionNode *p_expression);
void _assess_assignment(const GDScriptParser::AssignmentNode *p_assignment);
- void _extract_from_call(const GDScriptParser::CallNode *p_call);
- void _extract_fd_constant_strings(const GDScriptParser::ExpressionNode *p_expression);
+ void _assess_call(const GDScriptParser::CallNode *p_call);
+
+ void _extract_fd_filter_string(const GDScriptParser::ExpressionNode *p_expression);
+ void _extract_fd_filter_array(const GDScriptParser::ExpressionNode *p_expression);
public:
virtual Error parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural) override;