From bf6a2fc329bc05f3c08a1a62568e8933dc692bf9 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Wed, 17 Oct 2018 07:33:42 +0000 Subject: [PATCH] [clangd] Simplify client capabilities parsing. Summary: Instead of parsing into structs that mirror LSP, simply parse into a flat struct that contains the info we need. This is an exception to our strategy with Protocol.h, which seems justified: - the structure here is very large and deeply nested - we care about almost none of it - we should never have to serialize client capabilities Reviewers: kadircet Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D53266 llvm-svn: 344673 --- clang-tools-extra/clangd/ClangdLSPServer.cpp | 31 ++----- clang-tools-extra/clangd/Protocol.cpp | 111 +++++++++-------------- clang-tools-extra/clangd/Protocol.h | 126 ++++++--------------------- 3 files changed, 76 insertions(+), 192 deletions(-) diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp index 1585fbf..073d8b1 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -97,29 +97,14 @@ void ClangdLSPServer::onInitialize(InitializeParams &Params) { 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; - SupportsCodeAction = - Params.capabilities.textDocument.codeActionLiteralSupport; - - 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; + SupportsCodeAction = Params.capabilities.CodeActionStructure; reply(json::Object{ {{"capabilities", diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp index a1e8226..805225a 100644 --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -161,35 +161,6 @@ bool fromJSON(const json::Value &E, TraceLevel &Out) { 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) || @@ -201,24 +172,18 @@ bool fromJSON(const json::Value &E, SymbolKind &Out) { 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); @@ -237,34 +202,46 @@ SymbolKind adjustKindToCapability(SymbolKind Kind, } } -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); - if (auto *CodeAction = Params.getAsObject()->getObject("codeAction")) - if (CodeAction->getObject("codeActionLiteralSupport")) - R.codeActionLiteralSupport = true; - 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 *CodeAction = TextDocument->getObject("codeAction")) { + if (CodeAction->getObject("codeActionLiteralSupport")) + R.CodeActionStructure = true; + } + } + 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; } @@ -560,24 +537,18 @@ adjustKindToCapability(CompletionItemKind Kind, } } -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}}; diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h index 940c78f..5d2a5a2 100644 --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -238,18 +238,6 @@ enum class TextDocumentSyncKind { 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, @@ -280,58 +268,16 @@ enum class CompletionItemKind { 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, @@ -361,62 +307,44 @@ enum class SymbolKind { 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; + /// Whether the client accepts diagnostics with category attached to it + /// using the "category" extension. + /// textDocument.publishDiagnostics.categorySupport + bool DiagnosticCategory = false; - /// Flattened from codeAction.codeActionLiteralSupport. - // FIXME: flatten other properties in this way. - bool codeActionLiteralSupport = false; -}; -bool fromJSON(const llvm::json::Value &, TextDocumentClientCapabilities &); + /// Client supports snippets as insert text. + /// textDocument.completion.completionItem.snippetSupport + bool CompletionSnippets = false; -struct ClientCapabilities { - // Workspace specific client capabilities. - llvm::Optional workspace; + /// The supported set of CompletionItemKinds for textDocument/completion. + /// textDocument.completion.completionItemKind.valueSet + llvm::Optional CompletionItemKinds; - // Text document specific client capabilities. - TextDocumentClientCapabilities textDocument; + /// Client supports CodeAction return value for textDocument/codeAction. + /// textDocument.codeAction.codeActionLiteralSupport. + bool CodeActionStructure = false; }; - bool fromJSON(const llvm::json::Value &, ClientCapabilities &); /// Clangd extension that's used in the 'compilationDatabaseChanges' in -- 2.7.4