diff options
Diffstat (limited to 'modules/gdscript/gdscript_editor.cpp')
-rw-r--r-- | modules/gdscript/gdscript_editor.cpp | 221 |
1 files changed, 137 insertions, 84 deletions
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 71d2699c2e..350962ba1b 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,6 +33,7 @@ #include "core/config/engine.h" #include "core/core_constants.h" #include "core/io/file_access.h" +#include "editor_templates/templates.gen.h" #include "gdscript_analyzer.h" #include "gdscript_compiler.h" #include "gdscript_parser.h" @@ -55,68 +56,44 @@ void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const { p_delimiters->push_back("\"\"\" \"\"\""); } -String GDScriptLanguage::_get_processed_template(const String &p_template, const String &p_base_class_name) const { - String processed_template = p_template; +bool GDScriptLanguage::is_using_templates() { + return true; +} +Ref<Script> GDScriptLanguage::make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const { + Ref<GDScript> script; + script.instantiate(); + String processed_template = p_template; #ifdef TOOLS_ENABLED - if (EDITOR_DEF("text_editor/completion/add_type_hints", false)) { - processed_template = processed_template.replace("%INT_TYPE%", ": int"); - processed_template = processed_template.replace("%STRING_TYPE%", ": String"); - processed_template = processed_template.replace("%FLOAT_TYPE%", ": float"); - processed_template = processed_template.replace("%VOID_RETURN%", " -> void"); - } else { - processed_template = processed_template.replace("%INT_TYPE%", ""); - processed_template = processed_template.replace("%STRING_TYPE%", ""); - processed_template = processed_template.replace("%FLOAT_TYPE%", ""); - processed_template = processed_template.replace("%VOID_RETURN%", ""); + if (!EDITOR_GET("text_editor/completion/add_type_hints")) { + processed_template = processed_template.replace(": int", "") + .replace(": String", "") + .replace(": float", "") + .replace(":=", "=") + .replace(" -> void", ""); } #else - processed_template = processed_template.replace("%INT_TYPE%", ""); - processed_template = processed_template.replace("%STRING_TYPE%", ""); - processed_template = processed_template.replace("%FLOAT_TYPE%", ""); - processed_template = processed_template.replace("%VOID_RETURN%", ""); + processed_template = processed_template.replace(": int", "") + .replace(": String", "") + .replace(": float", "") + .replace(" -> void", ""); #endif - processed_template = processed_template.replace("%BASE%", p_base_class_name); - processed_template = processed_template.replace("%TS%", _get_indentation()); - - return processed_template; -} - -Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { - String _template = "extends %BASE%\n" - "\n" - "\n" - "# Declare member variables here. Examples:\n" - "# var a%INT_TYPE% = 2\n" - "# var b%STRING_TYPE% = \"text\"\n" - "\n" - "\n" - "# Called when the node enters the scene tree for the first time.\n" - "func _ready()%VOID_RETURN%:\n" - "%TS%pass # Replace with function body.\n" - "\n" - "\n" - "# Called every frame. 'delta' is the elapsed time since the previous frame.\n" - "#func _process(delta%FLOAT_TYPE%)%VOID_RETURN%:\n" - "#%TS%pass\n"; - - _template = _get_processed_template(_template, p_base_class_name); - - Ref<GDScript> script; - script.instantiate(); - script->set_source_code(_template); - + processed_template = processed_template.replace("_BASE_", p_base_class_name) + .replace("_CLASS_", p_class_name) + .replace("_TS_", _get_indentation()); + script->set_source_code(processed_template); return script; } -bool GDScriptLanguage::is_using_templates() { - return true; -} - -void GDScriptLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) { - String _template = _get_processed_template(p_script->get_source_code(), p_base_class_name); - p_script->set_source_code(_template); +Vector<ScriptLanguage::ScriptTemplate> GDScriptLanguage::get_built_in_templates(StringName p_object) { + Vector<ScriptLanguage::ScriptTemplate> templates; + for (int i = 0; i < TEMPLATES_ARRAY_SIZE; i++) { + if (TEMPLATES[i].inherit == p_object) { + templates.append(TEMPLATES[i]); + } + } + return templates; } static void get_function_names_recursively(const GDScriptParser::ClassNode *p_class, const String &p_prefix, Map<int, String> &r_funcs) { @@ -236,7 +213,7 @@ Script *GDScriptLanguage::create_script() const { /* DEBUGGER FUNCTIONS */ bool GDScriptLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) { - //break because of parse error + // break because of parse error if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) { _debug_parse_err_line = p_line; @@ -509,7 +486,7 @@ struct GDScriptCompletionIdentifier { static String _get_visual_datatype(const PropertyInfo &p_info, bool p_is_arg = true) { if (p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { String enum_name = p_info.class_name; - if (enum_name.find(".") == -1) { + if (!enum_name.contains(".")) { return enum_name; } return enum_name.get_slice(".", 1); @@ -603,12 +580,50 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio if (par->default_value) { String def_val = "<unknown>"; - if (par->default_value->type == GDScriptParser::Node::LITERAL) { - const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(par->default_value); - def_val = literal->value.get_construct_string(); - } else if (par->default_value->type == GDScriptParser::Node::IDENTIFIER) { - const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(par->default_value); - def_val = id->name.operator String(); + switch (par->default_value->type) { + case GDScriptParser::Node::LITERAL: { + const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(par->default_value); + def_val = literal->value.get_construct_string(); + } break; + case GDScriptParser::Node::IDENTIFIER: { + const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(par->default_value); + def_val = id->name.operator String(); + } break; + case GDScriptParser::Node::CALL: { + const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(par->default_value); + if (call->is_constant && call->reduced) { + def_val = call->function_name.operator String() + call->reduced_value.operator String(); + } + } break; + case GDScriptParser::Node::ARRAY: { + const GDScriptParser::ArrayNode *arr = static_cast<const GDScriptParser::ArrayNode *>(par->default_value); + if (arr->is_constant && arr->reduced) { + def_val = arr->reduced_value.operator String(); + } + } break; + case GDScriptParser::Node::DICTIONARY: { + const GDScriptParser::DictionaryNode *dict = static_cast<const GDScriptParser::DictionaryNode *>(par->default_value); + if (dict->is_constant && dict->reduced) { + def_val = dict->reduced_value.operator String(); + } + } break; + case GDScriptParser::Node::SUBSCRIPT: { + const GDScriptParser::SubscriptNode *sub = static_cast<const GDScriptParser::SubscriptNode *>(par->default_value); + 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(); + } + } + } + } break; + default: + break; } arghint += " = " + def_val; } @@ -637,7 +652,7 @@ static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Map<String } static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_annotation, int p_argument, const String p_quote_style, Map<String, ScriptCodeCompletionOption> &r_result) { - if (p_annotation->name == "@export_range") { + if (p_annotation->name == SNAME("@export_range")) { if (p_argument == 3 || p_argument == 4) { // Slider hint. ScriptCodeCompletionOption slider1("or_greater", ScriptCodeCompletionOption::KIND_PLAIN_TEXT); @@ -647,7 +662,7 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a slider2.insert_text = slider2.display.quote(p_quote_style); r_result.insert(slider2.display, slider2); } - } else if (p_annotation->name == "@export_exp_easing") { + } else if (p_annotation->name == SNAME("@export_exp_easing")) { if (p_argument == 0 || p_argument == 1) { // Easing hint. ScriptCodeCompletionOption hint1("attenuation", ScriptCodeCompletionOption::KIND_PLAIN_TEXT); @@ -657,7 +672,7 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a hint2.insert_text = hint2.display.quote(p_quote_style); r_result.insert(hint2.display, hint2); } - } else if (p_annotation->name == "@export_node_path") { + } else if (p_annotation->name == SNAME("@export_node_path")) { ScriptCodeCompletionOption node("Node", ScriptCodeCompletionOption::KIND_CLASS); r_result.insert(node.display, node); List<StringName> node_types; @@ -669,6 +684,11 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CLASS); r_result.insert(option.display, option); } + } else if (p_annotation->name == SNAME("@warning_ignore")) { + for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) { + ScriptCodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + r_result.insert(warning.display, warning); + } } } @@ -935,7 +955,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base if (E.usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY)) { continue; } - if (E.name.find("/") != -1) { + if (E.name.contains("/")) { continue; } ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_MEMBER); @@ -946,8 +966,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base if (!_static || Engine::get_singleton()->has_singleton(type)) { List<MethodInfo> methods; - bool is_autocompleting_getters = GLOBAL_GET("debug/gdscript/completion/autocomplete_setters_and_getters").booleanize(); - ClassDB::get_method_list(type, &methods, false, !is_autocompleting_getters); + ClassDB::get_method_list(type, &methods, false, true); for (const MethodInfo &E : methods) { if (E.name.begins_with("_")) { continue; @@ -981,7 +1000,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base } for (const PropertyInfo &E : members) { - if (String(E.name).find("/") == -1) { + if (!String(E.name).contains("/")) { ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_MEMBER); r_result.insert(option.display, option); } @@ -1179,9 +1198,34 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type); static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type); +static bool _is_expression_named_identifier(const GDScriptParser::ExpressionNode *p_expression, const StringName &p_name) { + if (p_expression) { + switch (p_expression->type) { + case GDScriptParser::Node::IDENTIFIER: { + const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression); + if (id->name == p_name) { + return true; + } + } break; + case GDScriptParser::Node::CAST: { + const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression); + return _is_expression_named_identifier(cn->operand, p_name); + } break; + default: + break; + } + } + + return false; +} + static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::ExpressionNode *p_expression, GDScriptCompletionIdentifier &r_type) { bool found = false; + if (p_expression == nullptr) { + return false; + } + if (p_expression->is_constant) { // Already has a value, so just use that. r_type = _type_from_variant(p_expression->reduced_value); @@ -1361,11 +1405,11 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, Object *baseptr = base.value; - if (all_is_const && String(call->function_name) == "get_node" && ClassDB::is_parent_class(native_type.native_type, "Node") && args.size()) { + if (all_is_const && call->function_name == SNAME("get_node") && ClassDB::is_parent_class(native_type.native_type, SNAME("Node")) && args.size()) { String arg1 = args[0]; if (arg1.begins_with("/root/")) { String which = arg1.get_slice("/", 2); - if (which != "") { + if (!which.is_empty()) { // Try singletons first if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(which)) { r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[which]); @@ -1383,8 +1427,8 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, } if (!script.ends_with(".gd")) { - //not a script, try find the script anyway, - //may have some success + // not a script, try find the script anyway, + // may have some success script = script.get_basename() + ".gd"; } @@ -1881,6 +1925,14 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext & return true; } else if (init->start_line == p_context.current_line) { return false; + // Detects if variable is assigned to itself + } else if (_is_expression_named_identifier(init, member.variable->identifier->name)) { + if (member.variable->initializer->get_datatype().is_set()) { + r_type.type = member.variable->initializer->get_datatype(); + } else if (member.variable->get_datatype().is_set() && !member.variable->get_datatype().is_variant()) { + r_type.type = member.variable->get_datatype(); + } + return true; } else if (_guess_expression_type(p_context, init, r_type)) { return true; } else if (init->get_datatype().is_set() && !init->get_datatype().is_variant()) { @@ -2062,7 +2114,7 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex GDScriptParser::DataType base_type = p_base.type; bool is_static = base_type.is_meta_type; - if (is_static && p_method == "new") { + if (is_static && p_method == SNAME("new")) { r_type.type = base_type; r_type.type.is_meta_type = false; r_type.type.is_constant = false; @@ -2159,7 +2211,7 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex } static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_context, const String &p_enum_hint, Map<String, ScriptCodeCompletionOption> &r_result) { - if (p_enum_hint.find(".") == -1) { + if (!p_enum_hint.contains(".")) { // Global constant or in the current class. StringName current_enum = p_enum_hint; if (p_context.current_class && p_context.current_class->has_member(current_enum) && p_context.current_class->get_member(current_enum).type == GDScriptParser::ClassNode::Member::ENUM) { @@ -2251,7 +2303,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c r_arghint = _make_arguments_hint(info, p_argidx); } - if (p_argidx == 0 && ClassDB::is_parent_class(class_name, "Node") && (p_method == "get_node" || p_method == "has_node")) { + if (p_argidx == 0 && ClassDB::is_parent_class(class_name, SNAME("Node")) && (p_method == SNAME("get_node") || p_method == SNAME("has_node"))) { // Get autoloads List<PropertyInfo> props; ProjectSettings::get_singleton()->get_property_list(&props); @@ -2268,7 +2320,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c } } - if (p_argidx == 0 && method_args > 0 && ClassDB::is_parent_class(class_name, "InputEvent") && p_method.operator String().find("action") != -1) { + if (p_argidx == 0 && method_args > 0 && ClassDB::is_parent_class(class_name, SNAME("InputEvent")) && p_method.operator String().contains("action")) { // Get input actions List<PropertyInfo> props; ProjectSettings::get_singleton()->get_property_list(&props); @@ -2616,7 +2668,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c ClassDB::get_virtual_methods(class_name, &virtual_methods); for (const MethodInfo &mi : virtual_methods) { String method_hint = mi.name; - if (method_hint.find(":") != -1) { + if (method_hint.contains(":")) { method_hint = method_hint.get_slice(":", 0); } method_hint += "("; @@ -2627,7 +2679,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c method_hint += ", "; } String arg = mi.arguments[i].name; - if (arg.find(":") != -1) { + if (arg.contains(":")) { arg = arg.substr(0, arg.find(":")); } method_hint += arg; @@ -2719,10 +2771,10 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path String GDScriptLanguage::_get_indentation() const { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { - bool use_space_indentation = EDITOR_DEF("text_editor/behavior/indent/type", false); + bool use_space_indentation = EDITOR_GET("text_editor/behavior/indent/type"); if (use_space_indentation) { - int indent_size = EDITOR_DEF("text_editor/behavior/indent/size", 4); + int indent_size = EDITOR_GET("text_editor/behavior/indent/size"); String space_indent = ""; for (int i = 0; i < indent_size; i++) { @@ -2753,7 +2805,7 @@ void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_t } String st = l.substr(tc, l.length()).strip_edges(); - if (st == "" || st.begins_with("#")) { + if (st.is_empty() || st.begins_with("#")) { continue; //ignore! } @@ -2770,7 +2822,7 @@ void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_t } if (indent_stack.size() && indent_stack.back()->get() != tc) { - indent_stack.push_back(tc); //this is not right but gets the job done + indent_stack.push_back(tc); // this is not right but gets the job done } } @@ -2810,6 +2862,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION; r_result.location = base_type.class_type->get_member(p_symbol).get_line(); r_result.class_path = base_type.script_path; + r_result.script = GDScriptCache::get_shallow_script(r_result.class_path); return OK; } base_type = base_type.class_type->base_type; |