You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-04 12:00:25 +00:00
Merge pull request #106198 from SatLess/User-Func-Autocomplete
Add code completion for user-defined methods when overriding in GDScript
This commit is contained in:
@@ -3519,9 +3519,29 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
|
||||
} break;
|
||||
case GDScriptParser::COMPLETION_OVERRIDE_METHOD: {
|
||||
GDScriptParser::DataType native_type = completion_context.current_class->base_type;
|
||||
GDScriptParser::FunctionNode *function_node = static_cast<GDScriptParser::FunctionNode *>(completion_context.node);
|
||||
bool is_static = function_node != nullptr && function_node->is_static;
|
||||
while (native_type.is_set() && native_type.kind != GDScriptParser::DataType::NATIVE) {
|
||||
switch (native_type.kind) {
|
||||
case GDScriptParser::DataType::CLASS: {
|
||||
for (const GDScriptParser::ClassNode::Member &member : native_type.class_type->members) {
|
||||
if (member.type != GDScriptParser::ClassNode::Member::FUNCTION) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (options.has(member.function->identifier->name) || completion_context.current_class->has_function(member.function->identifier->name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_static != member.function->is_static) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String display_name = member.function->identifier->name;
|
||||
display_name += member.function->signature + ":";
|
||||
ScriptLanguage::CodeCompletionOption option(display_name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);
|
||||
options.insert(member.function->identifier->name, option); // Insert name instead of display to track duplicates.
|
||||
}
|
||||
native_type = native_type.class_type->base_type;
|
||||
} break;
|
||||
default: {
|
||||
@@ -3542,17 +3562,20 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
|
||||
bool use_type_hint = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints").operator bool();
|
||||
|
||||
List<MethodInfo> virtual_methods;
|
||||
ClassDB::get_virtual_methods(class_name, &virtual_methods);
|
||||
|
||||
{
|
||||
if (is_static) {
|
||||
// Not truly a virtual method, but can also be "overridden".
|
||||
MethodInfo static_init("_static_init");
|
||||
static_init.return_val.type = Variant::NIL;
|
||||
static_init.flags |= METHOD_FLAG_STATIC | METHOD_FLAG_VIRTUAL;
|
||||
virtual_methods.push_back(static_init);
|
||||
} else {
|
||||
ClassDB::get_virtual_methods(class_name, &virtual_methods);
|
||||
}
|
||||
|
||||
for (const MethodInfo &mi : virtual_methods) {
|
||||
if (options.has(mi.name) || completion_context.current_class->has_function(mi.name)) {
|
||||
continue;
|
||||
}
|
||||
String method_hint = mi.name;
|
||||
if (method_hint.contains_char(':')) {
|
||||
method_hint = method_hint.get_slicec(':', 0);
|
||||
|
||||
@@ -1609,7 +1609,7 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum(bool p_is_abstract, bool p_
|
||||
return enum_node;
|
||||
}
|
||||
|
||||
void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNode *p_body, const String &p_type) {
|
||||
void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNode *p_body, const String &p_type, int p_signature_start) {
|
||||
if (!check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE) && !is_at_end()) {
|
||||
bool default_used = false;
|
||||
do {
|
||||
@@ -1660,15 +1660,30 @@ void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNod
|
||||
current_class->has_static_data = true;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (p_type == "function" && p_signature_start != -1) {
|
||||
int signature_end_pos = tokenizer->get_current_position() - 1;
|
||||
String source_code = tokenizer->get_source_code();
|
||||
p_function->signature = source_code.substr(p_signature_start, signature_end_pos - p_signature_start);
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
// TODO: Improve token consumption so it synchronizes to a statement boundary. This way we can get into the function body with unrecognized tokens.
|
||||
consume(GDScriptTokenizer::Token::COLON, vformat(R"(Expected ":" after %s declaration.)", p_type));
|
||||
}
|
||||
|
||||
GDScriptParser::FunctionNode *GDScriptParser::parse_function(bool p_is_abstract, bool p_is_static) {
|
||||
FunctionNode *function = alloc_node<FunctionNode>();
|
||||
function->is_static = p_is_static;
|
||||
|
||||
make_completion_context(COMPLETION_OVERRIDE_METHOD, function);
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
// The signature is something like `(a: int, b: int = 0) -> void`.
|
||||
// We start one token earlier, since the parser looks one token ahead.
|
||||
const int signature_start_pos = tokenizer->get_current_position();
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after "func".)")) {
|
||||
complete_extents(function);
|
||||
return nullptr;
|
||||
@@ -1678,7 +1693,6 @@ GDScriptParser::FunctionNode *GDScriptParser::parse_function(bool p_is_abstract,
|
||||
current_function = function;
|
||||
|
||||
function->identifier = parse_identifier();
|
||||
function->is_static = p_is_static;
|
||||
|
||||
SuiteNode *body = alloc_node<SuiteNode>();
|
||||
|
||||
@@ -1687,13 +1701,18 @@ GDScriptParser::FunctionNode *GDScriptParser::parse_function(bool p_is_abstract,
|
||||
|
||||
push_multiline(true);
|
||||
consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected opening "(" after function name.)");
|
||||
parse_function_signature(function, body, "function");
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
parse_function_signature(function, body, "function", signature_start_pos);
|
||||
#else // !TOOLS_ENABLED
|
||||
parse_function_signature(function, body, "function", -1);
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
current_suite = previous_suite;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
function->min_local_doc_line = previous.end_line + 1;
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
function->body = parse_suite("function declaration", body);
|
||||
|
||||
@@ -3621,7 +3640,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p
|
||||
SuiteNode *previous_suite = current_suite;
|
||||
current_suite = body;
|
||||
|
||||
parse_function_signature(function, body, "lambda");
|
||||
parse_function_signature(function, body, "lambda", -1);
|
||||
|
||||
current_suite = previous_suite;
|
||||
|
||||
|
||||
@@ -862,6 +862,7 @@ public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
MemberDocData doc_data;
|
||||
int min_local_doc_line = 0;
|
||||
String signature; // For autocompletion.
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
bool resolved_signature = false;
|
||||
@@ -1508,7 +1509,7 @@ private:
|
||||
EnumNode *parse_enum(bool p_is_abstract, bool p_is_static);
|
||||
ParameterNode *parse_parameter();
|
||||
FunctionNode *parse_function(bool p_is_abstract, bool p_is_static);
|
||||
void parse_function_signature(FunctionNode *p_function, SuiteNode *p_body, const String &p_type);
|
||||
void parse_function_signature(FunctionNode *p_function, SuiteNode *p_body, const String &p_type, int p_signature_start);
|
||||
SuiteNode *parse_suite(const String &p_context, SuiteNode *p_suite = nullptr, bool p_for_lambda = false);
|
||||
// Annotations
|
||||
AnnotationNode *parse_annotation(uint32_t p_valid_targets);
|
||||
|
||||
@@ -200,6 +200,12 @@ public:
|
||||
|
||||
static String get_token_name(Token::Type p_token_type);
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
// This is a temporary solution, as Tokens are not able to store their position, only lines and columns.
|
||||
virtual int get_current_position() const { return 0; }
|
||||
virtual String get_source_code() const { return ""; }
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
virtual int get_cursor_line() const = 0;
|
||||
virtual int get_cursor_column() const = 0;
|
||||
virtual void set_cursor_position(int p_line, int p_column) = 0;
|
||||
@@ -285,6 +291,11 @@ public:
|
||||
|
||||
const Vector<int> &get_continuation_lines() const { return continuation_lines; }
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
virtual int get_current_position() const override { return position; }
|
||||
virtual String get_source_code() const override { return source; }
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
virtual int get_cursor_line() const override;
|
||||
virtual int get_cursor_column() const override;
|
||||
virtual void set_cursor_position(int p_line, int p_column) override;
|
||||
|
||||
@@ -6,3 +6,18 @@ var property_of_a
|
||||
|
||||
func func_of_a():
|
||||
pass
|
||||
|
||||
|
||||
func _func_of_a_underscore():
|
||||
pass
|
||||
|
||||
|
||||
static func func_of_a_static():
|
||||
pass
|
||||
|
||||
func func_of_a_args(a: int):
|
||||
pass
|
||||
|
||||
func func_of_a_callable(call := func():
|
||||
var x_of_a = 10):
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
scene="res://completion/get_node/get_node.tscn"
|
||||
[output]
|
||||
include=[
|
||||
; GDScript: class_a.notest.gd
|
||||
{"display": "func_of_a():"},
|
||||
{"display": "func_of_a_args(a: int):"},
|
||||
{"display": "func_of_a_callable(call := func():
|
||||
var x_of_a = 10):"}
|
||||
]
|
||||
exclude=[
|
||||
; GDScript: class_a.notest.gd
|
||||
{"display": "_static_init() -> void:"},
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
extends "res://completion/class_a.notest.gd"
|
||||
|
||||
func ➡
|
||||
@@ -0,0 +1,14 @@
|
||||
scene="res://completion/get_node/get_node.tscn"
|
||||
[output]
|
||||
include=[
|
||||
; GDScript: class_a.notest.gd
|
||||
{"display": "_static_init() -> void:"},
|
||||
{"display": "func_of_a_static():"},
|
||||
]
|
||||
exclude=[
|
||||
; GDScript: class_a.notest.gd
|
||||
{"display": "func_of_a():"},
|
||||
{"display": "func_of_a_args(a: int):"},
|
||||
{"display": "func_of_a_callable(call := func():
|
||||
var x_of_a = 10):"},
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
extends "res://completion/class_a.notest.gd"
|
||||
|
||||
static func ➡
|
||||
@@ -0,0 +1,10 @@
|
||||
scene="res://completion/get_node/get_node.tscn"
|
||||
[output]
|
||||
include=[
|
||||
; GDScript: class_a.notest.gd
|
||||
{"display": "_func_of_a_underscore():"},
|
||||
]
|
||||
exclude=[
|
||||
; GDScript: class_a.notest.gd
|
||||
{"display": "_static_init() -> void:"},
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
extends "res://completion/class_a.notest.gd"
|
||||
|
||||
func _➡
|
||||
Reference in New Issue
Block a user