feat(lsp_server): implement missing providers and json coverage

Implement workspace configuration/folders and apply WorkspaceEdit.changes.

Strengthen provider JSON coverage (all methods require params, no errors).

Verified: Release test_provider
This commit is contained in:
csh
2025-12-24 10:42:13 +08:00
parent f6943e69ef
commit 09e65224fe
93 changed files with 14203 additions and 856 deletions
@@ -23,6 +23,7 @@ set(SOURCES
fixtures.cppm
completion_test.cppm
json_flow_test.cppm
json_provider_coverage_test.cppm
definitions_test.cppm
provider_misc_test.cppm
provider_surface_test.cppm
@@ -49,6 +50,7 @@ target_sources(
${CMAKE_CURRENT_SOURCE_DIR}/fixtures.cppm
${CMAKE_CURRENT_SOURCE_DIR}/completion_test.cppm
${CMAKE_CURRENT_SOURCE_DIR}/json_flow_test.cppm
${CMAKE_CURRENT_SOURCE_DIR}/json_provider_coverage_test.cppm
${CMAKE_CURRENT_SOURCE_DIR}/definitions_test.cppm
${CMAKE_CURRENT_SOURCE_DIR}/provider_misc_test.cppm
${CMAKE_CURRENT_SOURCE_DIR}/provider_surface_test.cppm
@@ -121,6 +123,8 @@ target_sources(
../../src/protocol/types.cppm
../../src/protocol/protocol.cppm
../../src/provider/base/interface.cppm
../../src/provider/base/registry.cppm
../../src/provider/manifest.cppm
../../src/provider/text_document/completion.cppm
../../src/provider/text_document/definition.cppm
../../src/provider/text_document/did_open.cppm
@@ -0,0 +1,3 @@
var target: integer;
target := 1
target := target + 1
@@ -0,0 +1,8 @@
var red: string;
red := "#ff0000";
var green: string;
green := "#00FF00";
var with_alpha: string;
with_alpha := "#11223344";
@@ -0,0 +1,2 @@
var count := 1;
var name := "alpha";
@@ -0,0 +1,43 @@
unit TypeHierarchyUnit;
interface
type Base = class
public
function BaseMethod(): integer;
end;
type Mid = class(Base)
public
function MidMethod(): integer;
end;
type Derived = class(Mid)
public
function DerivedMethod(): integer;
end;
implementation
function Base.BaseMethod(): integer;
begin
return 0;
end;
function Mid.MidMethod(): integer;
begin
return 0;
end;
function Derived.DerivedMethod(): integer;
begin
return 0;
end;
procedure TestTypeHierarchy();
var d: Derived;
begin
d := new Derived;
end;
end.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -13,8 +13,6 @@ import lsp.scheduler.async_executor;
import lsp.test.provider.fixtures;
import lsp.provider.cancel_request.cancel_request;
import lsp.provider.client.register_capability;
import lsp.provider.client.unregister_capability;
import lsp.provider.code_action.resolve;
import lsp.provider.code_lens.resolve;
import lsp.provider.completion_item.resolve;
@@ -24,38 +22,40 @@ import lsp.provider.initialize.initialize;
import lsp.provider.initialized.initialized;
import lsp.provider.inlay_hint.resolve;
import lsp.provider.shutdown.shutdown;
import lsp.provider.telemetry.event;
import lsp.provider.trace.set_trace;
import lsp.provider.call_hierarchy.incoming_calls;
import lsp.provider.call_hierarchy.outgoing_calls;
import lsp.provider.type_hierarchy.supertypes;
import lsp.provider.type_hierarchy.subtypes;
import lsp.provider.window.log_message;
import lsp.provider.window.show_document;
import lsp.provider.window.show_message;
import lsp.provider.window.show_message_request;
import lsp.provider.window.work_done_progress_create;
import lsp.provider.workspace.apply_edit;
import lsp.provider.workspace.code_lens_refresh;
import lsp.provider.workspace.configuration;
import lsp.provider.client.register_capability;
import lsp.provider.client.unregister_capability;
import lsp.provider.workspace.diagnostic;
import lsp.provider.workspace.diagnostic_refresh;
import lsp.provider.workspace.did_change_configuration;
import lsp.provider.workspace.did_change_watched_files;
import lsp.provider.workspace.did_change_workspace_folders;
import lsp.provider.workspace.did_create_files;
import lsp.provider.workspace.did_delete_files;
import lsp.provider.workspace.did_rename_files;
import lsp.provider.workspace.execute_command;
import lsp.provider.workspace.configuration;
import lsp.provider.workspace.apply_edit;
import lsp.provider.workspace.workspace_folders;
import lsp.provider.workspace.code_lens_refresh;
import lsp.provider.workspace.diagnostic_refresh;
import lsp.provider.workspace.inlay_hint_refresh;
import lsp.provider.workspace.inline_value_refresh;
import lsp.provider.workspace.semantic_tokens_refresh;
import lsp.provider.workspace.execute_command;
import lsp.provider.workspace.symbol;
import lsp.provider.workspace.will_create_files;
import lsp.provider.workspace.will_delete_files;
import lsp.provider.workspace.will_rename_files;
import lsp.provider.workspace.workspace_folders;
import lsp.provider.workspace_symbol.resolve;
import lsp.provider.window.work_done_progress_create;
import lsp.provider.window.show_message_request;
import lsp.provider.window.show_document;
import lsp.provider.window.log_message;
import lsp.provider.window.show_message;
import lsp.provider.telemetry.event;
import lsp.provider.text_document.code_action;
import lsp.provider.text_document.code_lens;
import lsp.provider.text_document.color_presentation;
@@ -81,7 +81,6 @@ import lsp.provider.text_document.on_type_formatting;
import lsp.provider.text_document.prepare_call_hierarchy;
import lsp.provider.text_document.prepare_rename;
import lsp.provider.text_document.prepare_type_hierarchy;
import lsp.provider.text_document.publish_diagnostics;
import lsp.provider.text_document.range_formatting;
import lsp.provider.text_document.references;
import lsp.provider.text_document.rename;
@@ -89,6 +88,7 @@ import lsp.provider.text_document.selection_range;
import lsp.provider.text_document.semantic_tokens;
import lsp.provider.text_document.signature_help;
import lsp.provider.text_document.type_definition;
import lsp.provider.text_document.publish_diagnostics;
export namespace lsp::test::provider
{
@@ -234,32 +234,35 @@ namespace lsp::test::provider
CheckProviderMetadata<provider::inlay_hint::Resolve>("inlayHint/resolve", "InlayHintResolve");
CheckProviderMetadata<provider::workspace_symbol::Resolve>("workspaceSymbol/resolve", "WorkspaceSymbResolve");
CheckProviderMetadata<provider::type_hierarchy::Supertypes>("typeHierarchy/supertypes", "TypeHierarchySupertypes");
CheckProviderMetadata<provider::type_hierarchy::Subtypes>("typeHierarchy/subtypes", "WorkspaceSubtypes");
CheckProviderMetadata<provider::type_hierarchy::Subtypes>("typeHierarchy/subtypes", "TypeHierarchySubtypes");
CheckProviderMetadata<provider::call_hierarchy::IncomingCalls>("callHierarchy/incomingCalls",
"CallHierarchyIncomingCalls");
CheckProviderMetadata<provider::call_hierarchy::OutgoingCalls>("callHierarchy/outgoingCalls",
"CallHierarchyOutgoingCalls");
CheckProviderMetadata<provider::workspace::ApplyEdit>("workspace/applyEdit", "WorkspaceApplyEdit");
CheckProviderMetadata<provider::workspace::Configuration>("workspace/configuration", "WorkspaceConfiguration");
CheckProviderMetadata<provider::workspace::Diagnostic>("workspace/diagnostic", "WorkspaceDiagnostic");
CheckProviderMetadata<provider::workspace::DiagnosticRefresh>("workspace/diagnostic/refresh",
"WorkspaceDiagnosticRefresh");
CheckProviderMetadata<provider::workspace::ExecuteCommand>("workspace/executeCommand",
"WorkspaceExecuteCommand");
CheckProviderMetadata<provider::workspace::WorkspaceFolders>("workspace/workspaceFolders",
"WorkspaceWorkspaceFolders");
CheckProviderMetadata<provider::workspace::WillCreateFiles>("workspace/willCreateFiles", "WorkspaceWillCreateFiles");
CheckProviderMetadata<provider::workspace::WillDeleteFiles>("workspace/willDeleteFiles", "WorkspaceWillDeleteFiles");
CheckProviderMetadata<provider::workspace::WillRenameFiles>("workspace/willRenameFiles", "WorkspaceWillRenameFiles");
CheckProviderMetadata<provider::workspace::Symbol>("workspace/symbol", "WorkSpaceSymbol");
CheckProviderMetadata<provider::workspace::SemanticTokensRefresh>("workspace/semanticTokens/refresh",
"WorkspaceSemanticTokensRefresh");
CheckProviderMetadata<provider::workspace::InlineValueRefresh>("workspace/inlineValue/refresh",
"WorkspaceInlineValueRefresh");
CheckProviderMetadata<provider::workspace::InlayHintRefresh>("workspace/inlayHint/refresh",
"WorkspaceInlayHintRefresh");
CheckProviderMetadata<provider::workspace::CodeLensRefresh>("workspace/codeLens/refresh",
"WorkspaceCodeLensRefresh");
CheckProviderMetadata<provider::workspace::Configuration>("workspace/configuration", "WorkspaceConfiguration");
CheckProviderMetadata<provider::workspace::ApplyEdit>("workspace/applyEdit", "WorkspaceApplyEdit");
CheckProviderMetadata<provider::workspace::WorkspaceFolders>("workspace/workspaceFolders", "WorkspaceWorkspaceFolders");
CheckProviderMetadata<provider::workspace::CodeLensRefresh>("workspace/codeLens/refresh", "WorkspaceCodeLensRefresh");
CheckProviderMetadata<provider::workspace::DiagnosticRefresh>("workspace/diagnostic/refresh", "WorkspaceDiagnosticRefresh");
CheckProviderMetadata<provider::workspace::InlayHintRefresh>("workspace/inlayHint/refresh", "WorkspaceInlayHintRefresh");
CheckProviderMetadata<provider::workspace::InlineValueRefresh>("workspace/inlineValue/refresh", "WorkspaceInlineValueRefresh");
CheckProviderMetadata<provider::workspace::SemanticTokensRefresh>("workspace/semanticTokens/refresh", "WorkspaceSemanticTokensRefresh");
CheckProviderMetadata<provider::client::RegisterCapability>("client/registerCapability", "ClientRegisterCapability");
CheckProviderMetadata<provider::client::UnregisterCapability>("client/unregisterCapability", "ClientUnregisterCapability");
CheckProviderMetadata<provider::window::WorkDoneProgressCreate>("window/workDoneProgress/create", "WindowWorkDoneProgressCreate");
CheckProviderMetadata<provider::window::ShowMessageRequest>("window/showMessageRequest", "WindowShowMessageRequest");
CheckProviderMetadata<provider::window::ShowDocument>("window/showDocument", "WindowShowDocument");
CheckProviderMetadata<provider::window::LogMessage>("window/logMessage", "WindowLogMessage");
CheckProviderMetadata<provider::window::ShowMessage>("window/showMessage", "WindowShowMessage");
CheckProviderMetadata<provider::telemetry::Event>("telemetry/event", "TelemetryEvent");
CheckProviderMetadata<provider::text_document::PublishDiagnostics>("textDocument/publishDiagnostics", "TextDocumentPublishDiagnostics");
CheckProviderMetadata<provider::workspace::DidChangeConfiguration>("workspace/didChangeConfiguration",
"WorkspaceDidChangeConfiguration");
CheckProviderMetadata<provider::workspace::DidChangeWatchedFiles>("workspace/didChangeWatchedFiles",
@@ -269,15 +272,6 @@ namespace lsp::test::provider
CheckProviderMetadata<provider::workspace::DidCreateFiles>("workspace/didCreateFiles", "WorkspaceDidCreateFiles");
CheckProviderMetadata<provider::workspace::DidDeleteFiles>("workspace/didDeleteFiles", "WorkspaceDidDeleteFiles");
CheckProviderMetadata<provider::workspace::DidRenameFiles>("workspace/didRenameFiles", "WorkspaceDidRenameFiles");
CheckProviderMetadata<provider::window::ShowMessageRequest>("window/showMessageRequest",
"WindowShowMessageRequest");
CheckProviderMetadata<provider::window::ShowDocument>("window/showDocument", "WindowShowDocument");
CheckProviderMetadata<provider::window::WorkDoneProgressCreate>("window/workDoneProgress/create",
"WindowWorkDoneProgressCreate");
CheckProviderMetadata<provider::client::RegisterCapability>("client/registerCapability",
"ClientRegisterCapability");
CheckProviderMetadata<provider::client::UnregisterCapability>("client/unregisterCapability",
"ClientUnregisterCapability");
CheckProviderMetadata<provider::Initialized>("initialized", "Initialized");
CheckProviderMetadata<provider::Exit>("exit", "Exit");
CheckProviderMetadata<provider::CancelRequest>("$/cancelRequest", "CancelRequest");
@@ -285,11 +279,6 @@ namespace lsp::test::provider
CheckProviderMetadata<provider::text_document::DidOpen>("textDocument/didOpen", "TextDocumentDidOpen");
CheckProviderMetadata<provider::text_document::DidChange>("textDocument/didChange", "TextDocumentDidChange");
CheckProviderMetadata<provider::text_document::DidClose>("textDocument/didClose", "TextDocumentDidClose");
CheckProviderMetadata<provider::text_document::PublishDiagnostics>("textDocument/publishDiagnostics",
"TextDocumentPublishDiagnostics");
CheckProviderMetadata<provider::window::ShowMessage>("window/showMessage", "WindowShowMessage");
CheckProviderMetadata<provider::window::LogMessage>("window/logMessage", "WindowLogMessage");
CheckProviderMetadata<provider::telemetry::Event>("telemetry/event", "TelemetryEvent");
return result;
}
@@ -340,25 +329,25 @@ namespace lsp::test::provider
CheckRequestResponse<provider::type_hierarchy::Subtypes>();
CheckRequestResponse<provider::call_hierarchy::IncomingCalls>();
CheckRequestResponse<provider::call_hierarchy::OutgoingCalls>();
CheckRequestResponse<provider::workspace::ApplyEdit>();
CheckRequestResponse<provider::workspace::Configuration>();
CheckRequestResponse<provider::workspace::Diagnostic>();
CheckRequestResponse<provider::workspace::DiagnosticRefresh>();
CheckRequestResponse<provider::workspace::ExecuteCommand>();
CheckRequestResponse<provider::workspace::WorkspaceFolders>();
CheckRequestResponse<provider::workspace::WillCreateFiles>();
CheckRequestResponse<provider::workspace::WillDeleteFiles>();
CheckRequestResponse<provider::workspace::WillRenameFiles>();
CheckRequestResponse<provider::workspace::Symbol>();
CheckRequestResponse<provider::workspace::SemanticTokensRefresh>();
CheckRequestResponse<provider::workspace::InlineValueRefresh>();
CheckRequestResponse<provider::workspace::InlayHintRefresh>();
CheckRequestResponse<provider::workspace::Configuration>();
CheckRequestResponse<provider::workspace::ApplyEdit>();
CheckRequestResponse<provider::workspace::WorkspaceFolders>();
CheckRequestResponse<provider::workspace::CodeLensRefresh>();
CheckRequestResponse<provider::window::ShowMessageRequest>();
CheckRequestResponse<provider::window::ShowDocument>();
CheckRequestResponse<provider::window::WorkDoneProgressCreate>();
CheckRequestResponse<provider::workspace::DiagnosticRefresh>();
CheckRequestResponse<provider::workspace::InlayHintRefresh>();
CheckRequestResponse<provider::workspace::InlineValueRefresh>();
CheckRequestResponse<provider::workspace::SemanticTokensRefresh>();
CheckRequestResponse<provider::client::RegisterCapability>();
CheckRequestResponse<provider::client::UnregisterCapability>();
CheckRequestResponse<provider::window::WorkDoneProgressCreate>();
CheckRequestResponse<provider::window::ShowMessageRequest>();
CheckRequestResponse<provider::window::ShowDocument>();
return result;
}
@@ -369,16 +358,16 @@ namespace lsp::test::provider
CheckNotificationHandler<provider::Initialized>(std::nullopt);
CheckNotificationHandler<provider::Exit>(std::nullopt);
CheckNotificationHandler<provider::text_document::PublishDiagnostics>(std::nullopt);
CheckNotificationHandler<provider::window::ShowMessage>(std::nullopt);
CheckNotificationHandler<provider::window::LogMessage>(std::nullopt);
CheckNotificationHandler<provider::telemetry::Event>(std::nullopt);
CheckNotificationHandler<provider::workspace::DidChangeConfiguration>(std::nullopt);
CheckNotificationHandler<provider::workspace::DidChangeWatchedFiles>(std::nullopt);
CheckNotificationHandler<provider::workspace::DidChangeWorkspaceFolders>(std::nullopt);
CheckNotificationHandler<provider::workspace::DidCreateFiles>(std::nullopt);
CheckNotificationHandler<provider::workspace::DidDeleteFiles>(std::nullopt);
CheckNotificationHandler<provider::workspace::DidRenameFiles>(std::nullopt);
CheckNotificationHandler<provider::window::LogMessage>(std::nullopt);
CheckNotificationHandler<provider::window::ShowMessage>(std::nullopt);
CheckNotificationHandler<provider::telemetry::Event>(std::nullopt);
CheckNotificationHandler<provider::text_document::PublishDiagnostics>(std::nullopt);
return result;
}
@@ -95,9 +95,9 @@ namespace lsp::test::provider
return 0;
}
std::vector<protocol::ResponseMessage> ParseResponses(const std::string& data)
std::vector<std::string> ParseBodies(const std::string& data)
{
std::vector<protocol::ResponseMessage> responses;
std::vector<std::string> bodies;
std::size_t pos = 0;
while (pos < data.size())
{
@@ -120,11 +120,10 @@ namespace lsp::test::provider
break;
}
auto body = data.substr(body_start, length);
responses.push_back(DeserializeResponseOrThrow(body));
bodies.push_back(data.substr(body_start, length));
pos = body_start + length;
}
return responses;
return bodies;
}
protocol::CompletionItem BuildResolveItem(const std::string& uri)
@@ -263,13 +262,46 @@ namespace lsp::test::provider
std::filesystem::remove(input_path);
std::filesystem::remove(output_path);
auto responses = ParseResponses(output);
auto bodies = ParseBodies(output);
std::unordered_map<std::string, protocol::ResponseMessage> by_id;
for (const auto& response : responses)
bool saw_diagnostics = false;
for (const auto& body : bodies)
{
if (response.id.has_value())
auto any = codec::Deserialize<protocol::LSPAny>(body);
if (!any.has_value() || !any->Is<protocol::LSPObject>())
{
by_id[codec::debug::GetIdString(response.id.value())] = response;
continue;
}
const auto& obj = any->Get<protocol::LSPObject>();
const bool has_id = obj.contains("id");
const bool has_method = obj.contains("method");
const bool has_result = obj.contains("result");
const bool has_error = obj.contains("error");
if (has_id && (has_result || has_error))
{
auto response = DeserializeResponseOrThrow(body);
if (response.id.has_value())
{
by_id[codec::debug::GetIdString(response.id.value())] = std::move(response);
}
continue;
}
if (has_method && !has_id)
{
auto notification = codec::Deserialize<protocol::NotificationMessage>(body);
if (notification && notification->method == "textDocument/publishDiagnostics" && notification->params.has_value())
{
const auto& diag_params = notification->params->Get<protocol::LSPObject>();
auto uri_it = diag_params.find("uri");
if (uri_it != diag_params.end() && uri_it->second.Is<protocol::string>() && uri_it->second.Get<protocol::string>() == uri)
{
saw_diagnostics = true;
}
}
}
}
@@ -292,6 +324,7 @@ namespace lsp::test::provider
auto expected = FindPosition(content, "function UnitFunc", false);
assertTrue(location.range.start.line == expected.line, "Definition should resolve in document");
assertTrue(saw_diagnostics, "Server should publish diagnostics after didOpen");
assertTrue(!by_id["5"].error.has_value(), "Shutdown response should not contain error");
return result;
}
@@ -8,6 +8,7 @@ import lsp.test.framework;
import lsp.test.provider.completion;
import lsp.test.provider.definitions;
import lsp.test.provider.json_flow;
import lsp.test.provider.json_provider_coverage;
import lsp.test.provider.misc;
import lsp.test.provider.surface;
import lsp.test.provider.fixtures;
@@ -41,6 +42,8 @@ export int Run(int argc, char** argv)
lsp::test::provider::DefinitionTests::Register(runner);
std::cout << " - JSON flow tests" << std::endl;
lsp::test::provider::JsonFlowTests::Register(runner);
std::cout << " - JSON provider coverage tests" << std::endl;
lsp::test::provider::JsonProviderCoverageTests::Register(runner);
std::cout << " - Other provider tests" << std::endl;
lsp::test::provider::ProviderMiscTests::Register(runner);
std::cout << " - Provider surface tests" << std::endl;