From d04bc6f774e50818f218db4f707babee75ec168e Mon Sep 17 00:00:00 2001 From: Myriad-Dreamin <35292584+Myriad-Dreamin@users.noreply.github.com> Date: Mon, 6 Apr 2026 10:23:38 +0800 Subject: [PATCH] feat: register server capability correctly (#397) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - register workspace and text document capabilities through the structured LSP capability types - advertise completion, signature help, declaration, definition, implementation, type definition, and reference support more explicitly - add placeholder handlers for declaration, type definition, and implementation requests so the advertised capabilities have matching routes ## Testing - Not run (not requested) ## Summary by CodeRabbit * **New Features** * Added workspace folder support for improved project tracking. * Registered navigation handlers for type-definition, implementation, and declaration (currently return a “not supported yet” placeholder). * **Improvements** * Enhanced completion and signature help with explicit trigger characters and clearer capability declarations. * **Tests** * Relaxed capability assertions to recognize more nuanced enabled/disabled states. --- src/server/master_server.cpp | 82 +++++++++++++++++++++++++------- tests/integration/test_server.py | 15 ++++-- 2 files changed, 74 insertions(+), 23 deletions(-) diff --git a/src/server/master_server.cpp b/src/server/master_server.cpp index eff67091..430b8c08 100644 --- a/src/server/master_server.cpp +++ b/src/server/master_server.cpp @@ -11,6 +11,7 @@ #include "eventide/ipc/json_codec.h" #include "eventide/ipc/lsp/position.h" +#include "eventide/ipc/lsp/protocol.h" #include "eventide/ipc/lsp/uri.h" #include "eventide/reflection/enum.h" #include "eventide/serde/json/json.h" @@ -1475,6 +1476,8 @@ et::task> } void MasterServer::register_handlers() { + using StringVec = std::vector; + // === initialize === peer.on_request([this](RequestContext& ctx, const protocol::InitializeParams& params) -> RequestResult { @@ -1494,28 +1497,53 @@ void MasterServer::register_handlers() { // Build capabilities protocol::InitializeResult result; + auto& caps = result.capabilities; // Text document sync: incremental - protocol::TextDocumentSyncOptions sync_opts; - sync_opts.open_close = true; - sync_opts.change = protocol::TextDocumentSyncKind::Incremental; - sync_opts.save = protocol::variant{true}; - result.capabilities.text_document_sync = std::move(sync_opts); + caps.text_document_sync = protocol::TextDocumentSyncOptions{ + .open_close = true, + .change = protocol::TextDocumentSyncKind::Incremental, + .save = protocol::variant{true}, + }; + // watch workspace folder changes. + caps.workspace = protocol::WorkspaceOptions{}; + caps.workspace->workspace_folders = protocol::WorkspaceFoldersServerCapabilities{ + .supported = true, + .change_notifications = true, + }; // Feature capabilities - result.capabilities.hover_provider = true; - result.capabilities.completion_provider = protocol::CompletionOptions{}; - result.capabilities.signature_help_provider = protocol::SignatureHelpOptions{}; - result.capabilities.definition_provider = true; - result.capabilities.references_provider = true; - result.capabilities.document_symbol_provider = true; - result.capabilities.document_link_provider = protocol::DocumentLinkOptions{}; - result.capabilities.code_action_provider = true; - result.capabilities.folding_range_provider = true; - result.capabilities.inlay_hint_provider = true; - result.capabilities.call_hierarchy_provider = true; - result.capabilities.type_hierarchy_provider = true; - result.capabilities.workspace_symbol_provider = true; + caps.hover_provider = true; + caps.completion_provider = protocol::CompletionOptions{ + .trigger_characters = StringVec{".", "<", ">", ":", "\"", "/", "*"}, + }; + caps.signature_help_provider = protocol::SignatureHelpOptions{ + .trigger_characters = StringVec{"(", ")", "{", "}", "<", ">", ","}, + }; + /// FIXME: In the future, we would support work done progress. + caps.declaration_provider = protocol::DeclarationOptions{ + .work_done_progress = false, + }; + caps.definition_provider = protocol::DefinitionOptions{ + .work_done_progress = false, + }; + caps.implementation_provider = protocol::ImplementationOptions{ + .work_done_progress = false, + }; + caps.type_definition_provider = protocol::TypeDefinitionOptions{ + .work_done_progress = false, + }; + caps.references_provider = protocol::ReferenceOptions{ + .work_done_progress = false, + }; + caps.document_symbol_provider = true; + caps.document_link_provider = protocol::DocumentLinkOptions{}; + caps.code_action_provider = true; + caps.folding_range_provider = true; + caps.inlay_hint_provider = true; + caps.call_hierarchy_provider = true; + caps.type_hierarchy_provider = true; + caps.workspace_symbol_provider = true; // Semantic tokens protocol::SemanticTokensOptions sem_opts; @@ -1826,6 +1854,24 @@ void MasterServer::register_handlers() { co_return refs; }); + // --- textDocument/typeDefinition --- + peer.on_request( + [this](RequestContext& ctx, const protocol::TypeDefinitionParams& params) -> RawResult { + co_return serde_raw{"null"}; // not supported yet + }); + + // --- textDocument/implementation --- + peer.on_request( + [this](RequestContext& ctx, const protocol::ImplementationParams& params) -> RawResult { + co_return serde_raw{"null"}; // not supported yet + }); + + // --- textDocument/declaration --- + peer.on_request( + [this](RequestContext& ctx, const protocol::DeclarationParams& params) -> RawResult { + co_return serde_raw{"null"}; // not supported yet + }); + // ========================================================================= // Feature requests routed to stateless workers // ========================================================================= diff --git a/tests/integration/test_server.py b/tests/integration/test_server.py index 0aa1d5fb..b36f4174 100644 --- a/tests/integration/test_server.py +++ b/tests/integration/test_server.py @@ -38,14 +38,19 @@ async def test_server_info(client, workspace): @pytest.mark.workspace("hello_world") async def test_capabilities(client, workspace): + def capability_enabled(capability: object) -> bool: + return capability is True or ( + capability is not None and capability is not False + ) + caps = client.init_result.capabilities assert caps.hover_provider is True assert caps.completion_provider is not None - assert caps.definition_provider is True - assert caps.document_symbol_provider is True - assert caps.folding_range_provider is True - assert caps.inlay_hint_provider is True - assert caps.code_action_provider is True + assert capability_enabled(caps.definition_provider) + assert capability_enabled(caps.document_symbol_provider) + assert capability_enabled(caps.folding_range_provider) + assert capability_enabled(caps.inlay_hint_provider) + assert capability_enabled(caps.code_action_provider) assert caps.semantic_tokens_provider is not None