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