diff options
Diffstat (limited to 'modules/gdscript/gdscript_editor.cpp')
-rw-r--r-- | modules/gdscript/gdscript_editor.cpp | 549 |
1 files changed, 419 insertions, 130 deletions
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 3019354d93..1a574b3f3e 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -54,11 +54,16 @@ void GDScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const p_delimiters->push_back("#"); } +void GDScriptLanguage::get_doc_comment_delimiters(List<String> *p_delimiters) const { + p_delimiters->push_back("##"); +} + void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const { p_delimiters->push_back("\" \""); p_delimiters->push_back("' '"); p_delimiters->push_back("\"\"\" \"\"\""); p_delimiters->push_back("''' '''"); + // NOTE: StringName, NodePath and r-strings are not listed here. } bool GDScriptLanguage::is_using_templates() { @@ -75,25 +80,31 @@ Ref<Script> GDScriptLanguage::make_template(const String &p_template, const Stri #endif if (!type_hints) { processed_template = processed_template.replace(": int", "") + .replace(": Shader.Mode", "") + .replace(": VisualShader.Type", "") + .replace(": float", "") .replace(": String", "") .replace(": Array[String]", "") - .replace(": float", "") + .replace(": Node", "") .replace(": CharFXTransform", "") .replace(":=", "=") - .replace(" -> String", "") - .replace(" -> int", "") + .replace(" -> void", "") .replace(" -> bool", "") - .replace(" -> void", ""); + .replace(" -> int", "") + .replace(" -> PortType", "") + .replace(" -> String", "") + .replace(" -> Object", ""); } processed_template = processed_template.replace("_BASE_", p_base_class_name) - .replace("_CLASS_", p_class_name.to_pascal_case()) + .replace("_CLASS_SNAKE_CASE_", p_class_name.to_snake_case().validate_identifier()) + .replace("_CLASS_", p_class_name.to_pascal_case().validate_identifier()) .replace("_TS_", _get_indentation()); scr->set_source_code(processed_template); return scr; } -Vector<ScriptLanguage::ScriptTemplate> GDScriptLanguage::get_built_in_templates(StringName p_object) { +Vector<ScriptLanguage::ScriptTemplate> GDScriptLanguage::get_built_in_templates(const StringName &p_object) { Vector<ScriptLanguage::ScriptTemplate> templates; #ifdef TOOLS_ENABLED for (int i = 0; i < TEMPLATES_ARRAY_SIZE; i++) { @@ -190,10 +201,6 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li return true; } -bool GDScriptLanguage::has_named_classes() const { - return false; -} - bool GDScriptLanguage::supports_builtin_mode() const { return true; } @@ -203,7 +210,7 @@ bool GDScriptLanguage::supports_documentation() const { } int GDScriptLanguage::find_function(const String &p_function, const String &p_code) const { - GDScriptTokenizer tokenizer; + GDScriptTokenizerText tokenizer; tokenizer.set_source_code(p_code); int indent = 0; GDScriptTokenizer::Token current = tokenizer.scan(); @@ -500,7 +507,7 @@ String GDScriptLanguage::make_function(const String &p_class, const String &p_na s += p_args[i].get_slice(":", 0); if (th) { String type = p_args[i].get_slice(":", 1); - if (!type.is_empty() && type != "var") { + if (!type.is_empty()) { s += ": " + type; } } @@ -530,7 +537,7 @@ struct GDScriptCompletionIdentifier { // appears. For example, if you are completing code in a class that inherits Node2D, a property found on Node2D // will have a "better" (lower) location "score" than a property that is found on CanvasItem. -static int _get_property_location(StringName p_class, StringName p_property) { +static int _get_property_location(const StringName &p_class, const StringName &p_property) { if (!ClassDB::has_property(p_class, p_property)) { return ScriptLanguage::LOCATION_OTHER; } @@ -545,7 +552,20 @@ static int _get_property_location(StringName p_class, StringName p_property) { return depth | ScriptLanguage::LOCATION_PARENT_MASK; } -static int _get_constant_location(StringName p_class, StringName p_constant) { +static int _get_property_location(Ref<Script> p_script, const StringName &p_property) { + int depth = 0; + Ref<Script> scr = p_script; + while (scr.is_valid()) { + if (scr->get_member_line(p_property) != -1) { + return depth | ScriptLanguage::LOCATION_PARENT_MASK; + } + depth++; + scr = scr->get_base_script(); + } + return depth + _get_property_location(p_script->get_instance_base_type(), p_property); +} + +static int _get_constant_location(const StringName &p_class, const StringName &p_constant) { if (!ClassDB::has_integer_constant(p_class, p_constant)) { return ScriptLanguage::LOCATION_OTHER; } @@ -560,7 +580,20 @@ static int _get_constant_location(StringName p_class, StringName p_constant) { return depth | ScriptLanguage::LOCATION_PARENT_MASK; } -static int _get_signal_location(StringName p_class, StringName p_signal) { +static int _get_constant_location(Ref<Script> p_script, const StringName &p_constant) { + int depth = 0; + Ref<Script> scr = p_script; + while (scr.is_valid()) { + if (scr->get_member_line(p_constant) != -1) { + return depth | ScriptLanguage::LOCATION_PARENT_MASK; + } + depth++; + scr = scr->get_base_script(); + } + return depth + _get_constant_location(p_script->get_instance_base_type(), p_constant); +} + +static int _get_signal_location(const StringName &p_class, const StringName &p_signal) { if (!ClassDB::has_signal(p_class, p_signal)) { return ScriptLanguage::LOCATION_OTHER; } @@ -575,7 +608,20 @@ static int _get_signal_location(StringName p_class, StringName p_signal) { return depth | ScriptLanguage::LOCATION_PARENT_MASK; } -static int _get_method_location(StringName p_class, StringName p_method) { +static int _get_signal_location(Ref<Script> p_script, const StringName &p_signal) { + int depth = 0; + Ref<Script> scr = p_script; + while (scr.is_valid()) { + if (scr->get_member_line(p_signal) != -1) { + return depth | ScriptLanguage::LOCATION_PARENT_MASK; + } + depth++; + scr = scr->get_base_script(); + } + return depth + _get_signal_location(p_script->get_instance_base_type(), p_signal); +} + +static int _get_method_location(const StringName &p_class, const StringName &p_method) { if (!ClassDB::has_method(p_class, p_method)) { return ScriptLanguage::LOCATION_OTHER; } @@ -590,7 +636,7 @@ static int _get_method_location(StringName p_class, StringName p_method) { return depth | ScriptLanguage::LOCATION_PARENT_MASK; } -static int _get_enum_constant_location(StringName p_class, StringName p_enum_constant) { +static int _get_enum_constant_location(const StringName &p_class, const StringName &p_enum_constant) { if (!ClassDB::get_integer_constant_enum(p_class, p_enum_constant)) { return ScriptLanguage::LOCATION_OTHER; } @@ -613,9 +659,9 @@ static String _trim_parent_class(const String &p_class, const String &p_base_cla } Vector<String> names = p_class.split(".", false, 1); if (names.size() == 2) { - String first = names[0]; - String rest = names[1]; + const String &first = names[0]; if (ClassDB::class_exists(p_base_class) && ClassDB::class_exists(first) && ClassDB::is_parent_class(p_base_class, first)) { + const String &rest = names[1]; return rest; } } @@ -961,9 +1007,9 @@ static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite, } } -static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth); +static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, bool p_types_only, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth); -static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, bool p_only_functions, bool p_static, bool p_parent_only, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) { +static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, bool p_only_functions, bool p_types_only, bool p_static, bool p_parent_only, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) { ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT); if (!p_parent_only) { @@ -977,13 +1023,13 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, ScriptLanguage::CodeCompletionOption option; switch (member.type) { case GDScriptParser::ClassNode::Member::VARIABLE: - if (p_only_functions || outer || (p_static)) { + if (p_types_only || p_only_functions || outer || (p_static && !member.variable->is_static)) { continue; } option = ScriptLanguage::CodeCompletionOption(member.variable->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location); break; case GDScriptParser::ClassNode::Member::CONSTANT: - if (p_only_functions) { + if (p_types_only || p_only_functions) { continue; } if (r_result.has(member.constant->identifier->name)) { @@ -1001,7 +1047,7 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, option = ScriptLanguage::CodeCompletionOption(member.m_class->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, location); break; case GDScriptParser::ClassNode::Member::ENUM_VALUE: - if (p_only_functions) { + if (p_types_only || p_only_functions) { continue; } option = ScriptLanguage::CodeCompletionOption(member.enum_value.identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location); @@ -1013,7 +1059,7 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, option = ScriptLanguage::CodeCompletionOption(member.m_enum->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, location); break; case GDScriptParser::ClassNode::Member::FUNCTION: - if (outer || (p_static && !member.function->is_static) || member.function->identifier->name.operator String().begins_with("@")) { + if (p_types_only || outer || (p_static && !member.function->is_static) || member.function->identifier->name.operator String().begins_with("@")) { continue; } option = ScriptLanguage::CodeCompletionOption(member.function->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location); @@ -1024,7 +1070,7 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, } break; case GDScriptParser::ClassNode::Member::SIGNAL: - if (p_only_functions || outer || p_static) { + if (p_types_only || p_only_functions || outer || p_static) { continue; } option = ScriptLanguage::CodeCompletionOption(member.signal->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location); @@ -1036,6 +1082,10 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, } r_result.insert(option.display, option); } + if (p_types_only) { + break; // Otherwise, it will fill the results with types from the outer class (which is undesired for that case). + } + outer = true; clss = clss->outer; classes_processed++; @@ -1047,15 +1097,15 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, base_type.type = p_class->base_type; base_type.type.is_meta_type = p_static; - _find_identifiers_in_base(base_type, p_only_functions, r_result, p_recursion_depth + 1); + _find_identifiers_in_base(base_type, p_only_functions, p_types_only, r_result, p_recursion_depth + 1); } -static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) { +static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, bool p_types_only, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) { ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT); GDScriptParser::DataType base_type = p_base.type; - if (base_type.is_meta_type && base_type.kind != GDScriptParser::DataType::BUILTIN && base_type.kind != GDScriptParser::DataType::ENUM) { + if (!p_types_only && base_type.is_meta_type && base_type.kind != GDScriptParser::DataType::BUILTIN && base_type.kind != GDScriptParser::DataType::ENUM) { ScriptLanguage::CodeCompletionOption option("new", ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, ScriptLanguage::LOCATION_LOCAL); option.insert_text += "("; r_result.insert(option.display, option); @@ -1064,19 +1114,27 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base while (!base_type.has_no_type()) { switch (base_type.kind) { case GDScriptParser::DataType::CLASS: { - _find_identifiers_in_class(base_type.class_type, p_only_functions, base_type.is_meta_type, false, r_result, p_recursion_depth); + _find_identifiers_in_class(base_type.class_type, p_only_functions, p_types_only, base_type.is_meta_type, false, r_result, p_recursion_depth); // This already finds all parent identifiers, so we are done. base_type = GDScriptParser::DataType(); } break; case GDScriptParser::DataType::SCRIPT: { Ref<Script> scr = base_type.script_type; if (scr.is_valid()) { - if (!p_only_functions) { + if (p_types_only) { + // TODO: Need to implement Script::get_script_enum_list and retrieve the enum list from a script. + } else if (!p_only_functions) { if (!base_type.is_meta_type) { List<PropertyInfo> members; scr->get_script_property_list(&members); for (const PropertyInfo &E : members) { - int location = p_recursion_depth + _get_property_location(scr->get_class_name(), E.name); + if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL)) { + continue; + } + if (E.name.contains("/")) { + continue; + } + int location = p_recursion_depth + _get_property_location(scr, E.name); ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location); r_result.insert(option.display, option); } @@ -1084,7 +1142,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base List<MethodInfo> signals; scr->get_script_signal_list(&signals); for (const MethodInfo &E : signals) { - int location = p_recursion_depth + _get_signal_location(scr->get_class_name(), E.name); + int location = p_recursion_depth + _get_signal_location(scr, E.name); ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location); r_result.insert(option.display, option); } @@ -1092,26 +1150,28 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base HashMap<StringName, Variant> constants; scr->get_constants(&constants); for (const KeyValue<StringName, Variant> &E : constants) { - int location = p_recursion_depth + _get_constant_location(scr->get_class_name(), E.key); + int location = p_recursion_depth + _get_constant_location(scr, E.key); ScriptLanguage::CodeCompletionOption option(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location); r_result.insert(option.display, option); } } - List<MethodInfo> methods; - scr->get_script_method_list(&methods); - for (const MethodInfo &E : methods) { - if (E.name.begins_with("@")) { - continue; - } - int location = p_recursion_depth + _get_method_location(scr->get_class_name(), E.name); - ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location); - if (E.arguments.size()) { - option.insert_text += "("; - } else { - option.insert_text += "()"; + if (!p_types_only) { + List<MethodInfo> methods; + scr->get_script_method_list(&methods); + for (const MethodInfo &E : methods) { + if (E.name.begins_with("@")) { + continue; + } + int location = p_recursion_depth + _get_method_location(scr->get_class_name(), E.name); + ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location); + if (E.arguments.size()) { + option.insert_text += "("; + } else { + option.insert_text += "()"; + } + r_result.insert(option.display, option); } - r_result.insert(option.display, option); } Ref<Script> base_script = scr->get_base_script(); @@ -1119,6 +1179,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base base_type.script_type = base_script; } else { base_type.kind = GDScriptParser::DataType::NATIVE; + base_type.builtin_type = Variant::OBJECT; base_type.native_type = scr->get_instance_base_type(); } } else { @@ -1131,6 +1192,16 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base return; } + if (p_types_only) { + List<StringName> enums; + ClassDB::get_enum_list(type, &enums); + for (const StringName &E : enums) { + ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_ENUM); + r_result.insert(option.display, option); + } + return; + } + if (!p_only_functions) { List<String> constants; ClassDB::get_integer_constant_list(type, &constants); @@ -1144,7 +1215,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base List<PropertyInfo> pinfo; ClassDB::get_property_list(type, &pinfo); for (const PropertyInfo &E : pinfo) { - if (E.usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY)) { + if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL)) { continue; } if (E.name.contains("/")) { @@ -1189,6 +1260,10 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base } break; case GDScriptParser::DataType::ENUM: case GDScriptParser::DataType::BUILTIN: { + if (p_types_only) { + return; + } + Callable::CallError err; Variant tmp; Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err); @@ -1196,6 +1271,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base return; } + int location = ScriptLanguage::LOCATION_OTHER; + if (!p_only_functions) { List<PropertyInfo> members; if (p_base.value.get_type() != Variant::NIL) { @@ -1205,8 +1282,18 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base } for (const PropertyInfo &E : members) { + if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL)) { + continue; + } if (!String(E.name).contains("/")) { - ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER); + ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location); + if (base_type.kind == GDScriptParser::DataType::ENUM) { + // Sort enum members in their declaration order. + location += 1; + } + if (GDScriptParser::theme_color_names.has(E.name)) { + option.theme_color_name = GDScriptParser::theme_color_names[E.name]; + } r_result.insert(option.display, option); } } @@ -1219,7 +1306,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base // Enum types are static and cannot change, therefore we skip non-const dictionary methods. continue; } - ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); + ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location); if (E.arguments.size()) { option.insert_text += "("; } else { @@ -1244,7 +1331,7 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context } if (p_context.current_class) { - _find_identifiers_in_class(p_context.current_class, p_only_functions, (!p_context.current_function || p_context.current_function->is_static), false, r_result, p_recursion_depth); + _find_identifiers_in_class(p_context.current_class, p_only_functions, false, (!p_context.current_function || p_context.current_function->is_static), false, r_result, p_recursion_depth); } List<StringName> functions; @@ -1282,7 +1369,7 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context static const char *_keywords_with_space[] = { "and", "not", "or", "in", "as", "class", "class_name", "extends", "is", "func", "signal", "await", - "const", "enum", "static", "var", "if", "elif", "else", "for", "match", "while", + "const", "enum", "static", "var", "if", "elif", "else", "for", "match", "when", "while", nullptr }; @@ -1344,7 +1431,7 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context } } -static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value) { +static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value, GDScriptParser::CompletionContext &p_context) { GDScriptCompletionIdentifier ci; ci.value = p_value; ci.type.is_constant = true; @@ -1366,8 +1453,22 @@ static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value) { scr = obj->get_script(); } if (scr.is_valid()) { - ci.type.script_type = scr; + ci.type.script_path = scr->get_path(); + + if (scr->get_path().ends_with(".gd")) { + Error err; + Ref<GDScriptParserRef> parser = GDScriptCache::get_parser(scr->get_path(), GDScriptParserRef::INTERFACE_SOLVED, err); + if (err == OK) { + ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; + ci.type.class_type = parser->get_parser()->get_tree(); + ci.type.kind = GDScriptParser::DataType::CLASS; + p_context.dependent_parsers.push_back(parser); + return ci; + } + } + ci.type.kind = GDScriptParser::DataType::SCRIPT; + ci.type.script_type = scr; ci.type.native_type = scr->get_instance_base_type(); } else { ci.type.kind = GDScriptParser::DataType::NATIVE; @@ -1392,8 +1493,19 @@ static GDScriptCompletionIdentifier _type_from_property(const PropertyInfo &p_pr ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; ci.type.builtin_type = p_property.type; if (p_property.type == Variant::OBJECT) { - ci.type.kind = GDScriptParser::DataType::NATIVE; - ci.type.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name; + if (ScriptServer::is_global_class(p_property.class_name)) { + ci.type.kind = GDScriptParser::DataType::SCRIPT; + ci.type.script_path = ScriptServer::get_global_class_path(p_property.class_name); + ci.type.native_type = ScriptServer::get_global_class_native_base(p_property.class_name); + + Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_property.class_name)); + if (scr.is_valid()) { + ci.type.script_type = scr; + } + } else { + ci.type.kind = GDScriptParser::DataType::NATIVE; + ci.type.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name; + } } else { ci.type.kind = GDScriptParser::DataType::BUILTIN; } @@ -1455,25 +1567,27 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, if (p_expression->is_constant) { // Already has a value, so just use that. - r_type = _type_from_variant(p_expression->reduced_value); - if (p_expression->get_datatype().kind == GDScriptParser::DataType::ENUM) { - r_type.type = p_expression->get_datatype(); + r_type = _type_from_variant(p_expression->reduced_value, p_context); + switch (p_expression->get_datatype().kind) { + case GDScriptParser::DataType::ENUM: + case GDScriptParser::DataType::CLASS: + r_type.type = p_expression->get_datatype(); + break; + default: + break; } found = true; } else { switch (p_expression->type) { case GDScriptParser::Node::LITERAL: { const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(p_expression); - r_type = _type_from_variant(literal->value); + r_type = _type_from_variant(literal->value, p_context); found = true; } break; case GDScriptParser::Node::SELF: { if (p_context.current_class) { - if (p_context.type != GDScriptParser::COMPLETION_SUPER_METHOD) { - r_type.type = p_context.current_class->get_datatype(); - } else { - r_type.type = p_context.current_class->base_type; - } + r_type.type = p_context.current_class->get_datatype(); + r_type.type.is_meta_type = false; found = true; } } break; @@ -1615,6 +1729,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, native_type.script_type = parent; } else { native_type.kind = GDScriptParser::DataType::NATIVE; + native_type.builtin_type = Variant::OBJECT; native_type.native_type = native_type.script_type->get_instance_base_type(); if (!ClassDB::class_exists(native_type.native_type)) { native_type.kind = GDScriptParser::DataType::UNRESOLVED; @@ -1647,7 +1762,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, if (!which.is_empty()) { // Try singletons first if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(which)) { - r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[which]); + r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[which], p_context); found = true; } else { for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) { @@ -1698,7 +1813,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, if (ce.error == Callable::CallError::CALL_OK && ret.get_type() != Variant::NIL) { if (ret.get_type() != Variant::OBJECT || ret.operator Object *() != nullptr) { - r_type = _type_from_variant(ret); + r_type = _type_from_variant(ret, p_context); found = true; } } @@ -1726,7 +1841,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, if (base.value.get_type() == Variant::DICTIONARY && base.value.operator Dictionary().has(String(subscript->attribute->name))) { Variant value = base.value.operator Dictionary()[String(subscript->attribute->name)]; - r_type = _type_from_variant(value); + r_type = _type_from_variant(value, p_context); found = true; break; } @@ -1778,7 +1893,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, if (base.value.in(index.value)) { Variant value = base.value.get(index.value); - r_type = _type_from_variant(value); + r_type = _type_from_variant(value, p_context); found = true; break; } @@ -1833,7 +1948,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, bool valid = false; Variant res = base_val.get(index.value, &valid); if (valid) { - r_type = _type_from_variant(res); + r_type = _type_from_variant(res, p_context); r_type.value = Variant(); r_type.type.is_constant = false; found = true; @@ -1891,7 +2006,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, found = false; break; } - r_type = _type_from_variant(res); + r_type = _type_from_variant(res, p_context); if (!v1_use_value || !v2_use_value) { r_type.value = Variant(); r_type.type.is_constant = false; @@ -1980,6 +2095,21 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, default: break; } + } else { + if (p_context.current_class) { + GDScriptCompletionIdentifier base_identifier; + + GDScriptCompletionIdentifier base; + base.value = p_context.base; + base.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; + base.type.kind = GDScriptParser::DataType::CLASS; + base.type.class_type = p_context.current_class; + base.type.is_meta_type = p_context.current_function && p_context.current_function->is_static; + + if (_guess_identifier_type_from_base(p_context, base, p_identifier->name, base_identifier)) { + id_type = base_identifier.type; + } + } } while (suite) { @@ -2033,8 +2163,15 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, if (last_assigned_expression && last_assign_line < p_context.current_line) { GDScriptParser::CompletionContext c = p_context; c.current_line = last_assign_line; - r_type.assigned_expression = last_assigned_expression; - if (_guess_expression_type(c, last_assigned_expression, r_type)) { + GDScriptCompletionIdentifier assigned_type; + if (_guess_expression_type(c, last_assigned_expression, assigned_type)) { + if (id_type.is_set() && assigned_type.type.is_set() && !GDScriptAnalyzer::check_type_compatibility(id_type, assigned_type.type)) { + // The assigned type is incompatible. The annotated type takes priority. + r_type.assigned_expression = last_assigned_expression; + r_type.type = id_type; + } else { + r_type = assigned_type; + } return true; } } @@ -2092,20 +2229,6 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, return true; } - // Check current class (including inheritance). - if (p_context.current_class) { - GDScriptCompletionIdentifier base; - base.value = p_context.base; - base.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - base.type.kind = GDScriptParser::DataType::CLASS; - base.type.class_type = p_context.current_class; - base.type.is_meta_type = p_context.current_function && p_context.current_function->is_static; - - if (_guess_identifier_type_from_base(p_context, base, p_identifier->name, r_type)) { - return true; - } - } - // Check global scripts. if (ScriptServer::is_global_class(p_identifier->name)) { String script = ScriptServer::get_global_class_path(p_identifier->name); @@ -2126,7 +2249,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, } else { Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier->name)); if (scr.is_valid()) { - r_type = _type_from_variant(scr); + r_type = _type_from_variant(scr, p_context); r_type.type.is_meta_type = true; return true; } @@ -2136,7 +2259,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, // Check global variables (including autoloads). if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier->name)) { - r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier->name]); + r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier->name], p_context); return true; } @@ -2144,6 +2267,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, if (ClassDB::class_exists(p_identifier->name) && ClassDB::is_class_exposed(p_identifier->name)) { r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; r_type.type.kind = GDScriptParser::DataType::NATIVE; + r_type.type.builtin_type = Variant::OBJECT; r_type.type.native_type = p_identifier->name; r_type.type.is_constant = true; if (Engine::get_singleton()->has_singleton(p_identifier->name)) { @@ -2180,7 +2304,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext & } return true; case GDScriptParser::ClassNode::Member::VARIABLE: - if (!is_static) { + if (!is_static || member.variable->is_static) { if (member.variable->get_datatype().is_set() && !member.variable->get_datatype().is_variant()) { r_type.type = member.variable->get_datatype(); return true; @@ -2188,7 +2312,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext & const GDScriptParser::ExpressionNode *init = member.variable->initializer; if (init->is_constant) { r_type.value = init->reduced_value; - r_type = _type_from_variant(init->reduced_value); + r_type = _type_from_variant(init->reduced_value, p_context); return true; } else if (init->start_line == p_context.current_line) { return false; @@ -2215,7 +2339,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext & r_type.enumeration = member.m_enum->identifier->name; return true; case GDScriptParser::ClassNode::Member::ENUM_VALUE: - r_type = _type_from_variant(member.enum_value.value); + r_type = _type_from_variant(member.enum_value.value, p_context); return true; case GDScriptParser::ClassNode::Member::SIGNAL: r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; @@ -2251,25 +2375,29 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext & HashMap<StringName, Variant> constants; scr->get_constants(&constants); if (constants.has(p_identifier)) { - r_type = _type_from_variant(constants[p_identifier]); + r_type = _type_from_variant(constants[p_identifier], p_context); return true; } - if (!is_static) { - List<PropertyInfo> members; + List<PropertyInfo> members; + if (is_static) { + scr->get_property_list(&members); + } else { scr->get_script_property_list(&members); - for (const PropertyInfo &prop : members) { - if (prop.name == p_identifier) { - r_type = _type_from_property(prop); - return true; - } + } + for (const PropertyInfo &prop : members) { + if (prop.name == p_identifier) { + r_type = _type_from_property(prop); + return true; } } + Ref<Script> parent = scr->get_base_script(); if (parent.is_valid()) { base_type.script_type = parent; } else { base_type.kind = GDScriptParser::DataType::NATIVE; + base_type.builtin_type = Variant::OBJECT; base_type.native_type = scr->get_instance_base_type(); } } else { @@ -2311,7 +2439,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext & bool valid = false; Variant res = tmp.get(p_identifier, &valid); if (valid) { - r_type = _type_from_variant(res); + r_type = _type_from_variant(res, p_context); r_type.value = Variant(); r_type.type.is_constant = false; return true; @@ -2439,6 +2567,7 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex base_type.script_type = base_script; } else { base_type.kind = GDScriptParser::DataType::NATIVE; + base_type.builtin_type = Variant::OBJECT; base_type.native_type = scr->get_instance_base_type(); } } else { @@ -2578,6 +2707,64 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c r_arghint = _make_arguments_hint(info, p_argidx); } + if (p_argidx == 1 && p_context.node && p_context.node->type == GDScriptParser::Node::CALL && ClassDB::is_parent_class(class_name, SNAME("Tween")) && p_method == SNAME("tween_property")) { + // Get tweened objects properties. + GDScriptParser::ExpressionNode *tweened_object = static_cast<GDScriptParser::CallNode *>(p_context.node)->arguments[0]; + StringName native_type = tweened_object->datatype.native_type; + switch (tweened_object->datatype.kind) { + case GDScriptParser::DataType::SCRIPT: { + Ref<Script> script = tweened_object->datatype.script_type; + native_type = script->get_instance_base_type(); + int n = 0; + while (script.is_valid()) { + List<PropertyInfo> properties; + script->get_script_property_list(&properties); + for (const PropertyInfo &E : properties) { + if (E.usage & (PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_INTERNAL)) { + continue; + } + ScriptLanguage::CodeCompletionOption option(E.name.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::CodeCompletionLocation::LOCATION_LOCAL + n); + r_result.insert(option.display, option); + } + script = script->get_base_script(); + n++; + } + } break; + case GDScriptParser::DataType::CLASS: { + GDScriptParser::ClassNode *clss = tweened_object->datatype.class_type; + native_type = clss->base_type.native_type; + int n = 0; + while (clss) { + for (GDScriptParser::ClassNode::Member member : clss->members) { + if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) { + ScriptLanguage::CodeCompletionOption option(member.get_name().quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::CodeCompletionLocation::LOCATION_LOCAL + n); + r_result.insert(option.display, option); + } + } + if (clss->base_type.kind == GDScriptParser::DataType::Kind::CLASS) { + clss = clss->base_type.class_type; + n++; + } else { + native_type = clss->base_type.native_type; + clss = nullptr; + } + } + } break; + default: + break; + } + + List<PropertyInfo> properties; + ClassDB::get_property_list(native_type, &properties); + for (const PropertyInfo &E : properties) { + if (E.usage & (PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_INTERNAL)) { + continue; + } + ScriptLanguage::CodeCompletionOption option(E.name.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_MEMBER); + r_result.insert(option.display, option); + } + } + if (p_argidx == 0 && ClassDB::is_parent_class(class_name, SNAME("Node")) && (p_method == SNAME("get_node") || p_method == SNAME("has_node"))) { // Get autoloads List<PropertyInfo> props; @@ -2644,6 +2831,7 @@ static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, co if (p_context.base == nullptr) { return false; } + const GDScriptParser::GetNodeNode *get_node = nullptr; switch (p_subscript->base->type) { @@ -2652,6 +2840,11 @@ static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, co } break; case GDScriptParser::Node::IDENTIFIER: { + if (p_subscript->base->datatype.type_source == GDScriptParser::DataType::ANNOTATED_EXPLICIT) { + // Annotated type takes precedence. + return false; + } + const GDScriptParser::IdentifierNode *identifier_node = static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base); switch (identifier_node->source) { @@ -2692,10 +2885,19 @@ static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, co if (r_base != nullptr) { *r_base = node; } - r_base_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - r_base_type.kind = GDScriptParser::DataType::NATIVE; - r_base_type.native_type = node->get_class_name(); + + r_base_type.type_source = GDScriptParser::DataType::INFERRED; r_base_type.builtin_type = Variant::OBJECT; + r_base_type.native_type = node->get_class_name(); + + Ref<Script> scr = node->get_script(); + if (scr.is_null()) { + r_base_type.kind = GDScriptParser::DataType::NATIVE; + } else { + r_base_type.kind = GDScriptParser::DataType::SCRIPT; + r_base_type.script_type = scr; + } + return true; } } @@ -2722,8 +2924,6 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_call); GDScriptParser::Node::Type callee_type = call->get_callee_type(); - GDScriptCompletionIdentifier connect_base; - if (callee_type == GDScriptParser::Node::SUBSCRIPT) { const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee); @@ -2966,7 +3166,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c break; } - _find_identifiers_in_base(base, is_function, options, 0); + _find_identifiers_in_base(base, is_function, false, options, 0); } } break; case GDScriptParser::COMPLETION_SUBSCRIPT: { @@ -2976,7 +3176,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c break; } - _find_identifiers_in_base(base, false, options, 0); + _find_identifiers_in_base(base, false, false, options, 0); } break; case GDScriptParser::COMPLETION_TYPE_ATTRIBUTE: { if (!completion_context.current_class) { @@ -2984,25 +3184,41 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c } const GDScriptParser::TypeNode *type = static_cast<const GDScriptParser::TypeNode *>(completion_context.node); bool found = true; + GDScriptCompletionIdentifier base; base.type.kind = GDScriptParser::DataType::CLASS; base.type.type_source = GDScriptParser::DataType::INFERRED; base.type.is_constant = true; - base.type.class_type = completion_context.current_class; - base.value = completion_context.base; - for (int i = 0; i < completion_context.current_argument; i++) { - GDScriptCompletionIdentifier ci; - if (!_guess_identifier_type_from_base(completion_context, base, type->type_chain[i]->name, ci)) { - found = false; - break; + if (completion_context.current_argument == 1) { + StringName type_name = type->type_chain[0]->name; + + if (ClassDB::class_exists(type_name)) { + base.type.kind = GDScriptParser::DataType::NATIVE; + base.type.native_type = type_name; + } else if (ScriptServer::is_global_class(type_name)) { + base.type.kind = GDScriptParser::DataType::SCRIPT; + String scr_path = ScriptServer::get_global_class_path(type_name); + base.type.script_type = ResourceLoader::load(scr_path); + } + } + + if (base.type.kind == GDScriptParser::DataType::CLASS) { + base.type.class_type = completion_context.current_class; + base.value = completion_context.base; + + for (int i = 0; i < completion_context.current_argument; i++) { + GDScriptCompletionIdentifier ci; + if (!_guess_identifier_type_from_base(completion_context, base, type->type_chain[i]->name, ci)) { + found = false; + break; + } + base = ci; } - base = ci; } - // TODO: Improve this to only list types. if (found) { - _find_identifiers_in_base(base, false, options, 0); + _find_identifiers_in_base(base, false, true, options, 0); } r_forced = true; } break; @@ -3091,6 +3307,11 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c List<String> opts; p_owner->get_argument_options("get_node", 0, &opts); + bool for_unique_name = false; + if (completion_context.node != nullptr && completion_context.node->type == GDScriptParser::Node::GET_NODE && !static_cast<GDScriptParser::GetNodeNode *>(completion_context.node)->use_dollar) { + for_unique_name = true; + } + for (const String &E : opts) { r_forced = true; String opt = E.strip_edges(); @@ -3099,9 +3320,25 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c // or handle NodePaths which are valid identifiers and don't need quotes. opt = opt.unquote(); } - // The path needs quotes if it's not a valid identifier (with an exception - // for "/" as path separator, which also doesn't require quotes). - if (!opt.replace("/", "_").is_valid_identifier()) { + + if (for_unique_name) { + if (!opt.begins_with("%")) { + continue; + } + opt = opt.substr(1); + } + + // The path needs quotes if at least one of its components (excluding `/` separations) + // is not a valid identifier. + bool path_needs_quote = false; + for (const String &part : opt.split("/")) { + if (!part.is_valid_identifier()) { + path_needs_quote = true; + break; + } + } + + if (path_needs_quote) { // Ignore quote_style and just use double quotes for paths with apostrophes. // Double quotes don't need to be checked because they're not valid in node and property names. opt = opt.quote(opt.contains("'") ? "\"" : quote_style); // Handle user preference. @@ -3110,11 +3347,13 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c options.insert(option.display, option); } - // Get autoloads. - for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) { - String path = "/root/" + E.key; - ScriptLanguage::CodeCompletionOption option(path.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH); - options.insert(option.display, option); + if (!for_unique_name) { + // Get autoloads. + for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) { + String path = "/root/" + E.key; + ScriptLanguage::CodeCompletionOption option(path.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH); + options.insert(option.display, option); + } } } } break; @@ -3122,7 +3361,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c if (!completion_context.current_class) { break; } - _find_identifiers_in_class(completion_context.current_class, true, false, true, options, 0); + _find_identifiers_in_class(completion_context.current_class, true, false, false, true, options, 0); } break; } @@ -3249,6 +3488,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co base_type.script_type = base_script; } else { base_type.kind = GDScriptParser::DataType::NATIVE; + base_type.builtin_type = Variant::OBJECT; base_type.native_type = scr->get_instance_base_type(); } } else { @@ -3307,6 +3547,12 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co } if (ClassDB::has_property(class_name, p_symbol, true)) { + PropertyInfo prop_info; + ClassDB::get_property_info(class_name, p_symbol, &prop_info, true); + if (prop_info.usage & PROPERTY_USAGE_INTERNAL) { + return ERR_CANT_RESOLVE; + } + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY; r_result.class_name = base_type.native_type; r_result.class_member = p_symbol; @@ -3385,6 +3631,12 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co } } + if ("Variant" == p_symbol) { + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS; + r_result.class_name = "Variant"; + return OK; + } + if ("PI" == p_symbol || "TAU" == p_symbol || "INF" == p_symbol || "NAN" == p_symbol) { r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT; r_result.class_name = "@GDScript"; @@ -3414,14 +3666,50 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co analyzer.analyze(); if (context.current_class && context.current_class->extends.size() > 0) { + StringName class_name = context.current_class->extends[0]->name; + bool success = false; - ClassDB::get_integer_constant(context.current_class->extends[0]->name, p_symbol, &success); + ClassDB::get_integer_constant(class_name, p_symbol, &success); if (success) { r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT; - r_result.class_name = context.current_class->extends[0]->name; + r_result.class_name = class_name; r_result.class_member = p_symbol; return OK; } + do { + List<StringName> enums; + ClassDB::get_enum_list(class_name, &enums, true); + for (const StringName &enum_name : enums) { + if (enum_name == p_symbol) { + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM; + r_result.class_name = class_name; + r_result.class_member = p_symbol; + return OK; + } + } + class_name = ClassDB::get_parent_class_nocheck(class_name); + } while (class_name != StringName()); + } + + const GDScriptParser::TypeNode *type_node = dynamic_cast<const GDScriptParser::TypeNode *>(context.node); + if (type_node != nullptr && !type_node->type_chain.is_empty()) { + StringName class_name = type_node->type_chain[0]->name; + if (ScriptServer::is_global_class(class_name)) { + class_name = ScriptServer::get_global_class_native_base(class_name); + } + do { + List<StringName> enums; + ClassDB::get_enum_list(class_name, &enums, true); + for (const StringName &enum_name : enums) { + if (enum_name == p_symbol) { + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM; + r_result.class_name = class_name; + r_result.class_member = p_symbol; + return OK; + } + } + class_name = ClassDB::get_parent_class_nocheck(class_name); + } while (class_name != StringName()); } bool is_function = false; @@ -3451,7 +3739,8 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co case GDScriptParser::COMPLETION_ASSIGN: case GDScriptParser::COMPLETION_CALL_ARGUMENTS: case GDScriptParser::COMPLETION_IDENTIFIER: - case GDScriptParser::COMPLETION_PROPERTY_METHOD: { + case GDScriptParser::COMPLETION_PROPERTY_METHOD: + case GDScriptParser::COMPLETION_SUBSCRIPT: { GDScriptParser::DataType base_type; if (context.current_class) { if (context.type != GDScriptParser::COMPLETION_SUPER_METHOD) { |