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