//===--- ClangdLSPServer.h - LSP server --------------------------*- C++-*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H #include "ClangdServer.h" #include "Diagnostics.h" #include "GlobalCompilationDatabase.h" #include "LSPBinder.h" #include "Protocol.h" #include "Transport.h" #include "support/Context.h" #include "support/MemoryTree.h" #include "support/Path.h" #include "support/Threading.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/JSON.h" #include #include #include #include #include #include namespace clang { namespace clangd { /// This class exposes ClangdServer's capabilities via Language Server Protocol. /// /// MessageHandler binds the implemented LSP methods (e.g. onInitialize) to /// corresponding JSON-RPC methods ("initialize"). /// The server also supports $/cancelRequest (MessageHandler provides this). class ClangdLSPServer : private ClangdServer::Callbacks, private LSPBinder::RawOutgoing { public: struct Options : ClangdServer::Options { /// Supplies configuration (overrides ClangdServer::ContextProvider). config::Provider *ConfigProvider = nullptr; /// Look for compilation databases, rather than using compile commands /// set via LSP (extensions) only. bool UseDirBasedCDB = true; /// The offset-encoding to use, or std::nullopt to negotiate it over LSP. std::optional Encoding; /// If set, periodically called to release memory. /// Consider malloc_trim(3) std::function MemoryCleanup = nullptr; /// Per-feature options. Generally ClangdServer lets these vary /// per-request, but LSP allows limited/no customizations. clangd::CodeCompleteOptions CodeComplete; MarkupKind SignatureHelpDocumentationFormat = MarkupKind::PlainText; clangd::RenameOptions Rename; /// Returns true if the tweak should be enabled. std::function TweakFilter = [](const Tweak &T) { return !T.hidden(); // only enable non-hidden tweaks. }; /// Limit the number of references returned (0 means no limit). size_t ReferencesLimit = 0; }; ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts); /// The destructor blocks on any outstanding background tasks. ~ClangdLSPServer(); /// Run LSP server loop, communicating with the Transport provided in the /// constructor. This method must not be executed more than once. /// /// \return Whether we shut down cleanly with a 'shutdown' -> 'exit' sequence. bool run(); /// Profiles resource-usage. void profile(MemoryTree &MT) const; private: // Implement ClangdServer::Callbacks. void onDiagnosticsReady(PathRef File, llvm::StringRef Version, llvm::ArrayRef Diagnostics) override; void onFileUpdated(PathRef File, const TUStatus &Status) override; void onBackgroundIndexProgress(const BackgroundQueue::Stats &Stats) override; void onSemanticsMaybeChanged(PathRef File) override; void onInactiveRegionsReady(PathRef File, std::vector InactiveRegions) override; // LSP methods. Notifications have signature void(const Params&). // Calls have signature void(const Params&, Callback). void onInitialize(const InitializeParams &, Callback); void onInitialized(const InitializedParams &); void onShutdown(const NoParams &, Callback); void onSync(const NoParams &, Callback); void onDocumentDidOpen(const DidOpenTextDocumentParams &); void onDocumentDidChange(const DidChangeTextDocumentParams &); void onDocumentDidClose(const DidCloseTextDocumentParams &); void onDocumentDidSave(const DidSaveTextDocumentParams &); void onAST(const ASTParams &, Callback>); void onDocumentOnTypeFormatting(const DocumentOnTypeFormattingParams &, Callback>); void onDocumentRangeFormatting(const DocumentRangeFormattingParams &, Callback>); void onDocumentFormatting(const DocumentFormattingParams &, Callback>); // The results are serialized 'vector' if // SupportsHierarchicalDocumentSymbol is true and 'vector' // otherwise. void onDocumentSymbol(const DocumentSymbolParams &, Callback); void onFoldingRange(const FoldingRangeParams &, Callback>); void onCodeAction(const CodeActionParams &, Callback); void onCompletion(const CompletionParams &, Callback); void onSignatureHelp(const TextDocumentPositionParams &, Callback); void onGoToDeclaration(const TextDocumentPositionParams &, Callback>); void onGoToDefinition(const TextDocumentPositionParams &, Callback>); void onGoToType(const TextDocumentPositionParams &, Callback>); void onGoToImplementation(const TextDocumentPositionParams &, Callback>); void onReference(const ReferenceParams &, Callback>); void onSwitchSourceHeader(const TextDocumentIdentifier &, Callback>); void onDocumentHighlight(const TextDocumentPositionParams &, Callback>); void onFileEvent(const DidChangeWatchedFilesParams &); void onWorkspaceSymbol(const WorkspaceSymbolParams &, Callback>); void onPrepareRename(const TextDocumentPositionParams &, Callback>); void onRename(const RenameParams &, Callback); void onHover(const TextDocumentPositionParams &, Callback>); void onPrepareTypeHierarchy(const TypeHierarchyPrepareParams &, Callback>); void onSuperTypes(const ResolveTypeHierarchyItemParams &, Callback>>); void onSubTypes(const ResolveTypeHierarchyItemParams &, Callback>); void onTypeHierarchy(const TypeHierarchyPrepareParams &, Callback); void onResolveTypeHierarchy(const ResolveTypeHierarchyItemParams &, Callback); void onPrepareCallHierarchy(const CallHierarchyPrepareParams &, Callback>); void onCallHierarchyIncomingCalls( const CallHierarchyIncomingCallsParams &, Callback>); void onClangdInlayHints(const InlayHintsParams &, Callback); void onInlayHint(const InlayHintsParams &, Callback>); void onChangeConfiguration(const DidChangeConfigurationParams &); void onSymbolInfo(const TextDocumentPositionParams &, Callback>); void onSelectionRange(const SelectionRangeParams &, Callback>); void onDocumentLink(const DocumentLinkParams &, Callback>); void onSemanticTokens(const SemanticTokensParams &, Callback); void onSemanticTokensDelta(const SemanticTokensDeltaParams &, Callback); /// This is a clangd extension. Provides a json tree representing memory usage /// hierarchy. void onMemoryUsage(const NoParams &, Callback); void onCommand(const ExecuteCommandParams &, Callback); /// Implement commands. void onCommandApplyEdit(const WorkspaceEdit &, Callback); void onCommandApplyTweak(const TweakArgs &, Callback); /// Outgoing LSP calls. LSPBinder::OutgoingMethod ApplyWorkspaceEdit; LSPBinder::OutgoingNotification ShowMessage; LSPBinder::OutgoingNotification PublishDiagnostics; LSPBinder::OutgoingNotification NotifyFileStatus; LSPBinder::OutgoingNotification PublishInactiveRegions; LSPBinder::OutgoingMethod CreateWorkDoneProgress; LSPBinder::OutgoingNotification> BeginWorkDoneProgress; LSPBinder::OutgoingNotification> ReportWorkDoneProgress; LSPBinder::OutgoingNotification> EndWorkDoneProgress; LSPBinder::OutgoingMethod SemanticTokensRefresh; void applyEdit(WorkspaceEdit WE, llvm::json::Value Success, Callback Reply); void bindMethods(LSPBinder &, const ClientCapabilities &Caps); std::optional getDiagRef(StringRef File, const clangd::Diagnostic &D); /// Checks if completion request should be ignored. We need this due to the /// limitation of the LSP. Per LSP, a client sends requests for all "trigger /// character" we specify, but for '>' and ':' we need to check they actually /// produce '->' and '::', respectively. bool shouldRunCompletion(const CompletionParams &Params) const; void applyConfiguration(const ConfigurationSettings &Settings); /// Runs profiling and exports memory usage metrics if tracing is enabled and /// profiling hasn't happened recently. void maybeExportMemoryProfile(); PeriodicThrottler ShouldProfile; /// Run the MemoryCleanup callback if it's time. /// This method is thread safe. void maybeCleanupMemory(); PeriodicThrottler ShouldCleanupMemory; /// Since initialization of CDBs and ClangdServer is done lazily, the /// following context captures the one used while creating ClangdLSPServer and /// passes it to above mentioned object instances to make sure they share the /// same state. Context BackgroundContext; /// Used to indicate that the 'shutdown' request was received from the /// Language Server client. bool ShutdownRequestReceived = false; /// Used to indicate the ClangdLSPServer is being destroyed. std::atomic IsBeingDestroyed = {false}; // FIXME: The caching is a temporary solution to get corresponding clangd // diagnostic from a LSP diagnostic. // Ideally, ClangdServer can generate an identifier for each diagnostic, // emit them via the LSP's data field (which was newly added in LSP 3.16). std::mutex DiagRefMutex; struct DiagKey { clangd::Range Rng; std::string Message; bool operator<(const DiagKey &Other) const { return std::tie(Rng, Message) < std::tie(Other.Rng, Other.Message); } }; DiagKey toDiagKey(const clangd::Diagnostic &LSPDiag) { return {LSPDiag.range, LSPDiag.message}; } /// A map from LSP diagnostic to clangd-naive diagnostic. typedef std::map DiagnosticToDiagRefMap; /// Caches the mapping LSP and clangd-naive diagnostics per file. llvm::StringMap DiagRefMap; // Last semantic-tokens response, for incremental requests. std::mutex SemanticTokensMutex; llvm::StringMap LastSemanticTokens; // Most code should not deal with Transport, callMethod, notify directly. // Use LSPBinder to handle incoming and outgoing calls. clangd::Transport &Transp; class MessageHandler; std::unique_ptr MsgHandler; std::mutex TranspWriter; void callMethod(StringRef Method, llvm::json::Value Params, Callback CB) override; void notify(StringRef Method, llvm::json::Value Params) override; 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 CompletionItem.labelDetails. bool SupportsCompletionLabelDetails = false; /// 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; /// Whether the client supports attaching a container string to references. bool SupportsReferenceContainer = 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; /// Whether the client supports the versioned document changes. bool SupportsDocumentChanges = false; /// Whether the client supports change annotations on text edits. bool SupportsChangeAnnotation = false; std::mutex BackgroundIndexProgressMutex; enum class BackgroundIndexProgress { // Client doesn't support reporting progress. No transitions possible. Unsupported, // The queue is idle, and the client has no progress bar. // Can transition to Creating when we have some activity. Empty, // We've requested the client to create a progress bar. // Meanwhile, the state is buffered in PendingBackgroundIndexProgress. Creating, // The client has a progress bar, and we can send it updates immediately. Live, } BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported; // 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; // The CDB is created by the "initialize" LSP method. std::unique_ptr BaseCDB; // CDB is BaseCDB plus any commands overridden via LSP extensions. std::optional CDB; // The ClangdServer is created by the "initialize" LSP method. std::optional Server; }; } // namespace clangd } // namespace clang #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H