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