summaryrefslogtreecommitdiffstats
path: root/modules/gdscript/gdscript_editor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript_editor.cpp')
-rw-r--r--modules/gdscript/gdscript_editor.cpp483
1 files changed, 342 insertions, 141 deletions
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index cd34feb8b3..210e2c3898 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++) {
@@ -143,14 +154,26 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li
#endif
if (err) {
if (r_errors) {
- for (const GDScriptParser::ParserError &E : parser.get_errors()) {
- const GDScriptParser::ParserError &pe = E;
+ for (const GDScriptParser::ParserError &pe : parser.get_errors()) {
ScriptLanguage::ScriptError e;
+ e.path = p_path;
e.line = pe.line;
e.column = pe.column;
e.message = pe.message;
r_errors->push_back(e);
}
+
+ for (KeyValue<String, Ref<GDScriptParserRef>> E : analyzer.get_depended_parsers()) {
+ GDScriptParser *depended_parser = E.value->get_parser();
+ for (const GDScriptParser::ParserError &pe : depended_parser->get_errors()) {
+ ScriptLanguage::ScriptError e;
+ e.path = E.key;
+ e.line = pe.line;
+ e.column = pe.column;
+ e.message = pe.message;
+ r_errors->push_back(e);
+ }
+ }
}
return false;
} else {
@@ -178,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;
}
@@ -221,6 +240,10 @@ Script *GDScriptLanguage::create_script() const {
/* DEBUGGER FUNCTIONS */
+thread_local int GDScriptLanguage::_debug_parse_err_line = -1;
+thread_local String GDScriptLanguage::_debug_parse_err_file;
+thread_local String GDScriptLanguage::_debug_error;
+
bool GDScriptLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) {
// break because of parse error
@@ -229,6 +252,9 @@ bool GDScriptLanguage::debug_break_parse(const String &p_file, int p_line, const
_debug_parse_err_file = p_file;
_debug_error = p_error;
EngineDebugger::get_script_debugger()->debug(this, false, true);
+ // Because this is thread local, clear the memory afterwards.
+ _debug_parse_err_file = String();
+ _debug_error = String();
return true;
} else {
return false;
@@ -236,12 +262,15 @@ bool GDScriptLanguage::debug_break_parse(const String &p_file, int p_line, const
}
bool GDScriptLanguage::debug_break(const String &p_error, bool p_allow_continue) {
- if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
+ if (EngineDebugger::is_active()) {
_debug_parse_err_line = -1;
_debug_parse_err_file = "";
_debug_error = p_error;
bool is_error_breakpoint = p_error != "Breakpoint";
EngineDebugger::get_script_debugger()->debug(this, p_allow_continue, is_error_breakpoint);
+ // Because this is thread local, clear the memory afterwards.
+ _debug_parse_err_file = String();
+ _debug_error = String();
return true;
} else {
return false;
@@ -257,7 +286,7 @@ int GDScriptLanguage::debug_get_stack_level_count() const {
return 1;
}
- return _debug_call_stack_pos;
+ return _call_stack.stack_pos;
}
int GDScriptLanguage::debug_get_stack_level_line(int p_level) const {
@@ -265,11 +294,11 @@ int GDScriptLanguage::debug_get_stack_level_line(int p_level) const {
return _debug_parse_err_line;
}
- ERR_FAIL_INDEX_V(p_level, _debug_call_stack_pos, -1);
+ ERR_FAIL_INDEX_V(p_level, _call_stack.stack_pos, -1);
- int l = _debug_call_stack_pos - p_level - 1;
+ int l = _call_stack.stack_pos - p_level - 1;
- return *(_call_stack[l].line);
+ return *(_call_stack.levels[l].line);
}
String GDScriptLanguage::debug_get_stack_level_function(int p_level) const {
@@ -277,9 +306,9 @@ String GDScriptLanguage::debug_get_stack_level_function(int p_level) const {
return "";
}
- ERR_FAIL_INDEX_V(p_level, _debug_call_stack_pos, "");
- int l = _debug_call_stack_pos - p_level - 1;
- return _call_stack[l].function->get_name();
+ ERR_FAIL_INDEX_V(p_level, _call_stack.stack_pos, "");
+ int l = _call_stack.stack_pos - p_level - 1;
+ return _call_stack.levels[l].function->get_name();
}
String GDScriptLanguage::debug_get_stack_level_source(int p_level) const {
@@ -287,9 +316,9 @@ String GDScriptLanguage::debug_get_stack_level_source(int p_level) const {
return _debug_parse_err_file;
}
- ERR_FAIL_INDEX_V(p_level, _debug_call_stack_pos, "");
- int l = _debug_call_stack_pos - p_level - 1;
- return _call_stack[l].function->get_source();
+ ERR_FAIL_INDEX_V(p_level, _call_stack.stack_pos, "");
+ int l = _call_stack.stack_pos - p_level - 1;
+ return _call_stack.levels[l].function->get_source();
}
void GDScriptLanguage::debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {
@@ -297,17 +326,17 @@ void GDScriptLanguage::debug_get_stack_level_locals(int p_level, List<String> *p
return;
}
- ERR_FAIL_INDEX(p_level, _debug_call_stack_pos);
- int l = _debug_call_stack_pos - p_level - 1;
+ ERR_FAIL_INDEX(p_level, _call_stack.stack_pos);
+ int l = _call_stack.stack_pos - p_level - 1;
- GDScriptFunction *f = _call_stack[l].function;
+ GDScriptFunction *f = _call_stack.levels[l].function;
List<Pair<StringName, int>> locals;
- f->debug_get_stack_member_state(*_call_stack[l].line, &locals);
+ f->debug_get_stack_member_state(*_call_stack.levels[l].line, &locals);
for (const Pair<StringName, int> &E : locals) {
p_locals->push_back(E.first);
- p_values->push_back(_call_stack[l].stack[E.second]);
+ p_values->push_back(_call_stack.levels[l].stack[E.second]);
}
}
@@ -316,10 +345,10 @@ void GDScriptLanguage::debug_get_stack_level_members(int p_level, List<String> *
return;
}
- ERR_FAIL_INDEX(p_level, _debug_call_stack_pos);
- int l = _debug_call_stack_pos - p_level - 1;
+ ERR_FAIL_INDEX(p_level, _call_stack.stack_pos);
+ int l = _call_stack.stack_pos - p_level - 1;
- GDScriptInstance *instance = _call_stack[l].instance;
+ GDScriptInstance *instance = _call_stack.levels[l].instance;
if (!instance) {
return;
@@ -341,10 +370,10 @@ ScriptInstance *GDScriptLanguage::debug_get_stack_level_instance(int p_level) {
return nullptr;
}
- ERR_FAIL_INDEX_V(p_level, _debug_call_stack_pos, nullptr);
+ ERR_FAIL_INDEX_V(p_level, _call_stack.stack_pos, nullptr);
- int l = _debug_call_stack_pos - p_level - 1;
- ScriptInstance *instance = _call_stack[l].instance;
+ int l = _call_stack.stack_pos - p_level - 1;
+ ScriptInstance *instance = _call_stack.levels[l].instance;
return instance;
}
@@ -478,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;
}
}
@@ -508,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;
}
@@ -523,7 +552,7 @@ 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_constant_location(const StringName &p_class, const StringName &p_constant) {
if (!ClassDB::has_integer_constant(p_class, p_constant)) {
return ScriptLanguage::LOCATION_OTHER;
}
@@ -538,7 +567,7 @@ 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_signal_location(const StringName &p_class, const StringName &p_signal) {
if (!ClassDB::has_signal(p_class, p_signal)) {
return ScriptLanguage::LOCATION_OTHER;
}
@@ -553,7 +582,7 @@ 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_method_location(const StringName &p_class, const StringName &p_method) {
if (!ClassDB::has_method(p_class, p_method)) {
return ScriptLanguage::LOCATION_OTHER;
}
@@ -568,7 +597,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;
}
@@ -591,9 +620,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;
}
}
@@ -796,9 +825,10 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
ScriptLanguage::CodeCompletionOption node("Node", ScriptLanguage::CODE_COMPLETION_KIND_CLASS);
node.insert_text = node.display.quote(p_quote_style);
r_result.insert(node.display, node);
- List<StringName> node_types;
- ClassDB::get_inheriters_from_class("Node", &node_types);
- for (const StringName &E : node_types) {
+
+ List<StringName> native_classes;
+ ClassDB::get_inheriters_from_class("Node", &native_classes);
+ for (const StringName &E : native_classes) {
if (!ClassDB::is_class_exposed(E)) {
continue;
}
@@ -806,6 +836,17 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
option.insert_text = option.display.quote(p_quote_style);
r_result.insert(option.display, option);
}
+
+ List<StringName> global_script_classes;
+ ScriptServer::get_global_class_list(&global_script_classes);
+ for (const StringName &E : global_script_classes) {
+ if (!ClassDB::is_parent_class(ScriptServer::get_global_class_native_base(E), "Node")) {
+ continue;
+ }
+ ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS);
+ option.insert_text = option.display.quote(p_quote_style);
+ r_result.insert(option.display, option);
+ }
} else if (p_annotation->name == SNAME("@warning_ignore")) {
for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) {
ScriptLanguage::CodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
@@ -943,7 +984,7 @@ 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_only_functions || outer || (p_static && !member.variable->is_static)) {
continue;
}
option = ScriptLanguage::CodeCompletionOption(member.variable->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);
@@ -1042,7 +1083,13 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
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.class_name);
+ if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP)) {
+ continue;
+ }
+ if (E.name.contains("/")) {
+ continue;
+ }
+ int location = p_recursion_depth + _get_property_location(scr->get_class_name(), E.name);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);
r_result.insert(option.display, option);
}
@@ -1085,6 +1132,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 {
@@ -1110,13 +1158,13 @@ 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)) {
continue;
}
if (E.name.contains("/")) {
continue;
}
- int location = p_recursion_depth + _get_property_location(type, E.class_name);
+ int location = p_recursion_depth + _get_property_location(type, E.name);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);
r_result.insert(option.display, option);
}
@@ -1162,6 +1210,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) {
@@ -1171,8 +1221,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)) {
+ 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);
}
}
@@ -1185,7 +1245,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 {
@@ -1248,7 +1308,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
};
@@ -1310,7 +1370,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;
@@ -1332,8 +1392,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;
@@ -1358,8 +1432,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;
}
@@ -1381,7 +1466,7 @@ struct RecursionCheck {
}
};
-static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);
+static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::IdentifierNode *p_identifier, GDScriptCompletionIdentifier &r_type);
static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);
static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type);
@@ -1421,31 +1506,33 @@ 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) {
- r_type.type.kind = GDScriptParser::DataType::CLASS;
- r_type.type.type_source = GDScriptParser::DataType::INFERRED;
- r_type.type.is_constant = true;
- r_type.type.class_type = p_context.current_class;
- r_type.value = p_context.base;
+ r_type.type = p_context.current_class->get_datatype();
+ r_type.type.is_meta_type = false;
found = true;
}
} break;
case GDScriptParser::Node::IDENTIFIER: {
const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);
- found = _guess_identifier_type(p_context, id->name, r_type);
+ found = _guess_identifier_type(p_context, id, r_type);
} break;
case GDScriptParser::Node::DICTIONARY: {
// Try to recreate the dictionary.
@@ -1581,6 +1668,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;
@@ -1613,7 +1701,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()) {
@@ -1664,7 +1752,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;
}
}
@@ -1692,7 +1780,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;
}
@@ -1744,7 +1832,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;
}
@@ -1799,7 +1887,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;
@@ -1857,7 +1945,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;
@@ -1888,7 +1976,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
return found;
}
-static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) {
+static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::IdentifierNode *p_identifier, GDScriptCompletionIdentifier &r_type) {
static int recursion_depth = 0;
RecursionCheck recursion(&recursion_depth);
if (unlikely(recursion.check())) {
@@ -1902,36 +1990,49 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
GDScriptParser::SuiteNode *suite = p_context.current_suite;
bool is_function_parameter = false;
- if (suite) {
- if (suite->has_local(p_identifier)) {
- const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_identifier);
+ bool can_be_local = true;
+ switch (p_identifier->source) {
+ case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
+ case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
+ case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:
+ case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:
+ case GDScriptParser::IdentifierNode::MEMBER_CLASS:
+ case GDScriptParser::IdentifierNode::INHERITED_VARIABLE:
+ case GDScriptParser::IdentifierNode::STATIC_VARIABLE:
+ can_be_local = false;
+ break;
+ default:
+ break;
+ }
- id_type = local.get_datatype();
+ if (can_be_local && suite && suite->has_local(p_identifier->name)) {
+ const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_identifier->name);
- // Check initializer as the first assignment.
- switch (local.type) {
- case GDScriptParser::SuiteNode::Local::VARIABLE:
- if (local.variable->initializer) {
- last_assign_line = local.variable->initializer->end_line;
- last_assigned_expression = local.variable->initializer;
- }
- break;
- case GDScriptParser::SuiteNode::Local::CONSTANT:
- if (local.constant->initializer) {
- last_assign_line = local.constant->initializer->end_line;
- last_assigned_expression = local.constant->initializer;
- }
- break;
- case GDScriptParser::SuiteNode::Local::PARAMETER:
- if (local.parameter->initializer) {
- last_assign_line = local.parameter->initializer->end_line;
- last_assigned_expression = local.parameter->initializer;
- }
- is_function_parameter = true;
- break;
- default:
- break;
- }
+ id_type = local.get_datatype();
+
+ // Check initializer as the first assignment.
+ switch (local.type) {
+ case GDScriptParser::SuiteNode::Local::VARIABLE:
+ if (local.variable->initializer) {
+ last_assign_line = local.variable->initializer->end_line;
+ last_assigned_expression = local.variable->initializer;
+ }
+ break;
+ case GDScriptParser::SuiteNode::Local::CONSTANT:
+ if (local.constant->initializer) {
+ last_assign_line = local.constant->initializer->end_line;
+ last_assigned_expression = local.constant->initializer;
+ }
+ break;
+ case GDScriptParser::SuiteNode::Local::PARAMETER:
+ if (local.parameter->initializer) {
+ last_assign_line = local.parameter->initializer->end_line;
+ last_assigned_expression = local.parameter->initializer;
+ }
+ is_function_parameter = true;
+ break;
+ default:
+ break;
}
}
@@ -1946,7 +2047,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
const GDScriptParser::AssignmentNode *assign = static_cast<const GDScriptParser::AssignmentNode *>(suite->statements[i]);
if (assign->end_line > last_assign_line && assign->assignee && assign->assigned_value && assign->assignee->type == GDScriptParser::Node::IDENTIFIER) {
const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(assign->assignee);
- if (id->name == p_identifier) {
+ if (id->name == p_identifier->name && id->source == p_identifier->source) {
last_assign_line = assign->assigned_value->end_line;
last_assigned_expression = assign->assigned_value;
}
@@ -1964,7 +2065,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
// Credit: Zylann.
// TODO: this could be hacked to detect ANDed conditions too...
const GDScriptParser::TypeTestNode *type_test = static_cast<const GDScriptParser::TypeTestNode *>(suite->parent_if->condition);
- if (type_test->operand && type_test->test_type && type_test->operand->type == GDScriptParser::Node::IDENTIFIER && static_cast<const GDScriptParser::IdentifierNode *>(type_test->operand)->name == p_identifier) {
+ if (type_test->operand && type_test->test_type && type_test->operand->type == GDScriptParser::Node::IDENTIFIER && static_cast<const GDScriptParser::IdentifierNode *>(type_test->operand)->name == p_identifier->name && static_cast<const GDScriptParser::IdentifierNode *>(type_test->operand)->source == p_identifier->source) {
// Bingo.
GDScriptParser::CompletionContext c = p_context;
c.current_line = type_test->operand->start_line;
@@ -2000,8 +2101,8 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
case GDScriptParser::DataType::CLASS:
if (base_type.class_type->has_function(p_context.current_function->identifier->name)) {
GDScriptParser::FunctionNode *parent_function = base_type.class_type->get_member(p_context.current_function->identifier->name).function;
- if (parent_function->parameters_indices.has(p_identifier)) {
- const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier]];
+ if (parent_function->parameters_indices.has(p_identifier->name)) {
+ const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier->name]];
if ((!id_type.is_set() || id_type.is_variant()) && parameter->get_datatype().is_hard_type()) {
id_type = parameter->get_datatype();
}
@@ -2026,7 +2127,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
MethodInfo info;
if (ClassDB::get_method_info(base_type.native_type, p_context.current_function->identifier->name, &info)) {
for (const PropertyInfo &E : info.arguments) {
- if (E.name == p_identifier) {
+ if (E.name == p_identifier->name) {
r_type = _type_from_property(E);
return true;
}
@@ -2054,14 +2155,14 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
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, r_type)) {
+ 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)) {
- String script = ScriptServer::get_global_class_path(p_identifier);
+ if (ScriptServer::is_global_class(p_identifier->name)) {
+ String script = ScriptServer::get_global_class_path(p_identifier->name);
if (script.to_lower().ends_with(".gd")) {
Error err = OK;
Ref<GDScriptParserRef> parser = GDScriptCache::get_parser(script, GDScriptParserRef::INTERFACE_SOLVED, err);
@@ -2077,9 +2178,9 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
return true;
}
} else {
- Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier));
+ 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;
}
@@ -2088,20 +2189,21 @@ 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)) {
- r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier]);
+ 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], p_context);
return true;
}
// Check ClassDB.
- if (ClassDB::class_exists(p_identifier) && ClassDB::is_class_exposed(p_identifier)) {
+ 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.native_type = p_identifier;
+ 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)) {
+ if (Engine::get_singleton()->has_singleton(p_identifier->name)) {
r_type.type.is_meta_type = false;
- r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier);
+ r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier->name);
} else {
r_type.type.is_meta_type = true;
r_type.value = Variant();
@@ -2133,7 +2235,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;
@@ -2141,7 +2243,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;
@@ -2168,7 +2270,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;
@@ -2204,25 +2306,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 {
@@ -2264,7 +2370,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;
@@ -2392,6 +2498,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 {
@@ -2531,6 +2638,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;
@@ -2597,6 +2762,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) {
@@ -2605,6 +2771,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) {
@@ -2645,10 +2816,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;
}
}
@@ -2675,8 +2855,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);
@@ -3044,6 +3222,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();
@@ -3052,6 +3235,14 @@ 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();
}
+
+ if (for_unique_name) {
+ if (!opt.begins_with("%")) {
+ continue;
+ }
+ opt = opt.substr(1);
+ }
+
// 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()) {
@@ -3063,11 +3254,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;
@@ -3202,6 +3395,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 {
@@ -3338,6 +3532,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";
@@ -3404,7 +3604,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) {