27 #include "clang/Tooling/Core/Replacement.h"
28 #include "llvm/ADT/ArrayRef.h"
29 #include "llvm/ADT/Optional.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"
59 std::string encodeVersion(llvm::Optional<int64_t> LSPVersion) {
60 return LSPVersion ? llvm::to_string(*LSPVersion) :
"";
62 llvm::Optional<int64_t> decodeVersion(llvm::StringRef Encoded) {
64 if (llvm::to_integer(Encoded, Result, 10))
67 elog(
"unexpected non-numeric version {0}", Encoded);
71 const llvm::StringLiteral ApplyFixCommand =
"clangd.applyFix";
72 const llvm::StringLiteral ApplyTweakCommand =
"clangd.applyTweak";
76 CodeAction
toCodeAction(
const ClangdServer::TweakRef &T,
const URIForFile &File,
80 CA.kind = T.Kind.str();
87 CA.command->title = T.Title;
88 CA.command->command = std::string(ApplyTweakCommand);
92 Args.selection = Selection;
93 CA.command->argument = std::move(
Args);
97 void adjustSymbolKinds(llvm::MutableArrayRef<DocumentSymbol> Syms,
99 for (
auto &S : Syms) {
101 adjustSymbolKinds(S.children, Kinds);
124 llvm::Error validateEdits(
const ClangdServer &Server,
const FileEdits &FE) {
125 size_t InvalidFileCount = 0;
126 llvm::StringRef LastInvalidFile;
127 for (
const auto &It : FE) {
128 if (
auto Draft = Server.getDraft(It.first())) {
132 if (!It.second.canApplyTo(*Draft)) {
134 LastInvalidFile = It.first();
138 if (!InvalidFileCount)
139 return llvm::Error::success();
140 if (InvalidFileCount == 1)
141 return error(
"File must be saved first: {0}", LastInvalidFile);
142 return error(
"Files must be saved first: {0} (and {1} others)",
143 LastInvalidFile, InvalidFileCount - 1);
165 auto Handler = Server.Handlers.NotificationHandlers.find(
Method);
166 if (Handler != Server.Handlers.NotificationHandlers.end()) {
167 Handler->second(std::move(Params));
168 Server.maybeExportMemoryProfile();
169 Server.maybeCleanupMemory();
170 }
else if (!Server.Server) {
171 elog(
"Notification {0} before initialization",
Method);
172 }
else if (
Method ==
"$/cancelRequest") {
173 onCancel(std::move(Params));
175 log(
"unhandled notification {0}",
Method);
189 auto Handler = Server.Handlers.MethodHandlers.find(
Method);
190 if (Handler != Server.Handlers.MethodHandlers.end()) {
191 Handler->second(std::move(Params), std::move(Reply));
192 }
else if (!Server.Server) {
193 elog(
"Call {0} before initialization.",
Method);
194 Reply(llvm::make_error<LSPError>(
"server not initialized",
197 Reply(llvm::make_error<LSPError>(
"method not found",
204 llvm::Expected<llvm::json::Value> Result)
override {
208 if (
auto IntID =
ID.getAsInteger()) {
209 std::lock_guard<std::mutex> Mutex(CallMutex);
212 if (ReplyCallbacks[
Index].first == *IntID) {
213 ReplyHandler = std::move(ReplyCallbacks[
Index].second);
214 ReplyCallbacks.erase(ReplyCallbacks.begin() +
223 ReplyHandler = [&
ID](llvm::Expected<llvm::json::Value> Result) {
224 elog(
"received a reply with ID {0}, but there was no such call",
ID);
226 llvm::consumeError(Result.takeError());
232 log(
"<-- reply({0})",
ID);
233 ReplyHandler(std::move(Result));
235 auto Err = Result.takeError();
236 log(
"<-- reply({0}) error: {1}",
ID, Err);
237 ReplyHandler(std::move(Err));
246 llvm::Optional<std::pair<int, Callback<llvm::json::Value>>> OldestCB;
249 std::lock_guard<std::mutex> Mutex(CallMutex);
251 ReplyCallbacks.emplace_back(
ID, std::move(Reply));
256 if (ReplyCallbacks.size() > MaxReplayCallbacks) {
257 elog(
"more than {0} outstanding LSP calls, forgetting about {1}",
258 MaxReplayCallbacks, ReplyCallbacks.front().first);
259 OldestCB = std::move(ReplyCallbacks.front());
260 ReplyCallbacks.pop_front();
265 error(
"failed to receive a client reply for request ({0})",
277 std::atomic<bool> Replied = {
false};
278 std::chrono::steady_clock::time_point Start;
282 llvm::json::Object *TraceArgs;
288 Server(Server), TraceArgs(TraceArgs) {
291 ReplyOnce(ReplyOnce &&Other)
292 : Replied(Other.Replied.load()), Start(Other.Start),
293 ID(std::move(Other.
ID)), Method(std::move(Other.Method)),
294 Server(Other.Server), TraceArgs(Other.TraceArgs) {
295 Other.Server =
nullptr;
297 ReplyOnce &operator=(ReplyOnce &&) =
delete;
298 ReplyOnce(
const ReplyOnce &) =
delete;
299 ReplyOnce &operator=(
const ReplyOnce &) =
delete;
308 if (Server && !Server->IsBeingDestroyed && !Replied) {
309 elog(
"No reply to message {0}({1})", Method,
ID);
310 assert(
false &&
"must reply to all calls!");
311 (*this)(llvm::make_error<LSPError>(
"server failed to reply",
316 void operator()(llvm::Expected<llvm::json::Value> Reply) {
317 assert(Server &&
"moved-from!");
318 if (Replied.exchange(
true)) {
319 elog(
"Replied twice to message {0}({1})", Method,
ID);
320 assert(
false &&
"must reply to each call only once!");
323 auto Duration = std::chrono::steady_clock::now() - Start;
325 log(
"--> reply:{0}({1}) {2:ms}", Method,
ID, Duration);
327 (*TraceArgs)[
"Reply"] = *Reply;
328 std::lock_guard<std::mutex> Lock(Server->TranspWriter);
329 Server->Transp.reply(std::move(
ID), std::move(Reply));
331 llvm::Error Err = Reply.takeError();
332 log(
"--> reply:{0}({1}) {2:ms}, error: {3}", Method,
ID, Duration, Err);
334 (*TraceArgs)[
"Error"] = llvm::to_string(Err);
335 std::lock_guard<std::mutex> Lock(Server->TranspWriter);
336 Server->Transp.reply(std::move(
ID), std::move(Err));
344 mutable std::mutex RequestCancelersMutex;
345 llvm::StringMap<std::pair<
Canceler,
unsigned>> RequestCancelers;
346 unsigned NextRequestCookie = 0;
349 if (
auto *O = Params.getAsObject())
352 elog(
"Bad cancellation request: {0}", Params);
355 auto StrID = llvm::to_string(*
ID);
356 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
357 auto It = RequestCancelers.find(StrID);
358 if (It != RequestCancelers.end())
362 Context handlerContext()
const {
375 auto StrID = llvm::to_string(
ID);
376 auto Cookie = NextRequestCookie++;
378 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
379 RequestCancelers[StrID] = {std::move(Task.second), Cookie};
384 return Task.first.derive(llvm::make_scope_exit([
this, StrID, Cookie] {
385 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
386 auto It = RequestCancelers.find(StrID);
387 if (It != RequestCancelers.end() && It->second.second == Cookie)
388 RequestCancelers.erase(It);
398 static constexpr
int MaxReplayCallbacks = 100;
399 mutable std::mutex CallMutex;
401 std::deque<std::pair< int,
402 Callback<llvm::json::Value>>>
407 constexpr
int ClangdLSPServer::MessageHandler::MaxReplayCallbacks;
411 Callback<llvm::json::Value> CB) {
412 auto ID = MsgHandler->bindReply(std::move(CB));
413 log(
"--> {0}({1})", Method,
ID);
414 std::lock_guard<std::mutex> Lock(TranspWriter);
415 Transp.
call(Method, std::move(Params),
ID);
419 log(
"--> {0}", Method);
420 maybeCleanupMemory();
421 std::lock_guard<std::mutex> Lock(TranspWriter);
422 Transp.
notify(Method, std::move(Params));
426 std::vector<llvm::StringRef> Types;
434 std::vector<llvm::StringRef> Modifiers;
442 void ClangdLSPServer::onInitialize(
const InitializeParams &Params,
443 Callback<llvm::json::Value> Reply) {
445 if (Params.capabilities.offsetEncoding && !Opts.
Encoding) {
447 for (
OffsetEncoding Supported : *Params.capabilities.offsetEncoding)
454 if (Params.capabilities.TheiaSemanticHighlighting &&
455 !Params.capabilities.SemanticTokens) {
456 elog(
"Client requested legacy semanticHighlights notification, which is "
457 "no longer supported. Migrate to standard semanticTokens request");
460 if (Params.rootUri && *Params.rootUri)
462 else if (Params.rootPath && !Params.rootPath->empty())
465 return Reply(llvm::make_error<LSPError>(
"server already initialized",
468 DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS);
469 if (
const auto &Dir = Params.initializationOptions.compilationDatabasePath)
470 CDBOpts.CompileCommandsDir = Dir;
473 std::make_unique<DirectoryBasedGlobalCompilationDatabase>(CDBOpts);
480 CDB.emplace(BaseCDB.get(), Params.initializationOptions.fallbackFlags,
481 tooling::ArgumentsAdjuster(std::move(Mangler)));
486 WithContext MainContext(BackgroundContext.
clone());
487 llvm::Optional<WithContextValue> WithOffsetEncoding;
490 Server.emplace(*CDB, TFS, Opts,
491 static_cast<ClangdServer::Callbacks *
>(
this));
499 Params.capabilities.CompletionDocumentationFormat;
501 Params.capabilities.SignatureHelpDocumentationFormat;
505 Params.capabilities.DiagnosticRelatedInformation;
506 if (Params.capabilities.WorkspaceSymbolKinds)
507 SupportedSymbolKinds |= *Params.capabilities.WorkspaceSymbolKinds;
508 if (Params.capabilities.CompletionItemKinds)
509 SupportedCompletionItemKinds |= *Params.capabilities.CompletionItemKinds;
510 SupportsCodeAction = Params.capabilities.CodeActionStructure;
511 SupportsHierarchicalDocumentSymbol =
512 Params.capabilities.HierarchicalDocumentSymbol;
513 SupportFileStatus = Params.initializationOptions.FileStatus;
514 HoverContentFormat = Params.capabilities.HoverContentFormat;
515 SupportsOffsetsInSignatureHelp = Params.capabilities.OffsetsInSignatureHelp;
516 if (Params.capabilities.WorkDoneProgress)
517 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
518 BackgroundIndexSkipCreate = Params.capabilities.ImplicitProgressCreation;
521 llvm::json::Object ServerCaps{
528 {
"documentFormattingProvider",
true},
529 {
"documentRangeFormattingProvider",
true},
530 {
"documentOnTypeFormattingProvider",
532 {
"firstTriggerCharacter",
"\n"},
533 {
"moreTriggerCharacter", {}},
535 {
"completionProvider",
537 {
"allCommitCharacters",
538 {
" ",
"\t",
"(",
")",
"[",
"]",
"{",
"}",
"<",
539 ">",
":",
";",
",",
"+",
"-",
"/",
"*",
"%",
540 "^",
"&",
"#",
"?",
".",
"=",
"\"",
"'",
"|"}},
541 {
"resolveProvider",
false},
543 {
"triggerCharacters", {
".",
"<",
">",
":",
"\"",
"/",
"*"}},
545 {
"semanticTokensProvider",
547 {
"full", llvm::json::Object{{
"delta",
true}}},
553 {
"signatureHelpProvider",
555 {
"triggerCharacters", {
"(",
")",
"{",
"}",
"<",
">",
","}},
557 {
"declarationProvider",
true},
558 {
"definitionProvider",
true},
559 {
"implementationProvider",
true},
560 {
"typeDefinitionProvider",
true},
561 {
"documentHighlightProvider",
true},
562 {
"documentLinkProvider",
564 {
"resolveProvider",
false},
566 {
"hoverProvider",
true},
567 {
"selectionRangeProvider",
true},
568 {
"documentSymbolProvider",
true},
569 {
"workspaceSymbolProvider",
true},
570 {
"referencesProvider",
true},
571 {
"astProvider",
true},
572 {
"typeHierarchyProvider",
true},
573 {
"memoryUsageProvider",
true},
574 {
"compilationDatabase",
575 llvm::json::Object{{
"automaticReload",
true}}},
576 {
"callHierarchyProvider",
true},
577 {
"clangdInlayHintsProvider",
true},
578 {
"inlayHintProvider",
true},
582 LSPBinder Binder(Handlers, *
this);
583 bindMethods(Binder, Params.capabilities);
586 Mod.initializeLSP(Binder, Params.rawCapabilities, ServerCaps);
591 ServerCaps[
"renameProvider"] =
592 Params.capabilities.RenamePrepareSupport
593 ? llvm::json::Object{{
"prepareProvider",
true}}
600 ServerCaps[
"codeActionProvider"] =
601 Params.capabilities.CodeActionStructure
602 ? llvm::json::Object{{
"codeActionKinds",
609 ServerCaps[
"foldingRangeProvider"] =
true;
611 std::vector<llvm::StringRef>
Commands;
615 ServerCaps[
"executeCommandProvider"] =
616 llvm::json::Object{{
"commands",
Commands}};
618 llvm::json::Object Result{
624 {
"capabilities", std::move(ServerCaps)}}};
626 Result[
"offsetEncoding"] = *Opts.
Encoding;
627 Reply(std::move(Result));
631 applyConfiguration(Params.initializationOptions.ConfigSettings);
636 void ClangdLSPServer::onShutdown(
const NoParams &,
637 Callback<std::nullptr_t> Reply) {
639 ShutdownRequestReceived =
true;
645 void ClangdLSPServer::onSync(
const NoParams &, Callback<std::nullptr_t> Reply) {
646 if (Server->blockUntilIdleForTest(60))
649 Reply(
error(
"Not idle after a minute"));
652 void ClangdLSPServer::onDocumentDidOpen(
653 const DidOpenTextDocumentParams &Params) {
654 PathRef File = Params.textDocument.uri.file();
656 const std::string &Contents = Params.textDocument.text;
658 Server->addDocument(File, Contents,
659 encodeVersion(Params.textDocument.version),
663 void ClangdLSPServer::onDocumentDidChange(
664 const DidChangeTextDocumentParams &Params) {
666 if (Params.wantDiagnostics)
670 PathRef File = Params.textDocument.uri.file();
671 auto Code = Server->getDraft(File);
673 log(
"Trying to incrementally change non-added document: {0}", File);
676 std::string NewCode(*
Code);
677 for (
const auto &Change : Params.contentChanges) {
682 Server->removeDocument(File);
683 elog(
"Failed to update {0}: {1}", File, std::move(Err));
687 Server->addDocument(File, NewCode, encodeVersion(Params.textDocument.version),
691 void ClangdLSPServer::onDocumentDidSave(
692 const DidSaveTextDocumentParams &Params) {
693 Server->reparseOpenFilesIfNeeded([](llvm::StringRef) {
return true; });
696 void ClangdLSPServer::onFileEvent(
const DidChangeWatchedFilesParams &Params) {
701 Server->onFileEvent(Params);
708 void ClangdLSPServer::onCommand(
const ExecuteCommandParams &Params,
709 Callback<llvm::json::Value> Reply) {
712 return Reply(llvm::make_error<LSPError>(
713 llvm::formatv(
"Unsupported command \"{0}\".", Params.command).str(),
716 It->second(Params.argument, std::move(Reply));
719 void ClangdLSPServer::onCommandApplyEdit(
const WorkspaceEdit &WE,
720 Callback<llvm::json::Value> Reply) {
729 applyEdit(WE,
"Fix applied.", std::move(Reply));
732 void ClangdLSPServer::onCommandApplyTweak(
const TweakArgs &
Args,
733 Callback<llvm::json::Value> Reply) {
734 auto Action = [
this, Reply = std::move(Reply)](
735 llvm::Expected<Tweak::Effect> R)
mutable {
737 return Reply(R.takeError());
739 assert(R->ShowMessage || (!R->ApplyEdits.empty() &&
"tweak has no effect"));
741 if (R->ShowMessage) {
742 ShowMessageParams Msg;
743 Msg.message = *R->ShowMessage;
748 if (R->ApplyEdits.empty())
749 return Reply(
"Tweak applied.");
751 if (
auto Err = validateEdits(*Server, R->ApplyEdits))
752 return Reply(std::move(Err));
755 for (
const auto &It : R->ApplyEdits) {
757 It.second.asTextEdits();
760 return applyEdit(std::move(WE),
"Tweak applied.", std::move(Reply));
762 Server->applyTweak(
Args.file.file(),
Args.selection,
Args.tweakID,
767 Callback<llvm::json::Value> Reply) {
768 ApplyWorkspaceEditParams Edit;
769 Edit.edit = std::move(WE);
771 Edit, [Reply = std::move(Reply), SuccessMessage = std::move(Success)](
772 llvm::Expected<ApplyWorkspaceEditResponse> Response)
mutable {
774 return Reply(Response.takeError());
775 if (!Response->applied) {
776 std::string Reason = Response->failureReason
777 ? *Response->failureReason
779 return Reply(error(
"edits were not applied: {0}", Reason));
781 return Reply(SuccessMessage);
785 void ClangdLSPServer::onWorkspaceSymbol(
786 const WorkspaceSymbolParams &Params,
787 Callback<std::vector<SymbolInformation>> Reply) {
788 Server->workspaceSymbols(
790 [Reply = std::move(Reply),
791 this](
llvm::Expected<std::vector<SymbolInformation>> Items)
mutable {
793 return Reply(Items.takeError());
794 for (auto &Sym : *Items)
795 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds);
797 Reply(std::move(*Items));
801 void ClangdLSPServer::onPrepareRename(
const TextDocumentPositionParams &Params,
802 Callback<llvm::Optional<Range>> Reply) {
803 Server->prepareRename(
804 Params.textDocument.uri.file(), Params.position, llvm::None,
806 [Reply = std::move(Reply)](llvm::Expected<RenameResult> Result)
mutable {
808 return Reply(Result.takeError());
809 return Reply(std::move(Result->Target));
813 void ClangdLSPServer::onRename(
const RenameParams &Params,
814 Callback<WorkspaceEdit> Reply) {
815 Path File = std::string(Params.textDocument.uri.file());
816 if (!Server->getDraft(File))
817 return Reply(llvm::make_error<LSPError>(
819 Server->rename(File, Params.position, Params.newName, Opts.
Rename,
820 [File, Params, Reply = std::move(Reply),
821 this](llvm::Expected<RenameResult> R)
mutable {
823 return Reply(R.takeError());
824 if (auto Err = validateEdits(*Server, R->GlobalChanges))
825 return Reply(std::move(Err));
826 WorkspaceEdit Result;
827 for (const auto &Rep : R->GlobalChanges) {
828 Result.changes[URI::createFile(Rep.first()).toString()] =
829 Rep.second.asTextEdits();
835 void ClangdLSPServer::onDocumentDidClose(
836 const DidCloseTextDocumentParams &Params) {
837 PathRef File = Params.textDocument.uri.file();
838 Server->removeDocument(File);
841 std::lock_guard<std::mutex> Lock(FixItsMutex);
842 FixItsMap.erase(File);
845 std::lock_guard<std::mutex> HLock(SemanticTokensMutex);
846 LastSemanticTokens.erase(File);
853 PublishDiagnosticsParams Notification;
855 PublishDiagnostics(Notification);
858 void ClangdLSPServer::onDocumentOnTypeFormatting(
859 const DocumentOnTypeFormattingParams &Params,
860 Callback<std::vector<TextEdit>> Reply) {
861 auto File = Params.textDocument.uri.file();
862 Server->formatOnType(File, Params.position, Params.ch, std::move(Reply));
865 void ClangdLSPServer::onDocumentRangeFormatting(
866 const DocumentRangeFormattingParams &Params,
867 Callback<std::vector<TextEdit>> Reply) {
868 auto File = Params.textDocument.uri.file();
869 auto Code = Server->getDraft(File);
870 Server->formatFile(File, Params.range,
871 [
Code = std::move(
Code), Reply = std::move(Reply)](
872 llvm::Expected<tooling::Replacements> Result)
mutable {
874 Reply(replacementsToEdits(*Code, Result.get()));
876 Reply(Result.takeError());
880 void ClangdLSPServer::onDocumentFormatting(
881 const DocumentFormattingParams &Params,
882 Callback<std::vector<TextEdit>> Reply) {
883 auto File = Params.textDocument.uri.file();
884 auto Code = Server->getDraft(File);
885 Server->formatFile(File,
887 [
Code = std::move(
Code), Reply = std::move(Reply)](
888 llvm::Expected<tooling::Replacements> Result)
mutable {
892 Reply(Result.takeError());
898 static std::vector<SymbolInformation>
901 std::vector<SymbolInformation>
Results;
902 std::function<void(
const DocumentSymbol &, llvm::StringRef)> Process =
903 [&](
const DocumentSymbol &S, llvm::Optional<llvm::StringRef> ParentName) {
905 SI.
containerName = std::string(ParentName ?
"" : *ParentName);
911 Results.push_back(std::move(SI));
912 std::string FullName =
913 !ParentName ? S.
name : (ParentName->str() +
"::" + S.
name);
915 Process(
C, FullName);
917 for (
auto &S : Symbols)
922 void ClangdLSPServer::onDocumentSymbol(
const DocumentSymbolParams &Params,
923 Callback<llvm::json::Value> Reply) {
924 URIForFile FileURI = Params.textDocument.uri;
925 Server->documentSymbols(
926 Params.textDocument.uri.file(),
927 [
this, FileURI, Reply = std::move(Reply)](
930 return Reply(Items.takeError());
931 adjustSymbolKinds(*Items, SupportedSymbolKinds);
932 if (SupportsHierarchicalDocumentSymbol)
933 return Reply(std::move(*Items));
934 return Reply(flattenSymbolHierarchy(*Items, FileURI));
938 void ClangdLSPServer::onFoldingRange(
939 const FoldingRangeParams &Params,
940 Callback<std::vector<FoldingRange>> Reply) {
941 Server->foldingRanges(Params.textDocument.uri.file(), std::move(Reply));
951 Cmd.command = std::string(ApplyFixCommand);
952 Cmd.argument = *
Action.edit;
957 if (
Action.kind && *
Action.kind == CodeAction::QUICKFIX_KIND)
958 Cmd.title =
"Apply fix: " + Cmd.title;
962 void ClangdLSPServer::onCodeAction(
const CodeActionParams &Params,
963 Callback<llvm::json::Value> Reply) {
964 URIForFile File = Params.textDocument.uri;
966 auto KindAllowed = [Only(Params.context.only)](llvm::StringRef
Kind) {
969 return llvm::any_of(Only, [&](llvm::StringRef
Base) {
975 std::vector<CodeAction> FixIts;
976 if (KindAllowed(CodeAction::QUICKFIX_KIND)) {
977 for (
const Diagnostic &
D : Params.context.diagnostics) {
978 for (
auto &F : getFixes(File.file(),
D)) {
979 FixIts.push_back(
toCodeAction(F, Params.textDocument.uri));
980 FixIts.back().diagnostics = {
D};
986 auto ConsumeActions =
987 [
Diags = Params.context.diagnostics, Reply = std::move(Reply), File,
988 Selection = Params.range, FixIts = std::move(FixIts),
this](
989 llvm::Expected<std::vector<ClangdServer::TweakRef>> Tweaks)
mutable {
991 return Reply(Tweaks.takeError());
993 std::vector<CodeAction> Actions = std::move(FixIts);
994 Actions.reserve(Actions.size() + Tweaks->size());
995 for (
const auto &T : *Tweaks)
1000 CodeAction *OnlyFix =
nullptr;
1001 for (
auto &
Action : Actions) {
1002 if (
Action.kind && *
Action.kind == CodeAction::QUICKFIX_KIND) {
1011 OnlyFix->isPreferred =
true;
1012 if (
Diags.size() == 1 &&
Diags.front().range == Selection)
1013 OnlyFix->diagnostics = {
Diags.front()};
1016 if (SupportsCodeAction)
1017 return Reply(llvm::json::Array(Actions));
1019 for (
const auto &
Action : Actions) {
1021 Commands.push_back(std::move(*Command));
1023 return Reply(llvm::json::Array(
Commands));
1025 Server->enumerateTweaks(
1026 File.file(), Params.range,
1027 [
this, KindAllowed(std::move(KindAllowed))](
const Tweak &T) {
1028 return Opts.TweakFilter(T) && KindAllowed(T.kind());
1030 std::move(ConsumeActions));
1033 void ClangdLSPServer::onCompletion(
const CompletionParams &Params,
1034 Callback<CompletionList> Reply) {
1035 if (!shouldRunCompletion(Params)) {
1038 vlog(
"ignored auto-triggered completion, preceding char did not match");
1039 return Reply(CompletionList());
1041 auto Opts = this->Opts.CodeComplete;
1042 if (Params.limit && *Params.limit >= 0)
1043 Opts.Limit = *Params.limit;
1044 Server->codeComplete(Params.textDocument.uri.file(), Params.position, Opts,
1045 [Reply = std::move(Reply), Opts,
1046 this](llvm::Expected<CodeCompleteResult> List)
mutable {
1048 return Reply(List.takeError());
1049 CompletionList LSPList;
1050 LSPList.isIncomplete = List->HasMore;
1051 for (const auto &R : List->Completions) {
1052 CompletionItem C = R.render(Opts);
1053 C.kind = adjustKindToCapability(
1054 C.kind, SupportedCompletionItemKinds);
1055 LSPList.items.push_back(std::move(C));
1057 return Reply(std::move(LSPList));
1061 void ClangdLSPServer::onSignatureHelp(
const TextDocumentPositionParams &Params,
1062 Callback<SignatureHelp> Reply) {
1063 Server->signatureHelp(Params.textDocument.uri.file(), Params.position,
1064 Opts.SignatureHelpDocumentationFormat,
1065 [Reply = std::move(Reply),
this](
1066 llvm::Expected<SignatureHelp>
Signature)
mutable {
1068 return Reply(Signature.takeError());
1069 if (SupportsOffsetsInSignatureHelp)
1070 return Reply(std::move(*Signature));
1073 for (auto &SigInfo : Signature->signatures) {
1074 for (auto &Param : SigInfo.parameters)
1075 Param.labelOffsets.reset();
1101 void ClangdLSPServer::onGoToDefinition(
const TextDocumentPositionParams &Params,
1102 Callback<std::vector<Location>> Reply) {
1103 Server->locateSymbolAt(
1104 Params.textDocument.uri.file(), Params.position,
1105 [Params, Reply = std::move(Reply)](
1108 return Reply(Symbols.takeError());
1109 std::vector<Location> Defs;
1110 for (auto &S : *Symbols) {
1111 if (Location *Toggle = getToggle(Params, S))
1112 return Reply(std::vector<Location>{std::move(*Toggle)});
1113 Defs.push_back(S.Definition.value_or(S.PreferredDeclaration));
1115 Reply(std::move(Defs));
1119 void ClangdLSPServer::onGoToDeclaration(
1120 const TextDocumentPositionParams &Params,
1121 Callback<std::vector<Location>> Reply) {
1122 Server->locateSymbolAt(
1123 Params.textDocument.uri.file(), Params.position,
1124 [Params, Reply = std::move(Reply)](
1127 return Reply(Symbols.takeError());
1128 std::vector<Location> Decls;
1129 for (auto &S : *Symbols) {
1130 if (Location *Toggle = getToggle(Params, S))
1131 return Reply(std::vector<Location>{std::move(*Toggle)});
1132 Decls.push_back(std::move(S.PreferredDeclaration));
1134 Reply(std::move(Decls));
1138 void ClangdLSPServer::onSwitchSourceHeader(
1139 const TextDocumentIdentifier &Params,
1140 Callback<llvm::Optional<URIForFile>> Reply) {
1141 Server->switchSourceHeader(
1143 [Reply = std::move(Reply),
1146 return Reply(Path.takeError());
1148 return Reply(URIForFile::canonicalize(**Path, Params.uri.file()));
1149 return Reply(llvm::None);
1153 void ClangdLSPServer::onDocumentHighlight(
1154 const TextDocumentPositionParams &Params,
1155 Callback<std::vector<DocumentHighlight>> Reply) {
1156 Server->findDocumentHighlights(Params.textDocument.uri.file(),
1157 Params.position, std::move(Reply));
1160 void ClangdLSPServer::onHover(
const TextDocumentPositionParams &Params,
1161 Callback<llvm::Optional<Hover>> Reply) {
1162 Server->findHover(Params.textDocument.uri.file(), Params.position,
1163 [Reply = std::move(Reply),
this](
1166 return Reply(H.takeError());
1168 return Reply(llvm::None);
1171 R.contents.kind = HoverContentFormat;
1172 R.range = (*H)->SymRange;
1173 switch (HoverContentFormat) {
1174 case MarkupKind::PlainText:
1175 R.contents.value = (*H)->present().asPlainText();
1176 return Reply(std::move(R));
1177 case MarkupKind::Markdown:
1178 R.contents.value = (*H)->present().asMarkdown();
1179 return Reply(std::move(R));
1181 llvm_unreachable(
"unhandled MarkupKind");
1185 void ClangdLSPServer::onTypeHierarchy(
1186 const TypeHierarchyParams &Params,
1187 Callback<Optional<TypeHierarchyItem>> Reply) {
1188 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1189 Params.resolve, Params.direction, std::move(Reply));
1192 void ClangdLSPServer::onResolveTypeHierarchy(
1193 const ResolveTypeHierarchyItemParams &Params,
1194 Callback<Optional<TypeHierarchyItem>> Reply) {
1195 Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction,
1199 void ClangdLSPServer::onPrepareCallHierarchy(
1200 const CallHierarchyPrepareParams &Params,
1201 Callback<std::vector<CallHierarchyItem>> Reply) {
1202 Server->prepareCallHierarchy(Params.textDocument.uri.file(), Params.position,
1206 void ClangdLSPServer::onCallHierarchyIncomingCalls(
1207 const CallHierarchyIncomingCallsParams &Params,
1208 Callback<std::vector<CallHierarchyIncomingCall>> Reply) {
1209 Server->incomingCalls(Params.item, std::move(Reply));
1212 void ClangdLSPServer::onClangdInlayHints(
const InlayHintsParams &Params,
1213 Callback<llvm::json::Value> Reply) {
1218 auto Serialize = [Reply = std::move(Reply)](
1219 llvm::Expected<std::vector<InlayHint>>
Hints)
mutable {
1221 Reply(
Hints.takeError());
1224 llvm::json::Array Result;
1225 Result.reserve(
Hints->size());
1226 for (
auto &Hint : *
Hints) {
1227 Result.emplace_back(llvm::json::Object{
1228 {
"kind", llvm::to_string(Hint.kind)},
1229 {
"range", Hint.range},
1230 {
"position", Hint.position},
1234 ((Hint.paddingLeft ?
" " :
"") + llvm::StringRef(Hint.label) +
1235 (Hint.paddingRight ?
" " :
""))
1239 Reply(std::move(Result));
1241 Server->inlayHints(Params.textDocument.uri.file(), Params.range,
1242 std::move(Serialize));
1245 void ClangdLSPServer::onInlayHint(
const InlayHintsParams &Params,
1246 Callback<std::vector<InlayHint>> Reply) {
1247 Server->inlayHints(Params.textDocument.uri.file(), Params.range,
1251 void ClangdLSPServer::applyConfiguration(
1252 const ConfigurationSettings &Settings) {
1254 llvm::StringSet<> ModifiedFiles;
1255 for (
auto &
Entry : Settings.compilationDatabaseChanges) {
1257 auto Old = CDB->getCompileCommand(File);
1259 tooling::CompileCommand(std::move(
Entry.second.workingDirectory), File,
1260 std::move(
Entry.second.compilationCommand),
1263 CDB->setCompileCommand(File, std::move(New));
1264 ModifiedFiles.insert(File);
1268 Server->reparseOpenFilesIfNeeded(
1269 [&](llvm::StringRef File) {
return ModifiedFiles.count(File) != 0; });
1272 void ClangdLSPServer::maybeExportMemoryProfile() {
1276 static constexpr trace::Metric MemoryUsage(
1278 trace::Span
Tracer(
"ProfileBrief");
1281 record(MT,
"clangd_lsp_server", MemoryUsage);
1284 void ClangdLSPServer::maybeCleanupMemory() {
1285 if (!Opts.MemoryCleanup || !ShouldCleanupMemory())
1287 Opts.MemoryCleanup();
1291 void ClangdLSPServer::onChangeConfiguration(
1292 const DidChangeConfigurationParams &Params) {
1293 applyConfiguration(Params.settings);
1296 void ClangdLSPServer::onReference(
const ReferenceParams &Params,
1297 Callback<std::vector<Location>> Reply) {
1298 Server->findReferences(
1299 Params.textDocument.uri.file(), Params.position, Opts.ReferencesLimit,
1300 [Reply = std::move(Reply),
1301 IncludeDecl(Params.context.includeDeclaration)](
1302 llvm::Expected<ReferencesResult>
Refs)
mutable {
1304 return Reply(Refs.takeError());
1306 std::vector<Location> Result;
1307 Result.reserve(Refs->References.size());
1308 for (auto &Ref : Refs->References) {
1309 bool IsDecl = Ref.Attributes & ReferencesResult::Declaration;
1310 if (IncludeDecl || !IsDecl)
1311 Result.push_back(std::move(Ref.Loc));
1313 return Reply(std::move(Result));
1317 void ClangdLSPServer::onGoToType(
const TextDocumentPositionParams &Params,
1318 Callback<std::vector<Location>> Reply) {
1320 Params.textDocument.uri.file(), Params.position,
1321 [Reply = std::move(Reply)](
1324 return Reply(Types.takeError());
1325 std::vector<Location> Response;
1326 for (const LocatedSymbol &Sym : *Types)
1327 Response.push_back(Sym.PreferredDeclaration);
1328 return Reply(std::move(Response));
1332 void ClangdLSPServer::onGoToImplementation(
1333 const TextDocumentPositionParams &Params,
1334 Callback<std::vector<Location>> Reply) {
1335 Server->findImplementations(
1336 Params.textDocument.uri.file(), Params.position,
1337 [Reply = std::move(Reply)](
1340 return Reply(Overrides.takeError());
1341 std::vector<Location> Impls;
1342 for (const LocatedSymbol &Sym : *Overrides)
1343 Impls.push_back(Sym.PreferredDeclaration);
1344 return Reply(std::move(Impls));
1348 void ClangdLSPServer::onSymbolInfo(
const TextDocumentPositionParams &Params,
1349 Callback<std::vector<SymbolDetails>> Reply) {
1350 Server->symbolInfo(Params.textDocument.uri.file(), Params.position,
1354 void ClangdLSPServer::onSelectionRange(
1355 const SelectionRangeParams &Params,
1356 Callback<std::vector<SelectionRange>> Reply) {
1357 Server->semanticRanges(
1358 Params.textDocument.uri.file(), Params.positions,
1359 [Reply = std::move(Reply)](
1362 return Reply(Ranges.takeError());
1363 return Reply(std::move(*Ranges));
1367 void ClangdLSPServer::onDocumentLink(
1368 const DocumentLinkParams &Params,
1369 Callback<std::vector<DocumentLink>> Reply) {
1375 Server->documentLinks(
1376 Params.textDocument.uri.file(),
1377 [Reply = std::move(Reply)](
1380 return Reply(Links.takeError());
1382 return Reply(std::move(Links));
1388 for (
char &
C : llvm::reverse(S)) {
1395 S.insert(S.begin(),
'1');
1398 void ClangdLSPServer::onSemanticTokens(
const SemanticTokensParams &Params,
1399 Callback<SemanticTokens> CB) {
1400 auto File = Params.textDocument.uri.file();
1401 Server->semanticHighlights(
1402 Params.textDocument.uri.file(),
1403 [
this, File(File.str()), CB(std::move(CB)),
Code(Server->getDraft(File))](
1406 return CB(HT.takeError());
1407 SemanticTokens Result;
1408 Result.tokens = toSemanticTokens(*HT, *Code);
1410 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1411 auto &Last = LastSemanticTokens[File];
1413 Last.tokens = Result.tokens;
1414 increment(Last.resultId);
1415 Result.resultId = Last.resultId;
1417 CB(std::move(Result));
1421 void ClangdLSPServer::onSemanticTokensDelta(
1422 const SemanticTokensDeltaParams &Params,
1423 Callback<SemanticTokensOrDelta> CB) {
1424 auto File = Params.textDocument.uri.file();
1425 Server->semanticHighlights(
1426 Params.textDocument.uri.file(),
1427 [
this, PrevResultID(Params.previousResultId), File(File.str()),
1428 CB(std::move(CB)),
Code(Server->getDraft(File))](
1431 return CB(HT.takeError());
1432 std::vector<SemanticToken> Toks = toSemanticTokens(*HT, *Code);
1434 SemanticTokensOrDelta Result;
1436 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1437 auto &Last = LastSemanticTokens[File];
1439 if (PrevResultID == Last.resultId) {
1440 Result.edits = diffTokens(Last.tokens, Toks);
1442 vlog(
"semanticTokens/full/delta: wanted edits vs {0} but last "
1443 "result had ID {1}. Returning full token list.",
1444 PrevResultID, Last.resultId);
1445 Result.tokens = Toks;
1448 Last.tokens = std::move(Toks);
1449 increment(Last.resultId);
1450 Result.resultId = Last.resultId;
1453 CB(std::move(Result));
1457 void ClangdLSPServer::onMemoryUsage(
const NoParams &,
1458 Callback<MemoryTree> Reply) {
1459 llvm::BumpPtrAllocator DetailAlloc;
1460 MemoryTree MT(&DetailAlloc);
1462 Reply(std::move(MT));
1465 void ClangdLSPServer::onAST(
const ASTParams &Params,
1466 Callback<llvm::Optional<ASTNode>> CB) {
1467 Server->getAST(Params.textDocument.uri.file(), Params.range, std::move(CB));
1472 : ShouldProfile(std::chrono::minutes(5),
1473 std::chrono::minutes(1)),
1474 ShouldCleanupMemory(std::chrono::minutes(1),
1475 std::chrono::minutes(1)),
1476 BackgroundContext(
Context::current().clone()), Transp(Transp),
1478 SupportedSymbolKinds(defaultSymbolKinds()),
1479 SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts) {
1480 if (Opts.ConfigProvider) {
1481 assert(!Opts.ContextProvider &&
1482 "Only one of ConfigProvider and ContextProvider allowed!");
1484 Opts.ConfigProvider,
this);
1487 Bind.
method(
"initialize",
this, &ClangdLSPServer::onInitialize);
1490 void ClangdLSPServer::bindMethods(
LSPBinder &Bind,
1493 Bind.
notification(
"initialized",
this, &ClangdLSPServer::onInitialized);
1494 Bind.
method(
"shutdown",
this, &ClangdLSPServer::onShutdown);
1495 Bind.
method(
"sync",
this, &ClangdLSPServer::onSync);
1496 Bind.
method(
"textDocument/rangeFormatting",
this, &ClangdLSPServer::onDocumentRangeFormatting);
1497 Bind.
method(
"textDocument/onTypeFormatting",
this, &ClangdLSPServer::onDocumentOnTypeFormatting);
1498 Bind.
method(
"textDocument/formatting",
this, &ClangdLSPServer::onDocumentFormatting);
1499 Bind.
method(
"textDocument/codeAction",
this, &ClangdLSPServer::onCodeAction);
1500 Bind.
method(
"textDocument/completion",
this, &ClangdLSPServer::onCompletion);
1501 Bind.
method(
"textDocument/signatureHelp",
this, &ClangdLSPServer::onSignatureHelp);
1502 Bind.
method(
"textDocument/definition",
this, &ClangdLSPServer::onGoToDefinition);
1503 Bind.
method(
"textDocument/declaration",
this, &ClangdLSPServer::onGoToDeclaration);
1504 Bind.
method(
"textDocument/typeDefinition",
this, &ClangdLSPServer::onGoToType);
1505 Bind.
method(
"textDocument/implementation",
this, &ClangdLSPServer::onGoToImplementation);
1506 Bind.
method(
"textDocument/references",
this, &ClangdLSPServer::onReference);
1507 Bind.
method(
"textDocument/switchSourceHeader",
this, &ClangdLSPServer::onSwitchSourceHeader);
1508 Bind.
method(
"textDocument/prepareRename",
this, &ClangdLSPServer::onPrepareRename);
1509 Bind.
method(
"textDocument/rename",
this, &ClangdLSPServer::onRename);
1510 Bind.
method(
"textDocument/hover",
this, &ClangdLSPServer::onHover);
1511 Bind.
method(
"textDocument/documentSymbol",
this, &ClangdLSPServer::onDocumentSymbol);
1512 Bind.
method(
"workspace/executeCommand",
this, &ClangdLSPServer::onCommand);
1513 Bind.
method(
"textDocument/documentHighlight",
this, &ClangdLSPServer::onDocumentHighlight);
1514 Bind.
method(
"workspace/symbol",
this, &ClangdLSPServer::onWorkspaceSymbol);
1515 Bind.
method(
"textDocument/ast",
this, &ClangdLSPServer::onAST);
1516 Bind.
notification(
"textDocument/didOpen",
this, &ClangdLSPServer::onDocumentDidOpen);
1517 Bind.
notification(
"textDocument/didClose",
this, &ClangdLSPServer::onDocumentDidClose);
1518 Bind.
notification(
"textDocument/didChange",
this, &ClangdLSPServer::onDocumentDidChange);
1519 Bind.
notification(
"textDocument/didSave",
this, &ClangdLSPServer::onDocumentDidSave);
1520 Bind.
notification(
"workspace/didChangeWatchedFiles",
this, &ClangdLSPServer::onFileEvent);
1521 Bind.
notification(
"workspace/didChangeConfiguration",
this, &ClangdLSPServer::onChangeConfiguration);
1522 Bind.
method(
"textDocument/symbolInfo",
this, &ClangdLSPServer::onSymbolInfo);
1523 Bind.
method(
"textDocument/typeHierarchy",
this, &ClangdLSPServer::onTypeHierarchy);
1524 Bind.
method(
"typeHierarchy/resolve",
this, &ClangdLSPServer::onResolveTypeHierarchy);
1525 Bind.
method(
"textDocument/prepareCallHierarchy",
this, &ClangdLSPServer::onPrepareCallHierarchy);
1526 Bind.
method(
"callHierarchy/incomingCalls",
this, &ClangdLSPServer::onCallHierarchyIncomingCalls);
1527 Bind.
method(
"textDocument/selectionRange",
this, &ClangdLSPServer::onSelectionRange);
1528 Bind.
method(
"textDocument/documentLink",
this, &ClangdLSPServer::onDocumentLink);
1529 Bind.
method(
"textDocument/semanticTokens/full",
this, &ClangdLSPServer::onSemanticTokens);
1530 Bind.
method(
"textDocument/semanticTokens/full/delta",
this, &ClangdLSPServer::onSemanticTokensDelta);
1531 Bind.
method(
"clangd/inlayHints",
this, &ClangdLSPServer::onClangdInlayHints);
1532 Bind.
method(
"textDocument/inlayHint",
this, &ClangdLSPServer::onInlayHint);
1533 Bind.
method(
"$/memoryUsage",
this, &ClangdLSPServer::onMemoryUsage);
1535 Bind.
method(
"textDocument/foldingRange",
this, &ClangdLSPServer::onFoldingRange);
1536 Bind.
command(ApplyFixCommand,
this, &ClangdLSPServer::onCommandApplyEdit);
1537 Bind.
command(ApplyTweakCommand,
this, &ClangdLSPServer::onCommandApplyTweak);
1543 CreateWorkDoneProgress = Bind.
outgoingMethod(
"window/workDoneProgress/create");
1548 SemanticTokensRefresh = Bind.
outgoingMethod(
"workspace/semanticTokens/refresh");
1553 IsBeingDestroyed =
true;
1561 bool CleanExit =
true;
1562 if (
auto Err = Transp.
loop(*MsgHandler)) {
1563 elog(
"Transport error: {0}", std::move(Err));
1567 return CleanExit && ShutdownRequestReceived;
1572 Server->profile(MT.
child(
"clangd_server"));
1575 std::vector<Fix> ClangdLSPServer::getFixes(llvm::StringRef File,
1577 std::lock_guard<std::mutex> Lock(FixItsMutex);
1578 auto DiagToFixItsIter = FixItsMap.find(File);
1579 if (DiagToFixItsIter == FixItsMap.end())
1582 const auto &DiagToFixItsMap = DiagToFixItsIter->second;
1583 auto FixItsIter = DiagToFixItsMap.find(
D);
1584 if (FixItsIter == DiagToFixItsMap.end())
1587 return FixItsIter->second;
1596 bool ClangdLSPServer::shouldRunCompletion(
1597 const CompletionParams &Params)
const {
1600 auto Code = Server->getDraft(Params.textDocument.uri.file());
1606 vlog(
"could not convert position '{0}' to offset for file '{1}'",
1607 Params.position, Params.textDocument.uri.file());
1613 void ClangdLSPServer::onDiagnosticsReady(
PathRef File, llvm::StringRef Version,
1615 PublishDiagnosticsParams Notification;
1616 Notification.version = decodeVersion(Version);
1618 DiagnosticToReplacementMap LocalFixIts;
1622 auto &FixItsForDiagnostic = LocalFixIts[Diag];
1623 llvm::copy(Fixes, std::back_inserter(FixItsForDiagnostic));
1624 Notification.diagnostics.push_back(std::move(Diag));
1630 std::lock_guard<std::mutex> Lock(FixItsMutex);
1631 FixItsMap[File] = LocalFixIts;
1635 PublishDiagnostics(Notification);
1638 void ClangdLSPServer::onBackgroundIndexProgress(
1639 const BackgroundQueue::Stats &Stats) {
1640 static const char ProgressToken[] =
"backgroundIndexProgress";
1643 maybeCleanupMemory();
1645 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1647 auto NotifyProgress = [
this](
const BackgroundQueue::Stats &Stats) {
1648 if (BackgroundIndexProgressState != BackgroundIndexProgress::Live) {
1649 WorkDoneProgressBegin Begin;
1650 Begin.percentage =
true;
1651 Begin.title =
"indexing";
1652 BeginWorkDoneProgress({ProgressToken, std::move(Begin)});
1653 BackgroundIndexProgressState = BackgroundIndexProgress::Live;
1656 if (Stats.Completed < Stats.Enqueued) {
1657 assert(Stats.Enqueued > Stats.LastIdle);
1658 WorkDoneProgressReport Report;
1659 Report.percentage = 100 * (Stats.Completed - Stats.LastIdle) /
1660 (Stats.Enqueued - Stats.LastIdle);
1662 llvm::formatv(
"{0}/{1}", Stats.Completed - Stats.LastIdle,
1663 Stats.Enqueued - Stats.LastIdle);
1664 ReportWorkDoneProgress({ProgressToken, std::move(Report)});
1666 assert(Stats.Completed == Stats.Enqueued);
1667 EndWorkDoneProgress({ProgressToken, WorkDoneProgressEnd()});
1668 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
1672 switch (BackgroundIndexProgressState) {
1673 case BackgroundIndexProgress::Unsupported:
1675 case BackgroundIndexProgress::Creating:
1677 PendingBackgroundIndexProgress = Stats;
1679 case BackgroundIndexProgress::Empty: {
1680 if (BackgroundIndexSkipCreate) {
1681 NotifyProgress(Stats);
1685 PendingBackgroundIndexProgress = Stats;
1686 BackgroundIndexProgressState = BackgroundIndexProgress::Creating;
1687 WorkDoneProgressCreateParams CreateRequest;
1688 CreateRequest.token = ProgressToken;
1689 CreateWorkDoneProgress(
1691 [
this, NotifyProgress](llvm::Expected<std::nullptr_t>
E) {
1692 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1694 NotifyProgress(this->PendingBackgroundIndexProgress);
1696 elog(
"Failed to create background index progress bar: {0}",
1699 BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported;
1704 case BackgroundIndexProgress::Live:
1705 NotifyProgress(Stats);
1710 void ClangdLSPServer::onFileUpdated(
PathRef File,
const TUStatus &Status) {
1711 if (!SupportFileStatus)
1721 NotifyFileStatus(Status.render(File));
1724 void ClangdLSPServer::onSemanticsMaybeChanged(
PathRef File) {
1725 if (SemanticTokensRefresh) {
1726 SemanticTokensRefresh(NoParams{}, [](llvm::Expected<std::nullptr_t>
E) {
1729 elog(
"Failed to refresh semantic tokens: {0}",
E.takeError());