diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -77,6 +77,9 @@ void profile(MemoryTree &MT) const; private: + // Configure server using client capabilities to adjust response types and + // request handling. + void alignWithClient(const InitializeParams &Params); // Implement ClangdServer::Callbacks. void onDiagnosticsReady(PathRef File, llvm::StringRef Version, std::vector Diagnostics) override; @@ -250,22 +253,6 @@ LSPBinder::RawHandlers Handlers; const ThreadsafeFS &TFS; - /// Options used for diagnostics. - ClangdDiagnosticOptions DiagOpts; - /// The supported kinds of the client. - SymbolKindBitset SupportedSymbolKinds; - /// The supported completion item kinds of the client. - CompletionItemKindBitset SupportedCompletionItemKinds; - /// Whether the client supports CodeAction response objects. - bool SupportsCodeAction = false; - /// From capabilities of textDocument/documentSymbol. - bool SupportsHierarchicalDocumentSymbol = false; - /// Whether the client supports showing file status. - bool SupportFileStatus = false; - /// Which kind of markup should we use in textDocument/hover responses. - MarkupKind HoverContentFormat = MarkupKind::PlainText; - /// Whether the client supports offsets for parameter info labels. - bool SupportsOffsetsInSignatureHelp = false; std::mutex BackgroundIndexProgressMutex; enum class BackgroundIndexProgress { // Client doesn't support reporting progress. No transitions possible. @@ -282,10 +269,28 @@ // The progress to send when the progress bar is created. // Only valid in state Creating. BackgroundQueue::Stats PendingBackgroundIndexProgress; - /// LSP extension: skip WorkDoneProgressCreate, just send progress streams. - bool BackgroundIndexSkipCreate = false; Options Opts; + struct { + // Options used for diagnostics. + ClangdDiagnosticOptions DiagOpts; + // Set of symbol kinds to use. + SymbolKindBitset SymbolKinds; + // Set of completion item kinds to use. + CompletionItemKindBitset CompletionItemKinds; + // Whether to emit code actions or commands as code action responses. + bool EmitCodeAction = false; + // Whether to emit hierarchical or flat document symbol responses. + bool HierarchicalDocumentSymbol = false; + // Whether to send file status updates. + bool EmitFileStatus = false; + // Which kind of markup should we use in textDocument/hover responses. + MarkupKind HoverContentFormat = MarkupKind::PlainText; + // Whether to have offsets for parameter info labels. + bool OffsetsInSignatureHelp = false; + // LSP extension: skip WorkDoneProgressCreate, just send progress streams. + bool BackgroundIndexSkipCreate = false; + } ClientCapabilities; // The CDB is created by the "initialize" LSP method. std::unique_ptr BaseCDB; // CDB is BaseCDB plus any commands overridden via LSP extensions. diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -441,8 +441,7 @@ return Modifiers; } -void ClangdLSPServer::onInitialize(const InitializeParams &Params, - Callback Reply) { +void ClangdLSPServer::alignWithClient(const InitializeParams &Params) { // Determine character encoding first as it affects constructed ClangdServer. if (Params.capabilities.offsetEncoding && !Opts.Encoding) { Opts.Encoding = OffsetEncoding::UTF16; // fallback @@ -463,9 +462,6 @@ Opts.WorkspaceRoot = std::string(Params.rootUri->file()); else if (Params.rootPath && !Params.rootPath->empty()) Opts.WorkspaceRoot = *Params.rootPath; - if (Server) - return Reply(llvm::make_error("server already initialized", - ErrorCode::InvalidRequest)); Opts.CodeComplete.EnableSnippets = Params.capabilities.CompletionSnippets; Opts.CodeComplete.IncludeFixIts = Params.capabilities.CompletionFixes; @@ -475,25 +471,40 @@ Params.capabilities.CompletionDocumentationFormat; Opts.SignatureHelpDocumentationFormat = Params.capabilities.SignatureHelpDocumentationFormat; - DiagOpts.EmbedFixesInDiagnostics = Params.capabilities.DiagnosticFixes; - DiagOpts.SendDiagnosticCategory = Params.capabilities.DiagnosticCategory; - DiagOpts.EmitRelatedLocations = + Opts.LineFoldingOnly = Params.capabilities.LineFoldingOnly; + Opts.ImplicitCancellation = !Params.capabilities.CancelsStaleRequests; + + ClientCapabilities.DiagOpts.EmbedFixesInDiagnostics = + Params.capabilities.DiagnosticFixes; + ClientCapabilities.DiagOpts.SendDiagnosticCategory = + Params.capabilities.DiagnosticCategory; + ClientCapabilities.DiagOpts.EmitRelatedLocations = Params.capabilities.DiagnosticRelatedInformation; if (Params.capabilities.WorkspaceSymbolKinds) - SupportedSymbolKinds |= *Params.capabilities.WorkspaceSymbolKinds; + ClientCapabilities.SymbolKinds |= *Params.capabilities.WorkspaceSymbolKinds; if (Params.capabilities.CompletionItemKinds) - SupportedCompletionItemKinds |= *Params.capabilities.CompletionItemKinds; - SupportsCodeAction = Params.capabilities.CodeActionStructure; - SupportsHierarchicalDocumentSymbol = + ClientCapabilities.CompletionItemKinds |= + *Params.capabilities.CompletionItemKinds; + ClientCapabilities.EmitCodeAction = Params.capabilities.CodeActionStructure; + ClientCapabilities.HierarchicalDocumentSymbol = Params.capabilities.HierarchicalDocumentSymbol; - SupportFileStatus = Params.initializationOptions.FileStatus; - HoverContentFormat = Params.capabilities.HoverContentFormat; - Opts.LineFoldingOnly = Params.capabilities.LineFoldingOnly; - SupportsOffsetsInSignatureHelp = Params.capabilities.OffsetsInSignatureHelp; + ClientCapabilities.EmitFileStatus = Params.initializationOptions.FileStatus; + ClientCapabilities.HoverContentFormat = + Params.capabilities.HoverContentFormat; + ClientCapabilities.OffsetsInSignatureHelp = + Params.capabilities.OffsetsInSignatureHelp; + ClientCapabilities.BackgroundIndexSkipCreate = + Params.capabilities.ImplicitProgressCreation; +} + +void ClangdLSPServer::onInitialize(const InitializeParams &Params, + Callback Reply) { + if (Server) + return Reply(llvm::make_error("server already initialized", + ErrorCode::InvalidRequest)); + alignWithClient(Params); if (Params.capabilities.WorkDoneProgress) BackgroundIndexProgressState = BackgroundIndexProgress::Empty; - BackgroundIndexSkipCreate = Params.capabilities.ImplicitProgressCreation; - Opts.ImplicitCancellation = !Params.capabilities.CancelsStaleRequests; if (Opts.UseDirBasedCDB) { DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS); @@ -615,7 +626,6 @@ CodeAction::INFO_KIND}}} : llvm::json::Value(true); - std::vector Commands; for (llvm::StringRef Command : Handlers.CommandHandlers.keys()) Commands.push_back(Command); @@ -800,7 +810,7 @@ if (!Items) return Reply(Items.takeError()); for (auto &Sym : *Items) - Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds); + Sym.kind = adjustKindToCapability(Sym.kind, Opts.SymbolKinds); Reply(std::move(*Items)); }); @@ -936,8 +946,8 @@ llvm::Expected> Items) mutable { if (!Items) return Reply(Items.takeError()); - adjustSymbolKinds(*Items, SupportedSymbolKinds); - if (SupportsHierarchicalDocumentSymbol) + adjustSymbolKinds(*Items, Opts.SymbolKinds); + if (Opts.HierarchicalDocumentSymbol) return Reply(std::move(*Items)); return Reply(flattenSymbolHierarchy(*Items, FileURI)); }); @@ -1021,7 +1031,7 @@ OnlyFix->diagnostics = {Diags.front()}; } - if (SupportsCodeAction) + if (Opts.EmitCodeAction) return Reply(llvm::json::Array(Actions)); std::vector Commands; for (const auto &Action : Actions) { @@ -1059,7 +1069,7 @@ for (const auto &R : List->Completions) { CompletionItem C = R.render(Opts); C.kind = adjustKindToCapability( - C.kind, SupportedCompletionItemKinds); + C.kind, this->Opts.CompletionItemKinds); LSPList.items.push_back(std::move(C)); } return Reply(std::move(LSPList)); @@ -1074,7 +1084,7 @@ llvm::Expected Signature) mutable { if (!Signature) return Reply(Signature.takeError()); - if (SupportsOffsetsInSignatureHelp) + if (Opts.OffsetsInSignatureHelp) return Reply(std::move(*Signature)); // Strip out the offsets from signature help for // clients that only support string labels. @@ -1176,9 +1186,9 @@ return Reply(llvm::None); Hover R; - R.contents.kind = HoverContentFormat; + R.contents.kind = Opts.HoverContentFormat; R.range = (*H)->SymRange; - switch (HoverContentFormat) { + switch (Opts.HoverContentFormat) { case MarkupKind::PlainText: R.contents.value = (*H)->present().asPlainText(); return Reply(std::move(R)); @@ -1558,9 +1568,9 @@ ShouldCleanupMemory(/*Period=*/std::chrono::minutes(1), /*Delay=*/std::chrono::minutes(1)), BackgroundContext(Context::current().clone()), Transp(Transp), - MsgHandler(new MessageHandler(*this)), TFS(TFS), - SupportedSymbolKinds(defaultSymbolKinds()), - SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts) { + MsgHandler(new MessageHandler(*this)), TFS(TFS), Opts(Opts) { + this->Opts.SymbolKinds = defaultSymbolKinds(); + this->Opts.CompletionItemKinds = defaultCompletionItemKinds(); if (Opts.ConfigProvider) { assert(!Opts.ContextProvider && "Only one of ConfigProvider and ContextProvider allowed!"); @@ -1703,7 +1713,7 @@ Notification.uri = URIForFile::canonicalize(File, /*TUPath=*/File); DiagnosticToReplacementMap LocalFixIts; // Temporary storage for (auto &Diag : Diagnostics) { - toLSPDiags(Diag, Notification.uri, DiagOpts, + toLSPDiags(Diag, Notification.uri, Opts.DiagOpts, [&](clangd::Diagnostic Diag, llvm::ArrayRef Fixes) { auto &FixItsForDiagnostic = LocalFixIts[Diag]; llvm::copy(Fixes, std::back_inserter(FixItsForDiagnostic)); @@ -1763,7 +1773,7 @@ PendingBackgroundIndexProgress = Stats; return; case BackgroundIndexProgress::Empty: { - if (BackgroundIndexSkipCreate) { + if (Opts.BackgroundIndexSkipCreate) { NotifyProgress(Stats); break; } @@ -1794,7 +1804,7 @@ } void ClangdLSPServer::onFileUpdated(PathRef File, const TUStatus &Status) { - if (!SupportFileStatus) + if (!Opts.EmitFileStatus) return; // FIXME: we don't emit "BuildingFile" and `RunningAction`, as these // two statuses are running faster in practice, which leads the UI constantly