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