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 LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H 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 onDocumentFormatting(const DocumentFormattingParams &, 114 Callback<std::vector<TextEdit>>); 115 // The results are serialized 'vector<DocumentSymbol>' if 116 // SupportsHierarchicalDocumentSymbol is true and 'vector<SymbolInformation>' 117 // otherwise. 118 void onDocumentSymbol(const DocumentSymbolParams &, 119 Callback<llvm::json::Value>); 120 void onFoldingRange(const FoldingRangeParams &, 121 Callback<std::vector<FoldingRange>>); 122 void onCodeAction(const CodeActionParams &, Callback<llvm::json::Value>); 123 void onCompletion(const CompletionParams &, Callback<CompletionList>); 124 void onSignatureHelp(const TextDocumentPositionParams &, 125 Callback<SignatureHelp>); 126 void onGoToDeclaration(const TextDocumentPositionParams &, 127 Callback<std::vector<Location>>); 128 void onGoToDefinition(const TextDocumentPositionParams &, 129 Callback<std::vector<Location>>); 130 void onGoToType(const TextDocumentPositionParams &, 131 Callback<std::vector<Location>>); 132 void onGoToImplementation(const TextDocumentPositionParams &, 133 Callback<std::vector<Location>>); 134 void onReference(const ReferenceParams &, Callback<std::vector<ReferenceLocation>>); 135 void onSwitchSourceHeader(const TextDocumentIdentifier &, 136 Callback<std::optional<URIForFile>>); 137 void onDocumentHighlight(const TextDocumentPositionParams &, 138 Callback<std::vector<DocumentHighlight>>); 139 void onFileEvent(const DidChangeWatchedFilesParams &); 140 void onWorkspaceSymbol(const WorkspaceSymbolParams &, 141 Callback<std::vector<SymbolInformation>>); 142 void onPrepareRename(const TextDocumentPositionParams &, 143 Callback<PrepareRenameResult>); 144 void onRename(const RenameParams &, Callback<WorkspaceEdit>); 145 void onHover(const TextDocumentPositionParams &, 146 Callback<std::optional<Hover>>); 147 void onPrepareTypeHierarchy(const TypeHierarchyPrepareParams &, 148 Callback<std::vector<TypeHierarchyItem>>); 149 void onSuperTypes(const ResolveTypeHierarchyItemParams &, 150 Callback<std::optional<std::vector<TypeHierarchyItem>>>); 151 void onSubTypes(const ResolveTypeHierarchyItemParams &, 152 Callback<std::vector<TypeHierarchyItem>>); 153 void onTypeHierarchy(const TypeHierarchyPrepareParams &, 154 Callback<llvm::json::Value>); 155 void onResolveTypeHierarchy(const ResolveTypeHierarchyItemParams &, 156 Callback<llvm::json::Value>); 157 void onPrepareCallHierarchy(const CallHierarchyPrepareParams &, 158 Callback<std::vector<CallHierarchyItem>>); 159 void onCallHierarchyIncomingCalls( 160 const CallHierarchyIncomingCallsParams &, 161 Callback<std::vector<CallHierarchyIncomingCall>>); 162 void onCallHierarchyOutgoingCalls( 163 const CallHierarchyOutgoingCallsParams &, 164 Callback<std::vector<CallHierarchyOutgoingCall>>); 165 void onClangdInlayHints(const InlayHintsParams &, 166 Callback<llvm::json::Value>); 167 void onInlayHint(const InlayHintsParams &, Callback<std::vector<InlayHint>>); 168 void onChangeConfiguration(const DidChangeConfigurationParams &); 169 void onSymbolInfo(const TextDocumentPositionParams &, 170 Callback<std::vector<SymbolDetails>>); 171 void onSelectionRange(const SelectionRangeParams &, 172 Callback<std::vector<SelectionRange>>); 173 void onDocumentLink(const DocumentLinkParams &, 174 Callback<std::vector<DocumentLink>>); 175 void onSemanticTokens(const SemanticTokensParams &, Callback<SemanticTokens>); 176 void onSemanticTokensDelta(const SemanticTokensDeltaParams &, 177 Callback<SemanticTokensOrDelta>); 178 /// This is a clangd extension. Provides a json tree representing memory usage 179 /// hierarchy. 180 void onMemoryUsage(const NoParams &, Callback<MemoryTree>); 181 void onCommand(const ExecuteCommandParams &, Callback<llvm::json::Value>); 182 183 /// Implement commands. 184 void onCommandApplyEdit(const WorkspaceEdit &, Callback<llvm::json::Value>); 185 void onCommandApplyTweak(const TweakArgs &, Callback<llvm::json::Value>); 186 void onCommandApplyRename(const RenameParams &, Callback<llvm::json::Value>); 187 188 /// Outgoing LSP calls. 189 LSPBinder::OutgoingMethod<ApplyWorkspaceEditParams, 190 ApplyWorkspaceEditResponse> 191 ApplyWorkspaceEdit; 192 LSPBinder::OutgoingNotification<ShowMessageParams> ShowMessage; 193 LSPBinder::OutgoingNotification<PublishDiagnosticsParams> PublishDiagnostics; 194 LSPBinder::OutgoingNotification<FileStatus> NotifyFileStatus; 195 LSPBinder::OutgoingNotification<InactiveRegionsParams> PublishInactiveRegions; 196 LSPBinder::OutgoingMethod<WorkDoneProgressCreateParams, std::nullptr_t> 197 CreateWorkDoneProgress; 198 LSPBinder::OutgoingNotification<ProgressParams<WorkDoneProgressBegin>> 199 BeginWorkDoneProgress; 200 LSPBinder::OutgoingNotification<ProgressParams<WorkDoneProgressReport>> 201 ReportWorkDoneProgress; 202 LSPBinder::OutgoingNotification<ProgressParams<WorkDoneProgressEnd>> 203 EndWorkDoneProgress; 204 LSPBinder::OutgoingMethod<NoParams, std::nullptr_t> SemanticTokensRefresh; 205 206 void applyEdit(WorkspaceEdit WE, llvm::json::Value Success, 207 Callback<llvm::json::Value> Reply); 208 209 void bindMethods(LSPBinder &, const ClientCapabilities &Caps); 210 std::optional<ClangdServer::DiagRef> getDiagRef(StringRef File, 211 const clangd::Diagnostic &D); 212 213 /// Checks if completion request should be ignored. We need this due to the 214 /// limitation of the LSP. Per LSP, a client sends requests for all "trigger 215 /// character" we specify, but for '>' and ':' we need to check they actually 216 /// produce '->' and '::', respectively. 217 bool shouldRunCompletion(const CompletionParams &Params) const; 218 219 void applyConfiguration(const ConfigurationSettings &Settings); 220 221 /// Runs profiling and exports memory usage metrics if tracing is enabled and 222 /// profiling hasn't happened recently. 223 void maybeExportMemoryProfile(); 224 PeriodicThrottler ShouldProfile; 225 226 /// Run the MemoryCleanup callback if it's time. 227 /// This method is thread safe. 228 void maybeCleanupMemory(); 229 PeriodicThrottler ShouldCleanupMemory; 230 231 /// Since initialization of CDBs and ClangdServer is done lazily, the 232 /// following context captures the one used while creating ClangdLSPServer and 233 /// passes it to above mentioned object instances to make sure they share the 234 /// same state. 235 Context BackgroundContext; 236 237 /// Used to indicate that the 'shutdown' request was received from the 238 /// Language Server client. 239 bool ShutdownRequestReceived = false; 240 241 /// Used to indicate the ClangdLSPServer is being destroyed. 242 std::atomic<bool> IsBeingDestroyed = {false}; 243 244 // FIXME: The caching is a temporary solution to get corresponding clangd 245 // diagnostic from a LSP diagnostic. 246 // Ideally, ClangdServer can generate an identifier for each diagnostic, 247 // emit them via the LSP's data field (which was newly added in LSP 3.16). 248 std::mutex DiagRefMutex; 249 struct DiagKey { 250 clangd::Range Rng; 251 std::string Message; 252 bool operator<(const DiagKey &Other) const { 253 return std::tie(Rng, Message) < std::tie(Other.Rng, Other.Message); 254 } 255 }; 256 DiagKey toDiagKey(const clangd::Diagnostic &LSPDiag) { 257 return {LSPDiag.range, LSPDiag.message}; 258 } 259 /// A map from LSP diagnostic to clangd-naive diagnostic. 260 typedef std::map<DiagKey, ClangdServer::DiagRef> 261 DiagnosticToDiagRefMap; 262 /// Caches the mapping LSP and clangd-naive diagnostics per file. 263 llvm::StringMap<DiagnosticToDiagRefMap> 264 DiagRefMap; 265 266 // Last semantic-tokens response, for incremental requests. 267 std::mutex SemanticTokensMutex; 268 llvm::StringMap<SemanticTokens> LastSemanticTokens; 269 270 // Most code should not deal with Transport, callMethod, notify directly. 271 // Use LSPBinder to handle incoming and outgoing calls. 272 clangd::Transport &Transp; 273 class MessageHandler; 274 std::unique_ptr<MessageHandler> MsgHandler; 275 std::mutex TranspWriter; 276 277 void callMethod(StringRef Method, llvm::json::Value Params, 278 Callback<llvm::json::Value> CB) override; 279 void notify(StringRef Method, llvm::json::Value Params) override; 280 281 LSPBinder::RawHandlers Handlers; 282 283 const ThreadsafeFS &TFS; 284 /// Options used for diagnostics. 285 ClangdDiagnosticOptions DiagOpts; 286 /// The supported kinds of the client. 287 SymbolKindBitset SupportedSymbolKinds; 288 /// The supported completion item kinds of the client. 289 CompletionItemKindBitset SupportedCompletionItemKinds; 290 // Whether the client supports CompletionItem.labelDetails. 291 bool SupportsCompletionLabelDetails = false; 292 /// Whether the client supports CodeAction response objects. 293 bool SupportsCodeAction = false; 294 /// From capabilities of textDocument/documentSymbol. 295 bool SupportsHierarchicalDocumentSymbol = false; 296 /// Whether the client supports showing file status. 297 bool SupportFileStatus = false; 298 /// Whether the client supports attaching a container string to references. 299 bool SupportsReferenceContainer = false; 300 /// Which kind of markup should we use in textDocument/hover responses. 301 MarkupKind HoverContentFormat = MarkupKind::PlainText; 302 /// Whether the client supports offsets for parameter info labels. 303 bool SupportsOffsetsInSignatureHelp = false; 304 /// Whether the client supports the versioned document changes. 305 bool SupportsDocumentChanges = false; 306 /// Whether the client supports change annotations on text edits. 307 bool SupportsChangeAnnotation = false; 308 309 std::mutex BackgroundIndexProgressMutex; 310 enum class BackgroundIndexProgress { 311 // Client doesn't support reporting progress. No transitions possible. 312 Unsupported, 313 // The queue is idle, and the client has no progress bar. 314 // Can transition to Creating when we have some activity. 315 Empty, 316 // We've requested the client to create a progress bar. 317 // Meanwhile, the state is buffered in PendingBackgroundIndexProgress. 318 Creating, 319 // The client has a progress bar, and we can send it updates immediately. 320 Live, 321 } BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported; 322 // The progress to send when the progress bar is created. 323 // Only valid in state Creating. 324 BackgroundQueue::Stats PendingBackgroundIndexProgress; 325 /// LSP extension: skip WorkDoneProgressCreate, just send progress streams. 326 bool BackgroundIndexSkipCreate = false; 327 328 Options Opts; 329 // The CDB is created by the "initialize" LSP method. 330 std::unique_ptr<GlobalCompilationDatabase> BaseCDB; 331 // CDB is BaseCDB plus any commands overridden via LSP extensions. 332 std::optional<OverlayCDB> CDB; 333 // The ClangdServer is created by the "initialize" LSP method. 334 std::optional<ClangdServer> Server; 335 // Manages to build module files. 336 std::optional<ModulesBuilder> ModulesManager; 337 }; 338 } // namespace clangd 339 } // namespace clang 340 341 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H 342