Skip to content

Commit 0abd0ae

Browse files
Autocompletion: rework argument options string literal completion
1 parent f3af22b commit 0abd0ae

30 files changed

+201
-21
lines changed

modules/gdscript/gdscript_parser.cpp

+37-15
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,26 @@ void GDScriptParser::apply_pending_warnings() {
245245
}
246246
#endif
247247

248-
void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node, int p_argument, bool p_force) {
249-
if (!for_completion || (!p_force && completion_context.type != COMPLETION_NONE)) {
248+
void GDScriptParser::override_completion_context(const Node *p_for_node, CompletionType p_type, Node *p_node, int p_argument) {
249+
if (!for_completion) {
250+
return;
251+
}
252+
if (completion_context.node != p_for_node) {
253+
return;
254+
}
255+
CompletionContext context;
256+
context.type = p_type;
257+
context.current_class = current_class;
258+
context.current_function = current_function;
259+
context.current_suite = current_suite;
260+
context.current_line = tokenizer->get_cursor_line();
261+
context.current_argument = p_argument;
262+
context.node = p_node;
263+
completion_context = context;
264+
}
265+
266+
void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node, int p_argument) {
267+
if (!for_completion) {
250268
return;
251269
}
252270
if (previous.cursor_place != GDScriptTokenizerText::CURSOR_MIDDLE && previous.cursor_place != GDScriptTokenizerText::CURSOR_END && current.cursor_place == GDScriptTokenizerText::CURSOR_NONE) {
@@ -263,8 +281,8 @@ void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node
263281
completion_context = context;
264282
}
265283

266-
void GDScriptParser::make_completion_context(CompletionType p_type, Variant::Type p_builtin_type, bool p_force) {
267-
if (!for_completion || (!p_force && completion_context.type != COMPLETION_NONE)) {
284+
void GDScriptParser::make_completion_context(CompletionType p_type, Variant::Type p_builtin_type) {
285+
if (!for_completion) {
268286
return;
269287
}
270288
if (previous.cursor_place != GDScriptTokenizerText::CURSOR_MIDDLE && previous.cursor_place != GDScriptTokenizerText::CURSOR_END && current.cursor_place == GDScriptTokenizerText::CURSOR_NONE) {
@@ -1618,15 +1636,15 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali
16181636
advance();
16191637
// Arguments.
16201638
push_completion_call(annotation);
1621-
make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, 0, true);
1639+
make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, 0);
16221640
int argument_index = 0;
16231641
do {
16241642
if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
16251643
// Allow for trailing comma.
16261644
break;
16271645
}
16281646

1629-
make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, argument_index, true);
1647+
make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, argument_index);
16301648
set_last_completion_call_arg(argument_index++);
16311649
ExpressionNode *argument = parse_expression(false);
16321650
if (argument == nullptr) {
@@ -2567,8 +2585,11 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_literal(ExpressionNode *p_
25672585
}
25682586

25692587
LiteralNode *literal = alloc_node<LiteralNode>();
2570-
complete_extents(literal);
25712588
literal->value = previous.literal;
2589+
reset_extents(literal, p_previous_operand);
2590+
update_extents(literal);
2591+
make_completion_context(COMPLETION_NONE, literal, -1);
2592+
complete_extents(literal);
25722593
return literal;
25732594
}
25742595

@@ -3063,12 +3084,12 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode *
30633084
const IdentifierNode *id = static_cast<const IdentifierNode *>(p_previous_operand);
30643085
Variant::Type builtin_type = get_builtin_type(id->name);
30653086
if (builtin_type < Variant::VARIANT_MAX) {
3066-
make_completion_context(COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD, builtin_type, true);
3087+
make_completion_context(COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD, builtin_type);
30673088
is_builtin = true;
30683089
}
30693090
}
30703091
if (!is_builtin) {
3071-
make_completion_context(COMPLETION_ATTRIBUTE, attribute, -1, true);
3092+
make_completion_context(COMPLETION_ATTRIBUTE, attribute, -1);
30723093
}
30733094
}
30743095

@@ -3193,23 +3214,24 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
31933214
push_completion_call(call);
31943215
int argument_index = 0;
31953216
do {
3196-
make_completion_context(ct, call, argument_index++, true);
3217+
make_completion_context(ct, call, argument_index);
31973218
if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
31983219
// Allow for trailing comma.
31993220
break;
32003221
}
3201-
bool use_identifier_completion = current.cursor_place == GDScriptTokenizerText::CURSOR_END || current.cursor_place == GDScriptTokenizerText::CURSOR_MIDDLE;
32023222
ExpressionNode *argument = parse_expression(false);
32033223
if (argument == nullptr) {
32043224
push_error(R"(Expected expression as the function argument.)");
32053225
} else {
32063226
call->arguments.push_back(argument);
32073227

3208-
if (argument->type == Node::IDENTIFIER && use_identifier_completion) {
3209-
completion_context.type = COMPLETION_IDENTIFIER;
3228+
if (argument->type == Node::LITERAL) {
3229+
override_completion_context(argument, ct, call, argument_index);
32103230
}
32113231
}
3232+
32123233
ct = COMPLETION_CALL_ARGUMENTS;
3234+
argument_index++;
32133235
} while (match(GDScriptTokenizer::Token::COMMA));
32143236
pop_completion_call();
32153237

@@ -3222,7 +3244,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
32223244

32233245
GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p_previous_operand, bool p_can_assign) {
32243246
// We want code completion after a DOLLAR even if the current code is invalid.
3225-
make_completion_context(COMPLETION_GET_NODE, nullptr, -1, true);
3247+
make_completion_context(COMPLETION_GET_NODE, nullptr, -1);
32263248

32273249
if (!current.is_node_name() && !check(GDScriptTokenizer::Token::LITERAL) && !check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) {
32283250
push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name()));
@@ -3279,7 +3301,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p
32793301
path_state = PATH_STATE_SLASH;
32803302
}
32813303

3282-
make_completion_context(COMPLETION_GET_NODE, get_node, context_argument++, true);
3304+
make_completion_context(COMPLETION_GET_NODE, get_node, context_argument++);
32833305

32843306
if (match(GDScriptTokenizer::Token::LITERAL)) {
32853307
if (previous.literal.get_type() != Variant::STRING) {

modules/gdscript/gdscript_parser.h

+5-3
Original file line numberDiff line numberDiff line change
@@ -1455,9 +1455,11 @@ class GDScriptParser {
14551455
}
14561456
void apply_pending_warnings();
14571457
#endif
1458-
1459-
void make_completion_context(CompletionType p_type, Node *p_node, int p_argument = -1, bool p_force = false);
1460-
void make_completion_context(CompletionType p_type, Variant::Type p_builtin_type, bool p_force = false);
1458+
void make_completion_context(CompletionType p_type, Node *p_node, int p_argument = -1);
1459+
void make_completion_context(CompletionType p_type, Variant::Type p_builtin_type);
1460+
// In some cases it might become necessary to alter the completion context after parsing a subexpression.
1461+
// For example to not override COMPLETE_CALL_ARGUMENTS with COMPLETION_NONE from string literals.
1462+
void override_completion_context(const Node *p_for_node, CompletionType p_type, Node *p_node, int p_argument = -1);
14611463
void push_completion_call(Node *p_call);
14621464
void pop_completion_call();
14631465
void set_last_completion_call_arg(int p_argument);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[output]
2+
include=[
3+
{"insert_text": "\"property_of_a\""},
4+
{"insert_text": "\"name\""},
5+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
extends Node
2+
3+
const A = preload ("res://completion/class_a.notest.gd")
4+
5+
func _ready() -> void:
6+
var a := A.new()
7+
var tween := get_tree().create_tween()
8+
tween.tween_property(a, "➡")

modules/gdscript/tests/scripts/completion/common/identifiers.cfg modules/gdscript/tests/scripts/completion/common/identifiers_in_call.cfg

+5-1
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@ include=[
1111
{"display": "func_of_a"},
1212
{"display": "signal_of_a"},
1313

14-
; GDScript: self.gd
14+
; GDScript: identifiers.gd
1515
{"display": "test_signal_1"},
1616
{"display": "test_signal_2"},
1717
{"display": "test_var_1"},
1818
{"display": "test_var_2"},
1919
{"display": "test_func_1"},
2020
{"display": "test_func_2"},
21+
{"display": "test_parameter_1"},
22+
{"display": "test_parameter_2"},
23+
{"display": "local_test_var_1"},
24+
{"display": "local_test_var_2"},
2125
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
extends "res://completion/class_a.notest.gd"
2+
3+
signal test_signal_1(a)
4+
signal test_signal_2(a: int)
5+
6+
var test_var_1
7+
var test_var_2: int
8+
9+
func test_func_1(t):
10+
pass
11+
12+
func test_func_2(t: int) -> void:
13+
pass
14+
15+
func _init(test_parameter_1, test_parameter_2: String):
16+
var local_test_var_1
17+
var local_test_var_2: int
18+
print(t➡)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
scene="res://completion/get_node/get_node.tscn"
2+
[output]
3+
include=[
4+
; Node
5+
{"display": "add_child"},
6+
{"display": "owner"},
7+
{"display": "child_entered_tree"},
8+
9+
; GDScript: class_a.notest.gd
10+
{"display": "property_of_a"},
11+
{"display": "func_of_a"},
12+
{"display": "signal_of_a"},
13+
14+
; GDScript: identifiers.gd
15+
{"display": "test_signal_1"},
16+
{"display": "test_signal_2"},
17+
{"display": "test_var_1"},
18+
{"display": "test_var_2"},
19+
{"display": "test_func_1"},
20+
{"display": "test_func_2"},
21+
{"display": "test_parameter_1"},
22+
{"display": "test_parameter_2"},
23+
{"display": "local_test_var_1"},
24+
{"display": "local_test_var_2"},
25+
]

modules/gdscript/tests/scripts/completion/common/identifiers.gd modules/gdscript/tests/scripts/completion/common/identifiers_in_function_body.gd

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,7 @@ func test_func_1(t):
1212
func test_func_2(t: int) -> void:
1313
pass
1414

15-
func _init():
15+
func _init(test_parameter_1, test_parameter_2: String):
16+
var local_test_var_1
17+
var local_test_var_2: int
1618
t
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
scene="res://completion/get_node/get_node.tscn"
2+
[output]
3+
include=[
4+
; Node
5+
{"display": "add_child"},
6+
{"display": "owner"},
7+
{"display": "child_entered_tree"},
8+
9+
; GDScript: class_a.notest.gd
10+
{"display": "property_of_a"},
11+
{"display": "func_of_a"},
12+
{"display": "signal_of_a"},
13+
14+
; GDScript: identifiers.gd
15+
{"display": "test_signal_1"},
16+
{"display": "test_signal_2"},
17+
{"display": "test_var_1"},
18+
{"display": "test_var_2"},
19+
{"display": "test_func_1"},
20+
{"display": "test_func_2"},
21+
{"display": "test_parameter_1"},
22+
{"display": "test_parameter_2"},
23+
{"display": "local_test_var_1"},
24+
{"display": "local_test_var_2"},
25+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# godotengine/godot#92226
2+
extends "res://completion/class_a.notest.gd"
3+
4+
signal test_signal_1(a)
5+
signal test_signal_2(a: int)
6+
7+
var test_var_1
8+
var test_var_2: int
9+
10+
func test_func_1(t):
11+
pass
12+
13+
func test_func_2(t: int) -> void:
14+
pass
15+
16+
func _init(test_parameter_1, test_parameter_2: String):
17+
var local_test_var_1
18+
var local_test_var_2: int
19+
print(t
20+
21+
if true:
22+
pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
scene="res://completion/get_node/get_node.tscn"
2+
[output]
3+
exclude=[
4+
; Node
5+
{"display": "add_child"},
6+
{"display": "owner"},
7+
{"display": "child_entered_tree"},
8+
{"display": "add_child"},
9+
10+
; GDScript: class_a.notest.gd
11+
{"display": "property_of_a"},
12+
{"display": "func_of_a"},
13+
{"display": "signal_of_a"},
14+
15+
; GDScript: no_completion_in_string.gd
16+
{"display": "test_signal_1"},
17+
{"display": "test_signal_2"},
18+
{"display": "test_var_1"},
19+
{"display": "test_var_2"},
20+
{"display": "test_func_1"},
21+
{"display": "test_func_2"},
22+
{"display": "test_parameter_1"},
23+
{"display": "test_parameter_2"},
24+
{"display": "local_test_var_1"},
25+
{"display": "local_test_var_2"},
26+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# godotengine/godot#62945
2+
extends "res://completion/class_a.notest.gd"
3+
4+
signal test_signal_1(a)
5+
signal test_signal_2(a: int)
6+
7+
var test_var_1
8+
var test_var_2: int
9+
10+
func test_func_1(t):
11+
pass
12+
13+
func test_func_2(t: int) -> void:
14+
pass
15+
16+
func _init(test_parameter_1, test_parameter_2: String):
17+
var local_test_var_1
18+
var local_test_var_2: int
19+
var a = "➡"

modules/gdscript/tests/scripts/completion/common/self.gd

+1
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ func test_func_2(t: int) -> void:
1414

1515
func _init():
1616
self.➡
17+
pass

modules/gdscript/tests/scripts/completion/filter/organized_export.gd

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ extends CPUParticles2D
55
@export_subgroup("Test Subgroup")
66

77
func _init():
8-
8+
t
9+
pass

0 commit comments

Comments
 (0)