diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 45c03618e7..3f21f43402 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1764,6 +1764,113 @@ static bool _is_expression_named_identifier(const GDScriptParser::ExpressionNode return false; } +// Creates a map of exemplary results for some functions that return a structured dictionary. +// Setting this example as value allows autocompletion to suggest the specific keys in some cases. +static HashMap make_structure_samples() { + HashMap res; + const Array arr; + + { + Dictionary d; + d.set("major", 0); + d.set("minor", 0); + d.set("patch", 0); + d.set("hex", 0); + d.set("status", String()); + d.set("build", String()); + d.set("hash", String()); + d.set("timestamp", 0); + d.set("string", String()); + res["Engine::get_version_info"] = d; + } + + { + Dictionary d; + d.set("lead_developers", arr); + d.set("founders", arr); + d.set("project_managers", arr); + d.set("developers", arr); + res["Engine::get_author_info"] = d; + } + + { + Dictionary d; + d.set("platinum_sponsors", arr); + d.set("gold_sponsors", arr); + d.set("silver_sponsors", arr); + d.set("bronze_sponsors", arr); + d.set("mini_sponsors", arr); + d.set("gold_donors", arr); + d.set("silver_donors", arr); + d.set("bronze_donors", arr); + res["Engine::get_donor_info"] = d; + } + + { + Dictionary d; + d.set("physical", -1); + d.set("free", -1); + d.set("available", -1); + d.set("stack", -1); + res["OS::get_memory_info"] = d; + } + + { + Dictionary d; + d.set("year", 0); + d.set("month", 0); + d.set("day", 0); + d.set("weekday", 0); + d.set("hour", 0); + d.set("minute", 0); + d.set("second", 0); + d.set("dst", 0); + res["Time::get_datetime_dict_from_system"] = d; + } + + { + Dictionary d; + d.set("year", 0); + d.set("month", 0); + d.set("day", 0); + d.set("weekday", 0); + d.set("hour", 0); + d.set("minute", 0); + d.set("second", 0); + res["Time::get_datetime_dict_from_unix_time"] = d; + } + + { + Dictionary d; + d.set("year", 0); + d.set("month", 0); + d.set("day", 0); + d.set("weekday", 0); + res["Time::get_date_dict_from_system"] = d; + res["Time::get_date_dict_from_unix_time"] = d; + } + + { + Dictionary d; + d.set("hour", 0); + d.set("minute", 0); + d.set("second", 0); + res["Time::get_time_dict_from_system"] = d; + res["Time::get_time_dict_from_unix_time"] = d; + } + + { + Dictionary d; + d.set("bias", 0); + d.set("name", String()); + res["Time::get_time_zone_from_system"] = d; + } + + return res; +} + +static const HashMap structure_examples = make_structure_samples(); + static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::ExpressionNode *p_expression, GDScriptCompletionIdentifier &r_type) { bool found = false; @@ -1884,156 +1991,68 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, } break; case GDScriptParser::Node::CALL: { const GDScriptParser::CallNode *call = static_cast(p_expression); - if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) { - r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - r_type.type.kind = GDScriptParser::DataType::BUILTIN; - r_type.type.builtin_type = GDScriptParser::get_builtin_type(call->function_name); - found = true; - break; - } else if (GDScriptUtilityFunctions::function_exists(call->function_name)) { - MethodInfo mi = GDScriptUtilityFunctions::get_function_info(call->function_name); - r_type = _type_from_property(mi.return_val); - found = true; - break; - } else { - GDScriptParser::CompletionContext c = p_context; - c.current_line = call->start_line; + GDScriptParser::CompletionContext c = p_context; + c.current_line = call->start_line; - GDScriptParser::Node::Type callee_type = call->get_callee_type(); + GDScriptParser::Node::Type callee_type = call->get_callee_type(); - GDScriptCompletionIdentifier base; - if (callee_type == GDScriptParser::Node::IDENTIFIER || call->is_super) { - // Simple call, so base is 'self'. - if (p_context.current_class) { - if (call->is_super) { - base.type = p_context.current_class->base_type; - base.value = p_context.base; - } else { - base.type.kind = GDScriptParser::DataType::CLASS; - base.type.type_source = GDScriptParser::DataType::INFERRED; - base.type.is_constant = true; - base.type.class_type = p_context.current_class; - base.value = p_context.base; - } + GDScriptCompletionIdentifier base; + if (callee_type == GDScriptParser::Node::IDENTIFIER || call->is_super) { + // Simple call, so base is 'self'. + if (p_context.current_class) { + if (call->is_super) { + base.type = p_context.current_class->base_type; + base.value = p_context.base; } else { - break; - } - } else if (callee_type == GDScriptParser::Node::SUBSCRIPT && static_cast(call->callee)->is_attribute) { - if (!_guess_expression_type(c, static_cast(call->callee)->base, base)) { - found = false; - break; + base.type.kind = GDScriptParser::DataType::CLASS; + base.type.type_source = GDScriptParser::DataType::INFERRED; + base.type.is_constant = true; + base.type.class_type = p_context.current_class; + base.value = p_context.base; } } else { break; } + } else if (callee_type == GDScriptParser::Node::SUBSCRIPT && static_cast(call->callee)->is_attribute) { + if (!_guess_expression_type(c, static_cast(call->callee)->base, base)) { + found = false; + break; + } + } else { + break; + } - // Try call if constant methods with constant arguments - if (base.type.is_constant && base.value.get_type() == Variant::OBJECT) { - GDScriptParser::DataType native_type = base.type; - - while (native_type.kind == GDScriptParser::DataType::CLASS) { - native_type = native_type.class_type->base_type; - } - - while (native_type.kind == GDScriptParser::DataType::SCRIPT) { - if (native_type.script_type.is_valid()) { - Ref