| 1 | //===--- ClangdLSPServer.h - LSP server --------------------------*- C++-*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H |
| 10 | #define |
| 11 | |
| 12 | #include "ClangdServer.h" |
| 13 | #include "Diagnostics.h" |
| 14 | #include "GlobalCompilationDatabase.h" |
| 15 | #include "LSPBinder.h" |
| 16 | #include "Protocol.h" |
| 17 | #include "Transport.h" |
| 18 | #include "support/Context.h" |
| 19 | #include "support/MemoryTree.h" |
| 20 | #include "support/Path.h" |
| 21 | #include "support/Threading.h" |
| 22 | #include "llvm/ADT/ArrayRef.h" |
| 23 | #include "llvm/Support/JSON.h" |
| 24 | #include <chrono> |
| 25 | #include <cstddef> |
| 26 | #include <cstdint> |
| 27 | #include <memory> |
| 28 | #include <optional> |
| 29 | #include <vector> |
| 30 | |
| 31 | namespace clang { |
| 32 | namespace clangd { |
| 33 | |
| 34 | /// This class exposes ClangdServer's capabilities via Language Server Protocol. |
| 35 | /// |
| 36 | /// MessageHandler binds the implemented LSP methods (e.g. onInitialize) to |
| 37 | /// corresponding JSON-RPC methods ("initialize"). |
| 38 | /// The server also supports $/cancelRequest (MessageHandler provides this). |
| 39 | class ClangdLSPServer : private ClangdServer::Callbacks, |
| 40 | private LSPBinder::RawOutgoing { |
| 41 | public: |
| 42 | struct Options : ClangdServer::Options { |
| 43 | /// Supplies configuration (overrides ClangdServer::ContextProvider). |
| 44 | config::Provider *ConfigProvider = nullptr; |
| 45 | /// Look for compilation databases, rather than using compile commands |
| 46 | /// set via LSP (extensions) only. |
| 47 | bool UseDirBasedCDB = true; |
| 48 | /// The offset-encoding to use, or std::nullopt to negotiate it over LSP. |
| 49 | std::optional<OffsetEncoding> Encoding; |
| 50 | /// If set, periodically called to release memory. |
| 51 | /// Consider malloc_trim(3) |
| 52 | std::function<void()> MemoryCleanup = nullptr; |
| 53 | |
| 54 | /// Per-feature options. Generally ClangdServer lets these vary |
| 55 | /// per-request, but LSP allows limited/no customizations. |
| 56 | clangd::CodeCompleteOptions CodeComplete; |
| 57 | MarkupKind SignatureHelpDocumentationFormat = MarkupKind::PlainText; |
| 58 | clangd::RenameOptions Rename; |
| 59 | /// Returns true if the tweak should be enabled. |
| 60 | std::function<bool(const Tweak &)> TweakFilter = [](const Tweak &T) { |
| 61 | return !T.hidden(); // only enable non-hidden tweaks. |
| 62 | }; |
| 63 | |
| 64 | /// Limit the number of references returned (0 means no limit). |
| 65 | size_t ReferencesLimit = 0; |
| 66 | |
| 67 | /// Flag to hint the experimental modules support is enabled. |
| 68 | bool EnableExperimentalModulesSupport = false; |
| 69 | }; |
| 70 | |
| 71 | ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS, |
| 72 | const ClangdLSPServer::Options &Opts); |
| 73 | /// The destructor blocks on any outstanding background tasks. |
| 74 | ~ClangdLSPServer(); |
| 75 | |
| 76 | ClangdLSPServer(const ClangdLSPServer &other) = delete; |
| 77 | ClangdLSPServer &operator=(const ClangdLSPServer &other) = delete; |
| 78 | |
| 79 | /// Run LSP server loop, communicating with the Transport provided in the |
| 80 | /// constructor. This method must not be executed more than once. |
| 81 | /// |
| 82 | /// \return Whether we shut down cleanly with a 'shutdown' -> 'exit' sequence. |
| 83 | bool run(); |
| 84 | |
| 85 | /// Profiles resource-usage. |
| 86 | void profile(MemoryTree &MT) const; |
| 87 | |
| 88 | private: |
| 89 | // Implement ClangdServer::Callbacks. |
| 90 | void onDiagnosticsReady(PathRef File, llvm::StringRef Version, |
| 91 | llvm::ArrayRef<Diag> Diagnostics) override; |
| 92 | void onFileUpdated(PathRef File, const TUStatus &Status) override; |
| 93 | void onBackgroundIndexProgress(const BackgroundQueue::Stats &Stats) override; |
| 94 | void onSemanticsMaybeChanged(PathRef File) override; |
| 95 | void onInactiveRegionsReady(PathRef File, |
| 96 | std::vector<Range> InactiveRegions) override; |
| 97 | |
| 98 | // LSP methods. Notifications have signature void(const Params&). |
| 99 | // Calls have signature void(const Params&, Callback<Response>). |
| 100 | void onInitialize(const InitializeParams &, Callback<llvm::json::Value>); |
| 101 | void onInitialized(const InitializedParams &); |
| 102 | void onShutdown(const NoParams &, Callback<std::nullptr_t>); |
| 103 | void onSync(const NoParams &, Callback<std::nullptr_t>); |
| 104 | void onDocumentDidOpen(const DidOpenTextDocumentParams &); |
| 105 | void onDocumentDidChange(const DidChangeTextDocumentParams &); |
| 106 | void onDocumentDidClose(const DidCloseTextDocumentParams &); |
| 107 | void onDocumentDidSave(const DidSaveTextDocumentParams &); |
| 108 | void onAST(const ASTParams &, Callback<std::optional<ASTNode>>); |
| 109 | void onDocumentOnTypeFormatting(const DocumentOnTypeFormattingParams &, |
| 110 | Callback<std::vector<TextEdit>>); |
| 111 | void onDocumentRangeFormatting(const DocumentRangeFormattingParams &, |
| 112 | Callback<std::vector<TextEdit>>); |
| 113 | void onDocumentRangesFormatting(const DocumentRangesFormattingParams &, |
| 114 | Callback<std::vector<TextEdit>>); |
| 115 | void onDocumentFormatting(const DocumentFormattingParams &, |
| 116 | Callback<std::vector<TextEdit>>); |
| 117 | // The results are serialized 'vector<DocumentSymbol>' if |
| 118 | // SupportsHierarchicalDocumentSymbol is true and 'vector<SymbolInformation>' |
| 119 | // otherwise. |
| 120 | void onDocumentSymbol(const DocumentSymbolParams &, |
| 121 | Callback<llvm::json::Value>); |
| 122 | void onFoldingRange(const FoldingRangeParams &, |
| 123 | Callback<std::vector<FoldingRange>>); |
| 124 | void onCodeAction(const CodeActionParams &, Callback<llvm::json::Value>); |
| 125 | void onCompletion(const CompletionParams &, Callback<CompletionList>); |
| 126 | void onSignatureHelp(const TextDocumentPositionParams &, |
| 127 | Callback<SignatureHelp>); |
| 128 | void onGoToDeclaration(const TextDocumentPositionParams &, |
| 129 | Callback<std::vector<Location>>); |
| 130 | void onGoToDefinition(const TextDocumentPositionParams &, |
| 131 | Callback<std::vector<Location>>); |
| 132 | void onGoToType(const TextDocumentPositionParams &, |
| 133 | Callback<std::vector<Location>>); |
| 134 | void onGoToImplementation(const TextDocumentPositionParams &, |
| 135 | Callback<std::vector<Location>>); |
| 136 | void onReference(const ReferenceParams &, Callback<std::vector<ReferenceLocation>>); |
| 137 | void (const TextDocumentIdentifier &, |
| 138 | Callback<std::optional<URIForFile>>); |
| 139 | void onDocumentHighlight(const TextDocumentPositionParams &, |
| 140 | Callback<std::vector<DocumentHighlight>>); |
| 141 | void onFileEvent(const DidChangeWatchedFilesParams &); |
| 142 | void onWorkspaceSymbol(const WorkspaceSymbolParams &, |
| 143 | Callback<std::vector<SymbolInformation>>); |
| 144 | void onPrepareRename(const TextDocumentPositionParams &, |
| 145 | Callback<PrepareRenameResult>); |
| 146 | void onRename(const RenameParams &, Callback<WorkspaceEdit>); |
| 147 | void onHover(const TextDocumentPositionParams &, |
| 148 | Callback<std::optional<Hover>>); |
| 149 | void onPrepareTypeHierarchy(const TypeHierarchyPrepareParams &, |
| 150 | Callback<std::vector<TypeHierarchyItem>>); |
| 151 | void onSuperTypes(const ResolveTypeHierarchyItemParams &, |
| 152 | Callback<std::optional<std::vector<TypeHierarchyItem>>>); |
| 153 | void onSubTypes(const ResolveTypeHierarchyItemParams &, |
| 154 | Callback<std::vector<TypeHierarchyItem>>); |
| 155 | void onTypeHierarchy(const TypeHierarchyPrepareParams &, |
| 156 | Callback<llvm::json::Value>); |
| 157 | void onResolveTypeHierarchy(const ResolveTypeHierarchyItemParams &, |
| 158 | Callback<llvm::json::Value>); |
| 159 | void onPrepareCallHierarchy(const CallHierarchyPrepareParams &, |
| 160 | Callback<std::vector<CallHierarchyItem>>); |
| 161 | void onCallHierarchyIncomingCalls( |
| 162 | const CallHierarchyIncomingCallsParams &, |
| 163 | Callback<std::vector<CallHierarchyIncomingCall>>); |
| 164 | void onCallHierarchyOutgoingCalls( |
| 165 | const CallHierarchyOutgoingCallsParams &, |
| 166 | Callback<std::vector<CallHierarchyOutgoingCall>>); |
| 167 | void onClangdInlayHints(const InlayHintsParams &, |
| 168 | Callback<llvm::json::Value>); |
| 169 | void onInlayHint(const InlayHintsParams &, Callback<std::vector<InlayHint>>); |
| 170 | void onChangeConfiguration(const DidChangeConfigurationParams &); |
| 171 | void onSymbolInfo(const TextDocumentPositionParams &, |
| 172 | Callback<std::vector<SymbolDetails>>); |
| 173 | void onSelectionRange(const SelectionRangeParams &, |
| 174 | Callback<std::vector<SelectionRange>>); |
| 175 | void onDocumentLink(const DocumentLinkParams &, |
| 176 | Callback<std::vector<DocumentLink>>); |
| 177 | void onSemanticTokens(const SemanticTokensParams &, Callback<SemanticTokens>); |
| 178 | void onSemanticTokensDelta(const SemanticTokensDeltaParams &, |
| 179 | Callback<SemanticTokensOrDelta>); |
| 180 | /// This is a clangd extension. Provides a json tree representing memory usage |
| 181 | /// hierarchy. |
| 182 | void onMemoryUsage(const NoParams &, Callback<MemoryTree>); |
| 183 | void onCommand(const ExecuteCommandParams &, Callback<llvm::json::Value>); |
| 184 | |
| 185 | /// Implement commands. |
| 186 | void onCommandApplyEdit(const WorkspaceEdit &, Callback<llvm::json::Value>); |
| 187 | void onCommandApplyTweak(const TweakArgs &, Callback<llvm::json::Value>); |
| 188 | void onCommandApplyRename(const RenameParams &, Callback<llvm::json::Value>); |
| 189 | |
| 190 | /// Outgoing LSP calls. |
| 191 | LSPBinder::OutgoingMethod<ApplyWorkspaceEditParams, |
| 192 | ApplyWorkspaceEditResponse> |
| 193 | ApplyWorkspaceEdit; |
| 194 | LSPBinder::OutgoingNotification<ShowMessageParams> ShowMessage; |
| 195 | LSPBinder::OutgoingNotification<PublishDiagnosticsParams> PublishDiagnostics; |
| 196 | LSPBinder::OutgoingNotification<FileStatus> NotifyFileStatus; |
| 197 | LSPBinder::OutgoingNotification<InactiveRegionsParams> PublishInactiveRegions; |
| 198 | LSPBinder::OutgoingMethod<WorkDoneProgressCreateParams, std::nullptr_t> |
| 199 | CreateWorkDoneProgress; |
| 200 | LSPBinder::OutgoingNotification<ProgressParams<WorkDoneProgressBegin>> |
| 201 | BeginWorkDoneProgress; |
| 202 | LSPBinder::OutgoingNotification<ProgressParams<WorkDoneProgressReport>> |
| 203 | ReportWorkDoneProgress; |
| 204 | LSPBinder::OutgoingNotification<ProgressParams<WorkDoneProgressEnd>> |
| 205 | EndWorkDoneProgress; |
| 206 | LSPBinder::OutgoingMethod<NoParams, std::nullptr_t> SemanticTokensRefresh; |
| 207 | |
| 208 | void applyEdit(WorkspaceEdit WE, llvm::json::Value Success, |
| 209 | Callback<llvm::json::Value> Reply); |
| 210 | |
| 211 | void bindMethods(LSPBinder &, const ClientCapabilities &Caps); |
| 212 | std::optional<ClangdServer::DiagRef> getDiagRef(StringRef File, |
| 213 | const clangd::Diagnostic &D); |
| 214 | |
| 215 | /// Checks if completion request should be ignored. We need this due to the |
| 216 | /// limitation of the LSP. Per LSP, a client sends requests for all "trigger |
| 217 | /// character" we specify, but for '>' and ':' we need to check they actually |
| 218 | /// produce '->' and '::', respectively. |
| 219 | bool shouldRunCompletion(const CompletionParams &Params) const; |
| 220 | |
| 221 | void applyConfiguration(const ConfigurationSettings &Settings); |
| 222 | |
| 223 | /// Runs profiling and exports memory usage metrics if tracing is enabled and |
| 224 | /// profiling hasn't happened recently. |
| 225 | void maybeExportMemoryProfile(); |
| 226 | PeriodicThrottler ShouldProfile; |
| 227 | |
| 228 | /// Run the MemoryCleanup callback if it's time. |
| 229 | /// This method is thread safe. |
| 230 | void maybeCleanupMemory(); |
| 231 | PeriodicThrottler ShouldCleanupMemory; |
| 232 | |
| 233 | /// Since initialization of CDBs and ClangdServer is done lazily, the |
| 234 | /// following context captures the one used while creating ClangdLSPServer and |
| 235 | /// passes it to above mentioned object instances to make sure they share the |
| 236 | /// same state. |
| 237 | Context BackgroundContext; |
| 238 | |
| 239 | /// Used to indicate that the 'shutdown' request was received from the |
| 240 | /// Language Server client. |
| 241 | bool ShutdownRequestReceived = false; |
| 242 | |
| 243 | /// Used to indicate the ClangdLSPServer is being destroyed. |
| 244 | std::atomic<bool> IsBeingDestroyed = {false}; |
| 245 | |
| 246 | // FIXME: The caching is a temporary solution to get corresponding clangd |
| 247 | // diagnostic from a LSP diagnostic. |
| 248 | // Ideally, ClangdServer can generate an identifier for each diagnostic, |
| 249 | // emit them via the LSP's data field (which was newly added in LSP 3.16). |
| 250 | std::mutex DiagRefMutex; |
| 251 | struct DiagKey { |
| 252 | clangd::Range Rng; |
| 253 | std::string Message; |
| 254 | bool operator<(const DiagKey &Other) const { |
| 255 | return std::tie(args: Rng, args: Message) < std::tie(args: Other.Rng, args: Other.Message); |
| 256 | } |
| 257 | }; |
| 258 | DiagKey toDiagKey(const clangd::Diagnostic &LSPDiag) { |
| 259 | return {.Rng: LSPDiag.range, .Message: LSPDiag.message}; |
| 260 | } |
| 261 | /// A map from LSP diagnostic to clangd-naive diagnostic. |
| 262 | typedef std::map<DiagKey, ClangdServer::DiagRef> |
| 263 | DiagnosticToDiagRefMap; |
| 264 | /// Caches the mapping LSP and clangd-naive diagnostics per file. |
| 265 | llvm::StringMap<DiagnosticToDiagRefMap> |
| 266 | DiagRefMap; |
| 267 | |
| 268 | // Last semantic-tokens response, for incremental requests. |
| 269 | std::mutex SemanticTokensMutex; |
| 270 | llvm::StringMap<SemanticTokens> LastSemanticTokens; |
| 271 | |
| 272 | // Most code should not deal with Transport, callMethod, notify directly. |
| 273 | // Use LSPBinder to handle incoming and outgoing calls. |
| 274 | clangd::Transport &Transp; |
| 275 | class MessageHandler; |
| 276 | std::unique_ptr<MessageHandler> MsgHandler; |
| 277 | std::mutex TranspWriter; |
| 278 | |
| 279 | void callMethod(StringRef Method, llvm::json::Value Params, |
| 280 | Callback<llvm::json::Value> CB) override; |
| 281 | void notify(StringRef Method, llvm::json::Value Params) override; |
| 282 | |
| 283 | LSPBinder::RawHandlers Handlers; |
| 284 | |
| 285 | const ThreadsafeFS &TFS; |
| 286 | /// Options used for diagnostics. |
| 287 | ClangdDiagnosticOptions DiagOpts; |
| 288 | /// The supported kinds of the client. |
| 289 | SymbolKindBitset SupportedSymbolKinds; |
| 290 | /// The supported completion item kinds of the client. |
| 291 | CompletionItemKindBitset SupportedCompletionItemKinds; |
| 292 | // Whether the client supports CompletionItem.labelDetails. |
| 293 | bool SupportsCompletionLabelDetails = false; |
| 294 | /// Whether the client supports CodeAction response objects. |
| 295 | bool SupportsCodeAction = false; |
| 296 | /// From capabilities of textDocument/documentSymbol. |
| 297 | bool SupportsHierarchicalDocumentSymbol = false; |
| 298 | /// Whether the client supports showing file status. |
| 299 | bool SupportFileStatus = false; |
| 300 | /// Whether the client supports attaching a container string to references. |
| 301 | bool SupportsReferenceContainer = false; |
| 302 | /// Which kind of markup should we use in textDocument/hover responses. |
| 303 | MarkupKind HoverContentFormat = MarkupKind::PlainText; |
| 304 | /// Whether the client supports offsets for parameter info labels. |
| 305 | bool SupportsOffsetsInSignatureHelp = false; |
| 306 | /// Whether the client supports the versioned document changes. |
| 307 | bool SupportsDocumentChanges = false; |
| 308 | /// Whether the client supports change annotations on text edits. |
| 309 | bool SupportsChangeAnnotation = false; |
| 310 | |
| 311 | std::mutex BackgroundIndexProgressMutex; |
| 312 | enum class BackgroundIndexProgress { |
| 313 | // Client doesn't support reporting progress. No transitions possible. |
| 314 | Unsupported, |
| 315 | // The queue is idle, and the client has no progress bar. |
| 316 | // Can transition to Creating when we have some activity. |
| 317 | Empty, |
| 318 | // We've requested the client to create a progress bar. |
| 319 | // Meanwhile, the state is buffered in PendingBackgroundIndexProgress. |
| 320 | Creating, |
| 321 | // The client has a progress bar, and we can send it updates immediately. |
| 322 | Live, |
| 323 | } BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported; |
| 324 | // The progress to send when the progress bar is created. |
| 325 | // Only valid in state Creating. |
| 326 | BackgroundQueue::Stats PendingBackgroundIndexProgress; |
| 327 | /// LSP extension: skip WorkDoneProgressCreate, just send progress streams. |
| 328 | bool BackgroundIndexSkipCreate = false; |
| 329 | |
| 330 | Options Opts; |
| 331 | // The CDB is created by the "initialize" LSP method. |
| 332 | std::unique_ptr<GlobalCompilationDatabase> BaseCDB; |
| 333 | // CDB is BaseCDB plus any commands overridden via LSP extensions. |
| 334 | std::optional<OverlayCDB> CDB; |
| 335 | // The ClangdServer is created by the "initialize" LSP method. |
| 336 | std::optional<ClangdServer> Server; |
| 337 | // Manages to build module files. |
| 338 | std::optional<ModulesBuilder> ModulesManager; |
| 339 | }; |
| 340 | } // namespace clangd |
| 341 | } // namespace clang |
| 342 | |
| 343 | #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H |
| 344 | |