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 | /// Flag to hint the experimental modules support is enabled. |
68 | bool EnableExperimentalModulesSupport = false; |
69 | }; |
70 | |
71 | ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS, |
72 | const ClangdLSPServer::Options &Opts); |
73 | /// The destructor blocks on any outstanding background tasks. |
74 | ~ClangdLSPServer(); |
75 | |
76 | ClangdLSPServer(const ClangdLSPServer &other) = delete; |
77 | ClangdLSPServer &operator=(const ClangdLSPServer &other) = delete; |
78 | |
79 | /// Run LSP server loop, communicating with the Transport provided in the |
80 | /// constructor. This method must not be executed more than once. |
81 | /// |
82 | /// \return Whether we shut down cleanly with a 'shutdown' -> 'exit' sequence. |
83 | bool run(); |
84 | |
85 | /// Profiles resource-usage. |
86 | void profile(MemoryTree &MT) const; |
87 | |
88 | private: |
89 | // Implement ClangdServer::Callbacks. |
90 | void onDiagnosticsReady(PathRef File, llvm::StringRef Version, |
91 | llvm::ArrayRef<Diag> Diagnostics) override; |
92 | void onFileUpdated(PathRef File, const TUStatus &Status) override; |
93 | void onBackgroundIndexProgress(const BackgroundQueue::Stats &Stats) override; |
94 | void onSemanticsMaybeChanged(PathRef File) override; |
95 | void onInactiveRegionsReady(PathRef File, |
96 | std::vector<Range> InactiveRegions) override; |
97 | |
98 | // LSP methods. Notifications have signature void(const Params&). |
99 | // Calls have signature void(const Params&, Callback<Response>). |
100 | void onInitialize(const InitializeParams &, Callback<llvm::json::Value>); |
101 | void onInitialized(const InitializedParams &); |
102 | void onShutdown(const NoParams &, Callback<std::nullptr_t>); |
103 | void onSync(const NoParams &, Callback<std::nullptr_t>); |
104 | void onDocumentDidOpen(const DidOpenTextDocumentParams &); |
105 | void onDocumentDidChange(const DidChangeTextDocumentParams &); |
106 | void onDocumentDidClose(const DidCloseTextDocumentParams &); |
107 | void onDocumentDidSave(const DidSaveTextDocumentParams &); |
108 | void onAST(const ASTParams &, Callback<std::optional<ASTNode>>); |
109 | void onDocumentOnTypeFormatting(const DocumentOnTypeFormattingParams &, |
110 | Callback<std::vector<TextEdit>>); |
111 | void onDocumentRangeFormatting(const DocumentRangeFormattingParams &, |
112 | Callback<std::vector<TextEdit>>); |
113 | void onDocumentFormatting(const DocumentFormattingParams &, |
114 | Callback<std::vector<TextEdit>>); |
115 | // The results are serialized 'vector<DocumentSymbol>' if |
116 | // SupportsHierarchicalDocumentSymbol is true and 'vector<SymbolInformation>' |
117 | // otherwise. |
118 | void onDocumentSymbol(const DocumentSymbolParams &, |
119 | Callback<llvm::json::Value>); |
120 | void onFoldingRange(const FoldingRangeParams &, |
121 | Callback<std::vector<FoldingRange>>); |
122 | void onCodeAction(const CodeActionParams &, Callback<llvm::json::Value>); |
123 | void onCompletion(const CompletionParams &, Callback<CompletionList>); |
124 | void onSignatureHelp(const TextDocumentPositionParams &, |
125 | Callback<SignatureHelp>); |
126 | void onGoToDeclaration(const TextDocumentPositionParams &, |
127 | Callback<std::vector<Location>>); |
128 | void onGoToDefinition(const TextDocumentPositionParams &, |
129 | Callback<std::vector<Location>>); |
130 | void onGoToType(const TextDocumentPositionParams &, |
131 | Callback<std::vector<Location>>); |
132 | void onGoToImplementation(const TextDocumentPositionParams &, |
133 | Callback<std::vector<Location>>); |
134 | void onReference(const ReferenceParams &, Callback<std::vector<ReferenceLocation>>); |
135 | void (const TextDocumentIdentifier &, |
136 | Callback<std::optional<URIForFile>>); |
137 | void onDocumentHighlight(const TextDocumentPositionParams &, |
138 | Callback<std::vector<DocumentHighlight>>); |
139 | void onFileEvent(const DidChangeWatchedFilesParams &); |
140 | void onWorkspaceSymbol(const WorkspaceSymbolParams &, |
141 | Callback<std::vector<SymbolInformation>>); |
142 | void onPrepareRename(const TextDocumentPositionParams &, |
143 | Callback<PrepareRenameResult>); |
144 | void onRename(const RenameParams &, Callback<WorkspaceEdit>); |
145 | void onHover(const TextDocumentPositionParams &, |
146 | Callback<std::optional<Hover>>); |
147 | void onPrepareTypeHierarchy(const TypeHierarchyPrepareParams &, |
148 | Callback<std::vector<TypeHierarchyItem>>); |
149 | void onSuperTypes(const ResolveTypeHierarchyItemParams &, |
150 | Callback<std::optional<std::vector<TypeHierarchyItem>>>); |
151 | void onSubTypes(const ResolveTypeHierarchyItemParams &, |
152 | Callback<std::vector<TypeHierarchyItem>>); |
153 | void onTypeHierarchy(const TypeHierarchyPrepareParams &, |
154 | Callback<llvm::json::Value>); |
155 | void onResolveTypeHierarchy(const ResolveTypeHierarchyItemParams &, |
156 | Callback<llvm::json::Value>); |
157 | void onPrepareCallHierarchy(const CallHierarchyPrepareParams &, |
158 | Callback<std::vector<CallHierarchyItem>>); |
159 | void onCallHierarchyIncomingCalls( |
160 | const CallHierarchyIncomingCallsParams &, |
161 | Callback<std::vector<CallHierarchyIncomingCall>>); |
162 | void onCallHierarchyOutgoingCalls( |
163 | const CallHierarchyOutgoingCallsParams &, |
164 | Callback<std::vector<CallHierarchyOutgoingCall>>); |
165 | void onClangdInlayHints(const InlayHintsParams &, |
166 | Callback<llvm::json::Value>); |
167 | void onInlayHint(const InlayHintsParams &, Callback<std::vector<InlayHint>>); |
168 | void onChangeConfiguration(const DidChangeConfigurationParams &); |
169 | void onSymbolInfo(const TextDocumentPositionParams &, |
170 | Callback<std::vector<SymbolDetails>>); |
171 | void onSelectionRange(const SelectionRangeParams &, |
172 | Callback<std::vector<SelectionRange>>); |
173 | void onDocumentLink(const DocumentLinkParams &, |
174 | Callback<std::vector<DocumentLink>>); |
175 | void onSemanticTokens(const SemanticTokensParams &, Callback<SemanticTokens>); |
176 | void onSemanticTokensDelta(const SemanticTokensDeltaParams &, |
177 | Callback<SemanticTokensOrDelta>); |
178 | /// This is a clangd extension. Provides a json tree representing memory usage |
179 | /// hierarchy. |
180 | void onMemoryUsage(const NoParams &, Callback<MemoryTree>); |
181 | void onCommand(const ExecuteCommandParams &, Callback<llvm::json::Value>); |
182 | |
183 | /// Implement commands. |
184 | void onCommandApplyEdit(const WorkspaceEdit &, Callback<llvm::json::Value>); |
185 | void onCommandApplyTweak(const TweakArgs &, Callback<llvm::json::Value>); |
186 | void onCommandApplyRename(const RenameParams &, Callback<llvm::json::Value>); |
187 | |
188 | /// Outgoing LSP calls. |
189 | LSPBinder::OutgoingMethod<ApplyWorkspaceEditParams, |
190 | ApplyWorkspaceEditResponse> |
191 | ApplyWorkspaceEdit; |
192 | LSPBinder::OutgoingNotification<ShowMessageParams> ShowMessage; |
193 | LSPBinder::OutgoingNotification<PublishDiagnosticsParams> PublishDiagnostics; |
194 | LSPBinder::OutgoingNotification<FileStatus> NotifyFileStatus; |
195 | LSPBinder::OutgoingNotification<InactiveRegionsParams> PublishInactiveRegions; |
196 | LSPBinder::OutgoingMethod<WorkDoneProgressCreateParams, std::nullptr_t> |
197 | CreateWorkDoneProgress; |
198 | LSPBinder::OutgoingNotification<ProgressParams<WorkDoneProgressBegin>> |
199 | BeginWorkDoneProgress; |
200 | LSPBinder::OutgoingNotification<ProgressParams<WorkDoneProgressReport>> |
201 | ReportWorkDoneProgress; |
202 | LSPBinder::OutgoingNotification<ProgressParams<WorkDoneProgressEnd>> |
203 | EndWorkDoneProgress; |
204 | LSPBinder::OutgoingMethod<NoParams, std::nullptr_t> SemanticTokensRefresh; |
205 | |
206 | void applyEdit(WorkspaceEdit WE, llvm::json::Value Success, |
207 | Callback<llvm::json::Value> Reply); |
208 | |
209 | void bindMethods(LSPBinder &, const ClientCapabilities &Caps); |
210 | std::optional<ClangdServer::DiagRef> getDiagRef(StringRef File, |
211 | const clangd::Diagnostic &D); |
212 | |
213 | /// Checks if completion request should be ignored. We need this due to the |
214 | /// limitation of the LSP. Per LSP, a client sends requests for all "trigger |
215 | /// character" we specify, but for '>' and ':' we need to check they actually |
216 | /// produce '->' and '::', respectively. |
217 | bool shouldRunCompletion(const CompletionParams &Params) const; |
218 | |
219 | void applyConfiguration(const ConfigurationSettings &Settings); |
220 | |
221 | /// Runs profiling and exports memory usage metrics if tracing is enabled and |
222 | /// profiling hasn't happened recently. |
223 | void maybeExportMemoryProfile(); |
224 | PeriodicThrottler ShouldProfile; |
225 | |
226 | /// Run the MemoryCleanup callback if it's time. |
227 | /// This method is thread safe. |
228 | void maybeCleanupMemory(); |
229 | PeriodicThrottler ShouldCleanupMemory; |
230 | |
231 | /// Since initialization of CDBs and ClangdServer is done lazily, the |
232 | /// following context captures the one used while creating ClangdLSPServer and |
233 | /// passes it to above mentioned object instances to make sure they share the |
234 | /// same state. |
235 | Context BackgroundContext; |
236 | |
237 | /// Used to indicate that the 'shutdown' request was received from the |
238 | /// Language Server client. |
239 | bool ShutdownRequestReceived = false; |
240 | |
241 | /// Used to indicate the ClangdLSPServer is being destroyed. |
242 | std::atomic<bool> IsBeingDestroyed = {false}; |
243 | |
244 | // FIXME: The caching is a temporary solution to get corresponding clangd |
245 | // diagnostic from a LSP diagnostic. |
246 | // Ideally, ClangdServer can generate an identifier for each diagnostic, |
247 | // emit them via the LSP's data field (which was newly added in LSP 3.16). |
248 | std::mutex DiagRefMutex; |
249 | struct DiagKey { |
250 | clangd::Range Rng; |
251 | std::string Message; |
252 | bool operator<(const DiagKey &Other) const { |
253 | return std::tie(args: Rng, args: Message) < std::tie(args: Other.Rng, args: Other.Message); |
254 | } |
255 | }; |
256 | DiagKey toDiagKey(const clangd::Diagnostic &LSPDiag) { |
257 | return {.Rng: LSPDiag.range, .Message: LSPDiag.message}; |
258 | } |
259 | /// A map from LSP diagnostic to clangd-naive diagnostic. |
260 | typedef std::map<DiagKey, ClangdServer::DiagRef> |
261 | DiagnosticToDiagRefMap; |
262 | /// Caches the mapping LSP and clangd-naive diagnostics per file. |
263 | llvm::StringMap<DiagnosticToDiagRefMap> |
264 | DiagRefMap; |
265 | |
266 | // Last semantic-tokens response, for incremental requests. |
267 | std::mutex SemanticTokensMutex; |
268 | llvm::StringMap<SemanticTokens> LastSemanticTokens; |
269 | |
270 | // Most code should not deal with Transport, callMethod, notify directly. |
271 | // Use LSPBinder to handle incoming and outgoing calls. |
272 | clangd::Transport &Transp; |
273 | class MessageHandler; |
274 | std::unique_ptr<MessageHandler> MsgHandler; |
275 | std::mutex TranspWriter; |
276 | |
277 | void callMethod(StringRef Method, llvm::json::Value Params, |
278 | Callback<llvm::json::Value> CB) override; |
279 | void notify(StringRef Method, llvm::json::Value Params) override; |
280 | |
281 | LSPBinder::RawHandlers Handlers; |
282 | |
283 | const ThreadsafeFS &TFS; |
284 | /// Options used for diagnostics. |
285 | ClangdDiagnosticOptions DiagOpts; |
286 | /// The supported kinds of the client. |
287 | SymbolKindBitset SupportedSymbolKinds; |
288 | /// The supported completion item kinds of the client. |
289 | CompletionItemKindBitset SupportedCompletionItemKinds; |
290 | // Whether the client supports CompletionItem.labelDetails. |
291 | bool SupportsCompletionLabelDetails = false; |
292 | /// Whether the client supports CodeAction response objects. |
293 | bool SupportsCodeAction = false; |
294 | /// From capabilities of textDocument/documentSymbol. |
295 | bool SupportsHierarchicalDocumentSymbol = false; |
296 | /// Whether the client supports showing file status. |
297 | bool SupportFileStatus = false; |
298 | /// Whether the client supports attaching a container string to references. |
299 | bool SupportsReferenceContainer = false; |
300 | /// Which kind of markup should we use in textDocument/hover responses. |
301 | MarkupKind HoverContentFormat = MarkupKind::PlainText; |
302 | /// Whether the client supports offsets for parameter info labels. |
303 | bool SupportsOffsetsInSignatureHelp = false; |
304 | /// Whether the client supports the versioned document changes. |
305 | bool SupportsDocumentChanges = false; |
306 | /// Whether the client supports change annotations on text edits. |
307 | bool SupportsChangeAnnotation = false; |
308 | |
309 | std::mutex BackgroundIndexProgressMutex; |
310 | enum class BackgroundIndexProgress { |
311 | // Client doesn't support reporting progress. No transitions possible. |
312 | Unsupported, |
313 | // The queue is idle, and the client has no progress bar. |
314 | // Can transition to Creating when we have some activity. |
315 | Empty, |
316 | // We've requested the client to create a progress bar. |
317 | // Meanwhile, the state is buffered in PendingBackgroundIndexProgress. |
318 | Creating, |
319 | // The client has a progress bar, and we can send it updates immediately. |
320 | Live, |
321 | } BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported; |
322 | // The progress to send when the progress bar is created. |
323 | // Only valid in state Creating. |
324 | BackgroundQueue::Stats PendingBackgroundIndexProgress; |
325 | /// LSP extension: skip WorkDoneProgressCreate, just send progress streams. |
326 | bool BackgroundIndexSkipCreate = false; |
327 | |
328 | Options Opts; |
329 | // The CDB is created by the "initialize" LSP method. |
330 | std::unique_ptr<GlobalCompilationDatabase> BaseCDB; |
331 | // CDB is BaseCDB plus any commands overridden via LSP extensions. |
332 | std::optional<OverlayCDB> CDB; |
333 | // The ClangdServer is created by the "initialize" LSP method. |
334 | std::optional<ClangdServer> Server; |
335 | // Manages to build module files. |
336 | std::optional<ModulesBuilder> ModulesManager; |
337 | }; |
338 | } // namespace clangd |
339 | } // namespace clang |
340 | |
341 | #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H |
342 | |