summaryrefslogtreecommitdiffstats
path: root/modules/gdscript/gdscript_editor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript_editor.cpp')
-rw-r--r--modules/gdscript/gdscript_editor.cpp387
1 files changed, 278 insertions, 109 deletions
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 854c944e14..3de1decc18 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -97,8 +97,8 @@ Ref<Script> GDScriptLanguage::make_template(const String &p_template, const Stri
}
processed_template = processed_template.replace("_BASE_", p_base_class_name)
- .replace("_CLASS_SNAKE_CASE_", p_class_name.to_snake_case().validate_identifier())
- .replace("_CLASS_", p_class_name.to_pascal_case().validate_identifier())
+ .replace("_CLASS_SNAKE_CASE_", p_class_name.to_snake_case().validate_unicode_identifier())
+ .replace("_CLASS_", p_class_name.to_pascal_case().validate_unicode_identifier())
.replace("_TS_", _get_indentation());
scr->set_source_code(processed_template);
return scr;
@@ -163,7 +163,7 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li
r_errors->push_back(e);
}
- for (KeyValue<String, Ref<GDScriptParserRef>> E : analyzer.get_depended_parsers()) {
+ for (KeyValue<String, Ref<GDScriptParserRef>> E : parser.get_depended_parsers()) {
GDScriptParser *depended_parser = E.value->get_parser();
for (const GDScriptParser::ParserError &pe : depended_parser->get_errors()) {
ScriptLanguage::ScriptError e;
@@ -402,7 +402,9 @@ void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant>
}
const Variant &var = gl_array[E.value];
- if (Object *obj = var) {
+ bool freed = false;
+ const Object *obj = var.get_validated_object_with_check(freed);
+ if (obj && !freed) {
if (Object::cast_to<GDScriptNativeClass>(obj)) {
continue;
}
@@ -651,6 +653,21 @@ static int _get_enum_constant_location(const StringName &p_class, const StringNa
return depth | ScriptLanguage::LOCATION_PARENT_MASK;
}
+static int _get_enum_location(const StringName &p_class, const StringName &p_enum) {
+ if (!ClassDB::has_enum(p_class, p_enum)) {
+ return ScriptLanguage::LOCATION_OTHER;
+ }
+
+ int depth = 0;
+ StringName class_test = p_class;
+ while (class_test && !ClassDB::has_enum(class_test, p_enum, true)) {
+ class_test = ClassDB::get_parent_class(class_test);
+ depth++;
+ }
+
+ return depth | ScriptLanguage::LOCATION_PARENT_MASK;
+}
+
// END LOCATION METHODS
static String _trim_parent_class(const String &p_class, const String &p_base_class) {
@@ -680,6 +697,10 @@ static String _get_visual_datatype(const PropertyInfo &p_info, bool p_is_arg, co
return _trim_parent_class(class_name, p_base_class);
} else if (p_info.type == Variant::ARRAY && p_info.hint == PROPERTY_HINT_ARRAY_TYPE && !p_info.hint_string.is_empty()) {
return "Array[" + _trim_parent_class(p_info.hint_string, p_base_class) + "]";
+ } else if (p_info.type == Variant::DICTIONARY && p_info.hint == PROPERTY_HINT_DICTIONARY_TYPE && !p_info.hint_string.is_empty()) {
+ const String key = p_info.hint_string.get_slice(";", 0);
+ const String value = p_info.hint_string.get_slice(";", 1);
+ return "Dictionary[" + _trim_parent_class(key, p_base_class) + ", " + _trim_parent_class(value, p_base_class) + "]";
} else if (p_info.type == Variant::NIL) {
if (p_is_arg || (p_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) {
return "Variant";
@@ -739,13 +760,17 @@ static String _make_arguments_hint(const MethodInfo &p_info, int p_arg_idx, bool
return arghint;
}
-static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_function, int p_arg_idx) {
+static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_function, int p_arg_idx, bool p_just_args = false) {
String arghint;
- if (p_function->get_datatype().builtin_type == Variant::NIL) {
- arghint = "void " + p_function->identifier->name.operator String() + "(";
+ if (p_just_args) {
+ arghint = "(";
} else {
- arghint = p_function->get_datatype().to_string() + " " + p_function->identifier->name.operator String() + "(";
+ if (p_function->get_datatype().builtin_type == Variant::NIL) {
+ arghint = "void " + p_function->identifier->name.operator String() + "(";
+ } else {
+ arghint = p_function->get_datatype().to_string() + " " + p_function->identifier->name.operator String() + "(";
+ }
}
for (int i = 0; i < p_function->parameters.size(); i++) {
@@ -777,7 +802,7 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio
case GDScriptParser::Node::CALL: {
const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(par->initializer);
if (call->is_constant && call->reduced) {
- def_val = call->function_name.operator String() + call->reduced_value.operator String();
+ def_val = call->reduced_value.get_construct_string();
} else {
def_val = call->function_name.operator String() + (call->arguments.is_empty() ? "()" : "(...)");
}
@@ -785,7 +810,7 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio
case GDScriptParser::Node::ARRAY: {
const GDScriptParser::ArrayNode *arr = static_cast<const GDScriptParser::ArrayNode *>(par->initializer);
if (arr->is_constant && arr->reduced) {
- def_val = arr->reduced_value.operator String();
+ def_val = arr->reduced_value.get_construct_string();
} else {
def_val = arr->elements.is_empty() ? "[]" : "[...]";
}
@@ -793,24 +818,17 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio
case GDScriptParser::Node::DICTIONARY: {
const GDScriptParser::DictionaryNode *dict = static_cast<const GDScriptParser::DictionaryNode *>(par->initializer);
if (dict->is_constant && dict->reduced) {
- def_val = dict->reduced_value.operator String();
+ def_val = dict->reduced_value.get_construct_string();
} else {
def_val = dict->elements.is_empty() ? "{}" : "{...}";
}
} break;
case GDScriptParser::Node::SUBSCRIPT: {
const GDScriptParser::SubscriptNode *sub = static_cast<const GDScriptParser::SubscriptNode *>(par->initializer);
- if (sub->is_constant) {
- if (sub->datatype.kind == GDScriptParser::DataType::ENUM) {
- def_val = sub->get_datatype().to_string();
- } else if (sub->reduced) {
- const Variant::Type vt = sub->reduced_value.get_type();
- if (vt == Variant::Type::NIL || vt == Variant::Type::FLOAT || vt == Variant::Type::INT || vt == Variant::Type::STRING || vt == Variant::Type::STRING_NAME || vt == Variant::Type::BOOL || vt == Variant::Type::NODE_PATH) {
- def_val = sub->reduced_value.operator String();
- } else {
- def_val = sub->get_datatype().to_string() + sub->reduced_value.operator String();
- }
- }
+ if (sub->is_attribute && sub->datatype.kind == GDScriptParser::DataType::ENUM && !sub->datatype.is_meta_type) {
+ def_val = sub->get_datatype().to_string() + "." + sub->attribute->name;
+ } else if (sub->is_constant && sub->reduced) {
+ def_val = sub->reduced_value.get_construct_string();
}
} break;
default:
@@ -846,7 +864,8 @@ static void _get_directory_contents(EditorFileSystemDirectory *p_dir, HashMap<St
}
}
-static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_annotation, int p_argument, const String p_quote_style, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {
+static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_annotation, int p_argument, const String p_quote_style, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, String &r_arghint) {
+ r_arghint = _make_arguments_hint(p_annotation->info->info, p_argument, true);
if (p_annotation->name == SNAME("@export_range")) {
if (p_argument == 3 || p_argument == 4 || p_argument == 5) {
// Slider hint.
@@ -896,6 +915,29 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
option.insert_text = option.display.quote(p_quote_style);
r_result.insert(option.display, option);
}
+ } else if (p_annotation->name == SNAME("@export_custom")) {
+ switch (p_argument) {
+ case 0: {
+ static HashMap<StringName, int64_t> items;
+ if (unlikely(items.is_empty())) {
+ CoreConstants::get_enum_values(SNAME("PropertyHint"), &items);
+ }
+ for (const KeyValue<StringName, int64_t> &item : items) {
+ ScriptLanguage::CodeCompletionOption option(item.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);
+ r_result.insert(option.display, option);
+ }
+ } break;
+ case 2: {
+ static HashMap<StringName, int64_t> items;
+ if (unlikely(items.is_empty())) {
+ CoreConstants::get_enum_values(SNAME("PropertyUsageFlags"), &items);
+ }
+ for (const KeyValue<StringName, int64_t> &item : items) {
+ ScriptLanguage::CodeCompletionOption option(item.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);
+ r_result.insert(option.display, option);
+ }
+ } break;
+ }
} else if (p_annotation->name == SNAME("@warning_ignore")) {
for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) {
ScriptLanguage::CodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
@@ -1202,13 +1244,15 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
return;
}
+ List<StringName> enums;
+ ClassDB::get_enum_list(type, &enums);
+ for (const StringName &E : enums) {
+ int location = p_recursion_depth + _get_enum_location(type, E);
+ ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, location);
+ r_result.insert(option.display, option);
+ }
+
if (p_types_only) {
- List<StringName> enums;
- ClassDB::get_enum_list(type, &enums);
- for (const StringName &E : enums) {
- ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_ENUM);
- r_result.insert(option.display, option);
- }
return;
}
@@ -1268,7 +1312,20 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
return;
} break;
- case GDScriptParser::DataType::ENUM:
+ case GDScriptParser::DataType::ENUM: {
+ String type_str = base_type.native_type;
+ StringName type = type_str.get_slicec('.', 0);
+ StringName type_enum = base_type.enum_type;
+
+ List<StringName> enum_values;
+ ClassDB::get_enum_constants(type, type_enum, &enum_values);
+ for (const StringName &E : enum_values) {
+ int location = p_recursion_depth + _get_enum_constant_location(type, E);
+ ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);
+ r_result.insert(option.display, option);
+ }
+ }
+ [[fallthrough]];
case GDScriptParser::DataType::BUILTIN: {
if (p_types_only) {
return;
@@ -1464,22 +1521,19 @@ static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value, G
}
if (scr.is_valid()) {
ci.type.script_path = scr->get_path();
+ ci.type.script_type = scr;
+ ci.type.native_type = scr->get_instance_base_type();
+ ci.type.kind = GDScriptParser::DataType::SCRIPT;
if (scr->get_path().ends_with(".gd")) {
- Error err;
- Ref<GDScriptParserRef> parser = GDScriptCache::get_parser(scr->get_path(), GDScriptParserRef::INTERFACE_SOLVED, err);
- if (err == OK) {
+ Ref<GDScriptParserRef> parser = p_context.parser->get_depended_parser_for(scr->get_path());
+ if (parser.is_valid() && parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED) == OK) {
ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
ci.type.class_type = parser->get_parser()->get_tree();
ci.type.kind = GDScriptParser::DataType::CLASS;
- p_context.dependent_parsers.push_back(parser);
return ci;
}
}
-
- ci.type.kind = GDScriptParser::DataType::SCRIPT;
- ci.type.script_type = scr;
- ci.type.native_type = scr->get_instance_base_type();
} else {
ci.type.kind = GDScriptParser::DataType::NATIVE;
}
@@ -1665,6 +1719,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
if (full) {
// If not fully constant, setting this value is detrimental to the inference.
r_type.value = a;
+ r_type.type.is_constant = true;
}
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
r_type.type.kind = GDScriptParser::DataType::BUILTIN;
@@ -1753,8 +1808,6 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
if (mb && mb->is_const()) {
bool all_is_const = true;
Vector<Variant> args;
- GDScriptParser::CompletionContext c2 = p_context;
- c2.current_line = call->start_line;
for (int i = 0; all_is_const && i < call->arguments.size(); i++) {
GDScriptCompletionIdentifier arg;
@@ -1791,16 +1844,14 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
}
if (FileAccess::exists(script)) {
- Error err = OK;
- Ref<GDScriptParserRef> parser = GDScriptCache::get_parser(script, GDScriptParserRef::INTERFACE_SOLVED, err);
- if (err == OK) {
+ Ref<GDScriptParserRef> parser = p_context.parser->get_depended_parser_for(script);
+ if (parser.is_valid() && parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED) == OK) {
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
r_type.type.script_path = script;
r_type.type.class_type = parser->get_parser()->get_tree();
r_type.type.is_constant = false;
r_type.type.kind = GDScriptParser::DataType::CLASS;
r_type.value = Variant();
- p_context.dependent_parsers.push_back(parser);
found = true;
}
}
@@ -1901,11 +1952,14 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
break;
}
- if (base.value.in(index.value)) {
- Variant value = base.value.get(index.value);
- r_type = _type_from_variant(value, p_context);
- found = true;
- break;
+ {
+ bool valid;
+ Variant value = base.value.get(index.value, &valid);
+ if (valid) {
+ r_type = _type_from_variant(value, p_context);
+ found = true;
+ break;
+ }
}
// Look if it is a dictionary node.
@@ -1948,7 +2002,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
}
// Look for valid indexing in other types
- if (!found && (index.value.get_type() == Variant::STRING || index.value.get_type() == Variant::NODE_PATH)) {
+ if (!found && (index.value.is_string() || index.value.get_type() == Variant::NODE_PATH)) {
StringName id = index.value;
found = _guess_identifier_type_from_base(c, base, id, r_type);
} else if (!found && index.type.kind == GDScriptParser::DataType::BUILTIN) {
@@ -2034,6 +2088,12 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
found = false;
}
+ // If the found type was not fully analyzed we analyze it now.
+ if (found && r_type.type.kind == GDScriptParser::DataType::CLASS && !r_type.type.class_type->resolved_body) {
+ Error err;
+ Ref<GDScriptParserRef> r = GDScriptCache::get_parser(r_type.type.script_path, GDScriptParserRef::FULLY_SOLVED, err);
+ }
+
// Check type hint last. For collections we want chance to get the actual value first
// This way we can detect types from the content of dictionaries and arrays
if (!found && p_expression->get_datatype().is_hard_type()) {
@@ -2057,7 +2117,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
// Look in blocks first.
int last_assign_line = -1;
const GDScriptParser::ExpressionNode *last_assigned_expression = nullptr;
- GDScriptParser::DataType id_type;
+ GDScriptCompletionIdentifier id_type;
GDScriptParser::SuiteNode *suite = p_context.current_suite;
bool is_function_parameter = false;
@@ -2079,7 +2139,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
if (can_be_local && suite && suite->has_local(p_identifier->name)) {
const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_identifier->name);
- id_type = local.get_datatype();
+ id_type.type = local.get_datatype();
// Check initializer as the first assignment.
switch (local.type) {
@@ -2117,7 +2177,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
base.type.is_meta_type = p_context.current_function && p_context.current_function->is_static;
if (_guess_identifier_type_from_base(p_context, base, p_identifier->name, base_identifier)) {
- id_type = base_identifier.type;
+ id_type = base_identifier;
}
}
}
@@ -2157,7 +2217,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
c.current_line = type_test->operand->start_line;
c.current_suite = suite;
if (type_test->test_datatype.is_hard_type()) {
- id_type = type_test->test_datatype;
+ id_type.type = type_test->test_datatype;
if (last_assign_line < c.current_line) {
// Override last assignment.
last_assign_line = c.current_line;
@@ -2175,10 +2235,10 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
c.current_line = last_assign_line;
GDScriptCompletionIdentifier assigned_type;
if (_guess_expression_type(c, last_assigned_expression, assigned_type)) {
- if (id_type.is_set() && assigned_type.type.is_set() && !GDScriptAnalyzer::check_type_compatibility(id_type, assigned_type.type)) {
+ if (id_type.type.is_set() && assigned_type.type.is_set() && !GDScriptAnalyzer::check_type_compatibility(id_type.type, assigned_type.type)) {
// The assigned type is incompatible. The annotated type takes priority.
+ r_type = id_type;
r_type.assigned_expression = last_assigned_expression;
- r_type.type = id_type;
} else {
r_type = assigned_type;
}
@@ -2196,8 +2256,8 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
GDScriptParser::FunctionNode *parent_function = base_type.class_type->get_member(p_context.current_function->identifier->name).function;
if (parent_function->parameters_indices.has(p_identifier->name)) {
const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier->name]];
- if ((!id_type.is_set() || id_type.is_variant()) && parameter->get_datatype().is_hard_type()) {
- id_type = parameter->get_datatype();
+ if ((!id_type.type.is_set() || id_type.type.is_variant()) && parameter->get_datatype().is_hard_type()) {
+ id_type.type = parameter->get_datatype();
}
if (parameter->initializer) {
GDScriptParser::CompletionContext c = p_context;
@@ -2213,7 +2273,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
base_type = base_type.class_type->base_type;
break;
case GDScriptParser::DataType::NATIVE: {
- if (id_type.is_set() && !id_type.is_variant()) {
+ if (id_type.type.is_set() && !id_type.type.is_variant()) {
base_type = GDScriptParser::DataType();
break;
}
@@ -2234,8 +2294,8 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
}
}
- if (id_type.is_set() && !id_type.is_variant()) {
- r_type.type = id_type;
+ if (id_type.type.is_set() && !id_type.type.is_variant()) {
+ r_type = id_type;
return true;
}
@@ -2243,9 +2303,8 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
if (ScriptServer::is_global_class(p_identifier->name)) {
String script = ScriptServer::get_global_class_path(p_identifier->name);
if (script.to_lower().ends_with(".gd")) {
- Error err = OK;
- Ref<GDScriptParserRef> parser = GDScriptCache::get_parser(script, GDScriptParserRef::INTERFACE_SOLVED, err);
- if (err == OK) {
+ Ref<GDScriptParserRef> parser = p_context.parser->get_depended_parser_for(script);
+ if (parser.is_valid() && parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED) == OK) {
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
r_type.type.script_path = script;
r_type.type.class_type = parser->get_parser()->get_tree();
@@ -2253,7 +2312,6 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
r_type.type.is_constant = false;
r_type.type.kind = GDScriptParser::DataType::CLASS;
r_type.value = Variant();
- p_context.dependent_parsers.push_back(parser);
return true;
}
} else {
@@ -2671,6 +2729,25 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
while (base_type.is_set() && !base_type.is_variant()) {
switch (base_type.kind) {
case GDScriptParser::DataType::CLASS: {
+ if (base_type.is_meta_type && p_method == SNAME("new")) {
+ const GDScriptParser::ClassNode *current = base_type.class_type;
+
+ do {
+ if (current->has_member("_init")) {
+ const GDScriptParser::ClassNode::Member &member = current->get_member("_init");
+
+ if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) {
+ r_arghint = base_type.class_type->get_datatype().to_string() + " new" + _make_arguments_hint(member.function, p_argidx, true);
+ return;
+ }
+ }
+ current = current->base_type.class_type;
+ } while (current != nullptr);
+
+ r_arghint = base_type.class_type->get_datatype().to_string() + " new()";
+ return;
+ }
+
if (base_type.class_type->has_member(p_method)) {
const GDScriptParser::ClassNode::Member &member = base_type.class_type->get_member(p_method);
@@ -2682,6 +2759,20 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
base_type = base_type.class_type->base_type;
} break;
+ case GDScriptParser::DataType::SCRIPT: {
+ if (base_type.script_type->is_valid() && base_type.script_type->has_method(p_method)) {
+ r_arghint = _make_arguments_hint(base_type.script_type->get_method_info(p_method), p_argidx);
+ return;
+ }
+ Ref<Script> base_script = base_type.script_type->get_base_script();
+ if (base_script.is_valid()) {
+ base_type.script_type = base_script;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.builtin_type = Variant::OBJECT;
+ base_type.native_type = base_type.script_type->get_instance_base_type();
+ }
+ } break;
case GDScriptParser::DataType::NATIVE: {
StringName class_name = base_type.native_type;
if (!ClassDB::class_exists(class_name)) {
@@ -2703,10 +2794,10 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
// Handle user preference.
if (opt.is_quoted()) {
opt = opt.unquote().quote(quote_style);
- if (use_string_names && info.arguments[p_argidx].type == Variant::STRING_NAME) {
- opt = opt.indent("&");
- } else if (use_node_paths && info.arguments[p_argidx].type == Variant::NODE_PATH) {
- opt = opt.indent("^");
+ if (use_string_names && info.arguments.get(p_argidx).type == Variant::STRING_NAME) {
+ opt = "&" + opt;
+ } else if (use_node_paths && info.arguments.get(p_argidx).type == Variant::NODE_PATH) {
+ opt = "^" + opt;
}
}
ScriptLanguage::CodeCompletionOption option(opt, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);
@@ -2716,7 +2807,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
if (p_argidx < method_args) {
- PropertyInfo arg_info = info.arguments[p_argidx];
+ const PropertyInfo &arg_info = info.arguments.get(p_argidx);
if (arg_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
_find_enumeration_candidates(p_context, arg_info.class_name, r_result);
}
@@ -2741,7 +2832,11 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
if (E.usage & (PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_INTERNAL)) {
continue;
}
- ScriptLanguage::CodeCompletionOption option(E.name.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::CodeCompletionLocation::LOCATION_LOCAL + n);
+ String name = E.name.quote(quote_style);
+ if (use_node_paths) {
+ name = "^" + name;
+ }
+ ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::CodeCompletionLocation::LOCATION_LOCAL + n);
r_result.insert(option.display, option);
}
script = script->get_base_script();
@@ -2755,7 +2850,11 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
while (clss) {
for (GDScriptParser::ClassNode::Member member : clss->members) {
if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {
- ScriptLanguage::CodeCompletionOption option(member.get_name().quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::CodeCompletionLocation::LOCATION_LOCAL + n);
+ String name = member.get_name().quote(quote_style);
+ if (use_node_paths) {
+ name = "^" + name;
+ }
+ ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::CodeCompletionLocation::LOCATION_LOCAL + n);
r_result.insert(option.display, option);
}
}
@@ -2778,7 +2877,11 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
if (E.usage & (PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_INTERNAL)) {
continue;
}
- ScriptLanguage::CodeCompletionOption option(E.name.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_MEMBER);
+ String name = E.name.quote(quote_style);
+ if (use_node_paths) {
+ name = "^" + name;
+ }
+ ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER);
r_result.insert(option.display, option);
}
}
@@ -2794,8 +2897,11 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
continue;
}
String name = s.get_slice("/", 1);
- ScriptLanguage::CodeCompletionOption option("/root/" + name, ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);
- option.insert_text = option.display.quote(quote_style);
+ String path = ("/root/" + name).quote(quote_style);
+ if (use_node_paths) {
+ path = "^" + path;
+ }
+ ScriptLanguage::CodeCompletionOption option(path, ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);
r_result.insert(option.display, option);
}
}
@@ -2809,9 +2915,11 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
if (!s.begins_with("input/")) {
continue;
}
- String name = s.get_slice("/", 1);
+ String name = s.get_slice("/", 1).quote(quote_style);
+ if (use_string_names) {
+ name = "&" + name;
+ }
ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);
- option.insert_text = option.display.quote(quote_style);
r_result.insert(option.display, option);
}
}
@@ -2868,11 +2976,6 @@ static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, co
} break;
case GDScriptParser::Node::IDENTIFIER: {
- if (p_subscript->base->datatype.type_source == GDScriptParser::DataType::ANNOTATED_EXPLICIT) {
- // Annotated type takes precedence.
- return false;
- }
-
const GDScriptParser::IdentifierNode *identifier_node = static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base);
switch (identifier_node->source) {
@@ -2910,6 +3013,14 @@ static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, co
if (get_node != nullptr) {
const Object *node = p_context.base->call("get_node_or_null", NodePath(get_node->full_path));
if (node != nullptr) {
+ GDScriptParser::DataType assigned_type = _type_from_variant(node, p_context).type;
+ GDScriptParser::DataType base_type = p_subscript->base->datatype;
+
+ if (p_subscript->base->type == GDScriptParser::Node::IDENTIFIER && base_type.type_source == GDScriptParser::DataType::ANNOTATED_EXPLICIT && (assigned_type.kind != base_type.kind || assigned_type.script_path != base_type.script_path || assigned_type.native_type != base_type.native_type)) {
+ // Annotated type takes precedence.
+ return false;
+ }
+
if (r_base != nullptr) {
*r_base = node;
}
@@ -3076,7 +3187,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
break;
}
const GDScriptParser::AnnotationNode *annotation = static_cast<const GDScriptParser::AnnotationNode *>(completion_context.node);
- _find_annotation_arguments(annotation, completion_context.current_argument, quote_style, options);
+ _find_annotation_arguments(annotation, completion_context.current_argument, quote_style, options, r_call_hint);
r_forced = true;
} break;
case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD: {
@@ -3200,11 +3311,36 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
case GDScriptParser::COMPLETION_SUBSCRIPT: {
const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(completion_context.node);
GDScriptCompletionIdentifier base;
- if (!_guess_expression_type(completion_context, subscript->base, base)) {
- break;
- }
+ const bool res = _guess_expression_type(completion_context, subscript->base, base);
- _find_identifiers_in_base(base, false, false, options, 0);
+ // If the type is not known, we assume it is BUILTIN, since indices on arrays is the most common use case.
+ if (!subscript->is_attribute && (!res || base.type.kind == GDScriptParser::DataType::BUILTIN || base.type.is_variant())) {
+ if (base.value.get_type() == Variant::DICTIONARY) {
+ List<PropertyInfo> members;
+ base.value.get_property_list(&members);
+
+ for (const PropertyInfo &E : members) {
+ ScriptLanguage::CodeCompletionOption option(E.name.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::LOCATION_LOCAL);
+ options.insert(option.display, option);
+ }
+ }
+ if (!subscript->index || subscript->index->type != GDScriptParser::Node::LITERAL) {
+ _find_identifiers(completion_context, false, 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);
+ 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);
+ }
+ }
} break;
case GDScriptParser::COMPLETION_TYPE_ATTRIBUTE: {
if (!completion_context.current_class) {
@@ -3304,19 +3440,17 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
method_hint += "(";
- if (mi.arguments.size()) {
- for (int i = 0; i < mi.arguments.size(); i++) {
- if (i > 0) {
- method_hint += ", ";
- }
- String arg = mi.arguments[i].name;
- if (arg.contains(":")) {
- arg = arg.substr(0, arg.find(":"));
- }
- method_hint += arg;
- if (use_type_hint) {
- method_hint += ": " + _get_visual_datatype(mi.arguments[i], true, class_name);
- }
+ for (List<PropertyInfo>::ConstIterator arg_itr = mi.arguments.begin(); arg_itr != mi.arguments.end(); ++arg_itr) {
+ if (arg_itr != mi.arguments.begin()) {
+ method_hint += ", ";
+ }
+ String arg = arg_itr->name;
+ if (arg.contains(":")) {
+ arg = arg.substr(0, arg.find(":"));
+ }
+ method_hint += arg;
+ if (use_type_hint) {
+ method_hint += ": " + _get_visual_datatype(*arg_itr, true, class_name);
}
}
method_hint += ")";
@@ -3356,11 +3490,11 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
opt = opt.substr(1);
}
- // The path needs quotes if at least one of its components (excluding `/` separations)
+ // The path needs quotes if at least one of its components (excluding `%` prefix and `/` separations)
// is not a valid identifier.
bool path_needs_quote = false;
- for (const String &part : opt.split("/")) {
- if (!part.is_valid_identifier()) {
+ for (const String &part : opt.trim_prefix("%").split("/")) {
+ if (!part.is_valid_ascii_identifier()) {
path_needs_quote = true;
break;
}
@@ -3490,9 +3624,13 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
switch (base_type.kind) {
case GDScriptParser::DataType::CLASS: {
if (base_type.class_type) {
- if (base_type.class_type->has_member(p_symbol)) {
+ String name = p_symbol;
+ if (name == "new") {
+ name = "_init";
+ }
+ if (base_type.class_type->has_member(name)) {
r_result.type = ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION;
- r_result.location = base_type.class_type->get_member(p_symbol).get_line();
+ r_result.location = base_type.class_type->get_member(name).get_line();
r_result.class_path = base_type.script_path;
Error err = OK;
r_result.script = GDScriptCache::get_shallow_script(r_result.class_path, err);
@@ -3555,11 +3693,21 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
return OK;
}
- StringName enum_name = ClassDB::get_integer_constant_enum(class_name, p_symbol, true);
- if (enum_name != StringName()) {
- r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;
+ List<StringName> enums;
+ ClassDB::get_enum_list(class_name, &enums);
+ for (const StringName &E : enums) {
+ if (E == p_symbol) {
+ r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;
+ r_result.class_name = base_type.native_type;
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+ }
+
+ if (!String(ClassDB::get_integer_constant_enum(class_name, p_symbol, true)).is_empty()) {
+ r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;
r_result.class_name = base_type.native_type;
- r_result.class_member = enum_name;
+ r_result.class_member = p_symbol;
return OK;
}
@@ -3633,6 +3781,27 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
return OK;
}
} break;
+ case GDScriptParser::DataType::ENUM: {
+ if (base_type.class_type && base_type.class_type->has_member(base_type.enum_type)) {
+ GDScriptParser::EnumNode *base_enum = base_type.class_type->get_member(base_type.enum_type).m_enum;
+ for (const GDScriptParser::EnumNode::Value &value : base_enum->values) {
+ if (value.identifier && value.identifier->name == p_symbol) {
+ r_result.type = ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION;
+ r_result.class_path = base_type.script_path;
+ r_result.location = value.line;
+ Error err = OK;
+ r_result.script = GDScriptCache::get_shallow_script(r_result.class_path, err);
+ return err;
+ }
+ }
+ } else if (base_type.enum_values.has(p_symbol)) {
+ r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;
+ r_result.class_name = String(base_type.native_type).get_slicec('.', 0);
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+ base_type.kind = GDScriptParser::DataType::UNRESOLVED;
+ } break;
default: {
base_type.kind = GDScriptParser::DataType::UNRESOLVED;
} break;