clang-tools 22.0.0git
ClangdLSPServer.cpp
Go to the documentation of this file.
1//===--- ClangdLSPServer.cpp - 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#include "ClangdLSPServer.h"
10#include "ClangdServer.h"
11#include "CodeComplete.h"
12#include "CompileCommands.h"
13#include "Diagnostics.h"
14#include "Feature.h"
16#include "LSPBinder.h"
17#include "ModulesBuilder.h"
18#include "Protocol.h"
20#include "SourceCode.h"
21#include "TUScheduler.h"
22#include "URI.h"
23#include "refactor/Tweak.h"
25#include "support/Context.h"
26#include "support/MemoryTree.h"
27#include "support/Trace.h"
28#include "clang/Tooling/Core/Replacement.h"
29#include "llvm/ADT/ArrayRef.h"
30#include "llvm/ADT/FunctionExtras.h"
31#include "llvm/ADT/ScopeExit.h"
32#include "llvm/ADT/StringRef.h"
33#include "llvm/ADT/Twine.h"
34#include "llvm/Support/Allocator.h"
35#include "llvm/Support/Error.h"
36#include "llvm/Support/FormatVariadic.h"
37#include "llvm/Support/JSON.h"
38#include "llvm/Support/SHA1.h"
39#include "llvm/Support/ScopedPrinter.h"
40#include "llvm/Support/raw_ostream.h"
41#include <chrono>
42#include <cstddef>
43#include <cstdint>
44#include <functional>
45#include <map>
46#include <memory>
47#include <mutex>
48#include <optional>
49#include <string>
50#include <utility>
51#include <vector>
52
53namespace clang {
54namespace clangd {
55
56namespace {
57// Tracks end-to-end latency of high level lsp calls. Measurements are in
58// seconds.
59constexpr trace::Metric LSPLatency("lsp_latency", trace::Metric::Distribution,
60 "method_name");
61
62// LSP defines file versions as numbers that increase.
63// ClangdServer treats them as opaque and therefore uses strings instead.
64std::string encodeVersion(std::optional<int64_t> LSPVersion) {
65 return LSPVersion ? llvm::to_string(*LSPVersion) : "";
66}
67std::optional<int64_t> decodeVersion(llvm::StringRef Encoded) {
68 int64_t Result;
69 if (llvm::to_integer(Encoded, Result, 10))
70 return Result;
71 if (!Encoded.empty()) // Empty can be e.g. diagnostics on close.
72 elog("unexpected non-numeric version {0}", Encoded);
73 return std::nullopt;
74}
75
76const llvm::StringLiteral ApplyFixCommand = "clangd.applyFix";
77const llvm::StringLiteral ApplyTweakCommand = "clangd.applyTweak";
78const llvm::StringLiteral ApplyRenameCommand = "clangd.applyRename";
79
81 const URIForFile &File) {
82 CodeAction CA;
83 CA.title = R.FixMessage;
84 CA.kind = std::string(CodeAction::QUICKFIX_KIND);
85 CA.command.emplace();
86 CA.command->title = R.FixMessage;
87 CA.command->command = std::string(ApplyRenameCommand);
88 RenameParams Params;
90 Params.position = R.Diag.Range.start;
91 Params.newName = R.NewName;
92 CA.command->argument = Params;
93 return CA;
94}
95
96/// Transforms a tweak into a code action that would apply it if executed.
97/// EXPECTS: T.prepare() was called and returned true.
98CodeAction toCodeAction(const ClangdServer::TweakRef &T, const URIForFile &File,
99 Range Selection) {
100 CodeAction CA;
101 CA.title = T.Title;
102 CA.kind = T.Kind.str();
103 // This tweak may have an expensive second stage, we only run it if the user
104 // actually chooses it in the UI. We reply with a command that would run the
105 // corresponding tweak.
106 // FIXME: for some tweaks, computing the edits is cheap and we could send them
107 // directly.
108 CA.command.emplace();
109 CA.command->title = T.Title;
110 CA.command->command = std::string(ApplyTweakCommand);
111 TweakArgs Args;
112 Args.file = File;
113 Args.tweakID = T.ID;
114 Args.selection = Selection;
115 CA.command->argument = std::move(Args);
116 return CA;
117}
118
119/// Convert from Fix to LSP CodeAction.
120CodeAction toCodeAction(const Fix &F, const URIForFile &File,
121 const std::optional<int64_t> &Version,
122 bool SupportsDocumentChanges,
123 bool SupportChangeAnnotation) {
124 CodeAction Action;
125 Action.title = F.Message;
126 Action.kind = std::string(CodeAction::QUICKFIX_KIND);
127 Action.edit.emplace();
128 if (!SupportsDocumentChanges) {
129 Action.edit->changes.emplace();
130 auto &Changes = (*Action.edit->changes)[File.uri()];
131 for (const auto &E : F.Edits)
132 Changes.push_back({E.range, E.newText, /*annotationId=*/""});
133 } else {
134 Action.edit->documentChanges.emplace();
135 TextDocumentEdit &Edit = Action.edit->documentChanges->emplace_back();
136 Edit.textDocument = VersionedTextDocumentIdentifier{{File}, Version};
137 for (const auto &E : F.Edits)
138 Edit.edits.push_back(
139 {E.range, E.newText,
140 SupportChangeAnnotation ? E.annotationId : ""});
141 if (SupportChangeAnnotation) {
142 for (const auto &[AID, Annotation]: F.Annotations)
143 Action.edit->changeAnnotations[AID] = Annotation;
144 }
145 }
146 return Action;
147}
148
149void adjustSymbolKinds(llvm::MutableArrayRef<DocumentSymbol> Syms,
150 SymbolKindBitset Kinds) {
151 for (auto &S : Syms) {
152 S.kind = adjustKindToCapability(S.kind, Kinds);
153 adjustSymbolKinds(S.children, Kinds);
154 }
155}
156
157SymbolKindBitset defaultSymbolKinds() {
158 SymbolKindBitset Defaults;
159 for (size_t I = SymbolKindMin; I <= static_cast<size_t>(SymbolKind::Array);
160 ++I)
161 Defaults.set(I);
162 return Defaults;
163}
164
165CompletionItemKindBitset defaultCompletionItemKinds() {
167 for (size_t I = CompletionItemKindMin;
168 I <= static_cast<size_t>(CompletionItemKind::Reference); ++I)
169 Defaults.set(I);
170 return Defaults;
171}
172
173// Makes sure edits in \p FE are applicable to latest file contents reported by
174// editor. If not generates an error message containing information about files
175// that needs to be saved.
176llvm::Error validateEdits(const ClangdServer &Server, const FileEdits &FE) {
177 size_t InvalidFileCount = 0;
178 llvm::StringRef LastInvalidFile;
179 for (const auto &It : FE) {
180 if (auto Draft = Server.getDraft(It.first())) {
181 // If the file is open in user's editor, make sure the version we
182 // saw and current version are compatible as this is the text that
183 // will be replaced by editors.
184 if (!It.second.canApplyTo(*Draft)) {
185 ++InvalidFileCount;
186 LastInvalidFile = It.first();
187 }
188 }
189 }
190 if (!InvalidFileCount)
191 return llvm::Error::success();
192 if (InvalidFileCount == 1)
193 return error("File must be saved first: {0}", LastInvalidFile);
194 return error("Files must be saved first: {0} (and {1} others)",
195 LastInvalidFile, InvalidFileCount - 1);
196}
197} // namespace
198
199// MessageHandler dispatches incoming LSP messages.
200// It handles cross-cutting concerns:
201// - serializes/deserializes protocol objects to JSON
202// - logging of inbound messages
203// - cancellation handling
204// - basic call tracing
205// MessageHandler ensures that initialize() is called before any other handler.
207public:
208 MessageHandler(ClangdLSPServer &Server) : Server(Server) {}
209
210 bool onNotify(llvm::StringRef Method, llvm::json::Value Params) override {
211 trace::Span Tracer(Method, LSPLatency);
212 SPAN_ATTACH(Tracer, "Params", Params);
213 WithContext HandlerContext(handlerContext());
214 log("<-- {0}", Method);
215 if (Method == "exit")
216 return false;
217 auto Handler = Server.Handlers.NotificationHandlers.find(Method);
218 if (Handler != Server.Handlers.NotificationHandlers.end()) {
219 Handler->second(std::move(Params));
220 Server.maybeExportMemoryProfile();
221 Server.maybeCleanupMemory();
222 } else if (!Server.Server) {
223 elog("Notification {0} before initialization", Method);
224 } else if (Method == "$/cancelRequest") {
225 onCancel(std::move(Params));
226 } else {
227 log("unhandled notification {0}", Method);
228 }
229 return true;
230 }
231
232 bool onCall(llvm::StringRef Method, llvm::json::Value Params,
233 llvm::json::Value ID) override {
234 WithContext HandlerContext(handlerContext());
235 // Calls can be canceled by the client. Add cancellation context.
236 WithContext WithCancel(cancelableRequestContext(ID));
237 trace::Span Tracer(Method, LSPLatency);
238 SPAN_ATTACH(Tracer, "Params", Params);
239 ReplyOnce Reply(ID, Method, &Server, Tracer.Args);
240 log("<-- {0}({1})", Method, ID);
241 auto Handler = Server.Handlers.MethodHandlers.find(Method);
242 if (Handler != Server.Handlers.MethodHandlers.end()) {
243 Handler->second(std::move(Params), std::move(Reply));
244 } else if (!Server.Server) {
245 elog("Call {0} before initialization.", Method);
246 Reply(llvm::make_error<LSPError>("server not initialized",
248 } else {
249 Reply(llvm::make_error<LSPError>("method not found",
251 }
252 return true;
253 }
254
255 bool onReply(llvm::json::Value ID,
256 llvm::Expected<llvm::json::Value> Result) override {
257 WithContext HandlerContext(handlerContext());
258
259 Callback<llvm::json::Value> ReplyHandler = nullptr;
260 if (auto IntID = ID.getAsInteger()) {
261 std::lock_guard<std::mutex> Mutex(CallMutex);
262 // Find a corresponding callback for the request ID;
263 for (size_t Index = 0; Index < ReplyCallbacks.size(); ++Index) {
264 if (ReplyCallbacks[Index].first == *IntID) {
265 ReplyHandler = std::move(ReplyCallbacks[Index].second);
266 ReplyCallbacks.erase(ReplyCallbacks.begin() +
267 Index); // remove the entry
268 break;
269 }
270 }
271 }
272
273 if (!ReplyHandler) {
274 // No callback being found, use a default log callback.
275 ReplyHandler = [&ID](llvm::Expected<llvm::json::Value> Result) {
276 elog("received a reply with ID {0}, but there was no such call", ID);
277 if (!Result)
278 llvm::consumeError(Result.takeError());
279 };
280 }
281
282 // Log and run the reply handler.
283 if (Result) {
284 log("<-- reply({0})", ID);
285 ReplyHandler(std::move(Result));
286 } else {
287 auto Err = Result.takeError();
288 log("<-- reply({0}) error: {1}", ID, Err);
289 ReplyHandler(std::move(Err));
290 }
291 return true;
292 }
293
294 // Bind a reply callback to a request. The callback will be invoked when
295 // clangd receives the reply from the LSP client.
296 // Return a call id of the request.
297 llvm::json::Value bindReply(Callback<llvm::json::Value> Reply) {
298 std::optional<std::pair<int, Callback<llvm::json::Value>>> OldestCB;
299 int ID;
300 {
301 std::lock_guard<std::mutex> Mutex(CallMutex);
302 ID = NextCallID++;
303 ReplyCallbacks.emplace_back(ID, std::move(Reply));
304
305 // If the queue overflows, we assume that the client didn't reply the
306 // oldest request, and run the corresponding callback which replies an
307 // error to the client.
308 if (ReplyCallbacks.size() > MaxReplayCallbacks) {
309 elog("more than {0} outstanding LSP calls, forgetting about {1}",
310 MaxReplayCallbacks, ReplyCallbacks.front().first);
311 OldestCB = std::move(ReplyCallbacks.front());
312 ReplyCallbacks.pop_front();
313 }
314 }
315 if (OldestCB)
316 OldestCB->second(
317 error("failed to receive a client reply for request ({0})",
318 OldestCB->first));
319 return ID;
320 }
321
322private:
323 // Function object to reply to an LSP call.
324 // Each instance must be called exactly once, otherwise:
325 // - the bug is logged, and (in debug mode) an assert will fire
326 // - if there was no reply, an error reply is sent
327 // - if there were multiple replies, only the first is sent
328 class ReplyOnce {
329 std::atomic<bool> Replied = {false};
330 std::chrono::steady_clock::time_point Start;
331 llvm::json::Value ID;
332 std::string Method;
333 ClangdLSPServer *Server; // Null when moved-from.
334 llvm::json::Object *TraceArgs;
335
336 public:
337 ReplyOnce(const llvm::json::Value &ID, llvm::StringRef Method,
338 ClangdLSPServer *Server, llvm::json::Object *TraceArgs)
339 : Start(std::chrono::steady_clock::now()), ID(ID), Method(Method),
340 Server(Server), TraceArgs(TraceArgs) {
341 assert(Server);
342 }
343 ReplyOnce(ReplyOnce &&Other)
344 : Replied(Other.Replied.load()), Start(Other.Start),
345 ID(std::move(Other.ID)), Method(std::move(Other.Method)),
346 Server(Other.Server), TraceArgs(Other.TraceArgs) {
347 Other.Server = nullptr;
348 }
349 ReplyOnce &operator=(ReplyOnce &&) = delete;
350 ReplyOnce(const ReplyOnce &) = delete;
351 ReplyOnce &operator=(const ReplyOnce &) = delete;
352
353 ~ReplyOnce() {
354 // There's one legitimate reason to never reply to a request: clangd's
355 // request handler send a call to the client (e.g. applyEdit) and the
356 // client never replied. In this case, the ReplyOnce is owned by
357 // ClangdLSPServer's reply callback table and is destroyed along with the
358 // server. We don't attempt to send a reply in this case, there's little
359 // to be gained from doing so.
360 if (Server && !Server->IsBeingDestroyed && !Replied) {
361 elog("No reply to message {0}({1})", Method, ID);
362 assert(false && "must reply to all calls!");
363 (*this)(llvm::make_error<LSPError>("server failed to reply",
365 }
366 }
367
368 void operator()(llvm::Expected<llvm::json::Value> Reply) {
369 assert(Server && "moved-from!");
370 if (Replied.exchange(true)) {
371 elog("Replied twice to message {0}({1})", Method, ID);
372 assert(false && "must reply to each call only once!");
373 return;
374 }
375 auto Duration = std::chrono::steady_clock::now() - Start;
376 if (Reply) {
377 log("--> reply:{0}({1}) {2:ms}", Method, ID, Duration);
378 if (TraceArgs)
379 (*TraceArgs)["Reply"] = *Reply;
380 std::lock_guard<std::mutex> Lock(Server->TranspWriter);
381 Server->Transp.reply(std::move(ID), std::move(Reply));
382 } else {
383 llvm::Error Err = Reply.takeError();
384 log("--> reply:{0}({1}) {2:ms}, error: {3}", Method, ID, Duration, Err);
385 if (TraceArgs)
386 (*TraceArgs)["Error"] = llvm::to_string(Err);
387 std::lock_guard<std::mutex> Lock(Server->TranspWriter);
388 Server->Transp.reply(std::move(ID), std::move(Err));
389 }
390 }
391 };
392
393 // Method calls may be cancelled by ID, so keep track of their state.
394 // This needs a mutex: handlers may finish on a different thread, and that's
395 // when we clean up entries in the map.
396 mutable std::mutex RequestCancelersMutex;
397 llvm::StringMap<std::pair<Canceler, /*Cookie*/ unsigned>> RequestCancelers;
398 unsigned NextRequestCookie = 0; // To disambiguate reused IDs, see below.
399 void onCancel(const llvm::json::Value &Params) {
400 const llvm::json::Value *ID = nullptr;
401 if (auto *O = Params.getAsObject())
402 ID = O->get("id");
403 if (!ID) {
404 elog("Bad cancellation request: {0}", Params);
405 return;
406 }
407 auto StrID = llvm::to_string(*ID);
408 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
409 auto It = RequestCancelers.find(StrID);
410 if (It != RequestCancelers.end())
411 It->second.first(); // Invoke the canceler.
412 }
413
414 Context handlerContext() const {
415 return Context::current().derive(
417 Server.Opts.Encoding.value_or(OffsetEncoding::UTF16));
418 }
419
420 // We run cancelable requests in a context that does two things:
421 // - allows cancellation using RequestCancelers[ID]
422 // - cleans up the entry in RequestCancelers when it's no longer needed
423 // If a client reuses an ID, the last wins and the first cannot be canceled.
424 Context cancelableRequestContext(const llvm::json::Value &ID) {
425 auto Task = cancelableTask(
426 /*Reason=*/static_cast<int>(ErrorCode::RequestCancelled));
427 auto StrID = llvm::to_string(ID); // JSON-serialize ID for map key.
428 auto Cookie = NextRequestCookie++; // No lock, only called on main thread.
429 {
430 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
431 RequestCancelers[StrID] = {std::move(Task.second), Cookie};
432 }
433 // When the request ends, we can clean up the entry we just added.
434 // The cookie lets us check that it hasn't been overwritten due to ID
435 // reuse.
436 return Task.first.derive(llvm::make_scope_exit([this, StrID, Cookie] {
437 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
438 auto It = RequestCancelers.find(StrID);
439 if (It != RequestCancelers.end() && It->second.second == Cookie)
440 RequestCancelers.erase(It);
441 }));
442 }
443
444 // The maximum number of callbacks held in clangd.
445 //
446 // We bound the maximum size to the pending map to prevent memory leakage
447 // for cases where LSP clients don't reply for the request.
448 // This has to go after RequestCancellers and RequestCancellersMutex since it
449 // can contain a callback that has a cancelable context.
450 static constexpr int MaxReplayCallbacks = 100;
451 mutable std::mutex CallMutex;
452 int NextCallID = 0; /* GUARDED_BY(CallMutex) */
453 std::deque<std::pair</*RequestID*/ int,
454 /*ReplyHandler*/ Callback<llvm::json::Value>>>
455 ReplyCallbacks; /* GUARDED_BY(CallMutex) */
456
457 ClangdLSPServer &Server;
458};
459
460// call(), notify(), and reply() wrap the Transport, adding logging and locking.
461void ClangdLSPServer::callMethod(StringRef Method, llvm::json::Value Params,
463 auto ID = MsgHandler->bindReply(std::move(CB));
464 log("--> {0}({1})", Method, ID);
465 std::lock_guard<std::mutex> Lock(TranspWriter);
466 Transp.call(Method, std::move(Params), ID);
467}
468
469void ClangdLSPServer::notify(llvm::StringRef Method, llvm::json::Value Params) {
470 log("--> {0}", Method);
471 maybeCleanupMemory();
472 std::lock_guard<std::mutex> Lock(TranspWriter);
473 Transp.notify(Method, std::move(Params));
474}
475
476static std::vector<llvm::StringRef> semanticTokenTypes() {
477 std::vector<llvm::StringRef> Types;
478 for (unsigned I = 0; I <= static_cast<unsigned>(HighlightingKind::LastKind);
479 ++I)
480 Types.push_back(toSemanticTokenType(static_cast<HighlightingKind>(I)));
481 return Types;
482}
483
484static std::vector<llvm::StringRef> semanticTokenModifiers() {
485 std::vector<llvm::StringRef> Modifiers;
486 for (unsigned I = 0;
487 I <= static_cast<unsigned>(HighlightingModifier::LastModifier); ++I)
488 Modifiers.push_back(
490 return Modifiers;
491}
492
493void ClangdLSPServer::onInitialize(const InitializeParams &Params,
495 // Determine character encoding first as it affects constructed ClangdServer.
496 if (Params.capabilities.PositionEncodings && !Opts.Encoding) {
497 Opts.Encoding = OffsetEncoding::UTF16; // fallback
498 for (OffsetEncoding Supported : *Params.capabilities.PositionEncodings)
499 if (Supported != OffsetEncoding::UnsupportedEncoding) {
500 Opts.Encoding = Supported;
501 break;
502 }
503 }
504
505 if (Params.capabilities.TheiaSemanticHighlighting &&
506 !Params.capabilities.SemanticTokens) {
507 elog("Client requested legacy semanticHighlights notification, which is "
508 "no longer supported. Migrate to standard semanticTokens request");
509 }
510
511 if (Params.rootUri && *Params.rootUri)
512 Opts.WorkspaceRoot = std::string(Params.rootUri->file());
513 else if (Params.rootPath && !Params.rootPath->empty())
514 Opts.WorkspaceRoot = *Params.rootPath;
515 if (Server)
516 return Reply(llvm::make_error<LSPError>("server already initialized",
518
519 Opts.CodeComplete.EnableSnippets = Params.capabilities.CompletionSnippets;
520 Opts.CodeComplete.IncludeFixIts = Params.capabilities.CompletionFixes;
521 if (!Opts.CodeComplete.BundleOverloads)
522 Opts.CodeComplete.BundleOverloads = Params.capabilities.HasSignatureHelp;
523 Opts.CodeComplete.DocumentationFormat =
524 Params.capabilities.CompletionDocumentationFormat;
525 Opts.SignatureHelpDocumentationFormat =
526 Params.capabilities.SignatureHelpDocumentationFormat;
527 DiagOpts.EmbedFixesInDiagnostics = Params.capabilities.DiagnosticFixes;
528 DiagOpts.SendDiagnosticCategory = Params.capabilities.DiagnosticCategory;
529 DiagOpts.EmitRelatedLocations =
530 Params.capabilities.DiagnosticRelatedInformation;
531 if (Params.capabilities.WorkspaceSymbolKinds)
532 SupportedSymbolKinds |= *Params.capabilities.WorkspaceSymbolKinds;
533 if (Params.capabilities.CompletionItemKinds)
534 SupportedCompletionItemKinds |= *Params.capabilities.CompletionItemKinds;
535 SupportsCompletionLabelDetails = Params.capabilities.CompletionLabelDetail;
536 SupportsCodeAction = Params.capabilities.CodeActionStructure;
537 SupportsHierarchicalDocumentSymbol =
538 Params.capabilities.HierarchicalDocumentSymbol;
539 SupportsReferenceContainer = Params.capabilities.ReferenceContainer;
540 SupportFileStatus = Params.initializationOptions.FileStatus;
541 SupportsDocumentChanges = Params.capabilities.DocumentChanges;
542 SupportsChangeAnnotation = Params.capabilities.ChangeAnnotation;
543 HoverContentFormat = Params.capabilities.HoverContentFormat;
544 Opts.LineFoldingOnly = Params.capabilities.LineFoldingOnly;
545 SupportsOffsetsInSignatureHelp = Params.capabilities.OffsetsInSignatureHelp;
546 if (Params.capabilities.WorkDoneProgress)
547 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
548 BackgroundIndexSkipCreate = Params.capabilities.ImplicitProgressCreation;
549 Opts.ImplicitCancellation = !Params.capabilities.CancelsStaleRequests;
550 Opts.PublishInactiveRegions = Params.capabilities.InactiveRegions;
551
552 if (Opts.UseDirBasedCDB) {
553 DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS);
554 if (const auto &Dir = Params.initializationOptions.compilationDatabasePath)
555 CDBOpts.CompileCommandsDir = Dir;
556 CDBOpts.ContextProvider = Opts.ContextProvider;
557 if (Opts.StrongWorkspaceMode)
558 CDBOpts.applyFallbackWorkingDirectory(Opts.WorkspaceRoot);
559 BaseCDB =
560 std::make_unique<DirectoryBasedGlobalCompilationDatabase>(CDBOpts);
561 }
562 auto Mangler = CommandMangler::detect();
563 Mangler.SystemIncludeExtractor =
564 getSystemIncludeExtractor(llvm::ArrayRef(Opts.QueryDriverGlobs));
565 if (Opts.ResourceDir)
566 Mangler.ResourceDir = *Opts.ResourceDir;
567 CDB.emplace(BaseCDB.get(), Params.initializationOptions.fallbackFlags,
568 std::move(Mangler));
569
570 if (Opts.EnableExperimentalModulesSupport) {
571 ModulesManager.emplace(*CDB);
572 Opts.ModulesManager = &*ModulesManager;
573 }
574
575 {
576 // Switch caller's context with LSPServer's background context. Since we
577 // rather want to propagate information from LSPServer's context into the
578 // Server, CDB, etc.
579 WithContext MainContext(BackgroundContext.clone());
580 std::optional<WithContextValue> WithOffsetEncoding;
581 if (Opts.Encoding)
582 WithOffsetEncoding.emplace(kCurrentOffsetEncoding, *Opts.Encoding);
583 Server.emplace(*CDB, TFS, Opts,
584 static_cast<ClangdServer::Callbacks *>(this));
585 }
586
587 llvm::json::Object ServerCaps{
588 {"textDocumentSync",
589 llvm::json::Object{
590 {"openClose", true},
591 {"change", (int)TextDocumentSyncKind::Incremental},
592 {"save", true},
593 }},
594 {"documentFormattingProvider", true},
595 {"documentRangeFormattingProvider",
596 llvm::json::Object{
597 {"rangesSupport", true},
598 }},
599 {"documentOnTypeFormattingProvider",
600 llvm::json::Object{
601 {"firstTriggerCharacter", "\n"},
602 {"moreTriggerCharacter", {}},
603 }},
604 {"completionProvider",
605 llvm::json::Object{
606 // We don't set `(` etc as allCommitCharacters as they interact
607 // poorly with snippet results.
608 // See https://github.com/clangd/vscode-clangd/issues/357
609 // Hopefully we can use them one day without this side-effect:
610 // https://github.com/microsoft/vscode/issues/42544
611 {"resolveProvider", false},
612 // We do extra checks, e.g. that > is part of ->.
613 {"triggerCharacters", {".", "<", ">", ":", "\"", "/", "*"}},
614 }},
615 {"semanticTokensProvider",
616 llvm::json::Object{
617 {"full", llvm::json::Object{{"delta", true}}},
618 {"range", false},
619 {"legend",
620 llvm::json::Object{{"tokenTypes", semanticTokenTypes()},
621 {"tokenModifiers", semanticTokenModifiers()}}},
622 }},
623 {"signatureHelpProvider",
624 llvm::json::Object{
625 {"triggerCharacters", {"(", ")", "{", "}", "<", ">", ","}},
626 }},
627 {"declarationProvider", true},
628 {"definitionProvider", true},
629 {"implementationProvider", true},
630 {"typeDefinitionProvider", true},
631 {"documentHighlightProvider", true},
632 {"documentLinkProvider",
633 llvm::json::Object{
634 {"resolveProvider", false},
635 }},
636 {"hoverProvider", true},
637 {"selectionRangeProvider", true},
638 {"documentSymbolProvider", true},
639 {"workspaceSymbolProvider", true},
640 {"referencesProvider", true},
641 {"astProvider", true}, // clangd extension
642 {"typeHierarchyProvider", true},
643 // Unfortunately our extension made use of the same capability name as the
644 // standard. Advertise this capability to tell clients that implement our
645 // extension we really have support for the standardized one as well.
646 {"standardTypeHierarchyProvider", true}, // clangd extension
647 {"memoryUsageProvider", true}, // clangd extension
648 {"compilationDatabase", // clangd extension
649 llvm::json::Object{{"automaticReload", true}}},
650 {"inactiveRegionsProvider", true}, // clangd extension
651 {"callHierarchyProvider", true},
652 {"clangdInlayHintsProvider", true},
653 {"inlayHintProvider", true},
654 {"foldingRangeProvider", true},
655 };
656
657 {
658 LSPBinder Binder(Handlers, *this);
659 bindMethods(Binder, Params.capabilities);
660 if (Opts.FeatureModules)
661 for (auto &Mod : *Opts.FeatureModules)
662 Mod.initializeLSP(Binder, Params.rawCapabilities, ServerCaps);
663 }
664
665 // Per LSP, renameProvider can be either boolean or RenameOptions.
666 // RenameOptions will be specified if the client states it supports prepare.
667 ServerCaps["renameProvider"] =
668 Params.capabilities.RenamePrepareSupport
669 ? llvm::json::Object{{"prepareProvider", true}}
670 : llvm::json::Value(true);
671
672 // Per LSP, codeActionProvider can be either boolean or CodeActionOptions.
673 // CodeActionOptions is only valid if the client supports action literal
674 // via textDocument.codeAction.codeActionLiteralSupport.
675 ServerCaps["codeActionProvider"] =
676 Params.capabilities.CodeActionStructure
677 ? llvm::json::Object{{"codeActionKinds",
681 : llvm::json::Value(true);
682
683 std::vector<llvm::StringRef> Commands;
684 for (llvm::StringRef Command : Handlers.CommandHandlers.keys())
685 Commands.push_back(Command);
686 llvm::sort(Commands);
687 ServerCaps["executeCommandProvider"] =
688 llvm::json::Object{{"commands", Commands}};
689
690 if (Opts.Encoding)
691 ServerCaps["positionEncoding"] = *Opts.Encoding;
692
693 llvm::json::Object Result{
694 {{"serverInfo",
695 llvm::json::Object{
696 {"name", "clangd"},
697 {"version", llvm::formatv("{0} {1} {2}", versionString(),
699 {"capabilities", std::move(ServerCaps)}}};
700
701 // TODO: offsetEncoding capability is a deprecated clangd extension and should
702 // be deleted.
703 if (Opts.Encoding)
704 Result["offsetEncoding"] = *Opts.Encoding;
705 Reply(std::move(Result));
706
707 // Apply settings after we're fully initialized.
708 // This can start background indexing and in turn trigger LSP notifications.
709 applyConfiguration(Params.initializationOptions.ConfigSettings);
710}
711
712void ClangdLSPServer::onInitialized(const InitializedParams &Params) {}
713
714void ClangdLSPServer::onShutdown(const NoParams &,
716 // Do essentially nothing, just say we're ready to exit.
717 ShutdownRequestReceived = true;
718 Reply(nullptr);
719}
720
721// sync is a clangd extension: it blocks until all background work completes.
722// It blocks the calling thread, so no messages are processed until it returns!
723void ClangdLSPServer::onSync(const NoParams &, Callback<std::nullptr_t> Reply) {
724 if (Server->blockUntilIdleForTest(/*TimeoutSeconds=*/60))
725 Reply(nullptr);
726 else
727 Reply(error("Not idle after a minute"));
728}
729
730void ClangdLSPServer::onDocumentDidOpen(
731 const DidOpenTextDocumentParams &Params) {
732 PathRef File = Params.textDocument.uri.file();
733
734 const std::string &Contents = Params.textDocument.text;
735
736 Server->addDocument(File, Contents,
737 encodeVersion(Params.textDocument.version),
739}
740
741void ClangdLSPServer::onDocumentDidChange(
742 const DidChangeTextDocumentParams &Params) {
743 auto WantDiags = WantDiagnostics::Auto;
744 if (Params.wantDiagnostics)
745 WantDiags =
746 *Params.wantDiagnostics ? WantDiagnostics::Yes : WantDiagnostics::No;
747
748 PathRef File = Params.textDocument.uri.file();
749 auto Code = Server->getDraft(File);
750 if (!Code) {
751 log("Trying to incrementally change non-added document: {0}", File);
752 return;
753 }
754 std::string NewCode(*Code);
755 for (const auto &Change : Params.contentChanges) {
756 if (auto Err = applyChange(NewCode, Change)) {
757 // If this fails, we are most likely going to be not in sync anymore with
758 // the client. It is better to remove the draft and let further
759 // operations fail rather than giving wrong results.
760 Server->removeDocument(File);
761 elog("Failed to update {0}: {1}", File, std::move(Err));
762 return;
763 }
764 }
765 Server->addDocument(File, NewCode, encodeVersion(Params.textDocument.version),
766 WantDiags, Params.forceRebuild);
767}
768
769void ClangdLSPServer::onDocumentDidSave(
770 const DidSaveTextDocumentParams &Params) {
771 Server->reparseOpenFilesIfNeeded([](llvm::StringRef) { return true; });
772}
773
774void ClangdLSPServer::onFileEvent(const DidChangeWatchedFilesParams &Params) {
775 // We could also reparse all open files here. However:
776 // - this could be frequent, and revalidating all the preambles isn't free
777 // - this is useful e.g. when switching git branches, but we're likely to see
778 // fresh headers but still have the old-branch main-file content
779 Server->onFileEvent(Params);
780 // FIXME: observe config files, immediately expire time-based caches, reparse:
781 // - compile_commands.json and compile_flags.txt
782 // - .clang_format and .clang-tidy
783 // - .clangd and clangd/config.yaml
784}
785
786void ClangdLSPServer::onCommand(const ExecuteCommandParams &Params,
788 auto It = Handlers.CommandHandlers.find(Params.command);
789 if (It == Handlers.CommandHandlers.end()) {
790 return Reply(llvm::make_error<LSPError>(
791 llvm::formatv("Unsupported command \"{0}\".", Params.command).str(),
793 }
794 It->second(Params.argument, std::move(Reply));
795}
796
797void ClangdLSPServer::onCommandApplyEdit(const WorkspaceEdit &WE,
799 // The flow for "apply-fix" :
800 // 1. We publish a diagnostic, including fixits
801 // 2. The user clicks on the diagnostic, the editor asks us for code actions
802 // 3. We send code actions, with the fixit embedded as context
803 // 4. The user selects the fixit, the editor asks us to apply it
804 // 5. We unwrap the changes and send them back to the editor
805 // 6. The editor applies the changes (applyEdit), and sends us a reply
806 // 7. We unwrap the reply and send a reply to the editor.
807 applyEdit(WE, "Fix applied.", std::move(Reply));
808}
809
810void ClangdLSPServer::onCommandApplyTweak(const TweakArgs &Args,
812 auto Action = [this, Reply = std::move(Reply)](
813 llvm::Expected<Tweak::Effect> R) mutable {
814 if (!R)
815 return Reply(R.takeError());
816
817 assert(R->ShowMessage || (!R->ApplyEdits.empty() && "tweak has no effect"));
818
819 if (R->ShowMessage) {
820 ShowMessageParams Msg;
821 Msg.message = *R->ShowMessage;
822 Msg.type = MessageType::Info;
823 ShowMessage(Msg);
824 }
825 // When no edit is specified, make sure we Reply().
826 if (R->ApplyEdits.empty())
827 return Reply("Tweak applied.");
828
829 if (auto Err = validateEdits(*Server, R->ApplyEdits))
830 return Reply(std::move(Err));
831
832 WorkspaceEdit WE;
833 // FIXME: use documentChanges when SupportDocumentChanges is true.
834 WE.changes.emplace();
835 for (const auto &It : R->ApplyEdits) {
836 (*WE.changes)[URI::createFile(It.first()).toString()] =
837 It.second.asTextEdits();
838 }
839 // ApplyEdit will take care of calling Reply().
840 return applyEdit(std::move(WE), "Tweak applied.", std::move(Reply));
841 };
842 Server->applyTweak(Args.file.file(), Args.selection, Args.tweakID,
843 std::move(Action));
844}
845
846void ClangdLSPServer::onCommandApplyRename(const RenameParams &R,
848 onRename(R, [this, Reply = std::move(Reply)](
849 llvm::Expected<WorkspaceEdit> Edit) mutable {
850 if (!Edit)
851 Reply(Edit.takeError());
852 applyEdit(std::move(*Edit), "Rename applied.", std::move(Reply));
853 });
854}
855
856void ClangdLSPServer::applyEdit(WorkspaceEdit WE, llvm::json::Value Success,
858 ApplyWorkspaceEditParams Edit;
859 Edit.edit = std::move(WE);
860 ApplyWorkspaceEdit(
861 Edit, [Reply = std::move(Reply), SuccessMessage = std::move(Success)](
862 llvm::Expected<ApplyWorkspaceEditResponse> Response) mutable {
863 if (!Response)
864 return Reply(Response.takeError());
865 if (!Response->applied) {
866 std::string Reason = Response->failureReason
867 ? *Response->failureReason
868 : "unknown reason";
869 return Reply(error("edits were not applied: {0}", Reason));
870 }
871 return Reply(SuccessMessage);
872 });
873}
874
875void ClangdLSPServer::onWorkspaceSymbol(
876 const WorkspaceSymbolParams &Params,
877 Callback<std::vector<SymbolInformation>> Reply) {
878 Server->workspaceSymbols(
879 Params.query, Params.limit.value_or(Opts.CodeComplete.Limit),
880 [Reply = std::move(Reply),
881 this](llvm::Expected<std::vector<SymbolInformation>> Items) mutable {
882 if (!Items)
883 return Reply(Items.takeError());
884 for (auto &Sym : *Items)
885 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds);
886
887 Reply(std::move(*Items));
888 });
889}
890
891void ClangdLSPServer::onPrepareRename(const TextDocumentPositionParams &Params,
893 Server->prepareRename(
894 Params.textDocument.uri.file(), Params.position, /*NewName*/ std::nullopt,
895 Opts.Rename,
896 [Reply = std::move(Reply)](llvm::Expected<RenameResult> Result) mutable {
897 if (!Result)
898 return Reply(Result.takeError());
899 PrepareRenameResult PrepareResult;
900 PrepareResult.range = Result->Target;
901 PrepareResult.placeholder = Result->Placeholder;
902 return Reply(std::move(PrepareResult));
903 });
904}
905
906void ClangdLSPServer::onRename(const RenameParams &Params,
908 Path File = std::string(Params.textDocument.uri.file());
909 if (!Server->getDraft(File))
910 return Reply(llvm::make_error<LSPError>(
911 "onRename called for non-added file", ErrorCode::InvalidParams));
912 Server->rename(File, Params.position, Params.newName, Opts.Rename,
913 [File, Params, Reply = std::move(Reply),
914 this](llvm::Expected<RenameResult> R) mutable {
915 if (!R)
916 return Reply(R.takeError());
917 if (auto Err = validateEdits(*Server, R->GlobalChanges))
918 return Reply(std::move(Err));
919 WorkspaceEdit Result;
920 // FIXME: use documentChanges if SupportDocumentChanges is
921 // true.
922 Result.changes.emplace();
923 for (const auto &Rep : R->GlobalChanges) {
924 (*Result
925 .changes)[URI::createFile(Rep.first()).toString()] =
926 Rep.second.asTextEdits();
927 }
928 Reply(Result);
929 });
930}
931
932void ClangdLSPServer::onDocumentDidClose(
933 const DidCloseTextDocumentParams &Params) {
934 PathRef File = Params.textDocument.uri.file();
935 Server->removeDocument(File);
936
937 {
938 std::lock_guard<std::mutex> Lock(DiagRefMutex);
939 DiagRefMap.erase(File);
940 }
941 {
942 std::lock_guard<std::mutex> HLock(SemanticTokensMutex);
943 LastSemanticTokens.erase(File);
944 }
945 // clangd will not send updates for this file anymore, so we empty out the
946 // list of diagnostics shown on the client (e.g. in the "Problems" pane of
947 // VSCode). Note that this cannot race with actual diagnostics responses
948 // because removeDocument() guarantees no diagnostic callbacks will be
949 // executed after it returns.
950 PublishDiagnosticsParams Notification;
951 Notification.uri = URIForFile::canonicalize(File, /*TUPath=*/File);
952 PublishDiagnostics(Notification);
953}
954
955void ClangdLSPServer::onDocumentOnTypeFormatting(
956 const DocumentOnTypeFormattingParams &Params,
957 Callback<std::vector<TextEdit>> Reply) {
958 auto File = Params.textDocument.uri.file();
959 Server->formatOnType(File, Params.position, Params.ch, std::move(Reply));
960}
961
962void ClangdLSPServer::onDocumentRangeFormatting(
963 const DocumentRangeFormattingParams &Params,
964 Callback<std::vector<TextEdit>> Reply) {
965 onDocumentRangesFormatting(
966 DocumentRangesFormattingParams{Params.textDocument, {Params.range}},
967 std::move(Reply));
968}
969
970void ClangdLSPServer::onDocumentRangesFormatting(
971 const DocumentRangesFormattingParams &Params,
972 Callback<std::vector<TextEdit>> Reply) {
973 auto File = Params.textDocument.uri.file();
974 auto Code = Server->getDraft(File);
975 Server->formatFile(File, Params.ranges,
976 [Code = std::move(Code), Reply = std::move(Reply)](
977 llvm::Expected<tooling::Replacements> Result) mutable {
978 if (Result)
979 Reply(replacementsToEdits(*Code, Result.get()));
980 else
981 Reply(Result.takeError());
982 });
983}
984
985void ClangdLSPServer::onDocumentFormatting(
986 const DocumentFormattingParams &Params,
987 Callback<std::vector<TextEdit>> Reply) {
988 auto File = Params.textDocument.uri.file();
989 auto Code = Server->getDraft(File);
990 Server->formatFile(File,
991 /*Rngs=*/{},
992 [Code = std::move(Code), Reply = std::move(Reply)](
993 llvm::Expected<tooling::Replacements> Result) mutable {
994 if (Result)
995 Reply(replacementsToEdits(*Code, Result.get()));
996 else
997 Reply(Result.takeError());
998 });
999}
1000
1001/// The functions constructs a flattened view of the DocumentSymbol hierarchy.
1002/// Used by the clients that do not support the hierarchical view.
1003static std::vector<SymbolInformation>
1004flattenSymbolHierarchy(llvm::ArrayRef<DocumentSymbol> Symbols,
1005 const URIForFile &FileURI) {
1006 std::vector<SymbolInformation> Results;
1007 std::function<void(const DocumentSymbol &, llvm::StringRef)> Process =
1008 [&](const DocumentSymbol &S, std::optional<llvm::StringRef> ParentName) {
1010 SI.containerName = std::string(ParentName ? "" : *ParentName);
1011 SI.name = S.name;
1012 SI.kind = S.kind;
1013 SI.location.range = S.range;
1014 SI.location.uri = FileURI;
1015
1016 Results.push_back(std::move(SI));
1017 std::string FullName =
1018 !ParentName ? S.name : (ParentName->str() + "::" + S.name);
1019 for (auto &C : S.children)
1020 Process(C, /*ParentName=*/FullName);
1021 };
1022 for (auto &S : Symbols)
1023 Process(S, /*ParentName=*/"");
1024 return Results;
1025}
1026
1027void ClangdLSPServer::onDocumentSymbol(const DocumentSymbolParams &Params,
1028 Callback<llvm::json::Value> Reply) {
1029 URIForFile FileURI = Params.textDocument.uri;
1030 Server->documentSymbols(
1031 Params.textDocument.uri.file(),
1032 [this, FileURI, Reply = std::move(Reply)](
1033 llvm::Expected<std::vector<DocumentSymbol>> Items) mutable {
1034 if (!Items)
1035 return Reply(Items.takeError());
1036 adjustSymbolKinds(*Items, SupportedSymbolKinds);
1037 if (SupportsHierarchicalDocumentSymbol)
1038 return Reply(std::move(*Items));
1039 return Reply(flattenSymbolHierarchy(*Items, FileURI));
1040 });
1041}
1042
1043void ClangdLSPServer::onFoldingRange(
1044 const FoldingRangeParams &Params,
1045 Callback<std::vector<FoldingRange>> Reply) {
1046 Server->foldingRanges(Params.textDocument.uri.file(), std::move(Reply));
1047}
1048
1049static std::optional<Command> asCommand(const CodeAction &Action) {
1050 Command Cmd;
1051 if (Action.command && Action.edit)
1052 return std::nullopt; // Not representable. (We never emit these anyway).
1053 if (Action.command) {
1054 Cmd = *Action.command;
1055 } else if (Action.edit) {
1056 Cmd.command = std::string(ApplyFixCommand);
1057 Cmd.argument = *Action.edit;
1058 } else {
1059 return std::nullopt;
1060 }
1061 Cmd.title = Action.title;
1062 if (Action.kind && *Action.kind == CodeAction::QUICKFIX_KIND)
1063 Cmd.title = "Apply fix: " + Cmd.title;
1064 return Cmd;
1065}
1066
1067void ClangdLSPServer::onCodeAction(const CodeActionParams &Params,
1068 Callback<llvm::json::Value> Reply) {
1069 URIForFile File = Params.textDocument.uri;
1070 std::map<ClangdServer::DiagRef, clangd::Diagnostic> ToLSPDiags;
1071 ClangdServer::CodeActionInputs Inputs;
1072
1073 for (const auto& LSPDiag : Params.context.diagnostics) {
1074 if (auto DiagRef = getDiagRef(File.file(), LSPDiag)) {
1075 ToLSPDiags[*DiagRef] = LSPDiag;
1076 Inputs.Diagnostics.push_back(*DiagRef);
1077 }
1078 }
1079 Inputs.File = File.file();
1080 Inputs.Selection = Params.range;
1081 Inputs.RequestedActionKinds = Params.context.only;
1082 Inputs.TweakFilter = [this](const Tweak &T) {
1083 return Opts.TweakFilter(T);
1084 };
1085 auto CB = [this,
1086 Reply = std::move(Reply),
1087 ToLSPDiags = std::move(ToLSPDiags), File,
1088 Selection = Params.range](
1089 llvm::Expected<ClangdServer::CodeActionResult> Fixits) mutable {
1090 if (!Fixits)
1091 return Reply(Fixits.takeError());
1092 std::vector<CodeAction> CAs;
1093 auto Version = decodeVersion(Fixits->Version);
1094 for (const auto &QF : Fixits->QuickFixes) {
1095 CAs.push_back(toCodeAction(QF.F, File, Version, SupportsDocumentChanges,
1096 SupportsChangeAnnotation));
1097 if (auto It = ToLSPDiags.find(QF.Diag);
1098 It != ToLSPDiags.end()) {
1099 CAs.back().diagnostics = {It->second};
1100 }
1101 }
1102
1103 for (const auto &R : Fixits->Renames)
1104 CAs.push_back(toCodeAction(R, File));
1105
1106 for (const auto &TR : Fixits->TweakRefs)
1107 CAs.push_back(toCodeAction(TR, File, Selection));
1108
1109 // If there's exactly one quick-fix, call it "preferred".
1110 // We never consider refactorings etc as preferred.
1111 CodeAction *OnlyFix = nullptr;
1112 for (auto &Action : CAs) {
1113 if (Action.kind && *Action.kind == CodeAction::QUICKFIX_KIND) {
1114 if (OnlyFix) {
1115 OnlyFix = nullptr;
1116 break;
1117 }
1118 OnlyFix = &Action;
1119 }
1120 }
1121 if (OnlyFix) {
1122 OnlyFix->isPreferred = true;
1123 if (ToLSPDiags.size() == 1 &&
1124 ToLSPDiags.begin()->second.range == Selection)
1125 OnlyFix->diagnostics = {ToLSPDiags.begin()->second};
1126 }
1127
1128 if (SupportsCodeAction)
1129 return Reply(llvm::json::Array(CAs));
1130 std::vector<Command> Commands;
1131 for (const auto &Action : CAs) {
1132 if (auto Command = asCommand(Action))
1133 Commands.push_back(std::move(*Command));
1134 }
1135 return Reply(llvm::json::Array(Commands));
1136 };
1137 Server->codeAction(Inputs, std::move(CB));
1138}
1139
1140void ClangdLSPServer::onCompletion(const CompletionParams &Params,
1141 Callback<CompletionList> Reply) {
1142 if (!shouldRunCompletion(Params)) {
1143 // Clients sometimes auto-trigger completions in undesired places (e.g.
1144 // 'a >^ '), we return empty results in those cases.
1145 vlog("ignored auto-triggered completion, preceding char did not match");
1146 return Reply(CompletionList());
1147 }
1148 auto Opts = this->Opts.CodeComplete;
1149 if (Params.limit && *Params.limit >= 0)
1150 Opts.Limit = *Params.limit;
1151 Server->codeComplete(Params.textDocument.uri.file(), Params.position, Opts,
1152 [Reply = std::move(Reply), Opts,
1153 this](llvm::Expected<CodeCompleteResult> List) mutable {
1154 if (!List)
1155 return Reply(List.takeError());
1156 CompletionList LSPList;
1157 LSPList.isIncomplete = List->HasMore;
1158 for (const auto &R : List->Completions) {
1159 CompletionItem C = R.render(Opts);
1160 C.kind = adjustKindToCapability(
1161 C.kind, SupportedCompletionItemKinds);
1162 if (!SupportsCompletionLabelDetails)
1163 removeCompletionLabelDetails(C);
1164 LSPList.items.push_back(std::move(C));
1165 }
1166 return Reply(std::move(LSPList));
1167 });
1168}
1169
1170void ClangdLSPServer::onSignatureHelp(const TextDocumentPositionParams &Params,
1171 Callback<SignatureHelp> Reply) {
1172 Server->signatureHelp(Params.textDocument.uri.file(), Params.position,
1173 Opts.SignatureHelpDocumentationFormat,
1174 [Reply = std::move(Reply), this](
1175 llvm::Expected<SignatureHelp> Signature) mutable {
1176 if (!Signature)
1177 return Reply(Signature.takeError());
1178 if (SupportsOffsetsInSignatureHelp)
1179 return Reply(std::move(*Signature));
1180 // Strip out the offsets from signature help for
1181 // clients that only support string labels.
1182 for (auto &SigInfo : Signature->signatures) {
1183 for (auto &Param : SigInfo.parameters)
1184 Param.labelOffsets.reset();
1185 }
1186 return Reply(std::move(*Signature));
1187 });
1188}
1189
1190// Go to definition has a toggle function: if def and decl are distinct, then
1191// the first press gives you the def, the second gives you the matching def.
1192// getToggle() returns the counterpart location that under the cursor.
1193//
1194// We return the toggled location alone (ignoring other symbols) to encourage
1195// editors to "bounce" quickly between locations, without showing a menu.
1197 LocatedSymbol &Sym) {
1198 // Toggle only makes sense with two distinct locations.
1199 if (!Sym.Definition || *Sym.Definition == Sym.PreferredDeclaration)
1200 return nullptr;
1201 if (Sym.Definition->uri.file() == Point.textDocument.uri.file() &&
1202 Sym.Definition->range.contains(Point.position))
1203 return &Sym.PreferredDeclaration;
1204 if (Sym.PreferredDeclaration.uri.file() == Point.textDocument.uri.file() &&
1206 return &*Sym.Definition;
1207 return nullptr;
1208}
1209
1210void ClangdLSPServer::onGoToDefinition(const TextDocumentPositionParams &Params,
1211 Callback<std::vector<Location>> Reply) {
1212 Server->locateSymbolAt(
1213 Params.textDocument.uri.file(), Params.position,
1214 [Params, Reply = std::move(Reply)](
1215 llvm::Expected<std::vector<LocatedSymbol>> Symbols) mutable {
1216 if (!Symbols)
1217 return Reply(Symbols.takeError());
1218 std::vector<Location> Defs;
1219 for (auto &S : *Symbols) {
1220 if (Location *Toggle = getToggle(Params, S))
1221 return Reply(std::vector<Location>{std::move(*Toggle)});
1222 Defs.push_back(S.Definition.value_or(S.PreferredDeclaration));
1223 }
1224 Reply(std::move(Defs));
1225 });
1226}
1227
1228void ClangdLSPServer::onGoToDeclaration(
1229 const TextDocumentPositionParams &Params,
1230 Callback<std::vector<Location>> Reply) {
1231 Server->locateSymbolAt(
1232 Params.textDocument.uri.file(), Params.position,
1233 [Params, Reply = std::move(Reply)](
1234 llvm::Expected<std::vector<LocatedSymbol>> Symbols) mutable {
1235 if (!Symbols)
1236 return Reply(Symbols.takeError());
1237 std::vector<Location> Decls;
1238 for (auto &S : *Symbols) {
1239 if (Location *Toggle = getToggle(Params, S))
1240 return Reply(std::vector<Location>{std::move(*Toggle)});
1241 Decls.push_back(std::move(S.PreferredDeclaration));
1242 }
1243 Reply(std::move(Decls));
1244 });
1245}
1246
1247void ClangdLSPServer::onSwitchSourceHeader(
1248 const TextDocumentIdentifier &Params,
1249 Callback<std::optional<URIForFile>> Reply) {
1250 Server->switchSourceHeader(
1251 Params.uri.file(),
1252 [Reply = std::move(Reply),
1253 Params](llvm::Expected<std::optional<clangd::Path>> Path) mutable {
1254 if (!Path)
1255 return Reply(Path.takeError());
1256 if (*Path)
1257 return Reply(URIForFile::canonicalize(**Path, Params.uri.file()));
1258 return Reply(std::nullopt);
1259 });
1260}
1261
1262void ClangdLSPServer::onDocumentHighlight(
1263 const TextDocumentPositionParams &Params,
1264 Callback<std::vector<DocumentHighlight>> Reply) {
1265 Server->findDocumentHighlights(Params.textDocument.uri.file(),
1266 Params.position, std::move(Reply));
1267}
1268
1269void ClangdLSPServer::onHover(const TextDocumentPositionParams &Params,
1270 Callback<std::optional<Hover>> Reply) {
1271 Server->findHover(Params.textDocument.uri.file(), Params.position,
1272 [Reply = std::move(Reply),
1273 this](llvm::Expected<std::optional<HoverInfo>> H) mutable {
1274 if (!H)
1275 return Reply(H.takeError());
1276 if (!*H)
1277 return Reply(std::nullopt);
1278
1279 Hover R;
1280 R.contents.kind = HoverContentFormat;
1281 R.range = (*H)->SymRange;
1282 switch (HoverContentFormat) {
1283 case MarkupKind::Markdown:
1284 case MarkupKind::PlainText:
1285 R.contents.value = (*H)->present(HoverContentFormat);
1286 return Reply(std::move(R));
1287 };
1288 llvm_unreachable("unhandled MarkupKind");
1289 });
1290}
1291
1292// Our extension has a different representation on the wire than the standard.
1293// https://clangd.llvm.org/extensions#type-hierarchy
1295 llvm::json::Object Result{{
1296 {"name", std::move(THI.name)},
1297 {"kind", static_cast<int>(THI.kind)},
1298 {"uri", std::move(THI.uri)},
1299 {"range", THI.range},
1300 {"selectionRange", THI.selectionRange},
1301 {"data", std::move(THI.data)},
1302 }};
1303 if (THI.deprecated)
1304 Result["deprecated"] = THI.deprecated;
1305 if (THI.detail)
1306 Result["detail"] = std::move(*THI.detail);
1307
1308 if (THI.parents) {
1309 llvm::json::Array Parents;
1310 for (auto &Parent : *THI.parents)
1311 Parents.emplace_back(serializeTHIForExtension(std::move(Parent)));
1312 Result["parents"] = std::move(Parents);
1313 }
1314
1315 if (THI.children) {
1316 llvm::json::Array Children;
1317 for (auto &child : *THI.children)
1318 Children.emplace_back(serializeTHIForExtension(std::move(child)));
1319 Result["children"] = std::move(Children);
1320 }
1321 return Result;
1322}
1323
1324void ClangdLSPServer::onTypeHierarchy(const TypeHierarchyPrepareParams &Params,
1325 Callback<llvm::json::Value> Reply) {
1326 auto Serialize =
1327 [Reply = std::move(Reply)](
1328 llvm::Expected<std::vector<TypeHierarchyItem>> Resp) mutable {
1329 if (!Resp) {
1330 Reply(Resp.takeError());
1331 return;
1332 }
1333 if (Resp->empty()) {
1334 Reply(nullptr);
1335 return;
1336 }
1337 Reply(serializeTHIForExtension(std::move(Resp->front())));
1338 };
1339 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1340 Params.resolve, Params.direction, std::move(Serialize));
1341}
1342
1343void ClangdLSPServer::onResolveTypeHierarchy(
1344 const ResolveTypeHierarchyItemParams &Params,
1345 Callback<llvm::json::Value> Reply) {
1346 auto Serialize =
1347 [Reply = std::move(Reply)](
1348 llvm::Expected<std::optional<TypeHierarchyItem>> Resp) mutable {
1349 if (!Resp) {
1350 Reply(Resp.takeError());
1351 return;
1352 }
1353 if (!*Resp) {
1354 Reply(std::move(*Resp));
1355 return;
1356 }
1357 Reply(serializeTHIForExtension(std::move(**Resp)));
1358 };
1359 Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction,
1360 std::move(Serialize));
1361}
1362
1363void ClangdLSPServer::onPrepareTypeHierarchy(
1364 const TypeHierarchyPrepareParams &Params,
1365 Callback<std::vector<TypeHierarchyItem>> Reply) {
1366 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1367 Params.resolve, Params.direction, std::move(Reply));
1368}
1369
1370void ClangdLSPServer::onSuperTypes(
1371 const ResolveTypeHierarchyItemParams &Params,
1372 Callback<std::optional<std::vector<TypeHierarchyItem>>> Reply) {
1373 Server->superTypes(Params.item, std::move(Reply));
1374}
1375
1376void ClangdLSPServer::onSubTypes(
1377 const ResolveTypeHierarchyItemParams &Params,
1378 Callback<std::vector<TypeHierarchyItem>> Reply) {
1379 Server->subTypes(Params.item, std::move(Reply));
1380}
1381
1382void ClangdLSPServer::onPrepareCallHierarchy(
1383 const CallHierarchyPrepareParams &Params,
1384 Callback<std::vector<CallHierarchyItem>> Reply) {
1385 Server->prepareCallHierarchy(Params.textDocument.uri.file(), Params.position,
1386 std::move(Reply));
1387}
1388
1389void ClangdLSPServer::onCallHierarchyIncomingCalls(
1390 const CallHierarchyIncomingCallsParams &Params,
1391 Callback<std::vector<CallHierarchyIncomingCall>> Reply) {
1392 Server->incomingCalls(Params.item, std::move(Reply));
1393}
1394
1395void ClangdLSPServer::onClangdInlayHints(const InlayHintsParams &Params,
1396 Callback<llvm::json::Value> Reply) {
1397 // Our extension has a different representation on the wire than the standard.
1398 // We have a "range" property and "kind" is represented as a string, not as an
1399 // enum value.
1400 // https://clangd.llvm.org/extensions#inlay-hints
1401 auto Serialize = [Reply = std::move(Reply)](
1402 llvm::Expected<std::vector<InlayHint>> Hints) mutable {
1403 if (!Hints) {
1404 Reply(Hints.takeError());
1405 return;
1406 }
1407 llvm::json::Array Result;
1408 Result.reserve(Hints->size());
1409 for (auto &Hint : *Hints) {
1410 Result.emplace_back(llvm::json::Object{
1411 {"kind", llvm::to_string(Hint.kind)},
1412 {"range", Hint.range},
1413 {"position", Hint.position},
1414 // Extension doesn't have paddingLeft/Right so adjust the label
1415 // accordingly.
1416 {"label",
1417 ((Hint.paddingLeft ? " " : "") + llvm::StringRef(Hint.joinLabels()) +
1418 (Hint.paddingRight ? " " : ""))
1419 .str()},
1420 });
1421 }
1422 Reply(std::move(Result));
1423 };
1424 Server->inlayHints(Params.textDocument.uri.file(), Params.range,
1425 std::move(Serialize));
1426}
1427
1428void ClangdLSPServer::onInlayHint(const InlayHintsParams &Params,
1429 Callback<std::vector<InlayHint>> Reply) {
1430 Server->inlayHints(Params.textDocument.uri.file(), Params.range,
1431 std::move(Reply));
1432}
1433
1434void ClangdLSPServer::onCallHierarchyOutgoingCalls(
1435 const CallHierarchyOutgoingCallsParams &Params,
1436 Callback<std::vector<CallHierarchyOutgoingCall>> Reply) {
1437 Server->outgoingCalls(Params.item, std::move(Reply));
1438}
1439
1440void ClangdLSPServer::applyConfiguration(
1441 const ConfigurationSettings &Settings) {
1442 // Per-file update to the compilation database.
1443 llvm::StringSet<> ModifiedFiles;
1444 for (auto &[File, Command] : Settings.compilationDatabaseChanges) {
1445 auto Cmd =
1446 tooling::CompileCommand(std::move(Command.workingDirectory), File,
1447 std::move(Command.compilationCommand),
1448 /*Output=*/"");
1449 if (CDB->setCompileCommand(File, std::move(Cmd))) {
1450 ModifiedFiles.insert(File);
1451 }
1452 }
1453
1454 Server->reparseOpenFilesIfNeeded(
1455 [&](llvm::StringRef File) { return ModifiedFiles.count(File) != 0; });
1456}
1457
1458void ClangdLSPServer::maybeExportMemoryProfile() {
1459 if (!trace::enabled() || !ShouldProfile())
1460 return;
1461
1462 static constexpr trace::Metric MemoryUsage(
1463 "memory_usage", trace::Metric::Value, "component_name");
1464 trace::Span Tracer("ProfileBrief");
1465 MemoryTree MT;
1466 profile(MT);
1467 record(MT, "clangd_lsp_server", MemoryUsage);
1468}
1469
1470void ClangdLSPServer::maybeCleanupMemory() {
1471 if (!Opts.MemoryCleanup || !ShouldCleanupMemory())
1472 return;
1473 Opts.MemoryCleanup();
1474}
1475
1476// FIXME: This function needs to be properly tested.
1477void ClangdLSPServer::onChangeConfiguration(
1478 const DidChangeConfigurationParams &Params) {
1479 applyConfiguration(Params.settings);
1480}
1481
1482void ClangdLSPServer::onReference(
1483 const ReferenceParams &Params,
1484 Callback<std::vector<ReferenceLocation>> Reply) {
1485 Server->findReferences(Params.textDocument.uri.file(), Params.position,
1486 Opts.ReferencesLimit, SupportsReferenceContainer,
1487 [Reply = std::move(Reply),
1488 IncludeDecl(Params.context.includeDeclaration)](
1489 llvm::Expected<ReferencesResult> Refs) mutable {
1490 if (!Refs)
1491 return Reply(Refs.takeError());
1492 // Filter out declarations if the client asked.
1493 std::vector<ReferenceLocation> Result;
1494 Result.reserve(Refs->References.size());
1495 for (auto &Ref : Refs->References) {
1496 bool IsDecl =
1497 Ref.Attributes & ReferencesResult::Declaration;
1498 if (IncludeDecl || !IsDecl)
1499 Result.push_back(std::move(Ref.Loc));
1500 }
1501 return Reply(std::move(Result));
1502 });
1503}
1504
1505void ClangdLSPServer::onGoToType(const TextDocumentPositionParams &Params,
1506 Callback<std::vector<Location>> Reply) {
1507 Server->findType(
1508 Params.textDocument.uri.file(), Params.position,
1509 [Reply = std::move(Reply)](
1510 llvm::Expected<std::vector<LocatedSymbol>> Types) mutable {
1511 if (!Types)
1512 return Reply(Types.takeError());
1513 std::vector<Location> Response;
1514 for (const LocatedSymbol &Sym : *Types)
1515 Response.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration));
1516 return Reply(std::move(Response));
1517 });
1518}
1519
1520void ClangdLSPServer::onGoToImplementation(
1521 const TextDocumentPositionParams &Params,
1522 Callback<std::vector<Location>> Reply) {
1523 Server->findImplementations(
1524 Params.textDocument.uri.file(), Params.position,
1525 [Reply = std::move(Reply)](
1526 llvm::Expected<std::vector<LocatedSymbol>> Overrides) mutable {
1527 if (!Overrides)
1528 return Reply(Overrides.takeError());
1529 std::vector<Location> Impls;
1530 for (const LocatedSymbol &Sym : *Overrides)
1531 Impls.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration));
1532 return Reply(std::move(Impls));
1533 });
1534}
1535
1536void ClangdLSPServer::onSymbolInfo(const TextDocumentPositionParams &Params,
1537 Callback<std::vector<SymbolDetails>> Reply) {
1538 Server->symbolInfo(Params.textDocument.uri.file(), Params.position,
1539 std::move(Reply));
1540}
1541
1542void ClangdLSPServer::onSelectionRange(
1543 const SelectionRangeParams &Params,
1544 Callback<std::vector<SelectionRange>> Reply) {
1545 Server->semanticRanges(
1546 Params.textDocument.uri.file(), Params.positions,
1547 [Reply = std::move(Reply)](
1548 llvm::Expected<std::vector<SelectionRange>> Ranges) mutable {
1549 if (!Ranges)
1550 return Reply(Ranges.takeError());
1551 return Reply(std::move(*Ranges));
1552 });
1553}
1554
1555void ClangdLSPServer::onDocumentLink(
1556 const DocumentLinkParams &Params,
1557 Callback<std::vector<DocumentLink>> Reply) {
1558
1559 // TODO(forster): This currently resolves all targets eagerly. This is slow,
1560 // because it blocks on the preamble/AST being built. We could respond to the
1561 // request faster by using string matching or the lexer to find the includes
1562 // and resolving the targets lazily.
1563 Server->documentLinks(
1564 Params.textDocument.uri.file(),
1565 [Reply = std::move(Reply)](
1566 llvm::Expected<std::vector<DocumentLink>> Links) mutable {
1567 if (!Links) {
1568 return Reply(Links.takeError());
1569 }
1570 return Reply(std::move(Links));
1571 });
1572}
1573
1574// Increment a numeric string: "" -> 1 -> 2 -> ... -> 9 -> 10 -> 11 ...
1575static void increment(std::string &S) {
1576 for (char &C : llvm::reverse(S)) {
1577 if (C != '9') {
1578 ++C;
1579 return;
1580 }
1581 C = '0';
1582 }
1583 S.insert(S.begin(), '1');
1584}
1585
1586void ClangdLSPServer::onSemanticTokens(const SemanticTokensParams &Params,
1587 Callback<SemanticTokens> CB) {
1588 auto File = Params.textDocument.uri.file();
1589 Server->semanticHighlights(
1590 Params.textDocument.uri.file(),
1591 [this, File(File.str()), CB(std::move(CB)), Code(Server->getDraft(File))](
1592 llvm::Expected<std::vector<HighlightingToken>> HT) mutable {
1593 if (!HT)
1594 return CB(HT.takeError());
1595 SemanticTokens Result;
1596 Result.tokens = toSemanticTokens(*HT, *Code);
1597 {
1598 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1599 auto &Last = LastSemanticTokens[File];
1600
1601 Last.tokens = Result.tokens;
1602 increment(Last.resultId);
1603 Result.resultId = Last.resultId;
1604 }
1605 CB(std::move(Result));
1606 });
1607}
1608
1609void ClangdLSPServer::onSemanticTokensDelta(
1610 const SemanticTokensDeltaParams &Params,
1611 Callback<SemanticTokensOrDelta> CB) {
1612 auto File = Params.textDocument.uri.file();
1613 Server->semanticHighlights(
1614 Params.textDocument.uri.file(),
1615 [this, PrevResultID(Params.previousResultId), File(File.str()),
1616 CB(std::move(CB)), Code(Server->getDraft(File))](
1617 llvm::Expected<std::vector<HighlightingToken>> HT) mutable {
1618 if (!HT)
1619 return CB(HT.takeError());
1620 std::vector<SemanticToken> Toks = toSemanticTokens(*HT, *Code);
1621
1622 SemanticTokensOrDelta Result;
1623 {
1624 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1625 auto &Last = LastSemanticTokens[File];
1626
1627 if (PrevResultID == Last.resultId) {
1628 Result.edits = diffTokens(Last.tokens, Toks);
1629 } else {
1630 vlog("semanticTokens/full/delta: wanted edits vs {0} but last "
1631 "result had ID {1}. Returning full token list.",
1632 PrevResultID, Last.resultId);
1633 Result.tokens = Toks;
1634 }
1635
1636 Last.tokens = std::move(Toks);
1637 increment(Last.resultId);
1638 Result.resultId = Last.resultId;
1639 }
1640
1641 CB(std::move(Result));
1642 });
1643}
1644
1645void ClangdLSPServer::onMemoryUsage(const NoParams &,
1646 Callback<MemoryTree> Reply) {
1647 llvm::BumpPtrAllocator DetailAlloc;
1648 MemoryTree MT(&DetailAlloc);
1649 profile(MT);
1650 Reply(std::move(MT));
1651}
1652
1653void ClangdLSPServer::onAST(const ASTParams &Params,
1654 Callback<std::optional<ASTNode>> CB) {
1655 Server->getAST(Params.textDocument.uri.file(), Params.range, std::move(CB));
1656}
1657
1659 const ClangdLSPServer::Options &Opts)
1660 : ShouldProfile(/*Period=*/std::chrono::minutes(5),
1661 /*Delay=*/std::chrono::minutes(1)),
1662 ShouldCleanupMemory(/*Period=*/std::chrono::minutes(1),
1663 /*Delay=*/std::chrono::minutes(1)),
1664 BackgroundContext(Context::current().clone()), Transp(Transp),
1665 MsgHandler(new MessageHandler(*this)), TFS(TFS),
1666 SupportedSymbolKinds(defaultSymbolKinds()),
1667 SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts) {
1668 if (Opts.ConfigProvider) {
1669 assert(!Opts.ContextProvider &&
1670 "Only one of ConfigProvider and ContextProvider allowed!");
1671 this->Opts.ContextProvider = ClangdServer::createConfiguredContextProvider(
1672 Opts.ConfigProvider, this);
1673 }
1674 LSPBinder Bind(this->Handlers, *this);
1675 Bind.method("initialize", this, &ClangdLSPServer::onInitialize);
1676}
1677
1678void ClangdLSPServer::bindMethods(LSPBinder &Bind,
1679 const ClientCapabilities &Caps) {
1680 // clang-format off
1681 Bind.notification("initialized", this, &ClangdLSPServer::onInitialized);
1682 Bind.method("shutdown", this, &ClangdLSPServer::onShutdown);
1683 Bind.method("sync", this, &ClangdLSPServer::onSync);
1684 Bind.method("textDocument/rangeFormatting", this, &ClangdLSPServer::onDocumentRangeFormatting);
1685 Bind.method("textDocument/rangesFormatting", this, &ClangdLSPServer::onDocumentRangesFormatting);
1686 Bind.method("textDocument/onTypeFormatting", this, &ClangdLSPServer::onDocumentOnTypeFormatting);
1687 Bind.method("textDocument/formatting", this, &ClangdLSPServer::onDocumentFormatting);
1688 Bind.method("textDocument/codeAction", this, &ClangdLSPServer::onCodeAction);
1689 Bind.method("textDocument/completion", this, &ClangdLSPServer::onCompletion);
1690 Bind.method("textDocument/signatureHelp", this, &ClangdLSPServer::onSignatureHelp);
1691 Bind.method("textDocument/definition", this, &ClangdLSPServer::onGoToDefinition);
1692 Bind.method("textDocument/declaration", this, &ClangdLSPServer::onGoToDeclaration);
1693 Bind.method("textDocument/typeDefinition", this, &ClangdLSPServer::onGoToType);
1694 Bind.method("textDocument/implementation", this, &ClangdLSPServer::onGoToImplementation);
1695 Bind.method("textDocument/references", this, &ClangdLSPServer::onReference);
1696 Bind.method("textDocument/switchSourceHeader", this, &ClangdLSPServer::onSwitchSourceHeader);
1697 Bind.method("textDocument/prepareRename", this, &ClangdLSPServer::onPrepareRename);
1698 Bind.method("textDocument/rename", this, &ClangdLSPServer::onRename);
1699 Bind.method("textDocument/hover", this, &ClangdLSPServer::onHover);
1700 Bind.method("textDocument/documentSymbol", this, &ClangdLSPServer::onDocumentSymbol);
1701 Bind.method("workspace/executeCommand", this, &ClangdLSPServer::onCommand);
1702 Bind.method("textDocument/documentHighlight", this, &ClangdLSPServer::onDocumentHighlight);
1703 Bind.method("workspace/symbol", this, &ClangdLSPServer::onWorkspaceSymbol);
1704 Bind.method("textDocument/ast", this, &ClangdLSPServer::onAST);
1705 Bind.notification("textDocument/didOpen", this, &ClangdLSPServer::onDocumentDidOpen);
1706 Bind.notification("textDocument/didClose", this, &ClangdLSPServer::onDocumentDidClose);
1707 Bind.notification("textDocument/didChange", this, &ClangdLSPServer::onDocumentDidChange);
1708 Bind.notification("textDocument/didSave", this, &ClangdLSPServer::onDocumentDidSave);
1709 Bind.notification("workspace/didChangeWatchedFiles", this, &ClangdLSPServer::onFileEvent);
1710 Bind.notification("workspace/didChangeConfiguration", this, &ClangdLSPServer::onChangeConfiguration);
1711 Bind.method("textDocument/symbolInfo", this, &ClangdLSPServer::onSymbolInfo);
1712 Bind.method("textDocument/typeHierarchy", this, &ClangdLSPServer::onTypeHierarchy);
1713 Bind.method("typeHierarchy/resolve", this, &ClangdLSPServer::onResolveTypeHierarchy);
1714 Bind.method("textDocument/prepareTypeHierarchy", this, &ClangdLSPServer::onPrepareTypeHierarchy);
1715 Bind.method("typeHierarchy/supertypes", this, &ClangdLSPServer::onSuperTypes);
1716 Bind.method("typeHierarchy/subtypes", this, &ClangdLSPServer::onSubTypes);
1717 Bind.method("textDocument/prepareCallHierarchy", this, &ClangdLSPServer::onPrepareCallHierarchy);
1718 Bind.method("callHierarchy/incomingCalls", this, &ClangdLSPServer::onCallHierarchyIncomingCalls);
1719 if (Opts.EnableOutgoingCalls)
1720 Bind.method("callHierarchy/outgoingCalls", this, &ClangdLSPServer::onCallHierarchyOutgoingCalls);
1721 Bind.method("textDocument/selectionRange", this, &ClangdLSPServer::onSelectionRange);
1722 Bind.method("textDocument/documentLink", this, &ClangdLSPServer::onDocumentLink);
1723 Bind.method("textDocument/semanticTokens/full", this, &ClangdLSPServer::onSemanticTokens);
1724 Bind.method("textDocument/semanticTokens/full/delta", this, &ClangdLSPServer::onSemanticTokensDelta);
1725 Bind.method("clangd/inlayHints", this, &ClangdLSPServer::onClangdInlayHints);
1726 Bind.method("textDocument/inlayHint", this, &ClangdLSPServer::onInlayHint);
1727 Bind.method("$/memoryUsage", this, &ClangdLSPServer::onMemoryUsage);
1728 Bind.method("textDocument/foldingRange", this, &ClangdLSPServer::onFoldingRange);
1729 Bind.command(ApplyFixCommand, this, &ClangdLSPServer::onCommandApplyEdit);
1730 Bind.command(ApplyTweakCommand, this, &ClangdLSPServer::onCommandApplyTweak);
1731 Bind.command(ApplyRenameCommand, this, &ClangdLSPServer::onCommandApplyRename);
1732
1733 ApplyWorkspaceEdit = Bind.outgoingMethod("workspace/applyEdit");
1734 PublishDiagnostics = Bind.outgoingNotification("textDocument/publishDiagnostics");
1735 if (Caps.InactiveRegions)
1736 PublishInactiveRegions = Bind.outgoingNotification("textDocument/inactiveRegions");
1737 ShowMessage = Bind.outgoingNotification("window/showMessage");
1738 NotifyFileStatus = Bind.outgoingNotification("textDocument/clangd.fileStatus");
1739 CreateWorkDoneProgress = Bind.outgoingMethod("window/workDoneProgress/create");
1740 BeginWorkDoneProgress = Bind.outgoingNotification("$/progress");
1741 ReportWorkDoneProgress = Bind.outgoingNotification("$/progress");
1742 EndWorkDoneProgress = Bind.outgoingNotification("$/progress");
1744 SemanticTokensRefresh = Bind.outgoingMethod("workspace/semanticTokens/refresh");
1745 // clang-format on
1746}
1747
1749 IsBeingDestroyed = true;
1750 // Explicitly destroy ClangdServer first, blocking on threads it owns.
1751 // This ensures they don't access any other members.
1752 Server.reset();
1753}
1754
1756 // Run the Language Server loop.
1757 bool CleanExit = true;
1758 if (auto Err = Transp.loop(*MsgHandler)) {
1759 elog("Transport error: {0}", std::move(Err));
1760 CleanExit = false;
1761 }
1762
1763 return CleanExit && ShutdownRequestReceived;
1764}
1765
1767 if (Server)
1768 Server->profile(MT.child("clangd_server"));
1769}
1770
1771std::optional<ClangdServer::DiagRef>
1772ClangdLSPServer::getDiagRef(StringRef File, const clangd::Diagnostic &D) {
1773 std::lock_guard<std::mutex> Lock(DiagRefMutex);
1774 auto DiagToDiagRefIter = DiagRefMap.find(File);
1775 if (DiagToDiagRefIter == DiagRefMap.end())
1776 return std::nullopt;
1777
1778 const auto &DiagToDiagRefMap = DiagToDiagRefIter->second;
1779 auto FixItsIter = DiagToDiagRefMap.find(toDiagKey(D));
1780 if (FixItsIter == DiagToDiagRefMap.end())
1781 return std::nullopt;
1782
1783 return FixItsIter->second;
1784}
1785
1786// A completion request is sent when the user types '>' or ':', but we only
1787// want to trigger on '->' and '::'. We check the preceding text to make
1788// sure it matches what we expected.
1789// Running the lexer here would be more robust (e.g. we can detect comments
1790// and avoid triggering completion there), but we choose to err on the side
1791// of simplicity here.
1792bool ClangdLSPServer::shouldRunCompletion(
1793 const CompletionParams &Params) const {
1794 if (Params.context.triggerKind != CompletionTriggerKind::TriggerCharacter)
1795 return true;
1796 auto Code = Server->getDraft(Params.textDocument.uri.file());
1797 if (!Code)
1798 return true; // completion code will log the error for untracked doc.
1799 auto Offset = positionToOffset(*Code, Params.position,
1800 /*AllowColumnsBeyondLineLength=*/false);
1801 if (!Offset) {
1802 vlog("could not convert position '{0}' to offset for file '{1}'",
1803 Params.position, Params.textDocument.uri.file());
1804 return true;
1805 }
1806 return allowImplicitCompletion(*Code, *Offset);
1807}
1808
1809void ClangdLSPServer::onDiagnosticsReady(PathRef File, llvm::StringRef Version,
1810 llvm::ArrayRef<Diag> Diagnostics) {
1811 PublishDiagnosticsParams Notification;
1812 Notification.version = decodeVersion(Version);
1813 Notification.uri = URIForFile::canonicalize(File, /*TUPath=*/File);
1814 DiagnosticToDiagRefMap LocalDiagMap; // Temporary storage
1815 for (auto &Diag : Diagnostics) {
1816 toLSPDiags(Diag, Notification.uri, DiagOpts,
1817 [&](clangd::Diagnostic LSPDiag, llvm::ArrayRef<Fix> Fixes) {
1818 if (DiagOpts.EmbedFixesInDiagnostics) {
1819 std::vector<CodeAction> CodeActions;
1820 for (const auto &Fix : Fixes)
1821 CodeActions.push_back(toCodeAction(
1822 Fix, Notification.uri, Notification.version,
1823 SupportsDocumentChanges, SupportsChangeAnnotation));
1824 LSPDiag.codeActions.emplace(std::move(CodeActions));
1825 if (LSPDiag.codeActions->size() == 1)
1826 LSPDiag.codeActions->front().isPreferred = true;
1827 }
1828 LocalDiagMap[toDiagKey(LSPDiag)] = {Diag.Range, Diag.Message};
1829 Notification.diagnostics.push_back(std::move(LSPDiag));
1830 });
1831 }
1832
1833 // Cache DiagRefMap
1834 {
1835 std::lock_guard<std::mutex> Lock(DiagRefMutex);
1836 DiagRefMap[File] = LocalDiagMap;
1837 }
1838
1839 // Send a notification to the LSP client.
1840 PublishDiagnostics(Notification);
1841}
1842
1843void ClangdLSPServer::onInactiveRegionsReady(
1844 PathRef File, std::vector<Range> InactiveRegions) {
1845 InactiveRegionsParams Notification;
1846 Notification.TextDocument = {URIForFile::canonicalize(File, /*TUPath=*/File)};
1847 Notification.InactiveRegions = std::move(InactiveRegions);
1848
1849 PublishInactiveRegions(Notification);
1850}
1851
1852void ClangdLSPServer::onBackgroundIndexProgress(
1853 const BackgroundQueue::Stats &Stats) {
1854 static const char ProgressToken[] = "backgroundIndexProgress";
1855
1856 // The background index did some work, maybe we need to cleanup
1857 maybeCleanupMemory();
1858
1859 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1860
1861 auto NotifyProgress = [this](const BackgroundQueue::Stats &Stats) {
1862 if (BackgroundIndexProgressState != BackgroundIndexProgress::Live) {
1863 WorkDoneProgressBegin Begin;
1864 Begin.percentage = true;
1865 Begin.title = "indexing";
1866 BeginWorkDoneProgress({ProgressToken, std::move(Begin)});
1867 BackgroundIndexProgressState = BackgroundIndexProgress::Live;
1868 }
1869
1870 if (Stats.Completed < Stats.Enqueued) {
1871 assert(Stats.Enqueued > Stats.LastIdle);
1872 WorkDoneProgressReport Report;
1873 Report.percentage = 100 * (Stats.Completed - Stats.LastIdle) /
1874 (Stats.Enqueued - Stats.LastIdle);
1875 Report.message =
1876 llvm::formatv("{0}/{1}", Stats.Completed - Stats.LastIdle,
1877 Stats.Enqueued - Stats.LastIdle);
1878 ReportWorkDoneProgress({ProgressToken, std::move(Report)});
1879 } else {
1880 assert(Stats.Completed == Stats.Enqueued);
1881 EndWorkDoneProgress({ProgressToken, WorkDoneProgressEnd()});
1882 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
1883 }
1884 };
1885
1886 switch (BackgroundIndexProgressState) {
1887 case BackgroundIndexProgress::Unsupported:
1888 return;
1889 case BackgroundIndexProgress::Creating:
1890 // Cache this update for when the progress bar is available.
1891 PendingBackgroundIndexProgress = Stats;
1892 return;
1893 case BackgroundIndexProgress::Empty: {
1894 if (BackgroundIndexSkipCreate) {
1895 NotifyProgress(Stats);
1896 break;
1897 }
1898 // Cache this update for when the progress bar is available.
1899 PendingBackgroundIndexProgress = Stats;
1900 BackgroundIndexProgressState = BackgroundIndexProgress::Creating;
1901 WorkDoneProgressCreateParams CreateRequest;
1902 CreateRequest.token = ProgressToken;
1903 CreateWorkDoneProgress(
1904 CreateRequest,
1905 [this, NotifyProgress](llvm::Expected<std::nullptr_t> E) {
1906 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1907 if (E) {
1908 NotifyProgress(this->PendingBackgroundIndexProgress);
1909 } else {
1910 elog("Failed to create background index progress bar: {0}",
1911 E.takeError());
1912 // give up forever rather than thrashing about
1913 BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported;
1914 }
1915 });
1916 break;
1917 }
1918 case BackgroundIndexProgress::Live:
1919 NotifyProgress(Stats);
1920 break;
1921 }
1922}
1923
1924void ClangdLSPServer::onFileUpdated(PathRef File, const TUStatus &Status) {
1925 if (!SupportFileStatus)
1926 return;
1927 // FIXME: we don't emit "BuildingFile" and `RunningAction`, as these
1928 // two statuses are running faster in practice, which leads the UI constantly
1929 // changing, and doesn't provide much value. We may want to emit status at a
1930 // reasonable time interval (e.g. 0.5s).
1931 if (Status.PreambleActivity == PreambleAction::Idle &&
1932 (Status.ASTActivity.K == ASTAction::Building ||
1933 Status.ASTActivity.K == ASTAction::RunningAction))
1934 return;
1935 NotifyFileStatus(Status.render(File));
1936}
1937
1938void ClangdLSPServer::onSemanticsMaybeChanged(PathRef File) {
1939 if (SemanticTokensRefresh) {
1940 SemanticTokensRefresh(NoParams{}, [](llvm::Expected<std::nullptr_t> E) {
1941 if (E)
1942 return;
1943 elog("Failed to refresh semantic tokens: {0}", E.takeError());
1944 });
1945 }
1946}
1947
1948} // namespace clangd
1949} // namespace clang
static cl::list< std::string > Commands("c", cl::desc("Specify command to run"), cl::value_desc("command"), cl::cat(ClangQueryCategory))
#define SPAN_ATTACH(S, Name, Expr)
Attach a key-value pair to a Span event.
Definition Trace.h:164
bool onReply(llvm::json::Value ID, llvm::Expected< llvm::json::Value > Result) override
bool onCall(llvm::StringRef Method, llvm::json::Value Params, llvm::json::Value ID) override
bool onNotify(llvm::StringRef Method, llvm::json::Value Params) override
llvm::json::Value bindReply(Callback< llvm::json::Value > Reply)
This class exposes ClangdServer's capabilities via Language Server Protocol.
~ClangdLSPServer()
The destructor blocks on any outstanding background tasks.
ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts)
void profile(MemoryTree &MT) const
Profiles resource-usage.
bool run()
Run LSP server loop, communicating with the Transport provided in the constructor.
Manages a collection of source files and derived data (ASTs, indexes), and provides language-aware fe...
A context is an immutable container for per-request data that must be propagated through layers that ...
Definition Context.h:69
Context derive(const Key< Type > &Key, std::decay_t< Type > Value) const &
Derives a child context It is safe to move or destroy a parent context after calling derive().
Definition Context.h:119
static const Context & current()
Returns the context for the current thread, creating it if needed.
Definition Context.cpp:27
LSPBinder collects a table of functions that handle LSP calls.
Definition LSPBinder.h:34
UntypedOutgoingMethod outgoingMethod(llvm::StringLiteral Method)
Bind a function object to be used for outgoing method calls.
Definition LSPBinder.h:216
void method(llvm::StringLiteral Method, ThisT *This, void(ThisT::*Handler)(const Param &, Callback< Result >))
Bind a handler for an LSP method.
Definition LSPBinder.h:133
void notification(llvm::StringLiteral Method, ThisT *This, void(ThisT::*Handler)(const Param &))
Bind a handler for an LSP notification.
Definition LSPBinder.h:146
UntypedOutgoingNotification outgoingNotification(llvm::StringLiteral Method)
Bind a function object to be used for outgoing notifications.
Definition LSPBinder.h:186
void command(llvm::StringLiteral Command, ThisT *This, void(ThisT::*Handler)(const Param &, Callback< Result >))
Bind a handler for an LSP command.
Definition LSPBinder.h:158
A threadsafe flag that is initially clear.
Definition Threading.h:91
Wrapper for vfs::FileSystem for use in multithreaded programs like clangd.
static URI createFile(llvm::StringRef AbsolutePath)
This creates a file:// URI for AbsolutePath. The path must be absolute.
Definition URI.cpp:237
std::string toString() const
Returns a string URI with all components percent-encoded.
Definition URI.cpp:160
WithContext replaces Context::current() with a provided scope.
Definition Context.h:185
Records an event whose duration is the lifetime of the Span object.
Definition Trace.h:143
llvm::json::Object *const Args
Mutable metadata, if this span is interested.
Definition Trace.h:154
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:45
@ Info
An information message.
Definition Protocol.h:738
llvm::StringRef toSemanticTokenModifier(HighlightingModifier Modifier)
constexpr auto CompletionItemKindMin
Definition Protocol.h:368
llvm::Error applyChange(std::string &Contents, const TextDocumentContentChangeEvent &Change)
Apply an incremental update to a text document.
llvm::StringRef toSemanticTokenType(HighlightingKind Kind)
SystemIncludeExtractorFn getSystemIncludeExtractor(llvm::ArrayRef< std::string > QueryDriverGlobs)
static std::vector< llvm::StringRef > semanticTokenModifiers()
constexpr auto SymbolKindMin
Definition Protocol.h:409
Key< OffsetEncoding > kCurrentOffsetEncoding
void toLSPDiags(const Diag &D, const URIForFile &File, const ClangdDiagnosticOptions &Opts, llvm::function_ref< void(clangd::Diagnostic, llvm::ArrayRef< Fix >)> OutFn)
Conversion to LSP diagnostics.
@ TriggerCharacter
Completion was triggered by a trigger character specified by the triggerCharacters properties of the ...
Definition Protocol.h:1231
bool allowImplicitCompletion(llvm::StringRef Content, unsigned Offset)
void record(const MemoryTree &MT, std::string RootName, const trace::Metric &Out)
Records total memory usage of each node under Out.
void vlog(const char *Fmt, Ts &&... Vals)
Definition Logger.h:72
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
Definition Function.h:28
std::string platformString()
Definition Feature.cpp:20
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
Definition Logger.h:79
llvm::json::Value serializeTHIForExtension(TypeHierarchyItem THI)
static std::vector< SymbolInformation > flattenSymbolHierarchy(llvm::ArrayRef< DocumentSymbol > Symbols, const URIForFile &FileURI)
The functions constructs a flattened view of the DocumentSymbol hierarchy.
std::bitset< SymbolKindMax+1 > SymbolKindBitset
Definition Protocol.h:411
static void increment(std::string &S)
std::string featureString()
Definition Feature.cpp:33
llvm::StringMap< Edit > FileEdits
A mapping from absolute file path (the one used for accessing the underlying VFS) to edits.
Definition SourceCode.h:209
SymbolKind adjustKindToCapability(SymbolKind Kind, SymbolKindBitset &SupportedSymbolKinds)
Definition Protocol.cpp:280
static std::optional< Command > asCommand(const CodeAction &Action)
std::pair< Context, Canceler > cancelableTask(int Reason)
Defines a new task whose cancellation may be requested.
NoParams InitializedParams
Definition Protocol.h:321
void log(const char *Fmt, Ts &&... Vals)
Definition Logger.h:67
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
std::bitset< CompletionItemKindMax+1 > CompletionItemKindBitset
Definition Protocol.h:372
static std::vector< llvm::StringRef > semanticTokenTypes()
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition Path.h:29
@ Auto
Diagnostics must not be generated for this snapshot.
Definition TUScheduler.h:56
@ No
Diagnostics must be generated for this snapshot.
Definition TUScheduler.h:55
static Location * getToggle(const TextDocumentPositionParams &Point, LocatedSymbol &Sym)
std::string Path
A typedef to represent a file path.
Definition Path.h:26
std::function< void()> Canceler
A canceller requests cancellation of a task, when called.
@ Incremental
Documents are synced by sending the full content on open.
Definition Protocol.h:334
void elog(const char *Fmt, Ts &&... Vals)
Definition Logger.h:61
std::string versionString()
Definition Feature.cpp:18
std::vector< TextEdit > replacementsToEdits(llvm::StringRef Code, const tooling::Replacements &Repls)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::optional< OffsetEncoding > Encoding
The offset-encoding to use, or std::nullopt to negotiate it over LSP.
bool EnableOutgoingCalls
Call hierarchy's outgoing calls feature requires additional index serving structures which increase m...
bool SemanticTokenRefreshSupport
Whether the client implementation supports a refresh request sent from the server to the client.
Definition Protocol.h:559
bool InactiveRegions
Whether the client supports the textDocument/inactiveRegions notification.
Definition Protocol.h:570
A code action represents a change that can be performed in code, e.g.
Definition Protocol.h:1077
static const llvm::StringLiteral INFO_KIND
Definition Protocol.h:1086
static const llvm::StringLiteral REFACTOR_KIND
Definition Protocol.h:1085
static const llvm::StringLiteral QUICKFIX_KIND
Definition Protocol.h:1084
std::optional< WorkspaceEdit > edit
The workspace edit this code action performs.
Definition Protocol.h:1099
std::optional< Command > command
A command this code action executes.
Definition Protocol.h:1103
std::optional< std::string > kind
The kind of the code action.
Definition Protocol.h:1083
std::string title
A short, human-readable, title for this code action.
Definition Protocol.h:1079
static CommandMangler detect()
Represents programming constructs like variables, classes, interfaces etc.
Definition Protocol.h:1111
std::vector< DocumentSymbol > children
Children of this symbol, e.g. properties of a class.
Definition Protocol.h:1135
std::string name
The name of this symbol.
Definition Protocol.h:1113
Range range
The range enclosing this symbol not including leading/trailing whitespace but everything else like co...
Definition Protocol.h:1128
SymbolKind kind
The kind of this symbol.
Definition Protocol.h:1119
A set of edits generated for a single file.
Definition SourceCode.h:189
Represents a single fix-it that editor can apply to fix the error.
Definition Diagnostics.h:81
Location PreferredDeclaration
Definition XRefs.h:45
std::optional< Location > Definition
Definition XRefs.h:47
URIForFile uri
The text document's URI.
Definition Protocol.h:213
A tree that can be used to represent memory usage of nested components while preserving the hierarchy...
Definition MemoryTree.h:30
MemoryTree & child(llvm::StringLiteral Name)
No copy of the Name.
Definition MemoryTree.h:39
bool contains(Position Pos) const
Definition Protocol.h:202
TextDocumentIdentifier textDocument
The document that was opened.
Definition Protocol.h:1443
Represents information about programming constructs like variables, classes, interfaces etc.
Definition Protocol.h:1142
std::string containerName
The name of the symbol containing this symbol.
Definition Protocol.h:1153
Location location
The location of this symbol.
Definition Protocol.h:1150
SymbolKind kind
The kind of this symbol.
Definition Protocol.h:1147
std::string name
The name of this symbol.
Definition Protocol.h:1144
URIForFile uri
The text document's URI.
Definition Protocol.h:133
TextDocumentIdentifier textDocument
The text document.
Definition Protocol.h:1217
Position position
The position inside the text document.
Definition Protocol.h:1220
Arguments for the 'applyTweak' command.
Definition Protocol.h:1045
URIForFile file
A file provided by the client on a textDocument/codeAction request.
Definition Protocol.h:1047
Range range
The range enclosing this symbol not including leading/trailing whitespace but everything else,...
Definition Protocol.h:1524
URIForFile uri
The resource identifier of this item.
Definition Protocol.h:1520
Range selectionRange
The range that should be selected and revealed when this symbol is being picked, e....
Definition Protocol.h:1528
SymbolKind kind
The kind of this item.
Definition Protocol.h:1514
std::optional< std::vector< TypeHierarchyItem > > children
If this type hierarchy item is resolved, it contains the direct children of the current item.
Definition Protocol.h:1553
std::optional< std::vector< TypeHierarchyItem > > parents
This is a clangd exntesion.
Definition Protocol.h:1547
bool deprecated
true if the hierarchy item is deprecated.
Definition Protocol.h:1544
std::optional< std::string > detail
More detail for this item, e.g. the signature of a function.
Definition Protocol.h:1517
ResolveParams data
A data entry field that is preserved between a type hierarchy prepare and supertypes or subtypes requ...
Definition Protocol.h:1540
std::string name
The name of this item.
Definition Protocol.h:1511
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Canonicalizes AbsPath via URI.
Definition Protocol.cpp:46
llvm::StringRef file() const
Retrieves absolute path to the file.
Definition Protocol.h:104
The edit should either provide changes or documentChanges.
Definition Protocol.h:1024
The parameters of a Workspace Symbol Request.
Definition Protocol.h:1191
Represents measurements of clangd events, e.g.
Definition Trace.h:38
@ Distribution
A distribution of values with a meaningful mean and count.
Definition Trace.h:52