Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix autocompletion for enum members in the core classes #84532

Merged
merged 1 commit into from
Feb 8, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 115 additions & 54 deletions modules/gdscript/gdscript_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -636,19 +636,6 @@ 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) {
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;
}
depth++;
scr = scr->get_base_script();
}
return depth + _get_method_location(p_script->get_instance_base_type(), 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;
Expand Down Expand Up @@ -1020,9 +1007,9 @@ static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite,
}
}

static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth);
static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, bool p_types_only, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth);

static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, bool p_only_functions, bool p_static, bool p_parent_only, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {
static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, bool p_only_functions, bool p_types_only, bool p_static, bool p_parent_only, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {
ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);

if (!p_parent_only) {
Expand All @@ -1036,13 +1023,13 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
ScriptLanguage::CodeCompletionOption option;
switch (member.type) {
case GDScriptParser::ClassNode::Member::VARIABLE:
if (p_only_functions || outer || (p_static && !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)) {
Expand All @@ -1060,7 +1047,7 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
option = ScriptLanguage::CodeCompletionOption(member.m_class->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, location);
break;
case GDScriptParser::ClassNode::Member::ENUM_VALUE:
if (p_only_functions) {
if (p_types_only || p_only_functions) {
continue;
}
option = ScriptLanguage::CodeCompletionOption(member.enum_value.identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);
Expand All @@ -1072,7 +1059,7 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
option = ScriptLanguage::CodeCompletionOption(member.m_enum->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, location);
break;
case GDScriptParser::ClassNode::Member::FUNCTION:
if (outer || (p_static && !member.function->is_static) || member.function->identifier->name.operator String().begins_with("@")) {
if (p_types_only || outer || (p_static && !member.function->is_static) || member.function->identifier->name.operator String().begins_with("@")) {
continue;
}
option = ScriptLanguage::CodeCompletionOption(member.function->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);
Expand All @@ -1083,7 +1070,7 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
}
break;
case GDScriptParser::ClassNode::Member::SIGNAL:
if (p_only_functions || outer || p_static) {
if (p_types_only || p_only_functions || outer || p_static) {
continue;
}
option = ScriptLanguage::CodeCompletionOption(member.signal->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);
Expand All @@ -1095,6 +1082,10 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
}
r_result.insert(option.display, option);
}
if (p_types_only) {
break; // Otherwise, it will fill the results with types from the outer class (which is undesired for that case).
}

outer = true;
clss = clss->outer;
classes_processed++;
Expand All @@ -1106,15 +1097,15 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
base_type.type = p_class->base_type;
base_type.type.is_meta_type = p_static;

_find_identifiers_in_base(base_type, p_only_functions, r_result, p_recursion_depth + 1);
_find_identifiers_in_base(base_type, p_only_functions, p_types_only, r_result, p_recursion_depth + 1);
}

static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {
static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, bool p_types_only, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {
ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);

GDScriptParser::DataType base_type = p_base.type;

if (base_type.is_meta_type && base_type.kind != GDScriptParser::DataType::BUILTIN && base_type.kind != GDScriptParser::DataType::ENUM) {
if (!p_types_only && base_type.is_meta_type && base_type.kind != GDScriptParser::DataType::BUILTIN && base_type.kind != GDScriptParser::DataType::ENUM) {
ScriptLanguage::CodeCompletionOption option("new", ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, ScriptLanguage::LOCATION_LOCAL);
option.insert_text += "(";
r_result.insert(option.display, option);
Expand All @@ -1123,14 +1114,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.
Chaosus marked this conversation as resolved.
Show resolved Hide resolved
} else if (!p_only_functions) {
if (!base_type.is_meta_type) {
List<PropertyInfo> members;
scr->get_script_property_list(&members);
Expand Down Expand Up @@ -1163,20 +1156,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();
Expand All @@ -1197,6 +1192,16 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
return;
}

if (p_types_only) {
List<StringName> enums;
ClassDB::get_enum_list(type, &enums);
for (const StringName &E : enums) {
ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_ENUM);
r_result.insert(option.display, option);
}
return;
}

if (!p_only_functions) {
List<String> constants;
ClassDB::get_integer_constant_list(type, &constants);
Expand Down Expand Up @@ -1255,6 +1260,10 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
} break;
case GDScriptParser::DataType::ENUM:
case GDScriptParser::DataType::BUILTIN: {
if (p_types_only) {
return;
}

Callable::CallError err;
Variant tmp;
Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);
Expand Down Expand Up @@ -1322,7 +1331,7 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context
}

if (p_context.current_class) {
_find_identifiers_in_class(p_context.current_class, p_only_functions, (!p_context.current_function || p_context.current_function->is_static), false, r_result, p_recursion_depth);
_find_identifiers_in_class(p_context.current_class, p_only_functions, false, (!p_context.current_function || p_context.current_function->is_static), false, r_result, p_recursion_depth);
}

List<StringName> functions;
Expand Down Expand Up @@ -3157,7 +3166,7 @@ ::Error GDScriptLanguage::complete_code(const String &p_code, const String &p_pa
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: {
Expand All @@ -3167,33 +3176,49 @@ ::Error GDScriptLanguage::complete_code(const String &p_code, const String &p_pa
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) {
break;
}
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;
Expand Down Expand Up @@ -3328,7 +3353,7 @@ ::Error GDScriptLanguage::complete_code(const String &p_code, const String &p_pa
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;
}

Expand Down Expand Up @@ -3633,14 +3658,50 @@ ::Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symb
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;
Expand Down
Loading