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
@@ -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;
}