diff options
-rw-r--r-- | core/io/resource_loader.cpp | 5 | ||||
-rw-r--r-- | doc/classes/Environment.xml | 7 | ||||
-rw-r--r-- | main/tests/test_gdscript.cpp | 24 | ||||
-rw-r--r-- | modules/gdscript/gdscript_functions.cpp | 7 | ||||
-rw-r--r-- | modules/gdscript/gdscript_parser.cpp | 16 | ||||
-rw-r--r-- | modules/gdscript/language_server/gdscript_extend_parser.cpp | 45 | ||||
-rw-r--r-- | modules/gdscript/language_server/gdscript_extend_parser.h | 2 | ||||
-rw-r--r-- | modules/gdscript/language_server/gdscript_language_server.cpp | 2 | ||||
-rw-r--r-- | modules/gdscript/language_server/gdscript_text_document.cpp | 15 | ||||
-rw-r--r-- | modules/gdscript/language_server/gdscript_text_document.h | 1 | ||||
-rw-r--r-- | modules/gdscript/language_server/gdscript_workspace.cpp | 51 | ||||
-rw-r--r-- | modules/gdscript/language_server/gdscript_workspace.h | 1 | ||||
-rw-r--r-- | modules/gdscript/language_server/lsp.hpp | 116 |
13 files changed, 278 insertions, 14 deletions
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 5d85634895..7471ab4241 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -277,6 +277,11 @@ RES ResourceLoader::_load(const String &p_path, const String &p_original_path, c ERR_FAIL_COND_V_MSG(found, RES(), "Failed loading resource: " + p_path + "."); +#ifdef TOOLS_ENABLED + FileAccessRef file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES); + ERR_FAIL_COND_V_MSG(!file_check->file_exists(p_path), RES(), "Resource file not found: " + p_path + "."); +#endif + ERR_FAIL_V_MSG(RES(), "No loader found for resource: " + p_path + "."); } diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index 00a80a084b..702ea0a999 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -32,7 +32,7 @@ <argument index="1" name="enabled" type="bool"> </argument> <description> - Enables or disables the glow level at index [code]idx[/code]. The more glow levels are enabled, the slower the glow effect will be. + Enables or disables the glow level at index [code]idx[/code]. Each level relies on the previous level. This means that enabling higher glow levels will slow down the glow effect rendering, even if previous levels aren't enabled. </description> </method> </methods> @@ -60,7 +60,6 @@ </member> <member name="ambient_light_sky_contribution" type="float" setter="set_ambient_light_sky_contribution" getter="get_ambient_light_sky_contribution" default="1.0"> Defines the amount of light that the sky brings on the scene. A value of 0 means that the sky's light emission has no effect on the scene illumination, thus all ambient illumination is provided by the ambient light. On the contrary, a value of 1 means that all the light that affects the scene is provided by the sky, thus the ambient light parameter has no effect on the scene. - This value is multiplied by [member ambient_light_energy], so it must be set to a value higher than [code]0[/code] for changes to be visible when adjusting [member ambient_light_sky_contribution]. </member> <member name="auto_exposure_enabled" type="bool" setter="set_tonemap_auto_exposure" getter="get_tonemap_auto_exposure" default="false"> If [code]true[/code], enables the tonemapping auto exposure mode of the scene renderer. If [code]true[/code], the renderer will automatically determine the exposure setting to adapt to the scene's illumination and the observed light. @@ -181,7 +180,7 @@ </member> <member name="glow_bicubic_upscale" type="bool" setter="set_glow_bicubic_upscale" getter="is_glow_bicubic_upscale_enabled" default="false"> Smooths out the blockiness created by sampling higher levels, at the cost of performance. - [b]Note:[/b] Only available when using the GLES3 renderer. + [b]Note:[/b] When using the GLES2 renderer, this is only available if the GPU supports the [code]GL_EXT_gpu_shader4[/code] extension. </member> <member name="glow_blend_mode" type="int" setter="set_glow_blend_mode" getter="get_glow_blend_mode" enum="Environment.GlowBlendMode" default="2"> The glow blending mode. @@ -262,7 +261,7 @@ The screen-space ambient occlusion edge sharpness. </member> <member name="ssao_enabled" type="bool" setter="set_ssao_enabled" getter="is_ssao_enabled" default="false"> - If [code]true[/code], the screen-space ambient occlusion effect is enabled. This is a costly effect and should be disabled first when having performance issues. + If [code]true[/code], the screen-space ambient occlusion effect is enabled. This darkens objects' corners and cavities to simulate ambient light not reaching the entire object as in real life. This works well for small, dynamic objects, but baked lighting or ambient occlusion textures will do a better job at displaying ambient occlusion on large static objects. This is a costly effect and should be disabled first when running into performance issues. </member> <member name="ssao_intensity" type="float" setter="set_ssao_intensity" getter="get_ssao_intensity" default="1.0"> The primary screen-space ambient occlusion intensity. See also [member ssao_radius]. diff --git a/main/tests/test_gdscript.cpp b/main/tests/test_gdscript.cpp index 729c5f99cf..a6ef0e9cf4 100644 --- a/main/tests/test_gdscript.cpp +++ b/main/tests/test_gdscript.cpp @@ -671,6 +671,30 @@ static void _disassemble_class(const Ref<GDScript> &p_class, const Vector<String incr += 2; } break; + case GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN: { + + txt += " assign typed builtin ("; + txt += Variant::get_type_name((Variant::Type)code[ip + 1]); + txt += ") "; + txt += DADDR(2); + txt += " = "; + txt += DADDR(3); + incr += 4; + + } break; + case GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE: { + Variant className = func.get_constant(code[ip + 1]); + GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(className.operator Object *()); + + txt += " assign typed native ("; + txt += nc->get_name().operator String(); + txt += ") "; + txt += DADDR(2); + txt += " = "; + txt += DADDR(3); + incr += 4; + + } break; case GDScriptFunction::OPCODE_CAST_TO_SCRIPT: { txt += " cast "; diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index e0e95f259e..01d62a1c62 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -2057,12 +2057,13 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { mi.return_val.type = Variant::BOOL; return mi; } break; - case FUNC_MAX: { + default: { ERR_FAIL_V(MethodInfo()); } break; } #endif - - return MethodInfo(); + MethodInfo mi; + mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + return mi; } diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 4a28114a7d..0a1eda37ca 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -8192,11 +8192,14 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { if (lh_type.has_type && rh_type.may_yield && op->arguments[1]->type == Node::TYPE_OPERATOR) { _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, op->line, _find_function_name(static_cast<OperatorNode *>(op->arguments[1]))); } -#endif // DEBUG_ENABLED bool type_match = check_types; +#endif // DEBUG_ENABLED if (check_types && !_is_type_compatible(lh_type, rh_type)) { +#ifdef DEBUG_ENABLED type_match = false; +#endif // DEBUG_ENABLED + // Try supertype test if (_is_type_compatible(rh_type, lh_type)) { _mark_line_as_unsafe(op->line); @@ -8227,7 +8230,9 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { op->arguments.write[1] = convert_call; +#ifdef DEBUG_ENABLED type_match = true; // Since we are converting, the type is matching +#endif // DEBUG_ENABLED } #ifdef DEBUG_ENABLED if (lh_type.builtin_type == Variant::INT && rh_type.builtin_type == Variant::REAL) { @@ -8240,8 +8245,10 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { if (!rh_type.has_type && (op->op != OperatorNode::OP_ASSIGN || lh_type.has_type || op->arguments[0]->type == Node::TYPE_OPERATOR)) { _mark_line_as_unsafe(op->line); } -#endif // DEBUG_ENABLED op->datatype.has_type = type_match; +#else + op->datatype.has_type = false; +#endif // DEBUG_ENABLED } break; case OperatorNode::OP_CALL: case OperatorNode::OP_PARENT_CALL: { @@ -8484,11 +8491,8 @@ Error GDScriptParser::_parse(const String &p_base_path) { current_class = main_class; current_function = NULL; current_block = NULL; -#ifdef DEBUG_ENABLED + if (for_completion) check_types = false; -#else - check_types = false; -#endif // Resolve all class-level stuff before getting into function blocks _check_class_level_types(main_class); diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 8287c9c084..c8ff471401 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -522,6 +522,51 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::search_symbol_defined_at_line(i return ret; } +Error ExtendGDScriptParser::get_left_function_call(const lsp::Position &p_position, lsp::Position &r_func_pos, int &r_arg_index) const { + + ERR_FAIL_INDEX_V(p_position.line, lines.size(), ERR_INVALID_PARAMETER); + + int bracket_stack = 0; + int index = 0; + + bool found = false; + for (int l = p_position.line; l >= 0; --l) { + String line = lines[l]; + int c = line.length() - 1; + if (l == p_position.line) { + c = MIN(c, p_position.character - 1); + } + + while (c >= 0) { + const CharType &charactor = line[c]; + if (charactor == ')') { + ++bracket_stack; + } else if (charactor == '(') { + --bracket_stack; + if (bracket_stack < 0) { + found = true; + } + } + if (bracket_stack <= 0 && charactor == ',') { + ++index; + } + --c; + if (found) { + r_func_pos.character = c; + break; + } + } + + if (found) { + r_func_pos.line = l; + r_arg_index = index; + return OK; + } + } + + return ERR_METHOD_NOT_FOUND; +} + const lsp::DocumentSymbol *ExtendGDScriptParser::get_symbol_defined_at_line(int p_line) const { if (p_line <= 0) { return &class_symbol; diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h index 5afe991d24..43dfce994b 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.h +++ b/modules/gdscript/language_server/gdscript_extend_parser.h @@ -83,6 +83,8 @@ public: _FORCE_INLINE_ const ClassMembers &get_members() const { return members; } _FORCE_INLINE_ const HashMap<String, ClassMembers> &get_inner_classes() const { return inner_classes; } + Error get_left_function_call(const lsp::Position &p_position, lsp::Position &r_func_pos, int &r_arg_index) const; + String get_text_for_completion(const lsp::Position &p_cursor) const; String get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol = "", bool p_func_requred = false) const; String get_identifier_under_position(const lsp::Position &p_position, Vector2i &p_offset) const; diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp index 159f2ba2d4..9675c07c00 100644 --- a/modules/gdscript/language_server/gdscript_language_server.cpp +++ b/modules/gdscript/language_server/gdscript_language_server.cpp @@ -38,7 +38,7 @@ GDScriptLanguageServer::GDScriptLanguageServer() { thread = NULL; thread_exit = false; _EDITOR_DEF("network/language_server/remote_port", 6008); - _EDITOR_DEF("network/language_server/enable_smart_resolve", false); + _EDITOR_DEF("network/language_server/enable_smart_resolve", true); _EDITOR_DEF("network/language_server/show_native_symbols_in_editor", false); } diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index b3d1b67af5..33ca4a5d15 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -50,6 +50,7 @@ void GDScriptTextDocument::_bind_methods() { ClassDB::bind_method(D_METHOD("hover"), &GDScriptTextDocument::hover); ClassDB::bind_method(D_METHOD("definition"), &GDScriptTextDocument::definition); ClassDB::bind_method(D_METHOD("declaration"), &GDScriptTextDocument::declaration); + ClassDB::bind_method(D_METHOD("signatureHelp"), &GDScriptTextDocument::signatureHelp); ClassDB::bind_method(D_METHOD("show_native_symbol_in_editor"), &GDScriptTextDocument::show_native_symbol_in_editor); } @@ -387,6 +388,20 @@ Variant GDScriptTextDocument::declaration(const Dictionary &p_params) { return arr; } +Variant GDScriptTextDocument::signatureHelp(const Dictionary &p_params) { + Variant ret; + + lsp::TextDocumentPositionParams params; + params.load(p_params); + + lsp::SignatureHelp s; + if (OK == GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_signature(params, s)) { + ret = s.to_json(); + } + + return ret; +} + GDScriptTextDocument::GDScriptTextDocument() { file_checker = FileAccess::create(FileAccess::ACCESS_RESOURCES); } diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h index d93d828003..b2fd0c31f9 100644 --- a/modules/gdscript/language_server/gdscript_text_document.h +++ b/modules/gdscript/language_server/gdscript_text_document.h @@ -67,6 +67,7 @@ public: Variant hover(const Dictionary &p_params); Array definition(const Dictionary &p_params); Variant declaration(const Dictionary &p_params); + Variant signatureHelp(const Dictionary &p_params); void initialize(); diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index 0661c4af26..1c0590cff1 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -252,6 +252,12 @@ Error GDScriptWorkspace::initialize() { bool arg_default_value_started = false; for (int j = 0; j < data.arguments.size(); j++) { const DocData::ArgumentDoc &arg = data.arguments[j]; + + lsp::DocumentSymbol symbol_arg; + symbol_arg.name = arg.name; + symbol_arg.kind = lsp::SymbolKind::Variable; + symbol_arg.detail = arg.type; + if (!arg_default_value_started && !arg.default_value.empty()) { arg_default_value_started = true; } @@ -263,6 +269,8 @@ Error GDScriptWorkspace::initialize() { arg_str += ", "; } params += arg_str; + + symbol.children.push_back(symbol_arg); } if (data.qualifiers.find("vararg") != -1) { params += params.empty() ? "..." : ", ..."; @@ -513,6 +521,49 @@ Dictionary GDScriptWorkspace::generate_script_api(const String &p_path) { return api; } +Error GDScriptWorkspace::resolve_signature(const lsp::TextDocumentPositionParams &p_doc_pos, lsp::SignatureHelp &r_signature) { + if (const ExtendGDScriptParser *parser = get_parse_result(get_file_path(p_doc_pos.textDocument.uri))) { + + lsp::TextDocumentPositionParams text_pos; + text_pos.textDocument = p_doc_pos.textDocument; + + if (parser->get_left_function_call(p_doc_pos.position, text_pos.position, r_signature.activeParameter) == OK) { + + List<const lsp::DocumentSymbol *> symbols; + + if (const lsp::DocumentSymbol *symbol = resolve_symbol(text_pos)) { + symbols.push_back(symbol); + } else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { + GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(text_pos, symbols); + } + + for (List<const lsp::DocumentSymbol *>::Element *E = symbols.front(); E; E = E->next()) { + const lsp::DocumentSymbol *symbol = E->get(); + if (symbol->kind == lsp::SymbolKind::Method || symbol->kind == lsp::SymbolKind::Function) { + + lsp::SignatureInformation signature_info; + signature_info.label = symbol->detail; + signature_info.documentation = symbol->render(); + + for (int i = 0; i < symbol->children.size(); i++) { + const lsp::DocumentSymbol &arg = symbol->children[i]; + lsp::ParameterInformation arg_info; + arg_info.label = arg.name; + signature_info.parameters.push_back(arg_info); + } + r_signature.signatures.push_back(signature_info); + break; + } + } + + if (r_signature.signatures.size()) { + return OK; + } + } + } + return ERR_METHOD_NOT_FOUND; +} + GDScriptWorkspace::GDScriptWorkspace() { ProjectSettings::get_singleton()->get_resource_path(); } diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h index f44153dcd0..146a5cb7c9 100644 --- a/modules/gdscript/language_server/gdscript_workspace.h +++ b/modules/gdscript/language_server/gdscript_workspace.h @@ -83,6 +83,7 @@ public: const lsp::DocumentSymbol *resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params); void resolve_document_links(const String &p_uri, List<lsp::DocumentLink> &r_list); Dictionary generate_script_api(const String &p_path); + Error resolve_signature(const lsp::TextDocumentPositionParams &p_doc_pos, lsp::SignatureHelp &r_signature); GDScriptWorkspace(); ~GDScriptWorkspace(); diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp index ffeea70008..379024844e 100644 --- a/modules/gdscript/language_server/lsp.hpp +++ b/modules/gdscript/language_server/lsp.hpp @@ -1402,6 +1402,120 @@ struct Hover { } }; +/** + * Represents a parameter of a callable-signature. A parameter can + * have a label and a doc-comment. + */ +struct ParameterInformation { + + /** + * The label of this parameter information. + * + * Either a string or an inclusive start and exclusive end offsets within its containing + * signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 + * string representation as `Position` and `Range` does. + * + * *Note*: a label of type string should be a substring of its containing signature label. + * Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. + */ + String label; + + /** + * The human-readable doc-comment of this parameter. Will be shown + * in the UI but can be omitted. + */ + MarkupContent documentation; + + Dictionary to_json() const { + Dictionary dict; + dict["label"] = label; + dict["documentation"] = documentation.to_json(); + return dict; + } +}; + +/** + * Represents the signature of something callable. A signature + * can have a label, like a function-name, a doc-comment, and + * a set of parameters. + */ +struct SignatureInformation { + /** + * The label of this signature. Will be shown in + * the UI. + */ + String label; + + /** + * The human-readable doc-comment of this signature. Will be shown + * in the UI but can be omitted. + */ + MarkupContent documentation; + + /** + * The parameters of this signature. + */ + Vector<ParameterInformation> parameters; + + Dictionary to_json() const { + Dictionary dict; + dict["label"] = label; + dict["documentation"] = documentation.to_json(); + Array args; + for (int i = 0; i < parameters.size(); i++) { + args.push_back(parameters[i].to_json()); + } + dict["parameters"] = args; + return dict; + } +}; + +/** + * Signature help represents the signature of something + * callable. There can be multiple signature but only one + * active and only one active parameter. + */ +struct SignatureHelp { + /** + * One or more signatures. + */ + Vector<SignatureInformation> signatures; + + /** + * The active signature. If omitted or the value lies outside the + * range of `signatures` the value defaults to zero or is ignored if + * `signatures.length === 0`. Whenever possible implementors should + * make an active decision about the active signature and shouldn't + * rely on a default value. + * In future version of the protocol this property might become + * mandatory to better express this. + */ + int activeSignature = 0; + + /** + * The active parameter of the active signature. If omitted or the value + * lies outside the range of `signatures[activeSignature].parameters` + * defaults to 0 if the active signature has parameters. If + * the active signature has no parameters it is ignored. + * In future version of the protocol this property might become + * mandatory to better express the active parameter if the + * active signature does have any. + */ + int activeParameter = 0; + + Dictionary to_json() const { + Dictionary dict; + Array sigs; + for (int i = 0; i < signatures.size(); i++) { + sigs.push_back(signatures[i].to_json()); + } + dict["signatures"] = sigs; + dict["activeSignature"] = activeSignature; + dict["activeParameter"] = activeParameter; + return dict; + } +}; + struct ServerCapabilities { /** * Defines how text documents are synced. Is either a detailed structure defining each notification or @@ -1532,6 +1646,8 @@ struct ServerCapabilities { Dictionary dict; dict["textDocumentSync"] = (int)textDocumentSync.change; dict["completionProvider"] = completionProvider.to_json(); + signatureHelpProvider.triggerCharacters.push_back(","); + signatureHelpProvider.triggerCharacters.push_back("("); dict["signatureHelpProvider"] = signatureHelpProvider.to_json(); dict["codeLensProvider"] = false; // codeLensProvider.to_json(); dict["documentOnTypeFormattingProvider"] = documentOnTypeFormattingProvider.to_json(); |