Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -89,27 +89,13 @@ else if (Params.rootPath && !Params.rootPath->empty()) Server->setRootPath(*Params.rootPath); - CCOpts.EnableSnippets = - Params.capabilities.textDocument.completion.completionItem.snippetSupport; - DiagOpts.EmbedFixesInDiagnostics = - Params.capabilities.textDocument.publishDiagnostics.clangdFixSupport; - DiagOpts.SendDiagnosticCategory = - Params.capabilities.textDocument.publishDiagnostics.categorySupport; - - if (Params.capabilities.workspace && Params.capabilities.workspace->symbol && - Params.capabilities.workspace->symbol->symbolKind && - Params.capabilities.workspace->symbol->symbolKind->valueSet) { - for (SymbolKind Kind : - *Params.capabilities.workspace->symbol->symbolKind->valueSet) { - SupportedSymbolKinds.set(static_cast(Kind)); - } - } - - if (Params.capabilities.textDocument.completion.completionItemKind && - Params.capabilities.textDocument.completion.completionItemKind->valueSet) - for (CompletionItemKind Kind : *Params.capabilities.textDocument.completion - .completionItemKind->valueSet) - SupportedCompletionItemKinds.set(static_cast(Kind)); + CCOpts.EnableSnippets = Params.capabilities.CompletionSnippets; + DiagOpts.EmbedFixesInDiagnostics = Params.capabilities.DiagnosticFixes; + DiagOpts.SendDiagnosticCategory = Params.capabilities.DiagnosticCategory; + if (Params.capabilities.WorkspaceSymbolKinds) + SupportedSymbolKinds |= *Params.capabilities.WorkspaceSymbolKinds; + if (Params.capabilities.CompletionItemKinds) + SupportedCompletionItemKinds |= *Params.capabilities.CompletionItemKinds; reply(json::Object{ {{"capabilities", Index: clangd/Protocol.h =================================================================== --- clangd/Protocol.h +++ clangd/Protocol.h @@ -221,18 +221,6 @@ Incremental = 2, }; -struct CompletionItemClientCapabilities { - /// Client supports snippets as insert text. - bool snippetSupport = false; - /// Client supports commit characters on a completion item. - bool commitCharacterSupport = false; - // Client supports the follow content formats for the documentation property. - // The order describes the preferred format of the client. - // NOTE: not used by clangd at the moment. - // std::vector documentationFormat; -}; -bool fromJSON(const llvm::json::Value &, CompletionItemClientCapabilities &); - /// The kind of a completion entry. enum class CompletionItemKind { Missing = 0, @@ -263,58 +251,16 @@ TypeParameter = 25, }; bool fromJSON(const llvm::json::Value &, CompletionItemKind &); - -struct CompletionItemKindCapabilities { - /// The CompletionItemKinds that the client supports. If not set, the client - /// only supports <= CompletionItemKind::Reference and will not fall back to a - /// valid default value. - llvm::Optional> valueSet; -}; -// Discards unknown CompletionItemKinds. -bool fromJSON(const llvm::json::Value &, std::vector &); -bool fromJSON(const llvm::json::Value &, CompletionItemKindCapabilities &); - constexpr auto CompletionItemKindMin = static_cast(CompletionItemKind::Text); constexpr auto CompletionItemKindMax = static_cast(CompletionItemKind::TypeParameter); using CompletionItemKindBitset = std::bitset; +bool fromJSON(const llvm::json::Value &, CompletionItemKindBitset &); CompletionItemKind adjustKindToCapability(CompletionItemKind Kind, CompletionItemKindBitset &supportedCompletionItemKinds); -struct CompletionClientCapabilities { - /// Whether completion supports dynamic registration. - bool dynamicRegistration = false; - /// The client supports the following `CompletionItem` specific capabilities. - CompletionItemClientCapabilities completionItem; - /// The CompletionItemKinds that the client supports. If not set, the client - /// only supports <= CompletionItemKind::Reference and will not fall back to a - /// valid default value. - llvm::Optional completionItemKind; - - /// The client supports to send additional context information for a - /// `textDocument/completion` request. - bool contextSupport = false; -}; -bool fromJSON(const llvm::json::Value &, CompletionClientCapabilities &); - -struct PublishDiagnosticsClientCapabilities { - // Whether the client accepts diagnostics with related information. - // NOTE: not used by clangd at the moment. - // bool relatedInformation; - - /// Whether the client accepts diagnostics with fixes attached using the - /// "clangd_fixes" extension. - bool clangdFixSupport = false; - - /// Whether the client accepts diagnostics with category attached to it - /// using the "category" extension. - bool categorySupport = false; -}; -bool fromJSON(const llvm::json::Value &, - PublishDiagnosticsClientCapabilities &); - /// A symbol kind. enum class SymbolKind { File = 1, @@ -344,58 +290,40 @@ Operator = 25, TypeParameter = 26 }; - +bool fromJSON(const llvm::json::Value &, SymbolKind &); constexpr auto SymbolKindMin = static_cast(SymbolKind::File); constexpr auto SymbolKindMax = static_cast(SymbolKind::TypeParameter); using SymbolKindBitset = std::bitset; - -bool fromJSON(const llvm::json::Value &, SymbolKind &); - -struct SymbolKindCapabilities { - /// The SymbolKinds that the client supports. If not set, the client only - /// supports <= SymbolKind::Array and will not fall back to a valid default - /// value. - llvm::Optional> valueSet; -}; -// Discards unknown SymbolKinds. -bool fromJSON(const llvm::json::Value &, std::vector &); -bool fromJSON(const llvm::json::Value &, SymbolKindCapabilities &); +bool fromJSON(const llvm::json::Value &, SymbolKindBitset &); SymbolKind adjustKindToCapability(SymbolKind Kind, SymbolKindBitset &supportedSymbolKinds); -struct WorkspaceSymbolCapabilities { - /// Capabilities SymbolKind. - llvm::Optional symbolKind; -}; -bool fromJSON(const llvm::json::Value &, WorkspaceSymbolCapabilities &); - -// FIXME: most of the capabilities are missing from this struct. Only the ones -// used by clangd are currently there. -struct WorkspaceClientCapabilities { - /// Capabilities specific to `workspace/symbol`. - llvm::Optional symbol; -}; -bool fromJSON(const llvm::json::Value &, WorkspaceClientCapabilities &); +// This struct doesn't mirror LSP! +// The protocol defines deeply nested structures for client capabilities. +// Instead of mapping them all, this just parses out the bits we care about. +struct ClientCapabilities { + /// The supported set of SymbolKinds for workspace/symbol. + /// workspace.symbol.symbolKind.valueSet + llvm::Optional WorkspaceSymbolKinds; -// FIXME: most of the capabilities are missing from this struct. Only the ones -// used by clangd are currently there. -struct TextDocumentClientCapabilities { - /// Capabilities specific to the `textDocument/completion` - CompletionClientCapabilities completion; + /// Whether the client accepts diagnostics with fixes attached using the + /// "clangd_fixes" extension. + /// textDocument.publishDiagnostics.clangdFixSupport + bool DiagnosticFixes = false; - /// Capabilities specific to the 'textDocument/publishDiagnostics' - PublishDiagnosticsClientCapabilities publishDiagnostics; -}; -bool fromJSON(const llvm::json::Value &, TextDocumentClientCapabilities &); + /// Whether the client accepts diagnostics with category attached to it + /// using the "category" extension. + /// textDocument.publishDiagnostics.categorySupport + bool DiagnosticCategory = false; -struct ClientCapabilities { - // Workspace specific client capabilities. - llvm::Optional workspace; + /// Client supports snippets as insert text. + /// textDocument.completion.completionItem.snippetSupport + bool CompletionSnippets = false; - // Text document specific client capabilities. - TextDocumentClientCapabilities textDocument; + /// The supported set of CompletionItemKinds for textDocument/completion. + /// textDocument.completion.completionItemKind.valueSet + llvm::Optional CompletionItemKinds; }; - bool fromJSON(const llvm::json::Value &, ClientCapabilities &); /// Clangd extension that's used in the 'compilationDatabaseChanges' in Index: clangd/Protocol.cpp =================================================================== --- clangd/Protocol.cpp +++ clangd/Protocol.cpp @@ -159,35 +159,6 @@ return false; } -bool fromJSON(const json::Value &Params, CompletionItemClientCapabilities &R) { - json::ObjectMapper O(Params); - if (!O) - return false; - O.map("snippetSupport", R.snippetSupport); - O.map("commitCharacterSupport", R.commitCharacterSupport); - return true; -} - -bool fromJSON(const json::Value &Params, CompletionClientCapabilities &R) { - json::ObjectMapper O(Params); - if (!O) - return false; - O.map("dynamicRegistration", R.dynamicRegistration); - O.map("completionItem", R.completionItem); - O.map("contextSupport", R.contextSupport); - return true; -} - -bool fromJSON(const llvm::json::Value &Params, - PublishDiagnosticsClientCapabilities &R) { - json::ObjectMapper O(Params); - if (!O) - return false; - O.map("clangdFixSupport", R.clangdFixSupport); - O.map("categorySupport", R.categorySupport); - return true; -} - bool fromJSON(const json::Value &E, SymbolKind &Out) { if (auto T = E.getAsInteger()) { if (*T < static_cast(SymbolKind::File) || @@ -199,24 +170,18 @@ return false; } -bool fromJSON(const json::Value &E, std::vector &Out) { +bool fromJSON(const json::Value &E, SymbolKindBitset &Out) { if (auto *A = E.getAsArray()) { - Out.clear(); for (size_t I = 0; I < A->size(); ++I) { SymbolKind KindOut; if (fromJSON((*A)[I], KindOut)) - Out.push_back(KindOut); + Out.set(size_t(KindOut)); } return true; } return false; } -bool fromJSON(const json::Value &Params, SymbolKindCapabilities &R) { - json::ObjectMapper O(Params); - return O && O.map("valueSet", R.valueSet); -} - SymbolKind adjustKindToCapability(SymbolKind Kind, SymbolKindBitset &SupportedSymbolKinds) { auto KindVal = static_cast(Kind); @@ -235,31 +200,42 @@ } } -bool fromJSON(const json::Value &Params, WorkspaceSymbolCapabilities &R) { - json::ObjectMapper O(Params); - return O && O.map("symbolKind", R.symbolKind); -} - -bool fromJSON(const json::Value &Params, WorkspaceClientCapabilities &R) { - json::ObjectMapper O(Params); - return O && O.map("symbol", R.symbol); -} - -bool fromJSON(const json::Value &Params, TextDocumentClientCapabilities &R) { - json::ObjectMapper O(Params); - if (!O) - return false; - O.map("completion", R.completion); - O.map("publishDiagnostics", R.publishDiagnostics); - return true; -} - bool fromJSON(const json::Value &Params, ClientCapabilities &R) { - json::ObjectMapper O(Params); + const json::Object *O = Params.getAsObject(); if (!O) return false; - O.map("textDocument", R.textDocument); - O.map("workspace", R.workspace); + if (auto *TextDocument = O->getObject("textDocument")) { + if (auto *Diagnostics = TextDocument->getObject("publishDiagnostics")) { + if (auto CategorySupport = Diagnostics->getBoolean("categorySupport")) + R.DiagnosticCategory = *CategorySupport; + if (auto ClangdFixSupport = Diagnostics->getBoolean("clangdFixSupport")) + R.DiagnosticFixes = *ClangdFixSupport; + } + if (auto *Completion = TextDocument->getObject("completion")) { + if (auto *Item = Completion->getObject("completionItem")) { + if (auto SnippetSupport = Item->getBoolean("snippetSupport")) + R.CompletionSnippets = *SnippetSupport; + } + if (auto *ItemKind = Completion->getObject("completionItemKind")) { + if (auto *ValueSet = ItemKind->get("valueSet")) { + R.CompletionItemKinds.emplace(); + if (!fromJSON(*ValueSet, *R.CompletionItemKinds)) + return false; + } + } + } + } + if (auto *Workspace = O->getObject("workspace")) { + if (auto *Symbol = Workspace->getObject("symbol")) { + if (auto *SymbolKind = Symbol->getObject("symbolKind")) { + if (auto *ValueSet = SymbolKind->get("valueSet")) { + R.WorkspaceSymbolKinds.emplace(); + if (!fromJSON(*ValueSet, *R.WorkspaceSymbolKinds)) + return false; + } + } + } + } return true; } @@ -529,24 +505,18 @@ } } -bool fromJSON(const json::Value &E, std::vector &Out) { +bool fromJSON(const json::Value &E, CompletionItemKindBitset &Out) { if (auto *A = E.getAsArray()) { - Out.clear(); for (size_t I = 0; I < A->size(); ++I) { CompletionItemKind KindOut; if (fromJSON((*A)[I], KindOut)) - Out.push_back(KindOut); + Out.set(size_t(KindOut)); } return true; } return false; } -bool fromJSON(const json::Value &Params, CompletionItemKindCapabilities &R) { - json::ObjectMapper O(Params); - return O && O.map("valueSet", R.valueSet); -} - json::Value toJSON(const CompletionItem &CI) { assert(!CI.label.empty() && "completion item label is required"); json::Object Result{{"label", CI.label}};