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