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.cpp276
1 files changed, 201 insertions, 75 deletions
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 44e104da05..cae85d7c70 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -163,7 +163,7 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li
r_errors->push_back(e);
}
- for (KeyValue<String, Ref<GDScriptParserRef>> E : analyzer.get_depended_parsers()) {
+ for (KeyValue<String, Ref<GDScriptParserRef>> E : parser.get_depended_parsers()) {
GDScriptParser *depended_parser = E.value->get_parser();
for (const GDScriptParser::ParserError &pe : depended_parser->get_errors()) {
ScriptLanguage::ScriptError e;
@@ -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();
@@ -636,27 +636,29 @@ static int _get_method_location(const StringName &p_class, const StringName &p_m
return depth | ScriptLanguage::LOCATION_PARENT_MASK;
}
-static int _get_method_location(Ref<Script> p_script, const StringName &p_method) {
+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;
+ }
+
int depth = 0;
- Ref<Script> scr = p_script;
- while (scr.is_valid()) {
- if (scr->get_member_line(p_method) != -1) {
- return depth | ScriptLanguage::LOCATION_PARENT_MASK;
- }
+ StringName class_test = p_class;
+ while (class_test && !ClassDB::get_integer_constant_enum(class_test, p_enum_constant, true)) {
+ class_test = ClassDB::get_parent_class(class_test);
depth++;
- scr = scr->get_base_script();
}
- return depth + _get_method_location(p_script->get_instance_base_type(), p_method);
+
+ return depth | ScriptLanguage::LOCATION_PARENT_MASK;
}
-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)) {
+static int _get_enum_location(const StringName &p_class, const StringName &p_enum) {
+ if (!ClassDB::has_enum(p_class, p_enum)) {
return ScriptLanguage::LOCATION_OTHER;
}
int depth = 0;
StringName class_test = p_class;
- while (class_test && !ClassDB::get_integer_constant_enum(class_test, p_enum_constant, true)) {
+ while (class_test && !ClassDB::has_enum(class_test, p_enum, true)) {
class_test = ClassDB::get_parent_class(class_test);
depth++;
}
@@ -791,18 +793,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: {
@@ -835,17 +843,21 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio
return arghint;
}
-static void _get_directory_contents(EditorFileSystemDirectory *p_dir, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_list) {
+static void _get_directory_contents(EditorFileSystemDirectory *p_dir, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_list, const StringName &p_required_type = StringName()) {
const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
+ const bool requires_type = p_required_type;
for (int i = 0; i < p_dir->get_file_count(); i++) {
+ if (requires_type && !ClassDB::is_parent_class(p_dir->get_file_type(i), p_required_type)) {
+ continue;
+ }
ScriptLanguage::CodeCompletionOption option(p_dir->get_file_path(i), ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH);
option.insert_text = option.display.quote(quote_style);
r_list.insert(option.display, option);
}
for (int i = 0; i < p_dir->get_subdir_count(); i++) {
- _get_directory_contents(p_dir->get_subdir(i), r_list);
+ _get_directory_contents(p_dir->get_subdir(i), r_list, p_required_type);
}
}
@@ -1020,9 +1032,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) {
@@ -1036,13 +1048,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)) {
@@ -1060,7 +1072,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);
@@ -1072,7 +1084,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);
@@ -1083,7 +1095,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);
@@ -1095,6 +1107,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++;
@@ -1106,15 +1122,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);
@@ -1123,14 +1139,16 @@ 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);
@@ -1163,20 +1181,22 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
}
- 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, 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();
@@ -1197,6 +1217,18 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
return;
}
+ List<StringName> enums;
+ ClassDB::get_enum_list(type, &enums);
+ for (const StringName &E : enums) {
+ int location = p_recursion_depth + _get_enum_location(type, E);
+ ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, location);
+ r_result.insert(option.display, option);
+ }
+
+ if (p_types_only) {
+ return;
+ }
+
if (!p_only_functions) {
List<String> constants;
ClassDB::get_integer_constant_list(type, &constants);
@@ -1253,8 +1285,25 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
return;
} break;
- case GDScriptParser::DataType::ENUM:
+ case GDScriptParser::DataType::ENUM: {
+ String type_str = base_type.native_type;
+ StringName type = type_str.get_slicec('.', 0);
+ StringName type_enum = base_type.enum_type;
+
+ List<StringName> enum_values;
+ ClassDB::get_enum_constants(type, type_enum, &enum_values);
+ for (const StringName &E : enum_values) {
+ int location = p_recursion_depth + _get_enum_constant_location(type, E);
+ ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);
+ r_result.insert(option.display, option);
+ }
+ }
+ [[fallthrough]];
case GDScriptParser::DataType::BUILTIN: {
+ if (p_types_only) {
+ return;
+ }
+
Callable::CallError err;
Variant tmp;
Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);
@@ -1322,7 +1371,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;
@@ -1646,6 +1695,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
if (full) {
// If not fully constant, setting this value is detrimental to the inference.
r_type.value = a;
+ r_type.type.is_constant = true;
}
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
r_type.type.kind = GDScriptParser::DataType::BUILTIN;
@@ -2646,6 +2696,8 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
GDScriptParser::DataType base_type = p_base.type;
const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
+ const bool use_string_names = EDITOR_GET("text_editor/completion/add_string_name_literals");
+ const bool use_node_paths = EDITOR_GET("text_editor/completion/add_node_path_literals");
while (base_type.is_set() && !base_type.is_variant()) {
switch (base_type.kind) {
@@ -2679,8 +2731,14 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
List<String> options;
obj->get_argument_options(p_method, p_argidx, &options);
for (String &opt : options) {
+ // Handle user preference.
if (opt.is_quoted()) {
- opt = opt.unquote().quote(quote_style); // Handle user preference.
+ opt = opt.unquote().quote(quote_style);
+ if (use_string_names && info.arguments.get(p_argidx).type == Variant::STRING_NAME) {
+ opt = opt.indent("&");
+ } else if (use_node_paths && info.arguments.get(p_argidx).type == Variant::NODE_PATH) {
+ opt = opt.indent("^");
+ }
}
ScriptLanguage::CodeCompletionOption option(opt, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);
r_result.insert(option.display, option);
@@ -2689,7 +2747,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
if (p_argidx < method_args) {
- PropertyInfo arg_info = info.arguments[p_argidx];
+ const PropertyInfo &arg_info = info.arguments.get(p_argidx);
if (arg_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
_find_enumeration_candidates(p_context, arg_info.class_name, r_result);
}
@@ -2788,6 +2846,16 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
r_result.insert(option.display, option);
}
}
+ if (EDITOR_GET("text_editor/completion/complete_file_paths")) {
+ if (p_argidx == 0 && p_method == SNAME("change_scene_to_file") && ClassDB::is_parent_class(class_name, SNAME("SceneTree"))) {
+ HashMap<String, ScriptLanguage::CodeCompletionOption> list;
+ _get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), list, SNAME("PackedScene"));
+ for (const KeyValue<String, ScriptLanguage::CodeCompletionOption> &key_value_pair : list) {
+ ScriptLanguage::CodeCompletionOption option = key_value_pair.value;
+ r_result.insert(option.display, option);
+ }
+ }
+ }
base_type.kind = GDScriptParser::DataType::UNRESOLVED;
} break;
@@ -3157,7 +3225,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: {
@@ -3167,7 +3235,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) {
@@ -3175,25 +3243,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;
@@ -3251,19 +3335,17 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
method_hint += "(";
- if (mi.arguments.size()) {
- for (int i = 0; i < mi.arguments.size(); i++) {
- if (i > 0) {
- method_hint += ", ";
- }
- String arg = mi.arguments[i].name;
- if (arg.contains(":")) {
- arg = arg.substr(0, arg.find(":"));
- }
- method_hint += arg;
- if (use_type_hint) {
- method_hint += ": " + _get_visual_datatype(mi.arguments[i], true, class_name);
- }
+ for (List<PropertyInfo>::ConstIterator arg_itr = mi.arguments.begin(); arg_itr != mi.arguments.end(); ++arg_itr) {
+ if (arg_itr != mi.arguments.begin()) {
+ method_hint += ", ";
+ }
+ String arg = arg_itr->name;
+ if (arg.contains(":")) {
+ arg = arg.substr(0, arg.find(":"));
+ }
+ method_hint += arg;
+ if (use_type_hint) {
+ method_hint += ": " + _get_visual_datatype(*arg_itr, true, class_name);
}
}
method_hint += ")";
@@ -3303,9 +3385,17 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
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()) {
+ // 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.
@@ -3328,7 +3418,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;
}
@@ -3633,14 +3723,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;