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.cpp397
1 files changed, 291 insertions, 106 deletions
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 87617cafab..babd2c1772 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -104,7 +104,7 @@ Ref<Script> GDScriptLanguage::make_template(const String &p_template, const Stri
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++) {
@@ -210,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();
@@ -537,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;
}
@@ -552,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;
}
@@ -567,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;
}
@@ -582,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;
}
@@ -597,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;
}
@@ -620,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;
}
}
@@ -739,18 +778,24 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio
const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(par->initializer);
if (call->is_constant && call->reduced) {
def_val = call->function_name.operator String() + call->reduced_value.operator String();
+ } else {
+ def_val = call->function_name.operator String() + (call->arguments.is_empty() ? "()" : "(...)");
}
} break;
case GDScriptParser::Node::ARRAY: {
const GDScriptParser::ArrayNode *arr = static_cast<const GDScriptParser::ArrayNode *>(par->initializer);
if (arr->is_constant && arr->reduced) {
def_val = arr->reduced_value.operator String();
+ } else {
+ def_val = arr->elements.is_empty() ? "[]" : "[...]";
}
} break;
case GDScriptParser::Node::DICTIONARY: {
const GDScriptParser::DictionaryNode *dict = static_cast<const GDScriptParser::DictionaryNode *>(par->initializer);
if (dict->is_constant && dict->reduced) {
def_val = dict->reduced_value.operator String();
+ } else {
+ def_val = dict->elements.is_empty() ? "{}" : "{...}";
}
} break;
case GDScriptParser::Node::SUBSCRIPT: {
@@ -968,9 +1013,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) {
@@ -984,13 +1029,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 && !member.variable->is_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)) {
@@ -1008,7 +1053,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);
@@ -1020,7 +1065,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);
@@ -1031,7 +1076,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);
@@ -1043,6 +1088,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++;
@@ -1054,15 +1103,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);
@@ -1071,25 +1120,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) {
- if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP)) {
+ 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->get_class_name(), E.name);
+ 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);
}
@@ -1097,7 +1148,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);
}
@@ -1105,26 +1156,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();
@@ -1145,6 +1198,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);
@@ -1158,7 +1221,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_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP)) {
+ if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL)) {
continue;
}
if (E.name.contains("/")) {
@@ -1203,6 +1266,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);
@@ -1210,6 +1277,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) {
@@ -1219,11 +1288,15 @@ 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)) {
+ 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];
}
@@ -1239,7 +1312,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 {
@@ -1264,7 +1337,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;
@@ -1364,7 +1437,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;
@@ -1386,8 +1459,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;
@@ -1412,8 +1499,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;
}
@@ -1475,7 +1573,7 @@ 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);
+ 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:
@@ -1489,16 +1587,13 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
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;
@@ -1673,7 +1768,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()) {
@@ -1724,7 +1819,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;
}
}
@@ -1752,7 +1847,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;
}
@@ -1804,7 +1899,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;
}
@@ -1859,7 +1954,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;
@@ -1917,7 +2012,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;
@@ -2006,6 +2101,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) {
@@ -2059,8 +2169,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;
}
}
@@ -2118,20 +2235,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);
@@ -2152,7 +2255,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;
}
@@ -2162,7 +2265,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;
}
@@ -2215,7 +2318,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;
@@ -2242,7 +2345,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;
@@ -2278,7 +2381,7 @@ 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;
}
@@ -2342,7 +2445,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;
@@ -3069,7 +3172,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: {
@@ -3079,7 +3182,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) {
@@ -3087,25 +3190,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;
@@ -3194,6 +3313,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();
@@ -3202,9 +3326,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.
@@ -3213,11 +3353,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;
@@ -3225,7 +3367,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;
}
@@ -3411,6 +3553,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;
@@ -3524,14 +3672,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;
@@ -3561,7 +3745,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) {