Prechádzať zdrojové kódy

Merge pull request #96375 from HolonProduction/completion-callable-braces

Autocompletion: Don't add parenthesis if `Callable` is expected
Thaddeus Crews 3 mesiacov pred
rodič
commit
9e158215d1
16 zmenil súbory, kde vykonal 260 pridanie a 89 odobranie
  1. 11 0
      modules/gdscript/gdscript_analyzer.cpp
  2. 133 71
      modules/gdscript/gdscript_editor.cpp
  3. 11 10
      modules/gdscript/gdscript_parser.cpp
  4. 6 8
      modules/gdscript/gdscript_parser.h
  5. 8 0
      modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/builtin_complete_braces.cfg
  6. 8 0
      modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/builtin_complete_braces.gd
  7. 8 0
      modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/builtin_incomplete_braces.cfg
  8. 8 0
      modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/builtin_incomplete_braces.gd
  9. 8 0
      modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/local_method_callable_arg_0.cfg
  10. 9 0
      modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/local_method_callable_arg_0.gd
  11. 8 0
      modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/local_method_callable_arg_1.cfg
  12. 9 0
      modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/local_method_callable_arg_1.gd
  13. 8 0
      modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/local_method_variant_arg_0.cfg
  14. 9 0
      modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/local_method_variant_arg_0.gd
  15. 8 0
      modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/native.cfg
  16. 8 0
      modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/native.gd

+ 11 - 0
modules/gdscript/gdscript_analyzer.cpp

@@ -1714,6 +1714,12 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
 		static_context = p_function->is_static;
 	}
 
+	MethodInfo method_info;
+	method_info.name = function_name;
+	if (p_function->is_static) {
+		method_info.flags |= MethodFlags::METHOD_FLAG_STATIC;
+	}
+
 	GDScriptParser::DataType prev_datatype = p_function->get_datatype();
 
 	GDScriptParser::DataType resolving_datatype;
@@ -1733,6 +1739,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
 
 	for (int i = 0; i < p_function->parameters.size(); i++) {
 		resolve_parameter(p_function->parameters[i]);
+		method_info.arguments.push_back(p_function->parameters[i]->get_datatype().to_property_info(p_function->parameters[i]->identifier->name));
 #ifdef DEBUG_ENABLED
 		if (p_function->parameters[i]->usages == 0 && !String(p_function->parameters[i]->identifier->name).begins_with("_")) {
 			parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, function_visible_name, p_function->parameters[i]->identifier->name);
@@ -1883,6 +1890,10 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
 	}
 #endif
 
+	method_info.default_arguments.append_array(p_function->default_arg_values);
+	method_info.return_val = p_function->get_datatype().to_property_info("");
+	p_function->info = method_info;
+
 	if (p_function->get_datatype().is_resolving()) {
 		p_function->set_datatype(prev_datatype);
 	}

+ 133 - 71
modules/gdscript/gdscript_editor.cpp

@@ -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;
 	}
 

+ 11 - 10
modules/gdscript/gdscript_parser.cpp

@@ -269,6 +269,9 @@ void GDScriptParser::override_completion_context(const Node *p_for_node, Complet
 	context.current_argument = p_argument;
 	context.node = p_node;
 	context.parser = this;
+	if (!completion_call_stack.is_empty()) {
+		context.call = completion_call_stack.back()->get();
+	}
 	completion_context = context;
 }
 
@@ -288,6 +291,9 @@ void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node
 	context.current_argument = p_argument;
 	context.node = p_node;
 	context.parser = this;
+	if (!completion_call_stack.is_empty()) {
+		context.call = completion_call_stack.back()->get();
+	}
 	completion_context = context;
 }
 
@@ -306,6 +312,9 @@ void GDScriptParser::make_completion_context(CompletionType p_type, Variant::Typ
 	context.current_line = tokenizer->get_cursor_line();
 	context.builtin_type = p_builtin_type;
 	context.parser = this;
+	if (!completion_call_stack.is_empty()) {
+		context.call = completion_call_stack.back()->get();
+	}
 	completion_context = context;
 }
 
@@ -317,9 +326,6 @@ void GDScriptParser::push_completion_call(Node *p_call) {
 	call.call = p_call;
 	call.argument = 0;
 	completion_call_stack.push_back(call);
-	if (previous.cursor_place == GDScriptTokenizerText::CURSOR_MIDDLE || previous.cursor_place == GDScriptTokenizerText::CURSOR_END || current.cursor_place == GDScriptTokenizerText::CURSOR_BEGINNING) {
-		completion_call = call;
-	}
 }
 
 void GDScriptParser::pop_completion_call() {
@@ -331,7 +337,7 @@ void GDScriptParser::pop_completion_call() {
 }
 
 void GDScriptParser::set_last_completion_call_arg(int p_argument) {
-	if (!for_completion || passed_cursor) {
+	if (!for_completion) {
 		return;
 	}
 	ERR_FAIL_COND_MSG(completion_call_stack.is_empty(), "Trying to set argument on empty completion call stack");
@@ -477,12 +483,6 @@ GDScriptTokenizer::Token GDScriptParser::advance() {
 	if (current.type == GDScriptTokenizer::Token::TK_EOF) {
 		ERR_FAIL_COND_V_MSG(current.type == GDScriptTokenizer::Token::TK_EOF, current, "GDScript parser bug: Trying to advance past the end of stream.");
 	}
-	if (for_completion && !completion_call_stack.is_empty()) {
-		if (completion_call.call == nullptr && tokenizer->is_past_cursor()) {
-			completion_call = completion_call_stack.back()->get();
-			passed_cursor = true;
-		}
-	}
 	previous = current;
 	current = tokenizer->scan();
 	while (current.type == GDScriptTokenizer::Token::ERROR) {
@@ -3340,6 +3340,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
 	int argument_index = 0;
 	do {
 		make_completion_context(ct, call, argument_index);
+		set_last_completion_call_arg(argument_index);
 		if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
 			// Allow for trailing comma.
 			break;

+ 6 - 8
modules/gdscript/gdscript_parser.h

@@ -1310,6 +1310,11 @@ public:
 		COMPLETION_TYPE_NAME_OR_VOID, // Same as TYPE_NAME, but allows void (in function return type).
 	};
 
+	struct CompletionCall {
+		Node *call = nullptr;
+		int argument = -1;
+	};
+
 	struct CompletionContext {
 		CompletionType type = COMPLETION_NONE;
 		ClassNode *current_class = nullptr;
@@ -1321,11 +1326,7 @@ public:
 		Node *node = nullptr;
 		Object *base = nullptr;
 		GDScriptParser *parser = nullptr;
-	};
-
-	struct CompletionCall {
-		Node *call = nullptr;
-		int argument = -1;
+		CompletionCall call;
 	};
 
 private:
@@ -1372,9 +1373,7 @@ private:
 	SuiteNode *current_suite = nullptr;
 
 	CompletionContext completion_context;
-	CompletionCall completion_call;
 	List<CompletionCall> completion_call_stack;
-	bool passed_cursor = false;
 	bool in_lambda = false;
 	bool lambda_ended = false; // Marker for when a lambda ends, to apply an end of statement if needed.
 
@@ -1586,7 +1585,6 @@ public:
 	static Variant::Type get_builtin_type(const StringName &p_type); // Excluding `Variant::NIL` and `Variant::OBJECT`.
 
 	CompletionContext get_completion_context() const { return completion_context; }
-	CompletionCall get_completion_call() const { return completion_call; }
 	void get_annotation_list(List<MethodInfo> *r_annotations) const;
 	bool annotation_exists(const String &p_annotation_name) const;
 

+ 8 - 0
modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/builtin_complete_braces.cfg

@@ -0,0 +1,8 @@
+[output]
+include=[
+    {"display": "hello_world", "insert_text": "hello_world"},
+]
+exclude=[
+    {"insert_text": "hello_world()"},
+    {"insert_text": "hello_world("},
+]

+ 8 - 0
modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/builtin_complete_braces.gd

@@ -0,0 +1,8 @@
+signal test
+
+func _init() -> void:
+    test.connect(h➡)
+    pass
+
+func hello_world():
+	pass

+ 8 - 0
modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/builtin_incomplete_braces.cfg

@@ -0,0 +1,8 @@
+[output]
+include=[
+    {"display": "hello_world", "insert_text": "hello_world"},
+]
+exclude=[
+    {"insert_text": "hello_world()"},
+    {"insert_text": "hello_world("},
+]

+ 8 - 0
modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/builtin_incomplete_braces.gd

@@ -0,0 +1,8 @@
+var arr: Array
+
+func _init() -> void:
+    arr.sort_custom(h➡
+    pass
+
+func hello_world():
+	pass

+ 8 - 0
modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/local_method_callable_arg_0.cfg

@@ -0,0 +1,8 @@
+[output]
+include=[
+    {"display": "hello_world", "insert_text": "hello_world"},
+]
+exclude=[
+    {"insert_text": "hello_world()"},
+    {"insert_text": "hello_world("},
+]

+ 9 - 0
modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/local_method_callable_arg_0.gd

@@ -0,0 +1,9 @@
+func test(a: Callable, b):
+    pass
+
+func _init() -> void:
+    test(h➡)
+    pass
+
+func hello_world():
+	pass

+ 8 - 0
modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/local_method_callable_arg_1.cfg

@@ -0,0 +1,8 @@
+[output]
+include=[
+    {"display": "hello_world", "insert_text": "hello_world"},
+]
+exclude=[
+    {"insert_text": "hello_world()"},
+    {"insert_text": "hello_world("},
+]

+ 9 - 0
modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/local_method_callable_arg_1.gd

@@ -0,0 +1,9 @@
+func test(a, b: Callable):
+    pass
+
+func _init() -> void:
+    test(hello_world(), h➡)
+    pass
+
+func hello_world():
+	pass

+ 8 - 0
modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/local_method_variant_arg_0.cfg

@@ -0,0 +1,8 @@
+[output]
+include=[
+    {"display": "hello_world()", "insert_text": "hello_world()"},
+]
+exclude=[
+    {"insert_text": "hello_world"},
+    {"insert_text": "hello_world("},
+]

+ 9 - 0
modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/local_method_variant_arg_0.gd

@@ -0,0 +1,9 @@
+func test(a, b: Callable):
+    pass
+
+func _init() -> void:
+    test(h➡)
+    pass
+
+func hello_world():
+	pass

+ 8 - 0
modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/native.cfg

@@ -0,0 +1,8 @@
+[output]
+include=[
+    {"display": "hello_world", "insert_text": "hello_world"},
+]
+exclude=[
+    {"display": "hello_world", "insert_text": "hello_world()"},
+    {"display": "hello_world", "insert_text": "hello_world("},
+]

+ 8 - 0
modules/gdscript/tests/scripts/completion/no_parenthesis_when_callable_is_expected/native.gd

@@ -0,0 +1,8 @@
+extends Node
+
+func _init() -> void:
+    create_tween().tween_callback(h➡)
+    pass
+
+func hello_world():
+	pass