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 "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
31namespace clang {
32namespace 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).
39class ClangdLSPServer : private ClangdServer::Callbacks,
40 private LSPBinder::RawOutgoing {
41public:
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
82private:
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 onSwitchSourceHeader(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

source code of clang-tools-extra/clangd/ClangdLSPServer.h