summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--editor/connections_dialog.cpp4
-rw-r--r--editor/editor_settings.cpp1
-rw-r--r--modules/gdscript/gdscript_editor.cpp235
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()) {