diff options
-rw-r--r-- | editor/connections_dialog.cpp | 4 | ||||
-rw-r--r-- | editor/editor_settings.cpp | 1 | ||||
-rw-r--r-- | modules/gdscript/gdscript_editor.cpp | 235 |
3 files changed, 186 insertions, 54 deletions
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 8933fd7fe8..c4f4e28fec 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -820,7 +820,9 @@ void ConnectionsDock::update_tree() { if (i > 0) signaldesc += ", "; String tname = "var"; - if (pi.type != Variant::NIL) { + if (pi.type == Variant::OBJECT && pi.class_name != StringName()) { + tname = pi.class_name.operator String(); + } else if (pi.type != Variant::NIL) { tname = Variant::get_type_name(pi.type); } signaldesc += tname + " " + (pi.name == "" ? String("arg " + itos(i)) : pi.name); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 4cfdb6f6d3..9736eb0969 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -404,6 +404,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("text_editor/completion/callhint_tooltip_offset", Vector2()); _initial_set("text_editor/files/restore_scripts_on_load", true); _initial_set("text_editor/completion/complete_file_paths", true); + _initial_set("text_editor/completion/add_type_hints", false); _initial_set("docks/scene_tree/start_create_dialog_fully_expanded", false); _initial_set("docks/scene_tree/draw_relationship_lines", false); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 8389f57761..a09c7a45cc 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -53,21 +53,45 @@ void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const { p_delimiters->push_back("' '"); } Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { +#ifdef TOOLS_ENABLED + bool th = EDITOR_DEF("text_editor/completion/add_type_hints", false); +#else + bool th = false; +#endif String _template = "extends %BASE%\n" "\n" "# Declare member variables here. Examples:\n" - "# var a = 2\n" - "# var b = \"text\"\n" + "# var a %INT_TYPE%= 2\n" + "# var b %STRING_TYPE%= \"text\"\n" "\n" "# Called when the node enters the scene tree for the first time.\n" - "func _ready():\n" + "func _ready()%VOID_RETURN%:\n" "%TS%pass # Replace with function body.\n" "\n" "# Called every frame. 'delta' is the elapsed time since the previous frame.\n" - "#func _process(delta):\n" + "#func _process(delta%FLOAT_TYPE%)%VOID_RETURN%:\n" "#%TS%pass\n"; +#ifdef TOOLS_ENABLED + if (EDITOR_DEF("text_editor/completion/add_type_hints", false)) { + _template = _template.replace("%INT_TYPE%", ": int "); + _template = _template.replace("%STRING_TYPE%", ": String "); + _template = _template.replace("%FLOAT_TYPE%", " : float"); + _template = _template.replace("%VOID_RETURN%", " -> void"); + } else { + _template = _template.replace("%INT_TYPE%", ""); + _template = _template.replace("%STRING_TYPE%", ""); + _template = _template.replace("%FLOAT_TYPE%", ""); + _template = _template.replace("%VOID_RETURN%", ""); + } +#else + _template = _template.replace("%INT_TYPE%", ""); + _template = _template.replace("%STRING_TYPE%", ""); + _template = _template.replace("%FLOAT_TYPE%", ""); + _template = _template.replace("%VOID_RETURN%", ""); +#endif + _template = _template.replace("%BASE%", p_base_class_name); _template = _template.replace("%TS%", _get_indentation()); @@ -415,15 +439,27 @@ void GDScriptLanguage::get_public_constants(List<Pair<String, Variant> > *p_cons String GDScriptLanguage::make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const { +#ifdef TOOLS_ENABLED + bool th = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints"); +#else + bool th = false; +#endif + String s = "func " + p_name + "("; if (p_args.size()) { for (int i = 0; i < p_args.size(); i++) { if (i > 0) s += ", "; s += p_args[i].get_slice(":", 0); + if (th) { + String type = p_args[i].get_slice(":", 1); + if (!type.empty() && type != "var") { + s += " : " + type; + } + } } } - s += "):\n" + _get_indentation() + "pass # Replace with function body.\n"; + s += String(")") + (th ? " -> void" : "") + ":\n" + _get_indentation() + "pass # Replace with function body.\n"; return s; } @@ -473,6 +509,30 @@ static GDScriptCompletionIdentifier _get_type_from_pinfo(const PropertyInfo &p_i return t; } +static bool _get_type_from_parser_type(const GDScriptParser::DataType &p_datatype, GDScriptCompletionIdentifier &r_type) { + if (p_datatype.has_type && p_datatype.kind != GDScriptParser::DataType::UNRESOLVED) { + switch (p_datatype.kind) { + case GDScriptParser::DataType::BUILTIN: { + r_type.type = p_datatype.builtin_type; + return true; + } break; + case GDScriptParser::DataType::NATIVE: { + r_type.type = Variant::OBJECT; + r_type.obj_type = p_datatype.native_type; + return true; + } break; + case GDScriptParser::DataType::SCRIPT: + case GDScriptParser::DataType::GDSCRIPT: { + r_type.type = Variant::OBJECT; + r_type.script = p_datatype.script_type; + r_type.obj_type = r_type.script->get_instance_base_type(); + return true; + } break; + } + } + return false; +} + struct GDScriptCompletionContext { const GDScriptParser::ClassNode *_class; @@ -592,6 +652,11 @@ static bool _guess_identifier_type(GDScriptCompletionContext &context, int p_lin static bool _guess_expression_type(GDScriptCompletionContext &context, const GDScriptParser::Node *p_node, int p_line, GDScriptCompletionIdentifier &r_type, bool p_for_indexing = false) { + GDScriptParser::DataType datatype = p_node->get_datatype(); + if (_get_type_from_parser_type(datatype, r_type)) { + return true; + } + if (p_node->type == GDScriptParser::Node::TYPE_CONSTANT) { const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(p_node); @@ -1060,6 +1125,18 @@ static bool _guess_identifier_type_in_block(GDScriptCompletionContext &context, } } + // Check type hint first + const GDScriptParser::BlockNode *blk = context.block; + while (blk) { + if (blk->variables.has(p_identifier)) { + GDScriptParser::DataType var_type = blk->variables[p_identifier]->get_datatype(); + if (_get_type_from_parser_type(var_type, r_type)) { + return true; + } + } + blk = blk->parent_block; + } + GDScriptCompletionIdentifier gdi = _get_native_class(context); if (gdi.obj_type != StringName()) { bool valid; @@ -1198,6 +1275,9 @@ static bool _guess_identifier_type(GDScriptCompletionContext &context, int p_lin if (context.function->arguments[i] == p_identifier) { argindex = i; + if (_get_type_from_parser_type(context.function->argument_types[i], r_type)) { + return true; + } break; } } @@ -1507,6 +1587,9 @@ static void _find_identifiers(GDScriptCompletionContext &context, int p_line, bo c.block = NULL; c.function = NULL; _find_identifiers_in_class(c, _static, p_only_functions, result); + if (!p_only_functions && clss->name != StringName()) { + result.insert(clss->name.operator String()); + } clss = clss->owner; } @@ -1533,23 +1616,51 @@ static void _find_identifiers(GDScriptCompletionContext &context, int p_line, bo } //autoload singletons - List<PropertyInfo> props; - ProjectSettings::get_singleton()->get_property_list(&props); + for (const Map<StringName, Variant>::Element *E = GDScriptLanguage::get_singleton()->get_named_globals_map().front(); E; E = E->next()) { + result.insert(E->key().operator String()); + } - for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + for (const Map<StringName, int>::Element *E = GDScriptLanguage::get_singleton()->get_global_map().front(); E; E = E->next()) { + result.insert(E->key().operator String()); + } +} - String s = E->get().name; - if (!s.begins_with("autoload/")) +static void _find_types(GDScriptCompletionContext &p_context, Set<String> &r_result, bool p_is_function) { + const GDScriptParser::ClassNode *clss = p_context._class; + + while (clss) { + + for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = clss->constant_expressions.front(); E; E = E->next()) { + r_result.insert(E->key().operator String()); + } + for (int i = 0; i < clss->subclasses.size(); i++) { + if (clss->subclasses[i]->name != StringName()) { + r_result.insert(clss->subclasses[i]->name.operator String()); + } + } + + clss = clss->owner; + } + + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + r_result.insert(Variant::get_type_name((Variant::Type)i)); + } + + List<StringName> native_classes; + ClassDB::get_class_list(&native_classes); + for (List<StringName>::Element *E = native_classes.front(); E; E = E->next()) { + String class_name = E->get().operator String(); + if (class_name.begins_with("_")) { + class_name = class_name.right(1); + } + if (Engine::get_singleton()->has_singleton(class_name)) { continue; - String name = s.get_slice("/", 1); - String path = ProjectSettings::get_singleton()->get(s); - if (path.begins_with("*")) { - result.insert(name); } + r_result.insert(class_name); } - for (const Map<StringName, int>::Element *E = GDScriptLanguage::get_singleton()->get_global_map().front(); E; E = E->next()) { - result.insert(E->key().operator String()); + if (p_is_function) { + r_result.insert("void"); } } @@ -1563,6 +1674,9 @@ static String _get_visual_datatype(const PropertyInfo &p_info, bool p_isarg = tr if (p_info.type == Variant::OBJECT && p_info.hint == PROPERTY_HINT_RESOURCE_TYPE) return p_info.hint_string; + if (p_info.type == Variant::OBJECT && p_info.class_name != StringName()) + return p_info.class_name.operator String(); + if (p_info.type == Variant::NIL) { if (p_isarg) return "var"; @@ -1575,7 +1689,16 @@ static String _get_visual_datatype(const PropertyInfo &p_info, bool p_isarg = tr static void _make_function_hint(const GDScriptParser::FunctionNode *p_func, int p_argidx, String &arghint) { - arghint = "func " + p_func->name + "("; + GDScriptParser::DataType rettype = p_func->return_type; + + if (rettype.has_type && rettype.kind == GDScriptParser::DataType::UNRESOLVED) { + // Can be unresolved due to incomplete parse, use the unresolved name instead + arghint = rettype.native_type.operator String(); + } else { + arghint = rettype.to_string(); + } + + arghint += " " + p_func->name + "("; for (int i = 0; i < p_func->arguments.size(); i++) { if (i > 0) arghint += ", "; @@ -1585,7 +1708,17 @@ static void _make_function_hint(const GDScriptParser::FunctionNode *p_func, int if (i == p_argidx) { arghint += String::chr(0xFFFF); } - arghint += p_func->arguments[i].operator String(); + + GDScriptParser::DataType argtype = p_func->argument_types[i]; + if (argtype.has_type && argtype.kind == GDScriptParser::DataType::UNRESOLVED) { + // Can be unresolved due to incomplete parse, use the unresolved name instead + arghint += argtype.native_type.operator String(); + } else { + arghint += argtype.to_string(); + } + + arghint += " " + p_func->arguments[i].operator String(); + int deffrom = p_func->arguments.size() - p_func->default_values.size(); if (i >= deffrom) { @@ -1598,9 +1731,8 @@ static void _make_function_hint(const GDScriptParser::FunctionNode *p_func, int const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(p_func->default_values[defidx]); if (op->op == GDScriptParser::OperatorNode::OP_ASSIGN) { const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(op->arguments[1]); - arghint += "=" + cn->value.get_construct_string(); + arghint += " = " + cn->value.get_construct_string(); } - } else { } } } @@ -1736,37 +1868,7 @@ static void _find_type_arguments(GDScriptCompletionContext &context, const GDScr if (func) { - arghint = "func " + String(p_method) + "("; - if (st) - arghint = "static " + arghint; - for (int i = 0; i < func->arguments.size(); i++) { - if (i > 0) - arghint += ", "; - else - arghint += " "; - if (i == p_argidx) { - arghint += String::chr(0xFFFF); - } - arghint += "var " + String(func->arguments[i]); - int deffrom = func->arguments.size() - func->default_values.size(); - if (i >= deffrom) { - - int defidx = deffrom - i; - - if (defidx >= 0 && defidx < func->default_values.size() && func->default_values[defidx]->type == GDScriptParser::Node::TYPE_OPERATOR) { - const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(func->default_values[defidx]); - if (op->op == GDScriptParser::OperatorNode::OP_ASSIGN) { - const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(op->arguments[1]); - arghint += "=" + cn->value.get_construct_string(); - } - } - } - if (i == p_argidx) { - arghint += String::chr(0xFFFF); - } - } - - arghint += " )"; + _make_function_hint(func, p_argidx, arghint); return; } } else { @@ -1816,7 +1918,7 @@ static void _find_type_arguments(GDScriptCompletionContext &context, const GDScr } else { - //regular method + //regular method #if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED) if (p_argidx < m->get_argument_count()) { PropertyInfo pi = m->get_argument_info(p_argidx); @@ -2423,6 +2525,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base if (cid.obj_type != StringName()) { List<MethodInfo> vm; ClassDB::get_virtual_methods(cid.obj_type, &vm); + bool type_hint = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints").operator bool(); for (List<MethodInfo>::Element *E = vm.front(); E; E = E->next()) { MethodInfo &mi = E->get(); @@ -2439,9 +2542,28 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base if (n.find(":") != -1) n = n.substr(0, n.find(":")); m += n; + if (type_hint && mi.arguments[i].type != Variant::NIL) { + m += " : "; + if (mi.arguments[i].type == Variant::OBJECT && mi.arguments[i].class_name != StringName()) { + m += mi.arguments[i].class_name.operator String(); + } else { + m += Variant::get_type_name(mi.arguments[i].type); + } + } + } + } + m += ")"; + if (type_hint && (mi.return_val.type != Variant::NIL || !(mi.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT))) { + m += " -> "; + if (mi.return_val.type == Variant::NIL) { + m += "void"; + } else if (mi.return_val.type == Variant::OBJECT && mi.return_val.class_name != StringName()) { + m += mi.return_val.class_name.operator String(); + } else { + m += Variant::get_type_name(mi.return_val.type); } } - m += "):"; + m += ":"; options.insert(m); } @@ -2509,6 +2631,13 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base } #endif } break; + case GDScriptParser::COMPLETION_TYPE_HINT: { + _find_types(context, options, p.get_completion_identifier_is_function()); + } break; + case GDScriptParser::COMPLETION_TYPE_HINT_INDEX: { + options.insert("SomeIndexedTypeHere"); + options.insert(p.get_completion_cursor().operator String()); + } break; } for (Set<String>::Element *E = options.front(); E; E = E->next()) { |