diff options
Diffstat (limited to 'modules/gdscript/editor')
-rw-r--r-- | modules/gdscript/editor/gdscript_docgen.cpp | 49 | ||||
-rw-r--r-- | modules/gdscript/editor/gdscript_docgen.h | 5 | ||||
-rw-r--r-- | modules/gdscript/editor/gdscript_highlighter.cpp | 131 | ||||
-rw-r--r-- | modules/gdscript/editor/gdscript_highlighter.h | 11 | ||||
-rw-r--r-- | modules/gdscript/editor/gdscript_translation_parser_plugin.cpp | 104 | ||||
-rw-r--r-- | modules/gdscript/editor/gdscript_translation_parser_plugin.h | 6 |
6 files changed, 174 insertions, 132 deletions
diff --git a/modules/gdscript/editor/gdscript_docgen.cpp b/modules/gdscript/editor/gdscript_docgen.cpp index 00179109a3..659140b9b1 100644 --- a/modules/gdscript/editor/gdscript_docgen.cpp +++ b/modules/gdscript/editor/gdscript_docgen.cpp @@ -32,21 +32,29 @@ #include "../gdscript.h" -String GDScriptDocGen::_get_script_path(const String &p_path) { +#include "core/config/project_settings.h" + +HashMap<String, String> GDScriptDocGen::singletons; + +String GDScriptDocGen::_get_script_name(const String &p_path) { + const HashMap<String, String>::ConstIterator E = singletons.find(p_path); + if (E) { + return E->value; + } return p_path.trim_prefix("res://").quote(); } String GDScriptDocGen::_get_class_name(const GDP::ClassNode &p_class) { const GDP::ClassNode *curr_class = &p_class; if (!curr_class->identifier) { // All inner classes have an identifier, so this is the outer class. - return _get_script_path(curr_class->fqcn); + return _get_script_name(curr_class->fqcn); } String full_name = curr_class->identifier->name; while (curr_class->outer) { curr_class = curr_class->outer; if (!curr_class->identifier) { // All inner classes have an identifier, so this is the outer class. - return vformat("%s.%s", _get_script_path(curr_class->fqcn), full_name); + return vformat("%s.%s", _get_script_name(curr_class->fqcn), full_name); } full_name = vformat("%s.%s", curr_class->identifier->name, full_name); } @@ -97,12 +105,12 @@ void GDScriptDocGen::_doctype_from_gdtype(const GDType &p_gdtype, String &r_type return; } if (!p_gdtype.script_type->get_path().is_empty()) { - r_type = _get_script_path(p_gdtype.script_type->get_path()); + r_type = _get_script_name(p_gdtype.script_type->get_path()); return; } } if (!p_gdtype.script_path.is_empty()) { - r_type = _get_script_path(p_gdtype.script_path); + r_type = _get_script_name(p_gdtype.script_path); return; } r_type = "Object"; @@ -221,24 +229,25 @@ String GDScriptDocGen::_docvalue_from_variant(const Variant &p_variant, int p_re } } -void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_class) { +void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_class) { p_script->_clear_doc(); DocData::ClassDoc &doc = p_script->doc; - doc.script_path = _get_script_path(p_script->get_script_path()); + doc.is_script_doc = true; + if (p_script->local_name == StringName()) { - doc.name = doc.script_path; + // This is an outer unnamed class. + doc.name = _get_script_name(p_script->get_script_path()); } else { + // This is an inner or global outer class. doc.name = p_script->local_name; + if (p_script->_owner) { + doc.name = p_script->_owner->doc.name + "." + doc.name; + } } - if (p_script->_owner) { - doc.name = p_script->_owner->doc.name + "." + doc.name; - doc.script_path = doc.script_path + "." + doc.name; - } - - doc.is_script_doc = true; + doc.script_path = p_script->get_script_path(); if (p_script->base.is_valid() && p_script->base->is_valid()) { if (!p_script->base->doc.name.is_empty()) { @@ -271,7 +280,7 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c // Recursively generate inner class docs. // Needs inner GDScripts to exist: previously generated in GDScriptCompiler::make_scripts(). - GDScriptDocGen::generate_docs(*p_script->subclasses[class_name], inner_class); + GDScriptDocGen::_generate_docs(*p_script->subclasses[class_name], inner_class); } break; case GDP::ClassNode::Member::CONSTANT: { @@ -451,3 +460,13 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c // Add doc to the outer-most class. p_script->_add_doc(doc); } + +void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_class) { + for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) { + if (E.value.is_singleton) { + singletons[E.value.path] = E.key; + } + } + _generate_docs(p_script, p_class); + singletons.clear(); +} diff --git a/modules/gdscript/editor/gdscript_docgen.h b/modules/gdscript/editor/gdscript_docgen.h index a326c02c5f..651a4fb198 100644 --- a/modules/gdscript/editor/gdscript_docgen.h +++ b/modules/gdscript/editor/gdscript_docgen.h @@ -39,10 +39,13 @@ class GDScriptDocGen { using GDP = GDScriptParser; using GDType = GDP::DataType; - static String _get_script_path(const String &p_path); + static HashMap<String, String> singletons; // Script path to singleton name. + + static String _get_script_name(const String &p_path); static String _get_class_name(const GDP::ClassNode &p_class); static void _doctype_from_gdtype(const GDType &p_gdtype, String &r_type, String &r_enum, bool p_is_return = false); static String _docvalue_from_variant(const Variant &p_variant, int p_recursion_level = 1); + static void _generate_docs(GDScript *p_script, const GDP::ClassNode *p_class); public: static void generate_docs(GDScript *p_script, const GDP::ClassNode *p_class); 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; |