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