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 "DraftStore.h" 14 #include "Features.inc" 15 #include "FindSymbols.h" 16 #include "GlobalCompilationDatabase.h" 17 #include "Protocol.h" 18 #include "Transport.h" 19 #include "support/Context.h" 20 #include "support/MemoryTree.h" 21 #include "support/Path.h" 22 #include "support/Threading.h" 23 #include "clang/Tooling/Core/Replacement.h" 24 #include "llvm/ADT/Optional.h" 25 #include "llvm/ADT/StringSet.h" 26 #include "llvm/Support/JSON.h" 27 #include <chrono> 28 #include <cstddef> 29 #include <memory> 30 31 namespace clang { 32 namespace clangd { 33 34 class SymbolIndex; 35 36 /// This class exposes ClangdServer's capabilities via Language Server Protocol. 37 /// 38 /// MessageHandler binds the implemented LSP methods (e.g. onInitialize) to 39 /// corresponding JSON-RPC methods ("initialize"). 40 /// The server also supports $/cancelRequest (MessageHandler provides this). 41 class ClangdLSPServer : private ClangdServer::Callbacks { 42 public: 43 struct Options : ClangdServer::Options { 44 /// Supplies configuration (overrides ClangdServer::ContextProvider). 45 config::Provider *ConfigProvider = nullptr; 46 /// Look for compilation databases, rather than using compile commands 47 /// set via LSP (extensions) only. 48 bool UseDirBasedCDB = true; 49 /// A fixed directory to search for a compilation database in. 50 /// If not set, we search upward from the source file. 51 llvm::Optional<Path> CompileCommandsDir; 52 /// The offset-encoding to use, or None to negotiate it over LSP. 53 llvm::Optional<OffsetEncoding> Encoding; 54 /// If set, periodically called to release memory. 55 /// Consider malloc_trim(3) 56 std::function<void()> MemoryCleanup = nullptr; 57 58 /// Per-feature options. Generally ClangdServer lets these vary 59 /// per-request, but LSP allows limited/no customizations. 60 clangd::CodeCompleteOptions CodeComplete; 61 clangd::RenameOptions Rename; 62 /// Returns true if the tweak should be enabled. 63 std::function<bool(const Tweak &)> TweakFilter = [](const Tweak &T) { 64 return !T.hidden(); // only enable non-hidden tweaks. 65 }; 66 }; 67 68 ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS, 69 const ClangdLSPServer::Options &Opts); 70 /// The destructor blocks on any outstanding background tasks. 71 ~ClangdLSPServer(); 72 73 /// Run LSP server loop, communicating with the Transport provided in the 74 /// constructor. This method must not be executed more than once. 75 /// 76 /// \return Whether we shut down cleanly with a 'shutdown' -> 'exit' sequence. 77 bool run(); 78 79 /// Profiles resource-usage. 80 void profile(MemoryTree &MT) const; 81 82 private: 83 // Implement ClangdServer::Callbacks. 84 void onDiagnosticsReady(PathRef File, llvm::StringRef Version, 85 std::vector<Diag> Diagnostics) override; 86 void onFileUpdated(PathRef File, const TUStatus &Status) override; 87 void 88 onHighlightingsReady(PathRef File, llvm::StringRef Version, 89 std::vector<HighlightingToken> Highlightings) override; 90 void onBackgroundIndexProgress(const BackgroundQueue::Stats &Stats) override; 91 92 // LSP methods. Notifications have signature void(const Params&). 93 // Calls have signature void(const Params&, Callback<Response>). 94 void onInitialize(const InitializeParams &, Callback<llvm::json::Value>); 95 void onInitialized(const InitializedParams &); 96 void onShutdown(Callback<std::nullptr_t>); 97 void onSync(Callback<std::nullptr_t>); 98 void onDocumentDidOpen(const DidOpenTextDocumentParams &); 99 void onDocumentDidChange(const DidChangeTextDocumentParams &); 100 void onDocumentDidClose(const DidCloseTextDocumentParams &); 101 void onDocumentDidSave(const DidSaveTextDocumentParams &); 102 void onAST(const ASTParams &, Callback<llvm::Optional<ASTNode>>); 103 void onDocumentOnTypeFormatting(const DocumentOnTypeFormattingParams &, 104 Callback<std::vector<TextEdit>>); 105 void onDocumentRangeFormatting(const DocumentRangeFormattingParams &, 106 Callback<std::vector<TextEdit>>); 107 void onDocumentFormatting(const DocumentFormattingParams &, 108 Callback<std::vector<TextEdit>>); 109 // The results are serialized 'vector<DocumentSymbol>' if 110 // SupportsHierarchicalDocumentSymbol is true and 'vector<SymbolInformation>' 111 // otherwise. 112 void onDocumentSymbol(const DocumentSymbolParams &, 113 Callback<llvm::json::Value>); 114 void onFoldingRange(const FoldingRangeParams &, 115 Callback<std::vector<FoldingRange>>); 116 void onCodeAction(const CodeActionParams &, Callback<llvm::json::Value>); 117 void onCompletion(const CompletionParams &, Callback<CompletionList>); 118 void onSignatureHelp(const TextDocumentPositionParams &, 119 Callback<SignatureHelp>); 120 void onGoToDeclaration(const TextDocumentPositionParams &, 121 Callback<std::vector<Location>>); 122 void onGoToDefinition(const TextDocumentPositionParams &, 123 Callback<std::vector<Location>>); 124 void onGoToImplementation(const TextDocumentPositionParams &, 125 Callback<std::vector<Location>>); 126 void onReference(const ReferenceParams &, Callback<std::vector<Location>>); 127 void onSwitchSourceHeader(const TextDocumentIdentifier &, 128 Callback<llvm::Optional<URIForFile>>); 129 void onDocumentHighlight(const TextDocumentPositionParams &, 130 Callback<std::vector<DocumentHighlight>>); 131 void onFileEvent(const DidChangeWatchedFilesParams &); 132 void onCommand(const ExecuteCommandParams &, Callback<llvm::json::Value>); 133 void onWorkspaceSymbol(const WorkspaceSymbolParams &, 134 Callback<std::vector<SymbolInformation>>); 135 void onPrepareRename(const TextDocumentPositionParams &, 136 Callback<llvm::Optional<Range>>); 137 void onRename(const RenameParams &, Callback<WorkspaceEdit>); 138 void onHover(const TextDocumentPositionParams &, 139 Callback<llvm::Optional<Hover>>); 140 void onTypeHierarchy(const TypeHierarchyParams &, 141 Callback<llvm::Optional<TypeHierarchyItem>>); 142 void onResolveTypeHierarchy(const ResolveTypeHierarchyItemParams &, 143 Callback<llvm::Optional<TypeHierarchyItem>>); 144 void onPrepareCallHierarchy(const CallHierarchyPrepareParams &, 145 Callback<std::vector<CallHierarchyItem>>); 146 void onCallHierarchyIncomingCalls( 147 const CallHierarchyIncomingCallsParams &, 148 Callback<std::vector<CallHierarchyIncomingCall>>); 149 void onCallHierarchyOutgoingCalls( 150 const CallHierarchyOutgoingCallsParams &, 151 Callback<std::vector<CallHierarchyOutgoingCall>>); 152 void onChangeConfiguration(const DidChangeConfigurationParams &); 153 void onSymbolInfo(const TextDocumentPositionParams &, 154 Callback<std::vector<SymbolDetails>>); 155 void onSelectionRange(const SelectionRangeParams &, 156 Callback<std::vector<SelectionRange>>); 157 void onDocumentLink(const DocumentLinkParams &, 158 Callback<std::vector<DocumentLink>>); 159 void onSemanticTokens(const SemanticTokensParams &, Callback<SemanticTokens>); 160 void onSemanticTokensDelta(const SemanticTokensDeltaParams &, 161 Callback<SemanticTokensOrDelta>); 162 /// This is a clangd extension. Provides a json tree representing memory usage 163 /// hierarchy. 164 void onMemoryUsage(Callback<MemoryTree>); 165 166 std::vector<Fix> getFixes(StringRef File, const clangd::Diagnostic &D); 167 168 /// Checks if completion request should be ignored. We need this due to the 169 /// limitation of the LSP. Per LSP, a client sends requests for all "trigger 170 /// character" we specify, but for '>' and ':' we need to check they actually 171 /// produce '->' and '::', respectively. 172 bool shouldRunCompletion(const CompletionParams &Params) const; 173 174 /// Requests a reparse of currently opened files using their latest source. 175 /// This will typically only rebuild if something other than the source has 176 /// changed (e.g. the CDB yields different flags, or files included in the 177 /// preamble have been modified). 178 void reparseOpenFilesIfNeeded( 179 llvm::function_ref<bool(llvm::StringRef File)> Filter); 180 void applyConfiguration(const ConfigurationSettings &Settings); 181 182 /// Sends a "publishSemanticHighlighting" notification to the LSP client. 183 void 184 publishTheiaSemanticHighlighting(const TheiaSemanticHighlightingParams &); 185 186 /// Sends a "publishDiagnostics" notification to the LSP client. 187 void publishDiagnostics(const PublishDiagnosticsParams &); 188 189 /// Runs profiling and exports memory usage metrics if tracing is enabled and 190 /// profiling hasn't happened recently. 191 void maybeExportMemoryProfile(); 192 PeriodicThrottler ShouldProfile; 193 194 /// Run the MemoryCleanup callback if it's time. 195 /// This method is thread safe. 196 void maybeCleanupMemory(); 197 PeriodicThrottler ShouldCleanupMemory; 198 199 /// Since initialization of CDBs and ClangdServer is done lazily, the 200 /// following context captures the one used while creating ClangdLSPServer and 201 /// passes it to above mentioned object instances to make sure they share the 202 /// same state. 203 Context BackgroundContext; 204 205 /// Used to indicate that the 'shutdown' request was received from the 206 /// Language Server client. 207 bool ShutdownRequestReceived = false; 208 209 /// Used to indicate the ClangdLSPServer is being destroyed. 210 std::atomic<bool> IsBeingDestroyed = {false}; 211 212 std::mutex FixItsMutex; 213 typedef std::map<clangd::Diagnostic, std::vector<Fix>, LSPDiagnosticCompare> 214 DiagnosticToReplacementMap; 215 /// Caches FixIts per file and diagnostics 216 llvm::StringMap<DiagnosticToReplacementMap> FixItsMap; 217 std::mutex HighlightingsMutex; 218 llvm::StringMap<std::vector<HighlightingToken>> FileToHighlightings; 219 // Last semantic-tokens response, for incremental requests. 220 std::mutex SemanticTokensMutex; 221 llvm::StringMap<SemanticTokens> LastSemanticTokens; 222 223 // Most code should not deal with Transport directly. 224 // MessageHandler deals with incoming messages, use call() etc for outgoing. 225 clangd::Transport &Transp; 226 class MessageHandler; 227 std::unique_ptr<MessageHandler> MsgHandler; 228 std::mutex TranspWriter; 229 230 template <typename T> parse(const llvm::json::Value & Raw,llvm::StringRef PayloadName,llvm::StringRef PayloadKind)231 static Expected<T> parse(const llvm::json::Value &Raw, 232 llvm::StringRef PayloadName, 233 llvm::StringRef PayloadKind) { 234 T Result; 235 llvm::json::Path::Root Root; 236 if (!fromJSON(Raw, Result, Root)) { 237 elog("Failed to decode {0} {1}: {2}", PayloadName, PayloadKind, 238 Root.getError()); 239 // Dump the relevant parts of the broken message. 240 std::string Context; 241 llvm::raw_string_ostream OS(Context); 242 Root.printErrorContext(Raw, OS); 243 vlog("{0}", OS.str()); 244 // Report the error (e.g. to the client). 245 return llvm::make_error<LSPError>( 246 llvm::formatv("failed to decode {0} {1}: {2}", PayloadName, 247 PayloadKind, fmt_consume(Root.getError())), 248 ErrorCode::InvalidParams); 249 } 250 return std::move(Result); 251 } 252 253 template <typename Response> call(StringRef Method,llvm::json::Value Params,Callback<Response> CB)254 void call(StringRef Method, llvm::json::Value Params, Callback<Response> CB) { 255 // Wrap the callback with LSP conversion and error-handling. 256 auto HandleReply = 257 [CB = std::move(CB), Ctx = Context::current().clone(), 258 Method = Method.str()]( 259 llvm::Expected<llvm::json::Value> RawResponse) mutable { 260 if (!RawResponse) 261 return CB(RawResponse.takeError()); 262 CB(parse<Response>(*RawResponse, Method, "response")); 263 }; 264 callRaw(Method, std::move(Params), std::move(HandleReply)); 265 } 266 void callRaw(StringRef Method, llvm::json::Value Params, 267 Callback<llvm::json::Value> CB); 268 void notify(StringRef Method, llvm::json::Value Params); progress(const llvm::json::Value & Token,T Value)269 template <typename T> void progress(const llvm::json::Value &Token, T Value) { 270 ProgressParams<T> Params; 271 Params.token = Token; 272 Params.value = std::move(Value); 273 notify("$/progress", Params); 274 } 275 276 const ThreadsafeFS &TFS; 277 /// Options used for diagnostics. 278 ClangdDiagnosticOptions DiagOpts; 279 /// The supported kinds of the client. 280 SymbolKindBitset SupportedSymbolKinds; 281 /// The supported completion item kinds of the client. 282 CompletionItemKindBitset SupportedCompletionItemKinds; 283 /// Whether the client supports CodeAction response objects. 284 bool SupportsCodeAction = false; 285 /// From capabilities of textDocument/documentSymbol. 286 bool SupportsHierarchicalDocumentSymbol = false; 287 /// Whether the client supports showing file status. 288 bool SupportFileStatus = false; 289 /// Which kind of markup should we use in textDocument/hover responses. 290 MarkupKind HoverContentFormat = MarkupKind::PlainText; 291 /// Whether the client supports offsets for parameter info labels. 292 bool SupportsOffsetsInSignatureHelp = false; 293 std::mutex BackgroundIndexProgressMutex; 294 enum class BackgroundIndexProgress { 295 // Client doesn't support reporting progress. No transitions possible. 296 Unsupported, 297 // The queue is idle, and the client has no progress bar. 298 // Can transition to Creating when we have some activity. 299 Empty, 300 // We've requested the client to create a progress bar. 301 // Meanwhile, the state is buffered in PendingBackgroundIndexProgress. 302 Creating, 303 // The client has a progress bar, and we can send it updates immediately. 304 Live, 305 } BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported; 306 // The progress to send when the progress bar is created. 307 // Only valid in state Creating. 308 BackgroundQueue::Stats PendingBackgroundIndexProgress; 309 /// LSP extension: skip WorkDoneProgressCreate, just send progress streams. 310 bool BackgroundIndexSkipCreate = false; 311 // Store of the current versions of the open documents. 312 DraftStore DraftMgr; 313 314 Options Opts; 315 // The CDB is created by the "initialize" LSP method. 316 std::unique_ptr<GlobalCompilationDatabase> BaseCDB; 317 // CDB is BaseCDB plus any commands overridden via LSP extensions. 318 llvm::Optional<OverlayCDB> CDB; 319 // The ClangdServer is created by the "initialize" LSP method. 320 llvm::Optional<ClangdServer> Server; 321 }; 322 } // namespace clangd 323 } // namespace clang 324 325 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H 326