diff options
Diffstat (limited to 'modules/gdscript/gdscript_editor.cpp')
-rw-r--r-- | modules/gdscript/gdscript_editor.cpp | 219 |
1 files changed, 155 insertions, 64 deletions
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 4ae39d80cd..636339ef1d 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_ascii_identifier()) + .replace("_CLASS_", p_class_name.to_pascal_case().validate_ascii_identifier()) .replace("_TS_", _get_indentation()); scr->set_source_code(processed_template); return scr; @@ -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; } @@ -754,13 +756,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++) { @@ -792,7 +798,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() ? "()" : "(...)"); } @@ -800,7 +806,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() ? "[]" : "[...]"; } @@ -808,24 +814,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: @@ -1517,22 +1516,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; } @@ -1807,8 +1803,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; @@ -1845,16 +1839,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; } } @@ -1955,11 +1947,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. @@ -2303,9 +2298,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(); @@ -2313,7 +2307,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 { @@ -2731,6 +2724,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); @@ -2742,6 +2754,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)) { @@ -2764,9 +2790,9 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c if (opt.is_quoted()) { opt = opt.unquote().quote(quote_style); if (use_string_names && info.arguments.get(p_argidx).type == Variant::STRING_NAME) { - opt = opt.indent("&"); + opt = "&" + opt; } else if (use_node_paths && info.arguments.get(p_argidx).type == Variant::NODE_PATH) { - opt = opt.indent("^"); + opt = "^" + opt; } } ScriptLanguage::CodeCompletionOption option(opt, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); @@ -2801,7 +2827,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(); @@ -2815,7 +2845,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); } } @@ -2838,7 +2872,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); } } @@ -2854,8 +2892,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); } } @@ -2869,9 +2910,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); } } @@ -3260,11 +3303,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) { @@ -3418,7 +3486,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c // is not a valid identifier. bool path_needs_quote = false; for (const String &part : opt.split("/")) { - if (!part.is_valid_identifier()) { + if (!part.is_valid_ascii_identifier()) { path_needs_quote = true; break; } @@ -3548,9 +3616,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); @@ -3613,11 +3685,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; } @@ -3691,6 +3773,15 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co return OK; } } break; + case GDScriptParser::DataType::ENUM: { + 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; |