summaryrefslogtreecommitdiffstats
path: root/modules/gdscript/gdscript_parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript_parser.cpp')
-rw-r--r--modules/gdscript/gdscript_parser.cpp77
1 files changed, 48 insertions, 29 deletions
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index ecef852b4b..92f9c5fa11 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -249,7 +249,7 @@ void GDScriptParser::override_completion_context(const Node *p_for_node, Complet
if (!for_completion) {
return;
}
- if (completion_context.node != p_for_node) {
+ if (p_for_node == nullptr || completion_context.node != p_for_node) {
return;
}
CompletionContext context;
@@ -260,11 +260,12 @@ void GDScriptParser::override_completion_context(const Node *p_for_node, Complet
context.current_line = tokenizer->get_cursor_line();
context.current_argument = p_argument;
context.node = p_node;
+ context.parser = this;
completion_context = context;
}
-void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node, int p_argument) {
- if (!for_completion) {
+void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node, int p_argument, bool p_force) {
+ if (!for_completion || (!p_force && completion_context.type != COMPLETION_NONE)) {
return;
}
if (previous.cursor_place != GDScriptTokenizerText::CURSOR_MIDDLE && previous.cursor_place != GDScriptTokenizerText::CURSOR_END && current.cursor_place == GDScriptTokenizerText::CURSOR_NONE) {
@@ -282,8 +283,8 @@ void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node
completion_context = context;
}
-void GDScriptParser::make_completion_context(CompletionType p_type, Variant::Type p_builtin_type) {
- if (!for_completion) {
+void GDScriptParser::make_completion_context(CompletionType p_type, Variant::Type p_builtin_type, bool p_force) {
+ if (!for_completion || (!p_force && completion_context.type != COMPLETION_NONE)) {
return;
}
if (previous.cursor_place != GDScriptTokenizerText::CURSOR_MIDDLE && previous.cursor_place != GDScriptTokenizerText::CURSOR_END && current.cursor_place == GDScriptTokenizerText::CURSOR_NONE) {
@@ -2470,7 +2471,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr
}
// Completion can appear whenever an expression is expected.
- make_completion_context(COMPLETION_IDENTIFIER, nullptr);
+ make_completion_context(COMPLETION_IDENTIFIER, nullptr, -1, false);
GDScriptTokenizer::Token token = current;
GDScriptTokenizer::Token::Type token_type = token.type;
@@ -2487,8 +2488,17 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr
advance(); // Only consume the token if there's a valid rule.
+ // After a token was consumed, update the completion context regardless of a previously set context.
+
ExpressionNode *previous_operand = (this->*prefix_rule)(nullptr, p_can_assign);
+#ifdef TOOLS_ENABLED
+ // HACK: We can't create a context in parse_identifier since it is used in places were we don't want completion.
+ if (previous_operand != nullptr && previous_operand->type == GDScriptParser::Node::IDENTIFIER && prefix_rule == static_cast<ParseFunction>(&GDScriptParser::parse_identifier)) {
+ make_completion_context(COMPLETION_IDENTIFIER, previous_operand);
+ }
+#endif
+
while (p_precedence <= get_rule(current.type)->precedence) {
if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL) || lambda_ended) {
return previous_operand;
@@ -2923,6 +2933,11 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
}
assignment->assignee = p_previous_operand;
assignment->assigned_value = parse_expression(false);
+#ifdef TOOLS_ENABLED
+ if (assignment->assigned_value != nullptr && assignment->assigned_value->type == GDScriptParser::Node::IDENTIFIER) {
+ override_completion_context(assignment->assigned_value, COMPLETION_ASSIGN, assignment);
+ }
+#endif
if (assignment->assigned_value == nullptr) {
push_error(R"(Expected an expression after "=".)");
}
@@ -3122,6 +3137,12 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_subscript(ExpressionNode *
subscript->base = p_previous_operand;
subscript->index = parse_expression(false);
+#ifdef TOOLS_ENABLED
+ if (subscript->index != nullptr && subscript->index->type == Node::LITERAL) {
+ override_completion_context(subscript->index, COMPLETION_SUBSCRIPT, subscript);
+ }
+#endif
+
if (subscript->index == nullptr) {
push_error(R"(Expected expression after "[".)");
}
@@ -4086,7 +4107,7 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation)
return true;
}
-bool GDScriptParser::tool_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
+bool GDScriptParser::tool_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
#ifdef DEBUG_ENABLED
if (_is_tool) {
push_error(R"("@tool" annotation can only be used once.)", p_annotation);
@@ -4097,7 +4118,7 @@ bool GDScriptParser::tool_annotation(const AnnotationNode *p_annotation, Node *p
return true;
}
-bool GDScriptParser::icon_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
+bool GDScriptParser::icon_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
ERR_FAIL_COND_V_MSG(p_target->type != Node::CLASS, false, R"("@icon" annotation can only be applied to classes.)");
ERR_FAIL_COND_V(p_annotation->resolved_arguments.is_empty(), false);
@@ -4128,7 +4149,7 @@ bool GDScriptParser::icon_annotation(const AnnotationNode *p_annotation, Node *p
return true;
}
-bool GDScriptParser::onready_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
+bool GDScriptParser::onready_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
ERR_FAIL_COND_V_MSG(p_target->type != Node::VARIABLE, false, R"("@onready" annotation can only be applied to class variables.)");
if (current_class && !ClassDB::is_parent_class(current_class->get_datatype().native_type, SNAME("Node"))) {
@@ -4261,7 +4282,7 @@ static StringName _find_narrowest_native_or_global_class(const GDScriptParser::D
}
template <PropertyHint t_hint, Variant::Type t_type>
-bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
+bool GDScriptParser::export_annotations(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
ERR_FAIL_COND_V_MSG(p_target->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
ERR_FAIL_NULL_V(p_class, false);
@@ -4500,7 +4521,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
// For `@export_storage` and `@export_custom`, there is no need to check the variable type, argument values,
// or handle array exports in a special way, so they are implemented as separate methods.
-bool GDScriptParser::export_storage_annotation(const AnnotationNode *p_annotation, Node *p_node, ClassNode *p_class) {
+bool GDScriptParser::export_storage_annotation(AnnotationNode *p_annotation, Node *p_node, ClassNode *p_class) {
ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
VariableNode *variable = static_cast<VariableNode *>(p_node);
@@ -4522,7 +4543,7 @@ bool GDScriptParser::export_storage_annotation(const AnnotationNode *p_annotatio
return true;
}
-bool GDScriptParser::export_custom_annotation(const AnnotationNode *p_annotation, Node *p_node, ClassNode *p_class) {
+bool GDScriptParser::export_custom_annotation(AnnotationNode *p_annotation, Node *p_node, ClassNode *p_class) {
ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
ERR_FAIL_COND_V_MSG(p_annotation->resolved_arguments.size() < 2, false, R"(Annotation "@export_custom" requires 2 arguments.)");
@@ -4551,31 +4572,29 @@ bool GDScriptParser::export_custom_annotation(const AnnotationNode *p_annotation
}
template <PropertyUsageFlags t_usage>
-bool GDScriptParser::export_group_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
- AnnotationNode *annotation = const_cast<AnnotationNode *>(p_annotation);
-
- if (annotation->resolved_arguments.is_empty()) {
+bool GDScriptParser::export_group_annotations(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
+ if (p_annotation->resolved_arguments.is_empty()) {
return false;
}
- annotation->export_info.name = annotation->resolved_arguments[0];
+ p_annotation->export_info.name = p_annotation->resolved_arguments[0];
switch (t_usage) {
case PROPERTY_USAGE_CATEGORY: {
- annotation->export_info.usage = t_usage;
+ p_annotation->export_info.usage = t_usage;
} break;
case PROPERTY_USAGE_GROUP: {
- annotation->export_info.usage = t_usage;
- if (annotation->resolved_arguments.size() == 2) {
- annotation->export_info.hint_string = annotation->resolved_arguments[1];
+ p_annotation->export_info.usage = t_usage;
+ if (p_annotation->resolved_arguments.size() == 2) {
+ p_annotation->export_info.hint_string = p_annotation->resolved_arguments[1];
}
} break;
case PROPERTY_USAGE_SUBGROUP: {
- annotation->export_info.usage = t_usage;
- if (annotation->resolved_arguments.size() == 2) {
- annotation->export_info.hint_string = annotation->resolved_arguments[1];
+ p_annotation->export_info.usage = t_usage;
+ if (p_annotation->resolved_arguments.size() == 2) {
+ p_annotation->export_info.hint_string = p_annotation->resolved_arguments[1];
}
} break;
}
@@ -4583,7 +4602,7 @@ bool GDScriptParser::export_group_annotations(const AnnotationNode *p_annotation
return true;
}
-bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
+bool GDScriptParser::warning_annotations(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
#ifndef DEBUG_ENABLED
// Only available in debug builds.
return true;
@@ -4658,7 +4677,7 @@ bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Nod
#endif // DEBUG_ENABLED
}
-bool GDScriptParser::rpc_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
+bool GDScriptParser::rpc_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
ERR_FAIL_COND_V_MSG(p_target->type != Node::FUNCTION, false, vformat(R"("%s" annotation can only be applied to functions.)", p_annotation->name));
FunctionNode *function = static_cast<FunctionNode *>(p_target);
@@ -4719,7 +4738,7 @@ bool GDScriptParser::rpc_annotation(const AnnotationNode *p_annotation, Node *p_
return true;
}
-bool GDScriptParser::static_unload_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
+bool GDScriptParser::static_unload_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
ERR_FAIL_COND_V_MSG(p_target->type != Node::CLASS, false, vformat(R"("%s" annotation can only be applied to classes.)", p_annotation->name));
ClassNode *class_node = static_cast<ClassNode *>(p_target);
if (class_node->annotated_static_unload) {
@@ -4790,9 +4809,9 @@ String GDScriptParser::DataType::to_string() const {
return class_type->fqcn;
case SCRIPT: {
if (is_meta_type) {
- return script_type != nullptr ? script_type->get_class_name().operator String() : "";
+ return script_type.is_valid() ? script_type->get_class_name().operator String() : "";
}
- String name = script_type != nullptr ? script_type->get_name() : "";
+ String name = script_type.is_valid() ? script_type->get_name() : "";
if (!name.is_empty()) {
return name;
}