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