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