From c416cbaf544193fb209a6b8dbf0b3bb1b00a1b96 Mon Sep 17 00:00:00 2001 From: HolonProduction Date: Sun, 8 Jun 2025 16:12:33 +0200 Subject: [PATCH] LSP: Fix type bind, mark as experimental, deprecate direct LSP access --- modules/gdscript/config.py | 3 + .../doc_classes/GDScriptLanguageProtocol.xml | 69 +++++++++ .../doc_classes/GDScriptTextDocument.xml | 139 ++++++++++++++++++ .../doc_classes/GDScriptWorkspace.xml | 67 +++++++++ .../gdscript_language_protocol.cpp | 13 +- .../gdscript_language_server.cpp | 8 +- .../gdscript_language_server.h | 2 - .../gdscript_text_document.cpp | 45 +++--- .../language_server/gdscript_workspace.cpp | 8 +- .../language_server/gdscript_workspace.h | 2 +- modules/gdscript/register_types.cpp | 15 +- modules/gdscript/tests/test_lsp.h | 4 +- 12 files changed, 332 insertions(+), 43 deletions(-) create mode 100644 modules/gdscript/doc_classes/GDScriptLanguageProtocol.xml create mode 100644 modules/gdscript/doc_classes/GDScriptTextDocument.xml create mode 100644 modules/gdscript/doc_classes/GDScriptWorkspace.xml diff --git a/modules/gdscript/config.py b/modules/gdscript/config.py index ecd33a5dacc..25278db61b2 100644 --- a/modules/gdscript/config.py +++ b/modules/gdscript/config.py @@ -11,7 +11,10 @@ def get_doc_classes(): return [ "@GDScript", "GDScript", + "GDScriptLanguageProtocol", "GDScriptSyntaxHighlighter", + "GDScriptTextDocument", + "GDScriptWorkspace", ] diff --git a/modules/gdscript/doc_classes/GDScriptLanguageProtocol.xml b/modules/gdscript/doc_classes/GDScriptLanguageProtocol.xml new file mode 100644 index 00000000000..2553f657904 --- /dev/null +++ b/modules/gdscript/doc_classes/GDScriptLanguageProtocol.xml @@ -0,0 +1,69 @@ + + + + GDScript language server. + + + Provides access to certain features that are implemented in the language server. + [b]Note:[/b] This class is not a language server client that can be used to access LSP functionality. It only provides access to a limited set of features that is implemented using the same technical foundation as the language server. + + + + + + + + Returns the language server's [GDScriptTextDocument] instance. + + + + + + Returns the language server's [GDScriptWorkspace] instance. + + + + + + + + + + + + + + + + + + Returns [code]true[/code] if the language server was initialized by a language server client, [code]false[/code] otherwise. + + + + + + Returns [code]true[/code] if the language server is providing the smart resolve feature, [code]false[/code] otherwise. The feature can be configured through the editor settings. + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/gdscript/doc_classes/GDScriptTextDocument.xml b/modules/gdscript/doc_classes/GDScriptTextDocument.xml new file mode 100644 index 00000000000..de401616505 --- /dev/null +++ b/modules/gdscript/doc_classes/GDScriptTextDocument.xml @@ -0,0 +1,139 @@ + + + + Document related language server functionality. + + + Provides language server functionality related to documents. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/gdscript/doc_classes/GDScriptWorkspace.xml b/modules/gdscript/doc_classes/GDScriptWorkspace.xml new file mode 100644 index 00000000000..883ddb17f40 --- /dev/null +++ b/modules/gdscript/doc_classes/GDScriptWorkspace.xml @@ -0,0 +1,67 @@ + + + + Workspace related language server functionality. + + + Provides language server functionality related to the workspace. + + + + + + + + + + + + + + + + + + + + + + + Returns the interface of the script in a machine-readable format. + + + + + + + Converts a URI to a file path. + + + + + + + Converts a file path to a URI. + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp index ac1e508c651..406b533353d 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.cpp +++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp @@ -174,15 +174,18 @@ String GDScriptLanguageProtocol::format_output(const String &p_text) { } void GDScriptLanguageProtocol::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_text_document"), &GDScriptLanguageProtocol::get_text_document); + ClassDB::bind_method(D_METHOD("get_workspace"), &GDScriptLanguageProtocol::get_workspace); + ClassDB::bind_method(D_METHOD("is_smart_resolve_enabled"), &GDScriptLanguageProtocol::is_smart_resolve_enabled); + ClassDB::bind_method(D_METHOD("is_initialized"), &GDScriptLanguageProtocol::is_initialized); + +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("initialize", "params"), &GDScriptLanguageProtocol::initialize); ClassDB::bind_method(D_METHOD("initialized", "params"), &GDScriptLanguageProtocol::initialized); ClassDB::bind_method(D_METHOD("on_client_connected"), &GDScriptLanguageProtocol::on_client_connected); - ClassDB::bind_method(D_METHOD("on_client_disconnected"), &GDScriptLanguageProtocol::on_client_disconnected); + ClassDB::bind_method(D_METHOD("on_client_disconnected", "client_id"), &GDScriptLanguageProtocol::on_client_disconnected); ClassDB::bind_method(D_METHOD("notify_client", "method", "params", "client_id"), &GDScriptLanguageProtocol::notify_client, DEFVAL(Variant()), DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("is_smart_resolve_enabled"), &GDScriptLanguageProtocol::is_smart_resolve_enabled); - ClassDB::bind_method(D_METHOD("get_text_document"), &GDScriptLanguageProtocol::get_text_document); - ClassDB::bind_method(D_METHOD("get_workspace"), &GDScriptLanguageProtocol::get_workspace); - ClassDB::bind_method(D_METHOD("is_initialized"), &GDScriptLanguageProtocol::is_initialized); +#endif // !DISABLE_DEPRECATED } Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) { diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp index aeff4d4e5a7..df5456d9b8f 100644 --- a/modules/gdscript/language_server/gdscript_language_server.cpp +++ b/modules/gdscript/language_server/gdscript_language_server.cpp @@ -62,7 +62,7 @@ void GDScriptLanguageServer::_notification(int p_what) { } if (started && !use_thread) { - protocol.poll(poll_limit_usec); + GDScriptLanguageProtocol::get_singleton()->poll(poll_limit_usec); } } break; @@ -88,7 +88,7 @@ void GDScriptLanguageServer::thread_main(void *p_userdata) { GDScriptLanguageServer *self = static_cast(p_userdata); while (self->thread_running) { // Poll 20 times per second - self->protocol.poll(self->poll_limit_usec); + GDScriptLanguageProtocol::get_singleton()->poll(self->poll_limit_usec); OS::get_singleton()->delay_usec(50000); } } @@ -98,7 +98,7 @@ void GDScriptLanguageServer::start() { port = (GDScriptLanguageServer::port_override > -1) ? GDScriptLanguageServer::port_override : (int)_EDITOR_GET("network/language_server/remote_port"); use_thread = (bool)_EDITOR_GET("network/language_server/use_thread"); poll_limit_usec = (int)_EDITOR_GET("network/language_server/poll_limit_usec"); - if (protocol.start(port, IPAddress(host)) == OK) { + if (GDScriptLanguageProtocol::get_singleton()->start(port, IPAddress(host)) == OK) { EditorNode::get_log()->add_message("--- GDScript language server started on port " + itos(port) + " ---", EditorLog::MSG_TYPE_EDITOR); if (use_thread) { thread_running = true; @@ -115,7 +115,7 @@ void GDScriptLanguageServer::stop() { thread_running = false; thread.wait_to_finish(); } - protocol.stop(); + GDScriptLanguageProtocol::get_singleton()->stop(); started = false; EditorNode::get_log()->add_message("--- GDScript language server stopped ---", EditorLog::MSG_TYPE_EDITOR); } diff --git a/modules/gdscript/language_server/gdscript_language_server.h b/modules/gdscript/language_server/gdscript_language_server.h index 12541ec5807..2abf3e2ea1d 100644 --- a/modules/gdscript/language_server/gdscript_language_server.h +++ b/modules/gdscript/language_server/gdscript_language_server.h @@ -37,8 +37,6 @@ class GDScriptLanguageServer : public EditorPlugin { GDCLASS(GDScriptLanguageServer, EditorPlugin); - GDScriptLanguageProtocol protocol; - Thread thread; bool thread_running = false; // There is no notification when the editor is initialized. We need to poll till we attempted to start the server. diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index 354fc4b69bb..e5e6367e52a 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -39,27 +39,30 @@ #include "servers/display/display_server.h" void GDScriptTextDocument::_bind_methods() { - ClassDB::bind_method(D_METHOD("didOpen"), &GDScriptTextDocument::didOpen); - ClassDB::bind_method(D_METHOD("didClose"), &GDScriptTextDocument::didClose); - ClassDB::bind_method(D_METHOD("didChange"), &GDScriptTextDocument::didChange); - ClassDB::bind_method(D_METHOD("willSaveWaitUntil"), &GDScriptTextDocument::willSaveWaitUntil); - ClassDB::bind_method(D_METHOD("didSave"), &GDScriptTextDocument::didSave); - ClassDB::bind_method(D_METHOD("nativeSymbol"), &GDScriptTextDocument::nativeSymbol); - ClassDB::bind_method(D_METHOD("documentSymbol"), &GDScriptTextDocument::documentSymbol); - ClassDB::bind_method(D_METHOD("completion"), &GDScriptTextDocument::completion); - ClassDB::bind_method(D_METHOD("resolve"), &GDScriptTextDocument::resolve); - ClassDB::bind_method(D_METHOD("rename"), &GDScriptTextDocument::rename); - ClassDB::bind_method(D_METHOD("prepareRename"), &GDScriptTextDocument::prepareRename); - ClassDB::bind_method(D_METHOD("references"), &GDScriptTextDocument::references); - ClassDB::bind_method(D_METHOD("foldingRange"), &GDScriptTextDocument::foldingRange); - ClassDB::bind_method(D_METHOD("codeLens"), &GDScriptTextDocument::codeLens); - ClassDB::bind_method(D_METHOD("documentLink"), &GDScriptTextDocument::documentLink); - ClassDB::bind_method(D_METHOD("colorPresentation"), &GDScriptTextDocument::colorPresentation); - 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); + ClassDB::bind_method(D_METHOD("show_native_symbol_in_editor", "symbol_id"), &GDScriptTextDocument::show_native_symbol_in_editor); + +#ifndef DISABLE_DEPRECATED + ClassDB::bind_method(D_METHOD("didOpen", "params"), &GDScriptTextDocument::didOpen); + ClassDB::bind_method(D_METHOD("didClose", "params"), &GDScriptTextDocument::didClose); + ClassDB::bind_method(D_METHOD("didChange", "params"), &GDScriptTextDocument::didChange); + ClassDB::bind_method(D_METHOD("willSaveWaitUntil", "params"), &GDScriptTextDocument::willSaveWaitUntil); + ClassDB::bind_method(D_METHOD("didSave", "params"), &GDScriptTextDocument::didSave); + ClassDB::bind_method(D_METHOD("nativeSymbol", "params"), &GDScriptTextDocument::nativeSymbol); + ClassDB::bind_method(D_METHOD("documentSymbol", "params"), &GDScriptTextDocument::documentSymbol); + ClassDB::bind_method(D_METHOD("completion", "params"), &GDScriptTextDocument::completion); + ClassDB::bind_method(D_METHOD("resolve", "params"), &GDScriptTextDocument::resolve); + ClassDB::bind_method(D_METHOD("rename", "params"), &GDScriptTextDocument::rename); + ClassDB::bind_method(D_METHOD("prepareRename", "params"), &GDScriptTextDocument::prepareRename); + ClassDB::bind_method(D_METHOD("references", "params"), &GDScriptTextDocument::references); + ClassDB::bind_method(D_METHOD("foldingRange", "params"), &GDScriptTextDocument::foldingRange); + ClassDB::bind_method(D_METHOD("codeLens", "params"), &GDScriptTextDocument::codeLens); + ClassDB::bind_method(D_METHOD("documentLink", "params"), &GDScriptTextDocument::documentLink); + ClassDB::bind_method(D_METHOD("colorPresentation", "params"), &GDScriptTextDocument::colorPresentation); + ClassDB::bind_method(D_METHOD("hover", "params"), &GDScriptTextDocument::hover); + ClassDB::bind_method(D_METHOD("definition", "params"), &GDScriptTextDocument::definition); + ClassDB::bind_method(D_METHOD("declaration", "params"), &GDScriptTextDocument::declaration); + ClassDB::bind_method(D_METHOD("signatureHelp", "params"), &GDScriptTextDocument::signatureHelp); +#endif // !DISABLE_DEPRECATED } void GDScriptTextDocument::didOpen(const Variant &p_param) { diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index 0d5e0e3d53f..abebe3b01e6 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -43,16 +43,16 @@ #include "editor/settings/editor_settings.h" void GDScriptWorkspace::_bind_methods() { - ClassDB::bind_method(D_METHOD("apply_new_signal"), &GDScriptWorkspace::apply_new_signal); + ClassDB::bind_method(D_METHOD("apply_new_signal", "obj", "function", "args"), &GDScriptWorkspace::apply_new_signal); ClassDB::bind_method(D_METHOD("get_file_path", "uri"), &GDScriptWorkspace::get_file_path); ClassDB::bind_method(D_METHOD("get_file_uri", "path"), &GDScriptWorkspace::get_file_uri); - ClassDB::bind_method(D_METHOD("publish_diagnostics", "path"), &GDScriptWorkspace::publish_diagnostics); ClassDB::bind_method(D_METHOD("generate_script_api", "path"), &GDScriptWorkspace::generate_script_api); #ifndef DISABLE_DEPRECATED - ClassDB::bind_method(D_METHOD("didDeleteFiles"), &GDScriptWorkspace::didDeleteFiles); + ClassDB::bind_method(D_METHOD("didDeleteFiles", "params"), &GDScriptWorkspace::didDeleteFiles); ClassDB::bind_method(D_METHOD("parse_script", "path", "content"), &GDScriptWorkspace::parse_script); - ClassDB::bind_method(D_METHOD("parse_local_script", "path"), &GDScriptWorkspace::parse_script); + ClassDB::bind_method(D_METHOD("parse_local_script", "path"), &GDScriptWorkspace::parse_local_script); + ClassDB::bind_method(D_METHOD("publish_diagnostics", "path"), &GDScriptWorkspace::publish_diagnostics); #endif } diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h index 27960db0788..8a2b2c5a627 100644 --- a/modules/gdscript/language_server/gdscript_workspace.h +++ b/modules/gdscript/language_server/gdscript_workspace.h @@ -41,7 +41,7 @@ class GDScriptWorkspace : public RefCounted { private: #ifndef DISABLE_DEPRECATED - void didDeleteFiles() {} + void didDeleteFiles(const Dictionary &p_params) {} Error parse_script(const String &p_path, const String &p_content) { WARN_DEPRECATED; return Error::FAILED; diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index 661d33ef16f..3ca31117bea 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -128,10 +128,9 @@ static void _editor_init() { #endif #ifndef GDSCRIPT_NO_LSP - register_lsp_types(); - GDScriptLanguageServer *lsp_plugin = memnew(GDScriptLanguageServer); - EditorNode::get_singleton()->add_editor_plugin(lsp_plugin); - Engine::get_singleton()->add_singleton(Engine::Singleton("GDScriptLanguageProtocol", GDScriptLanguageProtocol::get_singleton())); + Engine::Singleton singleton("GDScriptLanguageProtocol", GDScriptLanguageProtocol::get_singleton()); + singleton.editor_only = true; + Engine::get_singleton()->add_singleton(singleton); #endif // !GDSCRIPT_NO_LSP } @@ -163,6 +162,11 @@ void initialize_gdscript_module(ModuleInitializationLevel p_level) { EditorTranslationParser::get_singleton()->add_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD); } else if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { GDREGISTER_CLASS(GDScriptSyntaxHighlighter); +#ifndef GDSCRIPT_NO_LSP + register_lsp_types(); + memnew(GDScriptLanguageProtocol); + EditorPlugins::add_by_type(); +#endif // !GDSCRIPT_NO_LSP } #endif // TOOLS_ENABLED } @@ -193,6 +197,9 @@ void uninitialize_gdscript_module(ModuleInitializationLevel p_level) { if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { EditorTranslationParser::get_singleton()->remove_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD); gdscript_translation_parser_plugin.unref(); +#ifndef GDSCRIPT_NO_LSP + memdelete(GDScriptLanguageProtocol::get_singleton()); +#endif // GDSCRIPT_NO_LSP } #endif // TOOLS_ENABLED } diff --git a/modules/gdscript/tests/test_lsp.h b/modules/gdscript/tests/test_lsp.h index aca88028895..631ede4f17f 100644 --- a/modules/gdscript/tests/test_lsp.h +++ b/modules/gdscript/tests/test_lsp.h @@ -101,6 +101,8 @@ GDScriptLanguageProtocol *initialize(const String &p_root) { String absolute_root = dir->get_current_dir(); init_language(absolute_root); + // Recreate the singleton for each test, to ensure a clean state. + memdelete_notnull(GDScriptLanguageProtocol::get_singleton()); GDScriptLanguageProtocol *proto = memnew(GDScriptLanguageProtocol); TestGDScriptLanguageProtocolInitializer::setup_client(); @@ -492,7 +494,6 @@ func f(): test_resolve_symbols(uri, all_test_data, all_test_data); } - memdelete(proto); memdelete(efs); finish_language(); } @@ -531,7 +532,6 @@ func f(): REQUIRE(cls.documentation.contains("t3")); } - memdelete(proto); memdelete(efs); finish_language(); }