28#include "clang/Tooling/Core/Replacement.h"
29#include "llvm/ADT/ArrayRef.h"
30#include "llvm/ADT/FunctionExtras.h"
31#include "llvm/ADT/ScopeExit.h"
32#include "llvm/ADT/StringRef.h"
33#include "llvm/ADT/Twine.h"
34#include "llvm/Support/Allocator.h"
35#include "llvm/Support/Error.h"
36#include "llvm/Support/FormatVariadic.h"
37#include "llvm/Support/JSON.h"
38#include "llvm/Support/SHA1.h"
39#include "llvm/Support/ScopedPrinter.h"
40#include "llvm/Support/raw_ostream.h"
64std::string encodeVersion(std::optional<int64_t> LSPVersion) {
65 return LSPVersion ? llvm::to_string(*LSPVersion) :
"";
67std::optional<int64_t> decodeVersion(llvm::StringRef Encoded) {
69 if (llvm::to_integer(Encoded, Result, 10))
72 elog(
"unexpected non-numeric version {0}", Encoded);
76const llvm::StringLiteral ApplyFixCommand =
"clangd.applyFix";
77const llvm::StringLiteral ApplyTweakCommand =
"clangd.applyTweak";
78const llvm::StringLiteral ApplyRenameCommand =
"clangd.applyRename";
83 CA.
title = R.FixMessage;
86 CA.command->title = R.FixMessage;
87 CA.command->command = std::string(ApplyRenameCommand);
90 Params.position = R.Diag.Range.start;
91 Params.newName = R.NewName;
92 CA.command->argument = Params;
102 CA.kind =
T.Kind.str();
108 CA.command.emplace();
109 CA.command->title =
T.Title;
110 CA.command->command = std::string(ApplyTweakCommand);
114 Args.selection = Selection;
115 CA.command->argument = std::move(Args);
121 const std::optional<int64_t> &Version,
122 bool SupportsDocumentChanges,
123 bool SupportChangeAnnotation) {
125 Action.
title = F.Message;
127 Action.edit.emplace();
128 if (!SupportsDocumentChanges) {
129 Action.edit->changes.emplace();
130 auto &Changes = (*Action.edit->changes)[
File.uri()];
131 for (
const auto &E : F.Edits)
132 Changes.push_back({E.range, E.newText,
""});
134 Action.edit->documentChanges.emplace();
137 for (
const auto &E : F.Edits)
138 Edit.edits.push_back(
140 SupportChangeAnnotation ? E.annotationId :
""});
141 if (SupportChangeAnnotation) {
142 for (
const auto &[AID, Annotation]: F.Annotations)
143 Action.edit->changeAnnotations[AID] = Annotation;
149void adjustSymbolKinds(llvm::MutableArrayRef<DocumentSymbol> Syms,
151 for (
auto &S : Syms) {
153 adjustSymbolKinds(S.children, Kinds);
159 for (
size_t I =
SymbolKindMin; I <= static_cast<size_t>(SymbolKind::Array);
177 size_t InvalidFileCount = 0;
178 llvm::StringRef LastInvalidFile;
179 for (
const auto &It : FE) {
180 if (
auto Draft = Server.getDraft(It.first())) {
184 if (!It.second.canApplyTo(*Draft)) {
186 LastInvalidFile = It.first();
190 if (!InvalidFileCount)
191 return llvm::Error::success();
192 if (InvalidFileCount == 1)
193 return error(
"File must be saved first: {0}", LastInvalidFile);
194 return error(
"Files must be saved first: {0} (and {1} others)",
195 LastInvalidFile, InvalidFileCount - 1);
217 auto Handler = Server.Handlers.NotificationHandlers.find(
Method);
218 if (Handler != Server.Handlers.NotificationHandlers.end()) {
219 Handler->second(std::move(Params));
220 Server.maybeExportMemoryProfile();
221 Server.maybeCleanupMemory();
222 }
else if (!Server.Server) {
223 elog(
"Notification {0} before initialization",
Method);
224 }
else if (
Method ==
"$/cancelRequest") {
225 onCancel(std::move(Params));
227 log(
"unhandled notification {0}",
Method);
233 llvm::json::Value ID)
override {
236 WithContext WithCancel(cancelableRequestContext(ID));
239 ReplyOnce Reply(ID,
Method, &Server, Tracer.
Args);
241 auto Handler = Server.Handlers.MethodHandlers.find(
Method);
242 if (Handler != Server.Handlers.MethodHandlers.end()) {
243 Handler->second(std::move(Params), std::move(Reply));
244 }
else if (!Server.Server) {
245 elog(
"Call {0} before initialization.",
Method);
246 Reply(llvm::make_error<LSPError>(
"server not initialized",
249 Reply(llvm::make_error<LSPError>(
"method not found",
256 llvm::Expected<llvm::json::Value> Result)
override {
260 if (
auto IntID = ID.getAsInteger()) {
261 std::lock_guard<std::mutex> Mutex(CallMutex);
263 for (
size_t Index = 0; Index < ReplyCallbacks.size(); ++Index) {
264 if (ReplyCallbacks[Index].first == *IntID) {
265 ReplyHandler = std::move(ReplyCallbacks[Index].second);
266 ReplyCallbacks.erase(ReplyCallbacks.begin() +
275 ReplyHandler = [&ID](llvm::Expected<llvm::json::Value> Result) {
276 elog(
"received a reply with ID {0}, but there was no such call", ID);
278 llvm::consumeError(Result.takeError());
284 log(
"<-- reply({0})", ID);
285 ReplyHandler(std::move(Result));
287 auto Err = Result.takeError();
288 log(
"<-- reply({0}) error: {1}", ID, Err);
289 ReplyHandler(std::move(Err));
298 std::optional<std::pair<int, Callback<llvm::json::Value>>> OldestCB;
301 std::lock_guard<std::mutex> Mutex(CallMutex);
303 ReplyCallbacks.emplace_back(ID, std::move(Reply));
308 if (ReplyCallbacks.size() > MaxReplayCallbacks) {
309 elog(
"more than {0} outstanding LSP calls, forgetting about {1}",
310 MaxReplayCallbacks, ReplyCallbacks.front().first);
311 OldestCB = std::move(ReplyCallbacks.front());
312 ReplyCallbacks.pop_front();
317 error(
"failed to receive a client reply for request ({0})",
329 std::atomic<bool> Replied = {
false};
330 std::chrono::steady_clock::time_point Start;
331 llvm::json::Value ID;
334 llvm::json::Object *TraceArgs;
337 ReplyOnce(
const llvm::json::Value &ID, llvm::StringRef
Method,
339 : Start(std::chrono::steady_clock::now()), ID(ID),
Method(
Method),
340 Server(Server), TraceArgs(TraceArgs) {
343 ReplyOnce(ReplyOnce &&Other)
344 : Replied(Other.Replied.load()), Start(Other.Start),
346 Server(Other.Server), TraceArgs(Other.TraceArgs) {
347 Other.Server =
nullptr;
349 ReplyOnce &operator=(ReplyOnce &&) =
delete;
350 ReplyOnce(
const ReplyOnce &) =
delete;
351 ReplyOnce &operator=(
const ReplyOnce &) =
delete;
360 if (Server && !Server->IsBeingDestroyed && !Replied) {
361 elog(
"No reply to message {0}({1})",
Method, ID);
362 assert(
false &&
"must reply to all calls!");
363 (*this)(llvm::make_error<LSPError>(
"server failed to reply",
368 void operator()(llvm::Expected<llvm::json::Value> Reply) {
369 assert(Server &&
"moved-from!");
370 if (Replied.exchange(
true)) {
371 elog(
"Replied twice to message {0}({1})", Method, ID);
372 assert(
false &&
"must reply to each call only once!");
375 auto Duration = std::chrono::steady_clock::now() - Start;
377 log(
"--> reply:{0}({1}) {2:ms}", Method, ID, Duration);
379 (*TraceArgs)[
"Reply"] = *Reply;
380 std::lock_guard<std::mutex> Lock(Server->TranspWriter);
381 Server->Transp.reply(std::move(ID), std::move(Reply));
383 llvm::Error Err = Reply.takeError();
384 log(
"--> reply:{0}({1}) {2:ms}, error: {3}", Method, ID, Duration, Err);
386 (*TraceArgs)[
"Error"] = llvm::to_string(Err);
387 std::lock_guard<std::mutex> Lock(Server->TranspWriter);
388 Server->Transp.reply(std::move(ID), std::move(Err));
396 mutable std::mutex RequestCancelersMutex;
397 llvm::StringMap<std::pair<
Canceler,
unsigned>> RequestCancelers;
398 unsigned NextRequestCookie = 0;
399 void onCancel(
const llvm::json::Value &Params) {
400 const llvm::json::Value *ID =
nullptr;
401 if (
auto *O = Params.getAsObject())
404 elog(
"Bad cancellation request: {0}", Params);
407 auto StrID = llvm::to_string(*ID);
408 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
409 auto It = RequestCancelers.find(StrID);
410 if (It != RequestCancelers.end())
414 Context handlerContext()
const {
424 Context cancelableRequestContext(
const llvm::json::Value &ID) {
427 auto StrID = llvm::to_string(ID);
428 auto Cookie = NextRequestCookie++;
430 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
431 RequestCancelers[StrID] = {std::move(Task.second), Cookie};
436 return Task.first.derive(llvm::make_scope_exit([
this, StrID, Cookie] {
437 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
438 auto It = RequestCancelers.find(StrID);
439 if (It != RequestCancelers.end() && It->second.second == Cookie)
440 RequestCancelers.erase(It);
450 static constexpr int MaxReplayCallbacks = 100;
451 mutable std::mutex CallMutex;
453 std::deque<std::pair< int,
461void ClangdLSPServer::callMethod(StringRef
Method, llvm::json::Value Params,
463 auto ID = MsgHandler->bindReply(std::move(CB));
465 std::lock_guard<std::mutex> Lock(TranspWriter);
466 Transp.call(
Method, std::move(Params), ID);
469void ClangdLSPServer::notify(llvm::StringRef
Method, llvm::json::Value Params) {
471 maybeCleanupMemory();
472 std::lock_guard<std::mutex> Lock(TranspWriter);
473 Transp.notify(
Method, std::move(Params));
477 std::vector<llvm::StringRef> Types;
485 std::vector<llvm::StringRef> Modifiers;
493void ClangdLSPServer::onInitialize(
const InitializeParams &Params,
496 if (Params.capabilities.PositionEncodings && !Opts.
Encoding) {
498 for (
OffsetEncoding Supported : *Params.capabilities.PositionEncodings)
505 if (Params.capabilities.TheiaSemanticHighlighting &&
506 !Params.capabilities.SemanticTokens) {
507 elog(
"Client requested legacy semanticHighlights notification, which is "
508 "no longer supported. Migrate to standard semanticTokens request");
511 if (Params.rootUri && *Params.rootUri)
512 Opts.WorkspaceRoot = std::string(Params.rootUri->file());
513 else if (Params.rootPath && !Params.rootPath->empty())
514 Opts.WorkspaceRoot = *Params.rootPath;
516 return Reply(llvm::make_error<LSPError>(
"server already initialized",
519 Opts.CodeComplete.EnableSnippets = Params.capabilities.CompletionSnippets;
520 Opts.CodeComplete.IncludeFixIts = Params.capabilities.CompletionFixes;
521 if (!Opts.CodeComplete.BundleOverloads)
522 Opts.CodeComplete.BundleOverloads = Params.capabilities.HasSignatureHelp;
523 Opts.CodeComplete.DocumentationFormat =
524 Params.capabilities.CompletionDocumentationFormat;
525 Opts.SignatureHelpDocumentationFormat =
526 Params.capabilities.SignatureHelpDocumentationFormat;
527 DiagOpts.EmbedFixesInDiagnostics = Params.capabilities.DiagnosticFixes;
528 DiagOpts.SendDiagnosticCategory = Params.capabilities.DiagnosticCategory;
529 DiagOpts.EmitRelatedLocations =
530 Params.capabilities.DiagnosticRelatedInformation;
531 if (Params.capabilities.WorkspaceSymbolKinds)
532 SupportedSymbolKinds |= *Params.capabilities.WorkspaceSymbolKinds;
533 if (Params.capabilities.CompletionItemKinds)
534 SupportedCompletionItemKinds |= *Params.capabilities.CompletionItemKinds;
535 SupportsCompletionLabelDetails = Params.capabilities.CompletionLabelDetail;
536 SupportsCodeAction = Params.capabilities.CodeActionStructure;
537 SupportsHierarchicalDocumentSymbol =
538 Params.capabilities.HierarchicalDocumentSymbol;
539 SupportsReferenceContainer = Params.capabilities.ReferenceContainer;
540 SupportFileStatus = Params.initializationOptions.FileStatus;
541 SupportsDocumentChanges = Params.capabilities.DocumentChanges;
542 SupportsChangeAnnotation = Params.capabilities.ChangeAnnotation;
543 HoverContentFormat = Params.capabilities.HoverContentFormat;
544 Opts.LineFoldingOnly = Params.capabilities.LineFoldingOnly;
545 SupportsOffsetsInSignatureHelp = Params.capabilities.OffsetsInSignatureHelp;
546 if (Params.capabilities.WorkDoneProgress)
547 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
548 BackgroundIndexSkipCreate = Params.capabilities.ImplicitProgressCreation;
549 Opts.ImplicitCancellation = !Params.capabilities.CancelsStaleRequests;
550 Opts.PublishInactiveRegions = Params.capabilities.InactiveRegions;
552 if (Opts.UseDirBasedCDB) {
553 DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS);
554 if (
const auto &Dir = Params.initializationOptions.compilationDatabasePath)
555 CDBOpts.CompileCommandsDir = Dir;
556 CDBOpts.ContextProvider = Opts.ContextProvider;
557 if (Opts.StrongWorkspaceMode)
558 CDBOpts.applyFallbackWorkingDirectory(Opts.WorkspaceRoot);
560 std::make_unique<DirectoryBasedGlobalCompilationDatabase>(CDBOpts);
563 Mangler.SystemIncludeExtractor =
565 if (Opts.ResourceDir)
566 Mangler.ResourceDir = *Opts.ResourceDir;
567 CDB.emplace(BaseCDB.get(), Params.initializationOptions.fallbackFlags,
570 if (Opts.EnableExperimentalModulesSupport) {
571 ModulesManager.emplace(*CDB);
572 Opts.ModulesManager = &*ModulesManager;
579 WithContext MainContext(BackgroundContext.clone());
580 std::optional<WithContextValue> WithOffsetEncoding;
583 Server.emplace(*CDB, TFS, Opts,
584 static_cast<ClangdServer::Callbacks *
>(
this));
587 llvm::json::Object ServerCaps{
594 {
"documentFormattingProvider",
true},
595 {
"documentRangeFormattingProvider",
597 {
"rangesSupport",
true},
599 {
"documentOnTypeFormattingProvider",
601 {
"firstTriggerCharacter",
"\n"},
602 {
"moreTriggerCharacter", {}},
604 {
"completionProvider",
611 {
"resolveProvider",
false},
613 {
"triggerCharacters", {
".",
"<",
">",
":",
"\"",
"/",
"*"}},
615 {
"semanticTokensProvider",
617 {
"full", llvm::json::Object{{
"delta",
true}}},
623 {
"signatureHelpProvider",
625 {
"triggerCharacters", {
"(",
")",
"{",
"}",
"<",
">",
","}},
627 {
"declarationProvider",
true},
628 {
"definitionProvider",
true},
629 {
"implementationProvider",
true},
630 {
"typeDefinitionProvider",
true},
631 {
"documentHighlightProvider",
true},
632 {
"documentLinkProvider",
634 {
"resolveProvider",
false},
636 {
"hoverProvider",
true},
637 {
"selectionRangeProvider",
true},
638 {
"documentSymbolProvider",
true},
639 {
"workspaceSymbolProvider",
true},
640 {
"referencesProvider",
true},
641 {
"astProvider",
true},
642 {
"typeHierarchyProvider",
true},
646 {
"standardTypeHierarchyProvider",
true},
647 {
"memoryUsageProvider",
true},
648 {
"compilationDatabase",
649 llvm::json::Object{{
"automaticReload",
true}}},
650 {
"inactiveRegionsProvider",
true},
651 {
"callHierarchyProvider",
true},
652 {
"clangdInlayHintsProvider",
true},
653 {
"inlayHintProvider",
true},
654 {
"foldingRangeProvider",
true},
658 LSPBinder Binder(Handlers, *
this);
659 bindMethods(Binder, Params.capabilities);
660 if (Opts.FeatureModules)
661 for (
auto &Mod : *Opts.FeatureModules)
662 Mod.initializeLSP(Binder, Params.rawCapabilities, ServerCaps);
667 ServerCaps[
"renameProvider"] =
668 Params.capabilities.RenamePrepareSupport
669 ? llvm::json::Object{{
"prepareProvider",
true}}
670 : llvm::json::Value(
true);
675 ServerCaps[
"codeActionProvider"] =
676 Params.capabilities.CodeActionStructure
677 ? llvm::json::Object{{
"codeActionKinds",
681 : llvm::json::Value(
true);
683 std::vector<llvm::StringRef>
Commands;
684 for (llvm::StringRef Command : Handlers.CommandHandlers.keys())
687 ServerCaps[
"executeCommandProvider"] =
688 llvm::json::Object{{
"commands",
Commands}};
691 ServerCaps[
"positionEncoding"] = *Opts.Encoding;
693 llvm::json::Object Result{
699 {
"capabilities", std::move(ServerCaps)}}};
704 Result[
"offsetEncoding"] = *Opts.Encoding;
705 Reply(std::move(Result));
709 applyConfiguration(Params.initializationOptions.ConfigSettings);
714void ClangdLSPServer::onShutdown(
const NoParams &,
717 ShutdownRequestReceived =
true;
724 if (Server->blockUntilIdleForTest(60))
727 Reply(
error(
"Not idle after a minute"));
730void ClangdLSPServer::onDocumentDidOpen(
734 const std::string &Contents = Params.textDocument.text;
736 Server->addDocument(
File, Contents,
737 encodeVersion(Params.textDocument.version),
741void ClangdLSPServer::onDocumentDidChange(
744 if (Params.wantDiagnostics)
749 auto Code = Server->getDraft(
File);
751 log(
"Trying to incrementally change non-added document: {0}",
File);
754 std::string NewCode(*Code);
755 for (
const auto &Change : Params.contentChanges) {
760 Server->removeDocument(
File);
761 elog(
"Failed to update {0}: {1}",
File, std::move(Err));
765 Server->addDocument(
File, NewCode, encodeVersion(Params.textDocument.version),
766 WantDiags, Params.forceRebuild);
769void ClangdLSPServer::onDocumentDidSave(
771 Server->reparseOpenFilesIfNeeded([](llvm::StringRef) {
return true; });
779 Server->onFileEvent(Params);
788 auto It = Handlers.CommandHandlers.find(Params.command);
789 if (It == Handlers.CommandHandlers.end()) {
790 return Reply(llvm::make_error<LSPError>(
791 llvm::formatv(
"Unsupported command \"{0}\".", Params.command).str(),
794 It->second(Params.argument, std::move(Reply));
797void ClangdLSPServer::onCommandApplyEdit(
const WorkspaceEdit &WE,
807 applyEdit(WE,
"Fix applied.", std::move(Reply));
810void ClangdLSPServer::onCommandApplyTweak(
const TweakArgs &Args,
812 auto Action = [
this, Reply = std::move(Reply)](
813 llvm::Expected<Tweak::Effect> R)
mutable {
815 return Reply(R.takeError());
817 assert(R->ShowMessage || (!R->ApplyEdits.empty() &&
"tweak has no effect"));
819 if (R->ShowMessage) {
820 ShowMessageParams Msg;
821 Msg.message = *R->ShowMessage;
826 if (R->ApplyEdits.empty())
827 return Reply(
"Tweak applied.");
829 if (
auto Err = validateEdits(*Server, R->ApplyEdits))
830 return Reply(std::move(Err));
834 WE.changes.emplace();
835 for (
const auto &It : R->ApplyEdits) {
837 It.second.asTextEdits();
840 return applyEdit(std::move(WE),
"Tweak applied.", std::move(Reply));
842 Server->applyTweak(Args.file.file(), Args.selection, Args.tweakID,
846void ClangdLSPServer::onCommandApplyRename(
const RenameParams &R,
848 onRename(R, [
this, Reply = std::move(Reply)](
849 llvm::Expected<WorkspaceEdit> Edit)
mutable {
851 Reply(Edit.takeError());
852 applyEdit(std::move(*Edit),
"Rename applied.", std::move(Reply));
856void ClangdLSPServer::applyEdit(
WorkspaceEdit WE, llvm::json::Value Success,
858 ApplyWorkspaceEditParams Edit;
859 Edit.edit = std::move(WE);
861 Edit, [Reply = std::move(Reply), SuccessMessage = std::move(Success)](
862 llvm::Expected<ApplyWorkspaceEditResponse> Response)
mutable {
864 return Reply(Response.takeError());
865 if (!Response->applied) {
866 std::string Reason = Response->failureReason
867 ? *Response->failureReason
869 return Reply(
error(
"edits were not applied: {0}", Reason));
871 return Reply(SuccessMessage);
875void ClangdLSPServer::onWorkspaceSymbol(
877 Callback<std::vector<SymbolInformation>> Reply) {
878 Server->workspaceSymbols(
879 Params.query, Params.limit.value_or(Opts.CodeComplete.Limit),
880 [Reply = std::move(Reply),
881 this](llvm::Expected<std::vector<SymbolInformation>> Items)
mutable {
883 return Reply(Items.takeError());
884 for (auto &Sym : *Items)
885 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds);
887 Reply(std::move(*Items));
893 Server->prepareRename(
894 Params.textDocument.uri.file(), Params.position, std::nullopt,
896 [Reply = std::move(Reply)](llvm::Expected<RenameResult> Result)
mutable {
898 return Reply(Result.takeError());
899 PrepareRenameResult PrepareResult;
900 PrepareResult.range = Result->Target;
901 PrepareResult.placeholder = Result->Placeholder;
902 return Reply(std::move(PrepareResult));
906void ClangdLSPServer::onRename(
const RenameParams &Params,
908 Path File = std::string(Params.textDocument.uri.file());
909 if (!Server->getDraft(
File))
910 return Reply(llvm::make_error<LSPError>(
912 Server->rename(
File, Params.position, Params.newName, Opts.Rename,
913 [
File, Params, Reply = std::move(Reply),
914 this](llvm::Expected<RenameResult> R)
mutable {
916 return Reply(R.takeError());
917 if (auto Err = validateEdits(*Server, R->GlobalChanges))
918 return Reply(std::move(Err));
919 WorkspaceEdit Result;
922 Result.changes.emplace();
923 for (const auto &Rep : R->GlobalChanges) {
925 .changes)[URI::createFile(Rep.first()).toString()] =
926 Rep.second.asTextEdits();
932void ClangdLSPServer::onDocumentDidClose(
933 const DidCloseTextDocumentParams &Params) {
935 Server->removeDocument(File);
938 std::lock_guard<std::mutex> Lock(DiagRefMutex);
939 DiagRefMap.erase(File);
942 std::lock_guard<std::mutex> HLock(SemanticTokensMutex);
943 LastSemanticTokens.erase(File);
950 PublishDiagnosticsParams Notification;
951 Notification.uri = URIForFile::canonicalize(File, File);
952 PublishDiagnostics(Notification);
955void ClangdLSPServer::onDocumentOnTypeFormatting(
956 const DocumentOnTypeFormattingParams &Params,
957 Callback<std::vector<TextEdit>> Reply) {
958 auto File = Params.textDocument.uri.file();
959 Server->formatOnType(File, Params.position, Params.ch, std::move(Reply));
962void ClangdLSPServer::onDocumentRangeFormatting(
963 const DocumentRangeFormattingParams &Params,
964 Callback<std::vector<TextEdit>> Reply) {
965 onDocumentRangesFormatting(
966 DocumentRangesFormattingParams{Params.textDocument, {Params.range}},
970void ClangdLSPServer::onDocumentRangesFormatting(
971 const DocumentRangesFormattingParams &Params,
972 Callback<std::vector<TextEdit>> Reply) {
973 auto File = Params.textDocument.uri.file();
974 auto Code = Server->getDraft(File);
975 Server->formatFile(File, Params.ranges,
976 [Code = std::move(Code), Reply = std::move(Reply)](
977 llvm::Expected<tooling::Replacements> Result)
mutable {
979 Reply(replacementsToEdits(*Code, Result.get()));
981 Reply(Result.takeError());
985void ClangdLSPServer::onDocumentFormatting(
986 const DocumentFormattingParams &Params,
987 Callback<std::vector<TextEdit>> Reply) {
988 auto File = Params.textDocument.uri.file();
989 auto Code = Server->getDraft(File);
990 Server->formatFile(File,
992 [Code = std::move(Code), Reply = std::move(Reply)](
993 llvm::Expected<tooling::Replacements> Result)
mutable {
997 Reply(Result.takeError());
1003static std::vector<SymbolInformation>
1006 std::vector<SymbolInformation> Results;
1007 std::function<void(
const DocumentSymbol &, llvm::StringRef)> Process =
1008 [&](
const DocumentSymbol &S, std::optional<llvm::StringRef> ParentName) {
1010 SI.
containerName = std::string(ParentName ?
"" : *ParentName);
1016 Results.push_back(std::move(SI));
1017 std::string FullName =
1018 !ParentName ? S.
name : (ParentName->str() +
"::" + S.
name);
1020 Process(C, FullName);
1027void ClangdLSPServer::onDocumentSymbol(
const DocumentSymbolParams &Params,
1028 Callback<llvm::json::Value> Reply) {
1029 URIForFile FileURI = Params.textDocument.uri;
1030 Server->documentSymbols(
1031 Params.textDocument.uri.file(),
1032 [
this, FileURI, Reply = std::move(Reply)](
1033 llvm::Expected<std::vector<DocumentSymbol>> Items)
mutable {
1035 return Reply(Items.takeError());
1036 adjustSymbolKinds(*Items, SupportedSymbolKinds);
1037 if (SupportsHierarchicalDocumentSymbol)
1038 return Reply(std::move(*Items));
1039 return Reply(flattenSymbolHierarchy(*Items, FileURI));
1043void ClangdLSPServer::onFoldingRange(
1044 const FoldingRangeParams &Params,
1045 Callback<std::vector<FoldingRange>> Reply) {
1046 Server->foldingRanges(Params.textDocument.uri.file(), std::move(Reply));
1052 return std::nullopt;
1055 }
else if (Action.
edit) {
1056 Cmd.command = std::string(ApplyFixCommand);
1057 Cmd.argument = *Action.
edit;
1059 return std::nullopt;
1061 Cmd.title = Action.
title;
1063 Cmd.title =
"Apply fix: " + Cmd.title;
1067void ClangdLSPServer::onCodeAction(
const CodeActionParams &Params,
1068 Callback<llvm::json::Value> Reply) {
1069 URIForFile File = Params.textDocument.uri;
1070 std::map<ClangdServer::DiagRef, clangd::Diagnostic> ToLSPDiags;
1071 ClangdServer::CodeActionInputs Inputs;
1073 for (
const auto& LSPDiag : Params.context.diagnostics) {
1074 if (
auto DiagRef = getDiagRef(File.file(), LSPDiag)) {
1075 ToLSPDiags[*DiagRef] = LSPDiag;
1076 Inputs.Diagnostics.push_back(*DiagRef);
1079 Inputs.File =
File.file();
1080 Inputs.Selection = Params.range;
1081 Inputs.RequestedActionKinds = Params.context.only;
1082 Inputs.TweakFilter = [
this](
const Tweak &
T) {
1083 return Opts.TweakFilter(T);
1086 Reply = std::move(Reply),
1087 ToLSPDiags = std::move(ToLSPDiags),
File,
1088 Selection = Params.range](
1089 llvm::Expected<ClangdServer::CodeActionResult> Fixits)
mutable {
1091 return Reply(Fixits.takeError());
1092 std::vector<CodeAction> CAs;
1093 auto Version = decodeVersion(Fixits->Version);
1094 for (
const auto &QF : Fixits->QuickFixes) {
1095 CAs.push_back(toCodeAction(QF.F, File, Version, SupportsDocumentChanges,
1096 SupportsChangeAnnotation));
1097 if (
auto It = ToLSPDiags.find(QF.Diag);
1098 It != ToLSPDiags.end()) {
1099 CAs.back().diagnostics = {It->second};
1103 for (
const auto &R : Fixits->Renames)
1104 CAs.push_back(toCodeAction(R, File));
1106 for (
const auto &TR : Fixits->TweakRefs)
1107 CAs.push_back(toCodeAction(TR, File, Selection));
1111 CodeAction *OnlyFix =
nullptr;
1112 for (
auto &Action : CAs) {
1113 if (Action.kind && *Action.kind == CodeAction::QUICKFIX_KIND) {
1122 OnlyFix->isPreferred =
true;
1123 if (ToLSPDiags.size() == 1 &&
1124 ToLSPDiags.begin()->second.range == Selection)
1125 OnlyFix->diagnostics = {ToLSPDiags.begin()->second};
1128 if (SupportsCodeAction)
1129 return Reply(llvm::json::Array(CAs));
1131 for (
const auto &Action : CAs) {
1133 Commands.push_back(std::move(*Command));
1135 return Reply(llvm::json::Array(
Commands));
1137 Server->codeAction(Inputs, std::move(CB));
1140void ClangdLSPServer::onCompletion(
const CompletionParams &Params,
1141 Callback<CompletionList> Reply) {
1142 if (!shouldRunCompletion(Params)) {
1145 vlog(
"ignored auto-triggered completion, preceding char did not match");
1146 return Reply(CompletionList());
1148 auto Opts = this->Opts.CodeComplete;
1149 if (Params.limit && *Params.limit >= 0)
1150 Opts.Limit = *Params.limit;
1151 Server->codeComplete(Params.textDocument.uri.file(), Params.position, Opts,
1152 [Reply = std::move(Reply), Opts,
1153 this](llvm::Expected<CodeCompleteResult> List)
mutable {
1155 return Reply(List.takeError());
1156 CompletionList LSPList;
1157 LSPList.isIncomplete = List->HasMore;
1158 for (const auto &R : List->Completions) {
1159 CompletionItem C = R.render(Opts);
1160 C.kind = adjustKindToCapability(
1161 C.kind, SupportedCompletionItemKinds);
1162 if (!SupportsCompletionLabelDetails)
1163 removeCompletionLabelDetails(C);
1164 LSPList.items.push_back(std::move(C));
1166 return Reply(std::move(LSPList));
1170void ClangdLSPServer::onSignatureHelp(
const TextDocumentPositionParams &Params,
1171 Callback<SignatureHelp> Reply) {
1172 Server->signatureHelp(Params.textDocument.uri.file(), Params.position,
1173 Opts.SignatureHelpDocumentationFormat,
1174 [Reply = std::move(Reply),
this](
1175 llvm::Expected<SignatureHelp> Signature)
mutable {
1177 return Reply(Signature.takeError());
1178 if (SupportsOffsetsInSignatureHelp)
1179 return Reply(std::move(*Signature));
1182 for (auto &SigInfo : Signature->signatures) {
1183 for (auto &Param : SigInfo.parameters)
1184 Param.labelOffsets.reset();
1186 return Reply(std::move(*Signature));
1210void ClangdLSPServer::onGoToDefinition(
const TextDocumentPositionParams &Params,
1211 Callback<std::vector<Location>> Reply) {
1212 Server->locateSymbolAt(
1213 Params.textDocument.uri.file(), Params.position,
1214 [Params, Reply = std::move(Reply)](
1215 llvm::Expected<std::vector<LocatedSymbol>> Symbols)
mutable {
1217 return Reply(Symbols.takeError());
1218 std::vector<Location> Defs;
1219 for (auto &S : *Symbols) {
1220 if (Location *Toggle = getToggle(Params, S))
1221 return Reply(std::vector<Location>{std::move(*Toggle)});
1222 Defs.push_back(S.Definition.value_or(S.PreferredDeclaration));
1224 Reply(std::move(Defs));
1228void ClangdLSPServer::onGoToDeclaration(
1229 const TextDocumentPositionParams &Params,
1230 Callback<std::vector<Location>> Reply) {
1231 Server->locateSymbolAt(
1232 Params.textDocument.uri.file(), Params.position,
1233 [Params, Reply = std::move(Reply)](
1234 llvm::Expected<std::vector<LocatedSymbol>> Symbols)
mutable {
1236 return Reply(Symbols.takeError());
1237 std::vector<Location> Decls;
1238 for (auto &S : *Symbols) {
1239 if (Location *Toggle = getToggle(Params, S))
1240 return Reply(std::vector<Location>{std::move(*Toggle)});
1241 Decls.push_back(std::move(S.PreferredDeclaration));
1243 Reply(std::move(Decls));
1247void ClangdLSPServer::onSwitchSourceHeader(
1248 const TextDocumentIdentifier &Params,
1249 Callback<std::optional<URIForFile>> Reply) {
1250 Server->switchSourceHeader(
1252 [Reply = std::move(Reply),
1253 Params](llvm::Expected<std::optional<clangd::Path>> Path)
mutable {
1255 return Reply(Path.takeError());
1257 return Reply(URIForFile::canonicalize(**Path, Params.uri.file()));
1258 return Reply(std::nullopt);
1262void ClangdLSPServer::onDocumentHighlight(
1263 const TextDocumentPositionParams &Params,
1264 Callback<std::vector<DocumentHighlight>> Reply) {
1265 Server->findDocumentHighlights(Params.textDocument.uri.file(),
1266 Params.position, std::move(Reply));
1269void ClangdLSPServer::onHover(
const TextDocumentPositionParams &Params,
1270 Callback<std::optional<Hover>> Reply) {
1271 Server->findHover(Params.textDocument.uri.file(), Params.position,
1272 [Reply = std::move(Reply),
1273 this](llvm::Expected<std::optional<HoverInfo>> H)
mutable {
1275 return Reply(H.takeError());
1277 return Reply(std::nullopt);
1280 R.contents.kind = HoverContentFormat;
1281 R.range = (*H)->SymRange;
1282 switch (HoverContentFormat) {
1283 case MarkupKind::Markdown:
1284 case MarkupKind::PlainText:
1285 R.contents.value = (*H)->present(HoverContentFormat);
1286 return Reply(std::move(R));
1288 llvm_unreachable(
"unhandled MarkupKind");
1295 llvm::json::Object Result{{
1296 {
"name", std::move(THI.
name)},
1297 {
"kind",
static_cast<int>(THI.
kind)},
1298 {
"uri", std::move(THI.
uri)},
1299 {
"range", THI.
range},
1301 {
"data", std::move(THI.
data)},
1306 Result[
"detail"] = std::move(*THI.
detail);
1310 for (
auto &Parent : *THI.
parents)
1312 Result[
"parents"] = std::move(
Parents);
1319 Result[
"children"] = std::move(
Children);
1324void ClangdLSPServer::onTypeHierarchy(
const TypeHierarchyPrepareParams &Params,
1325 Callback<llvm::json::Value> Reply) {
1327 [Reply = std::move(Reply)](
1328 llvm::Expected<std::vector<TypeHierarchyItem>> Resp)
mutable {
1330 Reply(Resp.takeError());
1333 if (Resp->empty()) {
1339 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1340 Params.resolve, Params.direction, std::move(Serialize));
1343void ClangdLSPServer::onResolveTypeHierarchy(
1344 const ResolveTypeHierarchyItemParams &Params,
1345 Callback<llvm::json::Value> Reply) {
1347 [Reply = std::move(Reply)](
1348 llvm::Expected<std::optional<TypeHierarchyItem>> Resp)
mutable {
1350 Reply(Resp.takeError());
1354 Reply(std::move(*Resp));
1359 Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction,
1360 std::move(Serialize));
1363void ClangdLSPServer::onPrepareTypeHierarchy(
1364 const TypeHierarchyPrepareParams &Params,
1365 Callback<std::vector<TypeHierarchyItem>> Reply) {
1366 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1367 Params.resolve, Params.direction, std::move(Reply));
1370void ClangdLSPServer::onSuperTypes(
1371 const ResolveTypeHierarchyItemParams &Params,
1372 Callback<std::optional<std::vector<TypeHierarchyItem>>> Reply) {
1373 Server->superTypes(Params.item, std::move(Reply));
1376void ClangdLSPServer::onSubTypes(
1377 const ResolveTypeHierarchyItemParams &Params,
1378 Callback<std::vector<TypeHierarchyItem>> Reply) {
1379 Server->subTypes(Params.item, std::move(Reply));
1382void ClangdLSPServer::onPrepareCallHierarchy(
1383 const CallHierarchyPrepareParams &Params,
1384 Callback<std::vector<CallHierarchyItem>> Reply) {
1385 Server->prepareCallHierarchy(Params.textDocument.uri.file(), Params.position,
1389void ClangdLSPServer::onCallHierarchyIncomingCalls(
1390 const CallHierarchyIncomingCallsParams &Params,
1391 Callback<std::vector<CallHierarchyIncomingCall>> Reply) {
1392 Server->incomingCalls(Params.item, std::move(Reply));
1395void ClangdLSPServer::onClangdInlayHints(
const InlayHintsParams &Params,
1396 Callback<llvm::json::Value> Reply) {
1401 auto Serialize = [Reply = std::move(Reply)](
1402 llvm::Expected<std::vector<InlayHint>> Hints)
mutable {
1404 Reply(Hints.takeError());
1407 llvm::json::Array Result;
1408 Result.reserve(Hints->size());
1409 for (
auto &Hint : *Hints) {
1410 Result.emplace_back(llvm::json::Object{
1411 {
"kind", llvm::to_string(Hint.kind)},
1412 {
"range", Hint.range},
1413 {
"position", Hint.position},
1417 ((Hint.paddingLeft ?
" " :
"") + llvm::StringRef(Hint.joinLabels()) +
1418 (Hint.paddingRight ?
" " :
""))
1422 Reply(std::move(Result));
1424 Server->inlayHints(Params.textDocument.uri.file(), Params.range,
1425 std::move(Serialize));
1428void ClangdLSPServer::onInlayHint(
const InlayHintsParams &Params,
1429 Callback<std::vector<InlayHint>> Reply) {
1430 Server->inlayHints(Params.textDocument.uri.file(), Params.range,
1434void ClangdLSPServer::onCallHierarchyOutgoingCalls(
1435 const CallHierarchyOutgoingCallsParams &Params,
1436 Callback<std::vector<CallHierarchyOutgoingCall>> Reply) {
1437 Server->outgoingCalls(Params.item, std::move(Reply));
1440void ClangdLSPServer::applyConfiguration(
1441 const ConfigurationSettings &Settings) {
1443 llvm::StringSet<> ModifiedFiles;
1444 for (
auto &[File, Command] : Settings.compilationDatabaseChanges) {
1446 tooling::CompileCommand(std::move(Command.workingDirectory), File,
1447 std::move(Command.compilationCommand),
1449 if (CDB->setCompileCommand(File, std::move(Cmd))) {
1450 ModifiedFiles.insert(File);
1454 Server->reparseOpenFilesIfNeeded(
1455 [&](llvm::StringRef File) {
return ModifiedFiles.count(File) != 0; });
1458void ClangdLSPServer::maybeExportMemoryProfile() {
1459 if (!trace::enabled() || !ShouldProfile())
1462 static constexpr trace::Metric MemoryUsage(
1463 "memory_usage", trace::Metric::Value,
"component_name");
1464 trace::Span Tracer(
"ProfileBrief");
1467 record(MT,
"clangd_lsp_server", MemoryUsage);
1470void ClangdLSPServer::maybeCleanupMemory() {
1471 if (!Opts.MemoryCleanup || !ShouldCleanupMemory())
1473 Opts.MemoryCleanup();
1477void ClangdLSPServer::onChangeConfiguration(
1478 const DidChangeConfigurationParams &Params) {
1479 applyConfiguration(Params.settings);
1482void ClangdLSPServer::onReference(
1483 const ReferenceParams &Params,
1484 Callback<std::vector<ReferenceLocation>> Reply) {
1485 Server->findReferences(Params.textDocument.uri.file(), Params.position,
1486 Opts.ReferencesLimit, SupportsReferenceContainer,
1487 [Reply = std::move(Reply),
1488 IncludeDecl(Params.context.includeDeclaration)](
1489 llvm::Expected<ReferencesResult> Refs)
mutable {
1491 return Reply(Refs.takeError());
1493 std::vector<ReferenceLocation> Result;
1494 Result.reserve(Refs->References.size());
1495 for (auto &Ref : Refs->References) {
1497 Ref.Attributes & ReferencesResult::Declaration;
1498 if (IncludeDecl || !IsDecl)
1499 Result.push_back(std::move(Ref.Loc));
1501 return Reply(std::move(Result));
1505void ClangdLSPServer::onGoToType(
const TextDocumentPositionParams &Params,
1506 Callback<std::vector<Location>> Reply) {
1508 Params.textDocument.uri.file(), Params.position,
1509 [Reply = std::move(Reply)](
1510 llvm::Expected<std::vector<LocatedSymbol>> Types)
mutable {
1512 return Reply(Types.takeError());
1513 std::vector<Location> Response;
1514 for (const LocatedSymbol &Sym : *Types)
1515 Response.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration));
1516 return Reply(std::move(Response));
1520void ClangdLSPServer::onGoToImplementation(
1521 const TextDocumentPositionParams &Params,
1522 Callback<std::vector<Location>> Reply) {
1523 Server->findImplementations(
1524 Params.textDocument.uri.file(), Params.position,
1525 [Reply = std::move(Reply)](
1526 llvm::Expected<std::vector<LocatedSymbol>> Overrides)
mutable {
1528 return Reply(Overrides.takeError());
1529 std::vector<Location> Impls;
1530 for (const LocatedSymbol &Sym : *Overrides)
1531 Impls.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration));
1532 return Reply(std::move(Impls));
1536void ClangdLSPServer::onSymbolInfo(
const TextDocumentPositionParams &Params,
1537 Callback<std::vector<SymbolDetails>> Reply) {
1538 Server->symbolInfo(Params.textDocument.uri.file(), Params.position,
1542void ClangdLSPServer::onSelectionRange(
1543 const SelectionRangeParams &Params,
1544 Callback<std::vector<SelectionRange>> Reply) {
1545 Server->semanticRanges(
1546 Params.textDocument.uri.file(), Params.positions,
1547 [Reply = std::move(Reply)](
1548 llvm::Expected<std::vector<SelectionRange>> Ranges)
mutable {
1550 return Reply(Ranges.takeError());
1551 return Reply(std::move(*Ranges));
1555void ClangdLSPServer::onDocumentLink(
1556 const DocumentLinkParams &Params,
1557 Callback<std::vector<DocumentLink>> Reply) {
1563 Server->documentLinks(
1564 Params.textDocument.uri.file(),
1565 [Reply = std::move(Reply)](
1566 llvm::Expected<std::vector<DocumentLink>> Links)
mutable {
1568 return Reply(Links.takeError());
1570 return Reply(std::move(Links));
1576 for (
char &C : llvm::reverse(S)) {
1583 S.insert(S.begin(),
'1');
1586void ClangdLSPServer::onSemanticTokens(
const SemanticTokensParams &Params,
1587 Callback<SemanticTokens> CB) {
1588 auto File = Params.textDocument.uri.file();
1589 Server->semanticHighlights(
1590 Params.textDocument.uri.file(),
1591 [
this, File(File.str()), CB(std::move(CB)), Code(Server->getDraft(File))](
1592 llvm::Expected<std::vector<HighlightingToken>> HT)
mutable {
1594 return CB(HT.takeError());
1595 SemanticTokens Result;
1596 Result.tokens = toSemanticTokens(*HT, *Code);
1598 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1599 auto &Last = LastSemanticTokens[File];
1601 Last.tokens = Result.tokens;
1602 increment(Last.resultId);
1603 Result.resultId = Last.resultId;
1605 CB(std::move(Result));
1609void ClangdLSPServer::onSemanticTokensDelta(
1610 const SemanticTokensDeltaParams &Params,
1611 Callback<SemanticTokensOrDelta> CB) {
1612 auto File = Params.textDocument.uri.file();
1613 Server->semanticHighlights(
1614 Params.textDocument.uri.file(),
1615 [
this, PrevResultID(Params.previousResultId),
File(
File.str()),
1616 CB(std::move(CB)), Code(Server->getDraft(File))](
1617 llvm::Expected<std::vector<HighlightingToken>> HT)
mutable {
1619 return CB(HT.takeError());
1620 std::vector<SemanticToken> Toks = toSemanticTokens(*HT, *Code);
1622 SemanticTokensOrDelta Result;
1624 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1625 auto &Last = LastSemanticTokens[File];
1627 if (PrevResultID == Last.resultId) {
1628 Result.edits = diffTokens(Last.tokens, Toks);
1630 vlog(
"semanticTokens/full/delta: wanted edits vs {0} but last "
1631 "result had ID {1}. Returning full token list.",
1632 PrevResultID, Last.resultId);
1633 Result.tokens = Toks;
1636 Last.tokens = std::move(Toks);
1637 increment(Last.resultId);
1638 Result.resultId = Last.resultId;
1641 CB(std::move(Result));
1645void ClangdLSPServer::onMemoryUsage(
const NoParams &,
1646 Callback<MemoryTree> Reply) {
1647 llvm::BumpPtrAllocator DetailAlloc;
1648 MemoryTree MT(&DetailAlloc);
1650 Reply(std::move(MT));
1653void ClangdLSPServer::onAST(
const ASTParams &Params,
1654 Callback<std::optional<ASTNode>> CB) {
1655 Server->getAST(Params.textDocument.uri.file(), Params.range, std::move(CB));
1660 : ShouldProfile(std::chrono::minutes(5),
1661 std::chrono::minutes(1)),
1662 ShouldCleanupMemory(std::chrono::minutes(1),
1663 std::chrono::minutes(1)),
1664 BackgroundContext(
Context::current().clone()), Transp(Transp),
1666 SupportedSymbolKinds(defaultSymbolKinds()),
1667 SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts) {
1668 if (Opts.ConfigProvider) {
1669 assert(!Opts.ContextProvider &&
1670 "Only one of ConfigProvider and ContextProvider allowed!");
1671 this->Opts.ContextProvider = ClangdServer::createConfiguredContextProvider(
1672 Opts.ConfigProvider, this);
1675 Bind.
method(
"initialize",
this, &ClangdLSPServer::onInitialize);
1678void ClangdLSPServer::bindMethods(
LSPBinder &Bind,
1681 Bind.
notification(
"initialized",
this, &ClangdLSPServer::onInitialized);
1682 Bind.
method(
"shutdown",
this, &ClangdLSPServer::onShutdown);
1683 Bind.
method(
"sync",
this, &ClangdLSPServer::onSync);
1684 Bind.
method(
"textDocument/rangeFormatting",
this, &ClangdLSPServer::onDocumentRangeFormatting);
1685 Bind.
method(
"textDocument/rangesFormatting",
this, &ClangdLSPServer::onDocumentRangesFormatting);
1686 Bind.
method(
"textDocument/onTypeFormatting",
this, &ClangdLSPServer::onDocumentOnTypeFormatting);
1687 Bind.
method(
"textDocument/formatting",
this, &ClangdLSPServer::onDocumentFormatting);
1688 Bind.
method(
"textDocument/codeAction",
this, &ClangdLSPServer::onCodeAction);
1689 Bind.
method(
"textDocument/completion",
this, &ClangdLSPServer::onCompletion);
1690 Bind.
method(
"textDocument/signatureHelp",
this, &ClangdLSPServer::onSignatureHelp);
1691 Bind.
method(
"textDocument/definition",
this, &ClangdLSPServer::onGoToDefinition);
1692 Bind.
method(
"textDocument/declaration",
this, &ClangdLSPServer::onGoToDeclaration);
1693 Bind.
method(
"textDocument/typeDefinition",
this, &ClangdLSPServer::onGoToType);
1694 Bind.
method(
"textDocument/implementation",
this, &ClangdLSPServer::onGoToImplementation);
1695 Bind.
method(
"textDocument/references",
this, &ClangdLSPServer::onReference);
1696 Bind.
method(
"textDocument/switchSourceHeader",
this, &ClangdLSPServer::onSwitchSourceHeader);
1697 Bind.
method(
"textDocument/prepareRename",
this, &ClangdLSPServer::onPrepareRename);
1698 Bind.
method(
"textDocument/rename",
this, &ClangdLSPServer::onRename);
1699 Bind.
method(
"textDocument/hover",
this, &ClangdLSPServer::onHover);
1700 Bind.
method(
"textDocument/documentSymbol",
this, &ClangdLSPServer::onDocumentSymbol);
1701 Bind.
method(
"workspace/executeCommand",
this, &ClangdLSPServer::onCommand);
1702 Bind.
method(
"textDocument/documentHighlight",
this, &ClangdLSPServer::onDocumentHighlight);
1703 Bind.
method(
"workspace/symbol",
this, &ClangdLSPServer::onWorkspaceSymbol);
1704 Bind.
method(
"textDocument/ast",
this, &ClangdLSPServer::onAST);
1705 Bind.
notification(
"textDocument/didOpen",
this, &ClangdLSPServer::onDocumentDidOpen);
1706 Bind.
notification(
"textDocument/didClose",
this, &ClangdLSPServer::onDocumentDidClose);
1707 Bind.
notification(
"textDocument/didChange",
this, &ClangdLSPServer::onDocumentDidChange);
1708 Bind.
notification(
"textDocument/didSave",
this, &ClangdLSPServer::onDocumentDidSave);
1709 Bind.
notification(
"workspace/didChangeWatchedFiles",
this, &ClangdLSPServer::onFileEvent);
1710 Bind.
notification(
"workspace/didChangeConfiguration",
this, &ClangdLSPServer::onChangeConfiguration);
1711 Bind.
method(
"textDocument/symbolInfo",
this, &ClangdLSPServer::onSymbolInfo);
1712 Bind.
method(
"textDocument/typeHierarchy",
this, &ClangdLSPServer::onTypeHierarchy);
1713 Bind.
method(
"typeHierarchy/resolve",
this, &ClangdLSPServer::onResolveTypeHierarchy);
1714 Bind.
method(
"textDocument/prepareTypeHierarchy",
this, &ClangdLSPServer::onPrepareTypeHierarchy);
1715 Bind.
method(
"typeHierarchy/supertypes",
this, &ClangdLSPServer::onSuperTypes);
1716 Bind.
method(
"typeHierarchy/subtypes",
this, &ClangdLSPServer::onSubTypes);
1717 Bind.
method(
"textDocument/prepareCallHierarchy",
this, &ClangdLSPServer::onPrepareCallHierarchy);
1718 Bind.
method(
"callHierarchy/incomingCalls",
this, &ClangdLSPServer::onCallHierarchyIncomingCalls);
1720 Bind.
method(
"callHierarchy/outgoingCalls",
this, &ClangdLSPServer::onCallHierarchyOutgoingCalls);
1721 Bind.
method(
"textDocument/selectionRange",
this, &ClangdLSPServer::onSelectionRange);
1722 Bind.
method(
"textDocument/documentLink",
this, &ClangdLSPServer::onDocumentLink);
1723 Bind.
method(
"textDocument/semanticTokens/full",
this, &ClangdLSPServer::onSemanticTokens);
1724 Bind.
method(
"textDocument/semanticTokens/full/delta",
this, &ClangdLSPServer::onSemanticTokensDelta);
1725 Bind.
method(
"clangd/inlayHints",
this, &ClangdLSPServer::onClangdInlayHints);
1726 Bind.
method(
"textDocument/inlayHint",
this, &ClangdLSPServer::onInlayHint);
1727 Bind.
method(
"$/memoryUsage",
this, &ClangdLSPServer::onMemoryUsage);
1728 Bind.
method(
"textDocument/foldingRange",
this, &ClangdLSPServer::onFoldingRange);
1729 Bind.
command(ApplyFixCommand,
this, &ClangdLSPServer::onCommandApplyEdit);
1730 Bind.
command(ApplyTweakCommand,
this, &ClangdLSPServer::onCommandApplyTweak);
1731 Bind.
command(ApplyRenameCommand,
this, &ClangdLSPServer::onCommandApplyRename);
1739 CreateWorkDoneProgress = Bind.
outgoingMethod(
"window/workDoneProgress/create");
1744 SemanticTokensRefresh = Bind.
outgoingMethod(
"workspace/semanticTokens/refresh");
1749 IsBeingDestroyed =
true;
1757 bool CleanExit =
true;
1758 if (
auto Err = Transp.loop(*MsgHandler)) {
1759 elog(
"Transport error: {0}", std::move(Err));
1763 return CleanExit && ShutdownRequestReceived;
1768 Server->profile(MT.
child(
"clangd_server"));
1771std::optional<ClangdServer::DiagRef>
1773 std::lock_guard<std::mutex> Lock(DiagRefMutex);
1774 auto DiagToDiagRefIter = DiagRefMap.find(
File);
1775 if (DiagToDiagRefIter == DiagRefMap.end())
1776 return std::nullopt;
1778 const auto &DiagToDiagRefMap = DiagToDiagRefIter->second;
1779 auto FixItsIter = DiagToDiagRefMap.find(toDiagKey(D));
1780 if (FixItsIter == DiagToDiagRefMap.end())
1781 return std::nullopt;
1783 return FixItsIter->second;
1792bool ClangdLSPServer::shouldRunCompletion(
1793 const CompletionParams &Params)
const {
1796 auto Code = Server->getDraft(Params.textDocument.uri.file());
1802 vlog(
"could not convert position '{0}' to offset for file '{1}'",
1803 Params.position, Params.textDocument.uri.file());
1809void ClangdLSPServer::onDiagnosticsReady(
PathRef File, llvm::StringRef Version,
1810 llvm::ArrayRef<Diag> Diagnostics) {
1811 PublishDiagnosticsParams Notification;
1812 Notification.version = decodeVersion(Version);
1814 DiagnosticToDiagRefMap LocalDiagMap;
1815 for (
auto &Diag : Diagnostics) {
1817 [&](clangd::Diagnostic LSPDiag, llvm::ArrayRef<Fix> Fixes) {
1818 if (DiagOpts.EmbedFixesInDiagnostics) {
1819 std::vector<CodeAction> CodeActions;
1820 for (const auto &Fix : Fixes)
1821 CodeActions.push_back(toCodeAction(
1822 Fix, Notification.uri, Notification.version,
1823 SupportsDocumentChanges, SupportsChangeAnnotation));
1824 LSPDiag.codeActions.emplace(std::move(CodeActions));
1825 if (LSPDiag.codeActions->size() == 1)
1826 LSPDiag.codeActions->front().isPreferred = true;
1828 LocalDiagMap[toDiagKey(LSPDiag)] = {Diag.Range, Diag.Message};
1829 Notification.diagnostics.push_back(std::move(LSPDiag));
1835 std::lock_guard<std::mutex> Lock(DiagRefMutex);
1836 DiagRefMap[
File] = LocalDiagMap;
1843void ClangdLSPServer::onInactiveRegionsReady(
1844 PathRef File, std::vector<Range> InactiveRegions) {
1845 InactiveRegionsParams Notification;
1847 Notification.InactiveRegions = std::move(InactiveRegions);
1849 PublishInactiveRegions(Notification);
1852void ClangdLSPServer::onBackgroundIndexProgress(
1854 static const char ProgressToken[] =
"backgroundIndexProgress";
1857 maybeCleanupMemory();
1859 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1861 auto NotifyProgress = [
this](
const BackgroundQueue::Stats &Stats) {
1862 if (BackgroundIndexProgressState != BackgroundIndexProgress::Live) {
1863 WorkDoneProgressBegin Begin;
1864 Begin.percentage =
true;
1865 Begin.title =
"indexing";
1866 BeginWorkDoneProgress({ProgressToken, std::move(Begin)});
1867 BackgroundIndexProgressState = BackgroundIndexProgress::Live;
1870 if (Stats.Completed < Stats.Enqueued) {
1871 assert(Stats.Enqueued > Stats.LastIdle);
1872 WorkDoneProgressReport Report;
1873 Report.percentage = 100 * (Stats.Completed - Stats.LastIdle) /
1874 (Stats.Enqueued - Stats.LastIdle);
1876 llvm::formatv(
"{0}/{1}", Stats.Completed - Stats.LastIdle,
1877 Stats.Enqueued - Stats.LastIdle);
1878 ReportWorkDoneProgress({ProgressToken, std::move(Report)});
1880 assert(Stats.Completed == Stats.Enqueued);
1881 EndWorkDoneProgress({ProgressToken, WorkDoneProgressEnd()});
1882 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
1886 switch (BackgroundIndexProgressState) {
1887 case BackgroundIndexProgress::Unsupported:
1889 case BackgroundIndexProgress::Creating:
1891 PendingBackgroundIndexProgress = Stats;
1893 case BackgroundIndexProgress::Empty: {
1894 if (BackgroundIndexSkipCreate) {
1895 NotifyProgress(Stats);
1899 PendingBackgroundIndexProgress = Stats;
1900 BackgroundIndexProgressState = BackgroundIndexProgress::Creating;
1901 WorkDoneProgressCreateParams CreateRequest;
1902 CreateRequest.token = ProgressToken;
1903 CreateWorkDoneProgress(
1905 [
this, NotifyProgress](llvm::Expected<std::nullptr_t> E) {
1906 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1908 NotifyProgress(this->PendingBackgroundIndexProgress);
1910 elog(
"Failed to create background index progress bar: {0}",
1913 BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported;
1918 case BackgroundIndexProgress::Live:
1919 NotifyProgress(Stats);
1925 if (!SupportFileStatus)
1935 NotifyFileStatus(Status.render(
File));
1938void ClangdLSPServer::onSemanticsMaybeChanged(
PathRef File) {
1939 if (SemanticTokensRefresh) {
1940 SemanticTokensRefresh(NoParams{}, [](llvm::Expected<std::nullptr_t> E) {
1943 elog(
"Failed to refresh semantic tokens: {0}", E.takeError());
static cl::list< std::string > Commands("c", cl::desc("Specify command to run"), cl::value_desc("command"), cl::cat(ClangQueryCategory))
#define SPAN_ATTACH(S, Name, Expr)
Attach a key-value pair to a Span event.
bool onReply(llvm::json::Value ID, llvm::Expected< llvm::json::Value > Result) override
MessageHandler(ClangdLSPServer &Server)
bool onCall(llvm::StringRef Method, llvm::json::Value Params, llvm::json::Value ID) override
bool onNotify(llvm::StringRef Method, llvm::json::Value Params) override
llvm::json::Value bindReply(Callback< llvm::json::Value > Reply)
This class exposes ClangdServer's capabilities via Language Server Protocol.
~ClangdLSPServer()
The destructor blocks on any outstanding background tasks.
ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts)
void profile(MemoryTree &MT) const
Profiles resource-usage.
bool run()
Run LSP server loop, communicating with the Transport provided in the constructor.
Manages a collection of source files and derived data (ASTs, indexes), and provides language-aware fe...
A context is an immutable container for per-request data that must be propagated through layers that ...
Context derive(const Key< Type > &Key, std::decay_t< Type > Value) const &
Derives a child context It is safe to move or destroy a parent context after calling derive().
static const Context & current()
Returns the context for the current thread, creating it if needed.
LSPBinder collects a table of functions that handle LSP calls.
UntypedOutgoingMethod outgoingMethod(llvm::StringLiteral Method)
Bind a function object to be used for outgoing method calls.
void method(llvm::StringLiteral Method, ThisT *This, void(ThisT::*Handler)(const Param &, Callback< Result >))
Bind a handler for an LSP method.
void notification(llvm::StringLiteral Method, ThisT *This, void(ThisT::*Handler)(const Param &))
Bind a handler for an LSP notification.
UntypedOutgoingNotification outgoingNotification(llvm::StringLiteral Method)
Bind a function object to be used for outgoing notifications.
void command(llvm::StringLiteral Command, ThisT *This, void(ThisT::*Handler)(const Param &, Callback< Result >))
Bind a handler for an LSP command.
A threadsafe flag that is initially clear.
Wrapper for vfs::FileSystem for use in multithreaded programs like clangd.
static URI createFile(llvm::StringRef AbsolutePath)
This creates a file:// URI for AbsolutePath. The path must be absolute.
std::string toString() const
Returns a string URI with all components percent-encoded.
WithContext replaces Context::current() with a provided scope.
Records an event whose duration is the lifetime of the Span object.
llvm::json::Object *const Args
Mutable metadata, if this span is interested.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
@ Info
An information message.
llvm::StringRef toSemanticTokenModifier(HighlightingModifier Modifier)
constexpr auto CompletionItemKindMin
llvm::Error applyChange(std::string &Contents, const TextDocumentContentChangeEvent &Change)
Apply an incremental update to a text document.
llvm::StringRef toSemanticTokenType(HighlightingKind Kind)
SystemIncludeExtractorFn getSystemIncludeExtractor(llvm::ArrayRef< std::string > QueryDriverGlobs)
static std::vector< llvm::StringRef > semanticTokenModifiers()
constexpr auto SymbolKindMin
Key< OffsetEncoding > kCurrentOffsetEncoding
void toLSPDiags(const Diag &D, const URIForFile &File, const ClangdDiagnosticOptions &Opts, llvm::function_ref< void(clangd::Diagnostic, llvm::ArrayRef< Fix >)> OutFn)
Conversion to LSP diagnostics.
@ TriggerCharacter
Completion was triggered by a trigger character specified by the triggerCharacters properties of the ...
bool allowImplicitCompletion(llvm::StringRef Content, unsigned Offset)
void record(const MemoryTree &MT, std::string RootName, const trace::Metric &Out)
Records total memory usage of each node under Out.
void vlog(const char *Fmt, Ts &&... Vals)
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
std::string platformString()
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
llvm::json::Value serializeTHIForExtension(TypeHierarchyItem THI)
static std::vector< SymbolInformation > flattenSymbolHierarchy(llvm::ArrayRef< DocumentSymbol > Symbols, const URIForFile &FileURI)
The functions constructs a flattened view of the DocumentSymbol hierarchy.
std::bitset< SymbolKindMax+1 > SymbolKindBitset
static void increment(std::string &S)
std::string featureString()
llvm::StringMap< Edit > FileEdits
A mapping from absolute file path (the one used for accessing the underlying VFS) to edits.
SymbolKind adjustKindToCapability(SymbolKind Kind, SymbolKindBitset &SupportedSymbolKinds)
static std::optional< Command > asCommand(const CodeAction &Action)
std::pair< Context, Canceler > cancelableTask(int Reason)
Defines a new task whose cancellation may be requested.
NoParams InitializedParams
void log(const char *Fmt, Ts &&... Vals)
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
std::bitset< CompletionItemKindMax+1 > CompletionItemKindBitset
static std::vector< llvm::StringRef > semanticTokenTypes()
llvm::StringRef PathRef
A typedef to represent a ref to file path.
@ Auto
Diagnostics must not be generated for this snapshot.
@ No
Diagnostics must be generated for this snapshot.
static Location * getToggle(const TextDocumentPositionParams &Point, LocatedSymbol &Sym)
std::string Path
A typedef to represent a file path.
std::function< void()> Canceler
A canceller requests cancellation of a task, when called.
@ Incremental
Documents are synced by sending the full content on open.
void elog(const char *Fmt, Ts &&... Vals)
std::string versionString()
std::vector< TextEdit > replacementsToEdits(llvm::StringRef Code, const tooling::Replacements &Repls)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::optional< OffsetEncoding > Encoding
The offset-encoding to use, or std::nullopt to negotiate it over LSP.
bool EnableOutgoingCalls
Call hierarchy's outgoing calls feature requires additional index serving structures which increase m...
bool SemanticTokenRefreshSupport
Whether the client implementation supports a refresh request sent from the server to the client.
bool InactiveRegions
Whether the client supports the textDocument/inactiveRegions notification.
A code action represents a change that can be performed in code, e.g.
static const llvm::StringLiteral INFO_KIND
static const llvm::StringLiteral REFACTOR_KIND
static const llvm::StringLiteral QUICKFIX_KIND
std::optional< WorkspaceEdit > edit
The workspace edit this code action performs.
std::optional< Command > command
A command this code action executes.
std::optional< std::string > kind
The kind of the code action.
std::string title
A short, human-readable, title for this code action.
static CommandMangler detect()
Represents programming constructs like variables, classes, interfaces etc.
std::vector< DocumentSymbol > children
Children of this symbol, e.g. properties of a class.
std::string name
The name of this symbol.
Range range
The range enclosing this symbol not including leading/trailing whitespace but everything else like co...
SymbolKind kind
The kind of this symbol.
A set of edits generated for a single file.
Represents a single fix-it that editor can apply to fix the error.
Location PreferredDeclaration
std::optional< Location > Definition
URIForFile uri
The text document's URI.
A tree that can be used to represent memory usage of nested components while preserving the hierarchy...
MemoryTree & child(llvm::StringLiteral Name)
No copy of the Name.
bool contains(Position Pos) const
TextDocumentIdentifier textDocument
The document that was opened.
URIForFile uri
The text document's URI.
TextDocumentIdentifier textDocument
The text document.
Position position
The position inside the text document.
Arguments for the 'applyTweak' command.
URIForFile file
A file provided by the client on a textDocument/codeAction request.
Range range
The range enclosing this symbol not including leading/trailing whitespace but everything else,...
URIForFile uri
The resource identifier of this item.
Range selectionRange
The range that should be selected and revealed when this symbol is being picked, e....
SymbolKind kind
The kind of this item.
std::optional< std::vector< TypeHierarchyItem > > children
If this type hierarchy item is resolved, it contains the direct children of the current item.
std::optional< std::vector< TypeHierarchyItem > > parents
This is a clangd exntesion.
bool deprecated
true if the hierarchy item is deprecated.
std::optional< std::string > detail
More detail for this item, e.g. the signature of a function.
ResolveParams data
A data entry field that is preserved between a type hierarchy prepare and supertypes or subtypes requ...
std::string name
The name of this item.
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Canonicalizes AbsPath via URI.
llvm::StringRef file() const
Retrieves absolute path to the file.
The edit should either provide changes or documentChanges.
The parameters of a Workspace Symbol Request.
Represents measurements of clangd events, e.g.
@ Distribution
A distribution of values with a meaningful mean and count.