Autocompletion: Don't add parenthesis if Callable is expected

This commit is contained in:
HolonProduction 2024-08-31 12:12:19 +02:00
parent af2c713971
commit e7487263ad
16 changed files with 260 additions and 89 deletions

View file

@ -1091,9 +1091,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, bool p_types_only, 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, bool p_add_braces, 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) {
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, bool p_add_braces, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {
ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);
if (!p_parent_only) {
@ -1147,12 +1147,14 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
continue;
}
option = ScriptLanguage::CodeCompletionOption(member.function->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);
if (member.function->parameters.size() > 0 || (member.function->info.flags & METHOD_FLAG_VARARG)) {
option.insert_text += "(";
option.display += U"(\u2026)";
} else {
option.insert_text += "()";
option.display += "()";
if (p_add_braces) {
if (member.function->parameters.size() > 0 || (member.function->info.flags & METHOD_FLAG_VARARG)) {
option.insert_text += "(";
option.display += U"(\u2026)";
} else {
option.insert_text += "()";
option.display += "()";
}
}
break;
case GDScriptParser::ClassNode::Member::SIGNAL:
@ -1183,25 +1185,27 @@ 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, p_types_only, r_result, p_recursion_depth + 1);
_find_identifiers_in_base(base_type, p_only_functions, p_types_only, p_add_braces, r_result, p_recursion_depth + 1);
}
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_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, bool p_types_only, bool p_add_braces, 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 (!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 += "(";
option.display += U"(\u2026)";
if (p_add_braces) {
option.insert_text += "(";
option.display += U"(\u2026)";
}
r_result.insert(option.display, option);
}
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, p_types_only, 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, p_add_braces, r_result, p_recursion_depth);
// This already finds all parent identifiers, so we are done.
base_type = GDScriptParser::DataType();
} break;
@ -1252,12 +1256,14 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
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() || (E.flags & METHOD_FLAG_VARARG)) {
option.insert_text += "(";
option.display += U"(\u2026)";
} else {
option.insert_text += "()";
option.display += "()";
if (p_add_braces) {
if (E.arguments.size() || (E.flags & METHOD_FLAG_VARARG)) {
option.insert_text += "(";
option.display += U"(\u2026)";
} else {
option.insert_text += "()";
option.display += "()";
}
}
r_result.insert(option.display, option);
}
@ -1340,12 +1346,14 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
int location = p_recursion_depth + _get_method_location(type, E.name);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);
if (E.arguments.size() || (E.flags & METHOD_FLAG_VARARG)) {
option.insert_text += "(";
option.display += U"(\u2026)";
} else {
option.insert_text += "()";
option.display += "()";
if (p_add_braces) {
if (E.arguments.size() || (E.flags & METHOD_FLAG_VARARG)) {
option.insert_text += "(";
option.display += U"(\u2026)";
} else {
option.insert_text += "()";
option.display += "()";
}
}
r_result.insert(option.display, option);
}
@ -1413,12 +1421,14 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
continue;
}
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);
if (E.arguments.size() || (E.flags & METHOD_FLAG_VARARG)) {
option.insert_text += "(";
option.display += U"(\u2026)";
} else {
option.insert_text += "()";
option.display += "()";
if (p_add_braces) {
if (E.arguments.size() || (E.flags & METHOD_FLAG_VARARG)) {
option.insert_text += "(";
option.display += U"(\u2026)";
} else {
option.insert_text += "()";
option.display += "()";
}
}
r_result.insert(option.display, option);
}
@ -1432,14 +1442,14 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
}
static void _find_identifiers(const GDScriptParser::CompletionContext &p_context, bool p_only_functions, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {
static void _find_identifiers(const GDScriptParser::CompletionContext &p_context, bool p_only_functions, bool p_add_braces, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {
if (!p_only_functions && p_context.current_suite) {
// This includes function parameters, since they are also locals.
_find_identifiers_in_suite(p_context.current_suite, r_result);
}
if (p_context.current_class) {
_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);
_find_identifiers_in_class(p_context.current_class, p_only_functions, false, (!p_context.current_function || p_context.current_function->is_static), false, p_add_braces, r_result, p_recursion_depth);
}
List<StringName> functions;
@ -1448,12 +1458,14 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context
for (const StringName &E : functions) {
MethodInfo function = GDScriptUtilityFunctions::get_function_info(E);
ScriptLanguage::CodeCompletionOption option(String(E), ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);
if (function.arguments.size() || (function.flags & METHOD_FLAG_VARARG)) {
option.insert_text += "(";
option.display += U"(\u2026)";
} else {
option.insert_text += "()";
option.display += "()";
if (p_add_braces) {
if (function.arguments.size() || (function.flags & METHOD_FLAG_VARARG)) {
option.insert_text += "(";
option.display += U"(\u2026)";
} else {
option.insert_text += "()";
option.display += "()";
}
}
r_result.insert(option.display, option);
}
@ -1499,8 +1511,10 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context
const char **kwa = _keywords_with_args;
while (*kwa) {
ScriptLanguage::CodeCompletionOption option(*kwa, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);
option.insert_text += "(";
option.display += U"(\u2026)";
if (p_add_braces) {
option.insert_text += "(";
option.display += U"(\u2026)";
}
r_result.insert(option.display, option);
kwa++;
}
@ -1511,7 +1525,10 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context
for (const StringName &util_func_name : utility_func_names) {
ScriptLanguage::CodeCompletionOption option(util_func_name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);
option.insert_text += "(";
option.display += U"(\u2026)"; // As all utility functions contain an argument or more, this is hardcoded here.
if (p_add_braces) {
option.insert_text += "(";
option.display += U"(\u2026)"; // As all utility functions contain an argument or more, this is hardcoded here.
}
r_result.insert(option.display, option);
}
@ -1621,6 +1638,16 @@ static GDScriptCompletionIdentifier _type_from_property(const PropertyInfo &p_pr
return ci;
}
static GDScriptCompletionIdentifier _callable_type_from_method_info(const MethodInfo &p_method) {
GDScriptCompletionIdentifier ci;
ci.type.kind = GDScriptParser::DataType::BUILTIN;
ci.type.builtin_type = Variant::CALLABLE;
ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
ci.type.is_constant = true;
ci.type.method_info = p_method;
return ci;
}
#define MAX_COMPLETION_RECURSION 100
struct RecursionCheck {
int *counter;
@ -2458,14 +2485,13 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
r_type.type.kind = GDScriptParser::DataType::BUILTIN;
r_type.type.builtin_type = Variant::SIGNAL;
r_type.type.method_info = member.signal->method_info;
return true;
case GDScriptParser::ClassNode::Member::FUNCTION:
if (is_static && !member.function->is_static) {
return false;
}
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
r_type.type.kind = GDScriptParser::DataType::BUILTIN;
r_type.type.builtin_type = Variant::CALLABLE;
r_type = _callable_type_from_method_info(member.function->info);
return true;
case GDScriptParser::ClassNode::Member::CLASS:
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
@ -2505,6 +2531,12 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
}
}
if (scr->has_method(p_identifier)) {
MethodInfo mi = scr->get_method_info(p_identifier);
r_type = _callable_type_from_method_info(mi);
return true;
}
Ref<Script> parent = scr->get_base_script();
if (parent.is_valid()) {
base_type.script_type = parent;
@ -2539,23 +2571,35 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
return true;
}
}
MethodInfo method;
if (ClassDB::get_method_info(class_name, p_identifier, &method)) {
r_type = _callable_type_from_method_info(method);
return true;
}
return false;
} break;
case GDScriptParser::DataType::BUILTIN: {
Callable::CallError err;
Variant tmp;
Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
return false;
}
bool valid = false;
Variant res = tmp.get(p_identifier, &valid);
if (valid) {
r_type = _type_from_variant(res, p_context);
r_type.value = Variant();
r_type.type.is_constant = false;
if (Variant::has_builtin_method(base_type.builtin_type, p_identifier)) {
r_type = _callable_type_from_method_info(Variant::get_builtin_method_info(base_type.builtin_type, p_identifier));
return true;
} else {
Callable::CallError err;
Variant tmp;
Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
return false;
}
bool valid = false;
Variant res = tmp.get(p_identifier, &valid);
if (valid) {
r_type = _type_from_variant(res, p_context);
r_type.value = Variant();
r_type.type.is_constant = false;
return true;
}
}
return false;
} break;
@ -2726,6 +2770,22 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
return false;
}
static bool _guess_expecting_callable(GDScriptParser::CompletionContext &p_context) {
if (p_context.call.call != nullptr && p_context.call.call->type == GDScriptParser::Node::CALL) {
GDScriptParser::CallNode *call_node = static_cast<GDScriptParser::CallNode *>(p_context.call.call);
GDScriptCompletionIdentifier ci;
if (_guess_expression_type(p_context, call_node->callee, ci)) {
if (ci.type.kind == GDScriptParser::DataType::BUILTIN && ci.type.builtin_type == Variant::CALLABLE) {
if (p_context.call.argument >= 0 && p_context.call.argument < ci.type.method_info.arguments.size()) {
return ci.type.method_info.arguments.get(p_context.call.argument).type == Variant::CALLABLE;
}
}
}
}
return false;
}
static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_context, const String &p_enum_hint, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {
if (!p_enum_hint.contains_char('.')) {
// Global constant or in the current class.
@ -3267,10 +3327,12 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
for (const StringName &E : methods) {
if (Variant::is_builtin_method_static(completion_context.builtin_type, E)) {
ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);
if (Variant::get_builtin_method_argument_count(completion_context.builtin_type, E) > 0 || Variant::is_builtin_method_vararg(completion_context.builtin_type, E)) {
option.insert_text += "(";
} else {
option.insert_text += "()";
if (!_guess_expecting_callable(completion_context)) {
if (Variant::get_builtin_method_argument_count(completion_context.builtin_type, E) > 0 || Variant::is_builtin_method_vararg(completion_context.builtin_type, E)) {
option.insert_text += "(";
} else {
option.insert_text += "()";
}
}
options.insert(option.display, option);
}
@ -3328,7 +3390,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
break;
}
if (!_guess_expression_type(completion_context, static_cast<const GDScriptParser::AssignmentNode *>(completion_context.node)->assignee, type)) {
_find_identifiers(completion_context, false, options, 0);
_find_identifiers(completion_context, false, true, options, 0);
r_forced = true;
break;
}
@ -3337,7 +3399,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
_find_enumeration_candidates(completion_context, type.enumeration, options);
r_forced = options.size() > 0;
} else {
_find_identifiers(completion_context, false, options, 0);
_find_identifiers(completion_context, false, true, options, 0);
r_forced = true;
}
} break;
@ -3345,7 +3407,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
is_function = true;
[[fallthrough]];
case GDScriptParser::COMPLETION_IDENTIFIER: {
_find_identifiers(completion_context, is_function, options, 0);
_find_identifiers(completion_context, is_function, !_guess_expecting_callable(completion_context), options, 0);
} break;
case GDScriptParser::COMPLETION_ATTRIBUTE_METHOD:
is_function = true;
@ -3360,7 +3422,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
break;
}
_find_identifiers_in_base(base, is_function, false, options, 0);
_find_identifiers_in_base(base, is_function, false, !_guess_expecting_callable(completion_context), options, 0);
}
} break;
case GDScriptParser::COMPLETION_SUBSCRIPT: {
@ -3380,20 +3442,20 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
}
if (!subscript->index || subscript->index->type != GDScriptParser::Node::LITERAL) {
_find_identifiers(completion_context, false, options, 0);
_find_identifiers(completion_context, false, !_guess_expecting_callable(completion_context), options, 0);
}
} else if (res) {
if (!subscript->is_attribute) {
// Quote the options if they are not accessed as attribute.
HashMap<String, ScriptLanguage::CodeCompletionOption> opt;
_find_identifiers_in_base(base, false, false, opt, 0);
_find_identifiers_in_base(base, false, false, false, opt, 0);
for (const KeyValue<String, CodeCompletionOption> &E : opt) {
ScriptLanguage::CodeCompletionOption option(E.value.insert_text.quote(quote_style), E.value.kind, E.value.location);
options.insert(option.display, option);
}
} else {
_find_identifiers_in_base(base, false, false, options, 0);
_find_identifiers_in_base(base, false, false, !_guess_expecting_callable(completion_context), options, 0);
}
}
} break;
@ -3437,7 +3499,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
if (found) {
_find_identifiers_in_base(base, false, true, options, 0);
_find_identifiers_in_base(base, false, true, true, options, 0);
}
r_forced = true;
} break;
@ -3578,7 +3640,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, false, true, options, 0);
_find_identifiers_in_class(completion_context.current_class, true, false, false, true, !_guess_expecting_callable(completion_context), options, 0);
} break;
}