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,
459constexpr int ClangdLSPServer::MessageHandler::MaxReplayCallbacks;
462void ClangdLSPServer::callMethod(StringRef
Method, llvm::json::Value Params,
464 auto ID = MsgHandler->bindReply(std::move(CB));
466 std::lock_guard<std::mutex> Lock(TranspWriter);
467 Transp.call(
Method, std::move(Params), ID);
470void ClangdLSPServer::notify(llvm::StringRef
Method, llvm::json::Value Params) {
472 maybeCleanupMemory();
473 std::lock_guard<std::mutex> Lock(TranspWriter);
474 Transp.notify(
Method, std::move(Params));
478 std::vector<llvm::StringRef> Types;
486 std::vector<llvm::StringRef> Modifiers;
494void ClangdLSPServer::onInitialize(
const InitializeParams &Params,
497 if (Params.capabilities.PositionEncodings && !Opts.
Encoding) {
499 for (
OffsetEncoding Supported : *Params.capabilities.PositionEncodings)
506 if (Params.capabilities.TheiaSemanticHighlighting &&
507 !Params.capabilities.SemanticTokens) {
508 elog(
"Client requested legacy semanticHighlights notification, which is "
509 "no longer supported. Migrate to standard semanticTokens request");
512 if (Params.rootUri && *Params.rootUri)
513 Opts.WorkspaceRoot = std::string(Params.rootUri->file());
514 else if (Params.rootPath && !Params.rootPath->empty())
515 Opts.WorkspaceRoot = *Params.rootPath;
517 return Reply(llvm::make_error<LSPError>(
"server already initialized",
520 Opts.CodeComplete.EnableSnippets = Params.capabilities.CompletionSnippets;
521 Opts.CodeComplete.IncludeFixIts = Params.capabilities.CompletionFixes;
522 if (!Opts.CodeComplete.BundleOverloads)
523 Opts.CodeComplete.BundleOverloads = Params.capabilities.HasSignatureHelp;
524 Opts.CodeComplete.DocumentationFormat =
525 Params.capabilities.CompletionDocumentationFormat;
526 Opts.SignatureHelpDocumentationFormat =
527 Params.capabilities.SignatureHelpDocumentationFormat;
528 DiagOpts.EmbedFixesInDiagnostics = Params.capabilities.DiagnosticFixes;
529 DiagOpts.SendDiagnosticCategory = Params.capabilities.DiagnosticCategory;
530 DiagOpts.EmitRelatedLocations =
531 Params.capabilities.DiagnosticRelatedInformation;
532 if (Params.capabilities.WorkspaceSymbolKinds)
533 SupportedSymbolKinds |= *Params.capabilities.WorkspaceSymbolKinds;
534 if (Params.capabilities.CompletionItemKinds)
535 SupportedCompletionItemKinds |= *Params.capabilities.CompletionItemKinds;
536 SupportsCompletionLabelDetails = Params.capabilities.CompletionLabelDetail;
537 SupportsCodeAction = Params.capabilities.CodeActionStructure;
538 SupportsHierarchicalDocumentSymbol =
539 Params.capabilities.HierarchicalDocumentSymbol;
540 SupportsReferenceContainer = Params.capabilities.ReferenceContainer;
541 SupportFileStatus = Params.initializationOptions.FileStatus;
542 SupportsDocumentChanges = Params.capabilities.DocumentChanges;
543 SupportsChangeAnnotation = Params.capabilities.ChangeAnnotation;
544 HoverContentFormat = Params.capabilities.HoverContentFormat;
545 Opts.LineFoldingOnly = Params.capabilities.LineFoldingOnly;
546 SupportsOffsetsInSignatureHelp = Params.capabilities.OffsetsInSignatureHelp;
547 if (Params.capabilities.WorkDoneProgress)
548 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
549 BackgroundIndexSkipCreate = Params.capabilities.ImplicitProgressCreation;
550 Opts.ImplicitCancellation = !Params.capabilities.CancelsStaleRequests;
551 Opts.PublishInactiveRegions = Params.capabilities.InactiveRegions;
553 if (Opts.UseDirBasedCDB) {
554 DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS);
555 if (
const auto &Dir = Params.initializationOptions.compilationDatabasePath)
556 CDBOpts.CompileCommandsDir = Dir;
557 CDBOpts.ContextProvider = Opts.ContextProvider;
559 std::make_unique<DirectoryBasedGlobalCompilationDatabase>(CDBOpts);
562 Mangler.SystemIncludeExtractor =
564 if (Opts.ResourceDir)
565 Mangler.ResourceDir = *Opts.ResourceDir;
566 CDB.emplace(BaseCDB.get(), Params.initializationOptions.fallbackFlags,
569 if (Opts.EnableExperimentalModulesSupport) {
570 ModulesManager.emplace(*CDB);
571 Opts.ModulesManager = &*ModulesManager;
578 WithContext MainContext(BackgroundContext.clone());
579 std::optional<WithContextValue> WithOffsetEncoding;
582 Server.emplace(*CDB, TFS, Opts,
583 static_cast<ClangdServer::Callbacks *
>(
this));
586 llvm::json::Object ServerCaps{
593 {
"documentFormattingProvider",
true},
594 {
"documentRangeFormattingProvider",
596 {
"rangesSupport",
true},
598 {
"documentOnTypeFormattingProvider",
600 {
"firstTriggerCharacter",
"\n"},
601 {
"moreTriggerCharacter", {}},
603 {
"completionProvider",
610 {
"resolveProvider",
false},
612 {
"triggerCharacters", {
".",
"<",
">",
":",
"\"",
"/",
"*"}},
614 {
"semanticTokensProvider",
616 {
"full", llvm::json::Object{{
"delta",
true}}},
622 {
"signatureHelpProvider",
624 {
"triggerCharacters", {
"(",
")",
"{",
"}",
"<",
">",
","}},
626 {
"declarationProvider",
true},
627 {
"definitionProvider",
true},
628 {
"implementationProvider",
true},
629 {
"typeDefinitionProvider",
true},
630 {
"documentHighlightProvider",
true},
631 {
"documentLinkProvider",
633 {
"resolveProvider",
false},
635 {
"hoverProvider",
true},
636 {
"selectionRangeProvider",
true},
637 {
"documentSymbolProvider",
true},
638 {
"workspaceSymbolProvider",
true},
639 {
"referencesProvider",
true},
640 {
"astProvider",
true},
641 {
"typeHierarchyProvider",
true},
645 {
"standardTypeHierarchyProvider",
true},
646 {
"memoryUsageProvider",
true},
647 {
"compilationDatabase",
648 llvm::json::Object{{
"automaticReload",
true}}},
649 {
"inactiveRegionsProvider",
true},
650 {
"callHierarchyProvider",
true},
651 {
"clangdInlayHintsProvider",
true},
652 {
"inlayHintProvider",
true},
653 {
"foldingRangeProvider",
true},
657 LSPBinder Binder(Handlers, *
this);
658 bindMethods(Binder, Params.capabilities);
659 if (Opts.FeatureModules)
660 for (
auto &Mod : *Opts.FeatureModules)
661 Mod.initializeLSP(Binder, Params.rawCapabilities, ServerCaps);
666 ServerCaps[
"renameProvider"] =
667 Params.capabilities.RenamePrepareSupport
668 ? llvm::json::Object{{
"prepareProvider",
true}}
669 : llvm::json::Value(
true);
674 ServerCaps[
"codeActionProvider"] =
675 Params.capabilities.CodeActionStructure
676 ? llvm::json::Object{{
"codeActionKinds",
680 : llvm::json::Value(
true);
682 std::vector<llvm::StringRef>
Commands;
683 for (llvm::StringRef Command : Handlers.CommandHandlers.keys())
686 ServerCaps[
"executeCommandProvider"] =
687 llvm::json::Object{{
"commands",
Commands}};
690 ServerCaps[
"positionEncoding"] = *Opts.Encoding;
692 llvm::json::Object Result{
698 {
"capabilities", std::move(ServerCaps)}}};
703 Result[
"offsetEncoding"] = *Opts.Encoding;
704 Reply(std::move(Result));
708 applyConfiguration(Params.initializationOptions.ConfigSettings);
713void ClangdLSPServer::onShutdown(
const NoParams &,
716 ShutdownRequestReceived =
true;
723 if (Server->blockUntilIdleForTest(60))
726 Reply(
error(
"Not idle after a minute"));
729void ClangdLSPServer::onDocumentDidOpen(
733 const std::string &Contents = Params.textDocument.text;
735 Server->addDocument(
File, Contents,
736 encodeVersion(Params.textDocument.version),
740void ClangdLSPServer::onDocumentDidChange(
743 if (Params.wantDiagnostics)
748 auto Code = Server->getDraft(
File);
750 log(
"Trying to incrementally change non-added document: {0}",
File);
753 std::string NewCode(*Code);
754 for (
const auto &Change : Params.contentChanges) {
759 Server->removeDocument(
File);
760 elog(
"Failed to update {0}: {1}",
File, std::move(Err));
764 Server->addDocument(
File, NewCode, encodeVersion(Params.textDocument.version),
765 WantDiags, Params.forceRebuild);
768void ClangdLSPServer::onDocumentDidSave(
770 Server->reparseOpenFilesIfNeeded([](llvm::StringRef) {
return true; });
778 Server->onFileEvent(Params);
787 auto It = Handlers.CommandHandlers.find(Params.command);
788 if (It == Handlers.CommandHandlers.end()) {
789 return Reply(llvm::make_error<LSPError>(
790 llvm::formatv(
"Unsupported command \"{0}\".", Params.command).str(),
793 It->second(Params.argument, std::move(Reply));
796void ClangdLSPServer::onCommandApplyEdit(
const WorkspaceEdit &WE,
806 applyEdit(WE,
"Fix applied.", std::move(Reply));
809void ClangdLSPServer::onCommandApplyTweak(
const TweakArgs &Args,
811 auto Action = [
this, Reply = std::move(Reply)](
812 llvm::Expected<Tweak::Effect> R)
mutable {
814 return Reply(R.takeError());
816 assert(R->ShowMessage || (!R->ApplyEdits.empty() &&
"tweak has no effect"));
818 if (R->ShowMessage) {
819 ShowMessageParams Msg;
820 Msg.message = *R->ShowMessage;
825 if (R->ApplyEdits.empty())
826 return Reply(
"Tweak applied.");
828 if (
auto Err = validateEdits(*Server, R->ApplyEdits))
829 return Reply(std::move(Err));
833 WE.changes.emplace();
834 for (
const auto &It : R->ApplyEdits) {
836 It.second.asTextEdits();
839 return applyEdit(std::move(WE),
"Tweak applied.", std::move(Reply));
841 Server->applyTweak(Args.file.file(), Args.selection, Args.tweakID,
845void ClangdLSPServer::onCommandApplyRename(
const RenameParams &R,
847 onRename(R, [
this, Reply = std::move(Reply)](
848 llvm::Expected<WorkspaceEdit> Edit)
mutable {
850 Reply(Edit.takeError());
851 applyEdit(std::move(*Edit),
"Rename applied.", std::move(Reply));
855void ClangdLSPServer::applyEdit(
WorkspaceEdit WE, llvm::json::Value Success,
857 ApplyWorkspaceEditParams Edit;
858 Edit.edit = std::move(WE);
860 Edit, [Reply = std::move(Reply), SuccessMessage = std::move(Success)](
861 llvm::Expected<ApplyWorkspaceEditResponse> Response)
mutable {
863 return Reply(Response.takeError());
864 if (!Response->applied) {
865 std::string Reason = Response->failureReason
866 ? *Response->failureReason
868 return Reply(
error(
"edits were not applied: {0}", Reason));
870 return Reply(SuccessMessage);
874void ClangdLSPServer::onWorkspaceSymbol(
876 Callback<std::vector<SymbolInformation>> Reply) {
877 Server->workspaceSymbols(
878 Params.query, Params.limit.value_or(Opts.CodeComplete.Limit),
879 [Reply = std::move(Reply),
880 this](llvm::Expected<std::vector<SymbolInformation>> Items)
mutable {
882 return Reply(Items.takeError());
883 for (auto &Sym : *Items)
884 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds);
886 Reply(std::move(*Items));
892 Server->prepareRename(
893 Params.textDocument.uri.file(), Params.position, std::nullopt,
895 [Reply = std::move(Reply)](llvm::Expected<RenameResult> Result)
mutable {
897 return Reply(Result.takeError());
898 PrepareRenameResult PrepareResult;
899 PrepareResult.range = Result->Target;
900 PrepareResult.placeholder = Result->Placeholder;
901 return Reply(std::move(PrepareResult));
905void ClangdLSPServer::onRename(
const RenameParams &Params,
907 Path File = std::string(Params.textDocument.uri.file());
908 if (!Server->getDraft(
File))
909 return Reply(llvm::make_error<LSPError>(
911 Server->rename(
File, Params.position, Params.newName, Opts.Rename,
912 [
File, Params, Reply = std::move(Reply),
913 this](llvm::Expected<RenameResult> R)
mutable {
915 return Reply(R.takeError());
916 if (auto Err = validateEdits(*Server, R->GlobalChanges))
917 return Reply(std::move(Err));
918 WorkspaceEdit Result;
921 Result.changes.emplace();
922 for (const auto &Rep : R->GlobalChanges) {
924 .changes)[URI::createFile(Rep.first()).toString()] =
925 Rep.second.asTextEdits();
931void ClangdLSPServer::onDocumentDidClose(
932 const DidCloseTextDocumentParams &Params) {
934 Server->removeDocument(File);
937 std::lock_guard<std::mutex> Lock(DiagRefMutex);
938 DiagRefMap.erase(File);
941 std::lock_guard<std::mutex> HLock(SemanticTokensMutex);
942 LastSemanticTokens.erase(File);
949 PublishDiagnosticsParams Notification;
950 Notification.uri = URIForFile::canonicalize(File, File);
951 PublishDiagnostics(Notification);
954void ClangdLSPServer::onDocumentOnTypeFormatting(
955 const DocumentOnTypeFormattingParams &Params,
956 Callback<std::vector<TextEdit>> Reply) {
957 auto File = Params.textDocument.uri.file();
958 Server->formatOnType(File, Params.position, Params.ch, std::move(Reply));
961void ClangdLSPServer::onDocumentRangeFormatting(
962 const DocumentRangeFormattingParams &Params,
963 Callback<std::vector<TextEdit>> Reply) {
964 onDocumentRangesFormatting(
965 DocumentRangesFormattingParams{Params.textDocument, {Params.range}},
969void ClangdLSPServer::onDocumentRangesFormatting(
970 const DocumentRangesFormattingParams &Params,
971 Callback<std::vector<TextEdit>> Reply) {
972 auto File = Params.textDocument.uri.file();
973 auto Code = Server->getDraft(File);
974 Server->formatFile(File, Params.ranges,
975 [Code = std::move(Code), Reply = std::move(Reply)](
976 llvm::Expected<tooling::Replacements> Result)
mutable {
978 Reply(replacementsToEdits(*Code, Result.get()));
980 Reply(Result.takeError());
984void ClangdLSPServer::onDocumentFormatting(
985 const DocumentFormattingParams &Params,
986 Callback<std::vector<TextEdit>> Reply) {
987 auto File = Params.textDocument.uri.file();
988 auto Code = Server->getDraft(File);
989 Server->formatFile(File,
991 [Code = std::move(Code), Reply = std::move(Reply)](
992 llvm::Expected<tooling::Replacements> Result)
mutable {
996 Reply(Result.takeError());
1002static std::vector<SymbolInformation>
1005 std::vector<SymbolInformation> Results;
1006 std::function<void(
const DocumentSymbol &, llvm::StringRef)> Process =
1007 [&](
const DocumentSymbol &S, std::optional<llvm::StringRef> ParentName) {
1009 SI.
containerName = std::string(ParentName ?
"" : *ParentName);
1015 Results.push_back(std::move(SI));
1016 std::string FullName =
1017 !ParentName ? S.
name : (ParentName->str() +
"::" + S.
name);
1019 Process(C, FullName);
1026void ClangdLSPServer::onDocumentSymbol(
const DocumentSymbolParams &Params,
1027 Callback<llvm::json::Value> Reply) {
1028 URIForFile FileURI = Params.textDocument.uri;
1029 Server->documentSymbols(
1030 Params.textDocument.uri.file(),
1031 [
this, FileURI, Reply = std::move(Reply)](
1032 llvm::Expected<std::vector<DocumentSymbol>> Items)
mutable {
1034 return Reply(Items.takeError());
1035 adjustSymbolKinds(*Items, SupportedSymbolKinds);
1036 if (SupportsHierarchicalDocumentSymbol)
1037 return Reply(std::move(*Items));
1038 return Reply(flattenSymbolHierarchy(*Items, FileURI));
1042void ClangdLSPServer::onFoldingRange(
1043 const FoldingRangeParams &Params,
1044 Callback<std::vector<FoldingRange>> Reply) {
1045 Server->foldingRanges(Params.textDocument.uri.file(), std::move(Reply));
1051 return std::nullopt;
1054 }
else if (Action.
edit) {
1055 Cmd.command = std::string(ApplyFixCommand);
1056 Cmd.argument = *Action.
edit;
1058 return std::nullopt;
1060 Cmd.title = Action.
title;
1062 Cmd.title =
"Apply fix: " + Cmd.title;
1066void ClangdLSPServer::onCodeAction(
const CodeActionParams &Params,
1067 Callback<llvm::json::Value> Reply) {
1068 URIForFile File = Params.textDocument.uri;
1069 std::map<ClangdServer::DiagRef, clangd::Diagnostic> ToLSPDiags;
1070 ClangdServer::CodeActionInputs Inputs;
1072 for (
const auto& LSPDiag : Params.context.diagnostics) {
1073 if (
auto DiagRef = getDiagRef(File.file(), LSPDiag)) {
1074 ToLSPDiags[*DiagRef] = LSPDiag;
1075 Inputs.Diagnostics.push_back(*DiagRef);
1078 Inputs.File =
File.file();
1079 Inputs.Selection = Params.range;
1080 Inputs.RequestedActionKinds = Params.context.only;
1081 Inputs.TweakFilter = [
this](
const Tweak &
T) {
1082 return Opts.TweakFilter(T);
1085 Reply = std::move(Reply),
1086 ToLSPDiags = std::move(ToLSPDiags),
File,
1087 Selection = Params.range](
1088 llvm::Expected<ClangdServer::CodeActionResult> Fixits)
mutable {
1090 return Reply(Fixits.takeError());
1091 std::vector<CodeAction> CAs;
1092 auto Version = decodeVersion(Fixits->Version);
1093 for (
const auto &QF : Fixits->QuickFixes) {
1094 CAs.push_back(toCodeAction(QF.F, File, Version, SupportsDocumentChanges,
1095 SupportsChangeAnnotation));
1096 if (
auto It = ToLSPDiags.find(QF.Diag);
1097 It != ToLSPDiags.end()) {
1098 CAs.back().diagnostics = {It->second};
1102 for (
const auto &R : Fixits->Renames)
1103 CAs.push_back(toCodeAction(R, File));
1105 for (
const auto &TR : Fixits->TweakRefs)
1106 CAs.push_back(toCodeAction(TR, File, Selection));
1110 CodeAction *OnlyFix =
nullptr;
1111 for (
auto &Action : CAs) {
1112 if (Action.kind && *Action.kind == CodeAction::QUICKFIX_KIND) {
1121 OnlyFix->isPreferred =
true;
1122 if (ToLSPDiags.size() == 1 &&
1123 ToLSPDiags.begin()->second.range == Selection)
1124 OnlyFix->diagnostics = {ToLSPDiags.begin()->second};
1127 if (SupportsCodeAction)
1128 return Reply(llvm::json::Array(CAs));
1130 for (
const auto &Action : CAs) {
1132 Commands.push_back(std::move(*Command));
1134 return Reply(llvm::json::Array(
Commands));
1136 Server->codeAction(Inputs, std::move(CB));
1139void ClangdLSPServer::onCompletion(
const CompletionParams &Params,
1140 Callback<CompletionList> Reply) {
1141 if (!shouldRunCompletion(Params)) {
1144 vlog(
"ignored auto-triggered completion, preceding char did not match");
1145 return Reply(CompletionList());
1147 auto Opts = this->Opts.CodeComplete;
1148 if (Params.limit && *Params.limit >= 0)
1149 Opts.Limit = *Params.limit;
1150 Server->codeComplete(Params.textDocument.uri.file(), Params.position, Opts,
1151 [Reply = std::move(Reply), Opts,
1152 this](llvm::Expected<CodeCompleteResult> List)
mutable {
1154 return Reply(List.takeError());
1155 CompletionList LSPList;
1156 LSPList.isIncomplete = List->HasMore;
1157 for (const auto &R : List->Completions) {
1158 CompletionItem C = R.render(Opts);
1159 C.kind = adjustKindToCapability(
1160 C.kind, SupportedCompletionItemKinds);
1161 if (!SupportsCompletionLabelDetails)
1162 removeCompletionLabelDetails(C);
1163 LSPList.items.push_back(std::move(C));
1165 return Reply(std::move(LSPList));
1169void ClangdLSPServer::onSignatureHelp(
const TextDocumentPositionParams &Params,
1170 Callback<SignatureHelp> Reply) {
1171 Server->signatureHelp(Params.textDocument.uri.file(), Params.position,
1172 Opts.SignatureHelpDocumentationFormat,
1173 [Reply = std::move(Reply),
this](
1174 llvm::Expected<SignatureHelp> Signature)
mutable {
1176 return Reply(Signature.takeError());
1177 if (SupportsOffsetsInSignatureHelp)
1178 return Reply(std::move(*Signature));
1181 for (auto &SigInfo : Signature->signatures) {
1182 for (auto &Param : SigInfo.parameters)
1183 Param.labelOffsets.reset();
1185 return Reply(std::move(*Signature));
1209void ClangdLSPServer::onGoToDefinition(
const TextDocumentPositionParams &Params,
1210 Callback<std::vector<Location>> Reply) {
1211 Server->locateSymbolAt(
1212 Params.textDocument.uri.file(), Params.position,
1213 [Params, Reply = std::move(Reply)](
1214 llvm::Expected<std::vector<LocatedSymbol>> Symbols)
mutable {
1216 return Reply(Symbols.takeError());
1217 std::vector<Location> Defs;
1218 for (auto &S : *Symbols) {
1219 if (Location *Toggle = getToggle(Params, S))
1220 return Reply(std::vector<Location>{std::move(*Toggle)});
1221 Defs.push_back(S.Definition.value_or(S.PreferredDeclaration));
1223 Reply(std::move(Defs));
1227void ClangdLSPServer::onGoToDeclaration(
1228 const TextDocumentPositionParams &Params,
1229 Callback<std::vector<Location>> Reply) {
1230 Server->locateSymbolAt(
1231 Params.textDocument.uri.file(), Params.position,
1232 [Params, Reply = std::move(Reply)](
1233 llvm::Expected<std::vector<LocatedSymbol>> Symbols)
mutable {
1235 return Reply(Symbols.takeError());
1236 std::vector<Location> Decls;
1237 for (auto &S : *Symbols) {
1238 if (Location *Toggle = getToggle(Params, S))
1239 return Reply(std::vector<Location>{std::move(*Toggle)});
1240 Decls.push_back(std::move(S.PreferredDeclaration));
1242 Reply(std::move(Decls));
1246void ClangdLSPServer::onSwitchSourceHeader(
1247 const TextDocumentIdentifier &Params,
1248 Callback<std::optional<URIForFile>> Reply) {
1249 Server->switchSourceHeader(
1251 [Reply = std::move(Reply),
1252 Params](llvm::Expected<std::optional<clangd::Path>> Path)
mutable {
1254 return Reply(Path.takeError());
1256 return Reply(URIForFile::canonicalize(**Path, Params.uri.file()));
1257 return Reply(std::nullopt);
1261void ClangdLSPServer::onDocumentHighlight(
1262 const TextDocumentPositionParams &Params,
1263 Callback<std::vector<DocumentHighlight>> Reply) {
1264 Server->findDocumentHighlights(Params.textDocument.uri.file(),
1265 Params.position, std::move(Reply));
1268void ClangdLSPServer::onHover(
const TextDocumentPositionParams &Params,
1269 Callback<std::optional<Hover>> Reply) {
1270 Server->findHover(Params.textDocument.uri.file(), Params.position,
1271 [Reply = std::move(Reply),
1272 this](llvm::Expected<std::optional<HoverInfo>> H)
mutable {
1274 return Reply(H.takeError());
1276 return Reply(std::nullopt);
1279 R.contents.kind = HoverContentFormat;
1280 R.range = (*H)->SymRange;
1281 switch (HoverContentFormat) {
1282 case MarkupKind::Markdown:
1283 case MarkupKind::PlainText:
1284 R.contents.value = (*H)->present(HoverContentFormat);
1285 return Reply(std::move(R));
1287 llvm_unreachable(
"unhandled MarkupKind");
1294 llvm::json::Object Result{{
1295 {
"name", std::move(THI.
name)},
1296 {
"kind",
static_cast<int>(THI.
kind)},
1297 {
"uri", std::move(THI.
uri)},
1298 {
"range", THI.
range},
1300 {
"data", std::move(THI.
data)},
1305 Result[
"detail"] = std::move(*THI.
detail);
1309 for (
auto &Parent : *THI.
parents)
1311 Result[
"parents"] = std::move(
Parents);
1318 Result[
"children"] = std::move(
Children);
1323void ClangdLSPServer::onTypeHierarchy(
const TypeHierarchyPrepareParams &Params,
1324 Callback<llvm::json::Value> Reply) {
1326 [Reply = std::move(Reply)](
1327 llvm::Expected<std::vector<TypeHierarchyItem>> Resp)
mutable {
1329 Reply(Resp.takeError());
1332 if (Resp->empty()) {
1338 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1339 Params.resolve, Params.direction, std::move(Serialize));
1342void ClangdLSPServer::onResolveTypeHierarchy(
1343 const ResolveTypeHierarchyItemParams &Params,
1344 Callback<llvm::json::Value> Reply) {
1346 [Reply = std::move(Reply)](
1347 llvm::Expected<std::optional<TypeHierarchyItem>> Resp)
mutable {
1349 Reply(Resp.takeError());
1353 Reply(std::move(*Resp));
1358 Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction,
1359 std::move(Serialize));
1362void ClangdLSPServer::onPrepareTypeHierarchy(
1363 const TypeHierarchyPrepareParams &Params,
1364 Callback<std::vector<TypeHierarchyItem>> Reply) {
1365 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1366 Params.resolve, Params.direction, std::move(Reply));
1369void ClangdLSPServer::onSuperTypes(
1370 const ResolveTypeHierarchyItemParams &Params,
1371 Callback<std::optional<std::vector<TypeHierarchyItem>>> Reply) {
1372 Server->superTypes(Params.item, std::move(Reply));
1375void ClangdLSPServer::onSubTypes(
1376 const ResolveTypeHierarchyItemParams &Params,
1377 Callback<std::vector<TypeHierarchyItem>> Reply) {
1378 Server->subTypes(Params.item, std::move(Reply));
1381void ClangdLSPServer::onPrepareCallHierarchy(
1382 const CallHierarchyPrepareParams &Params,
1383 Callback<std::vector<CallHierarchyItem>> Reply) {
1384 Server->prepareCallHierarchy(Params.textDocument.uri.file(), Params.position,
1388void ClangdLSPServer::onCallHierarchyIncomingCalls(
1389 const CallHierarchyIncomingCallsParams &Params,
1390 Callback<std::vector<CallHierarchyIncomingCall>> Reply) {
1391 Server->incomingCalls(Params.item, std::move(Reply));
1394void ClangdLSPServer::onClangdInlayHints(
const InlayHintsParams &Params,
1395 Callback<llvm::json::Value> Reply) {
1400 auto Serialize = [Reply = std::move(Reply)](
1401 llvm::Expected<std::vector<InlayHint>> Hints)
mutable {
1403 Reply(Hints.takeError());
1406 llvm::json::Array Result;
1407 Result.reserve(Hints->size());
1408 for (
auto &Hint : *Hints) {
1409 Result.emplace_back(llvm::json::Object{
1410 {
"kind", llvm::to_string(Hint.kind)},
1411 {
"range", Hint.range},
1412 {
"position", Hint.position},
1416 ((Hint.paddingLeft ?
" " :
"") + llvm::StringRef(Hint.joinLabels()) +
1417 (Hint.paddingRight ?
" " :
""))
1421 Reply(std::move(Result));
1423 Server->inlayHints(Params.textDocument.uri.file(), Params.range,
1424 std::move(Serialize));
1427void ClangdLSPServer::onInlayHint(
const InlayHintsParams &Params,
1428 Callback<std::vector<InlayHint>> Reply) {
1429 Server->inlayHints(Params.textDocument.uri.file(), Params.range,
1433void ClangdLSPServer::onCallHierarchyOutgoingCalls(
1434 const CallHierarchyOutgoingCallsParams &Params,
1435 Callback<std::vector<CallHierarchyOutgoingCall>> Reply) {
1436 Server->outgoingCalls(Params.item, std::move(Reply));
1439void ClangdLSPServer::applyConfiguration(
1440 const ConfigurationSettings &Settings) {
1442 llvm::StringSet<> ModifiedFiles;
1443 for (
auto &[File, Command] : Settings.compilationDatabaseChanges) {
1445 tooling::CompileCommand(std::move(Command.workingDirectory), File,
1446 std::move(Command.compilationCommand),
1448 if (CDB->setCompileCommand(File, std::move(Cmd))) {
1449 ModifiedFiles.insert(File);
1453 Server->reparseOpenFilesIfNeeded(
1454 [&](llvm::StringRef File) {
return ModifiedFiles.count(File) != 0; });
1457void ClangdLSPServer::maybeExportMemoryProfile() {
1458 if (!trace::enabled() || !ShouldProfile())
1461 static constexpr trace::Metric MemoryUsage(
1462 "memory_usage", trace::Metric::Value,
"component_name");
1463 trace::Span Tracer(
"ProfileBrief");
1466 record(MT,
"clangd_lsp_server", MemoryUsage);
1469void ClangdLSPServer::maybeCleanupMemory() {
1470 if (!Opts.MemoryCleanup || !ShouldCleanupMemory())
1472 Opts.MemoryCleanup();
1476void ClangdLSPServer::onChangeConfiguration(
1477 const DidChangeConfigurationParams &Params) {
1478 applyConfiguration(Params.settings);
1481void ClangdLSPServer::onReference(
1482 const ReferenceParams &Params,
1483 Callback<std::vector<ReferenceLocation>> Reply) {
1484 Server->findReferences(Params.textDocument.uri.file(), Params.position,
1485 Opts.ReferencesLimit, SupportsReferenceContainer,
1486 [Reply = std::move(Reply),
1487 IncludeDecl(Params.context.includeDeclaration)](
1488 llvm::Expected<ReferencesResult> Refs)
mutable {
1490 return Reply(Refs.takeError());
1492 std::vector<ReferenceLocation> Result;
1493 Result.reserve(Refs->References.size());
1494 for (auto &Ref : Refs->References) {
1496 Ref.Attributes & ReferencesResult::Declaration;
1497 if (IncludeDecl || !IsDecl)
1498 Result.push_back(std::move(Ref.Loc));
1500 return Reply(std::move(Result));
1504void ClangdLSPServer::onGoToType(
const TextDocumentPositionParams &Params,
1505 Callback<std::vector<Location>> Reply) {
1507 Params.textDocument.uri.file(), Params.position,
1508 [Reply = std::move(Reply)](
1509 llvm::Expected<std::vector<LocatedSymbol>> Types)
mutable {
1511 return Reply(Types.takeError());
1512 std::vector<Location> Response;
1513 for (const LocatedSymbol &Sym : *Types)
1514 Response.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration));
1515 return Reply(std::move(Response));
1519void ClangdLSPServer::onGoToImplementation(
1520 const TextDocumentPositionParams &Params,
1521 Callback<std::vector<Location>> Reply) {
1522 Server->findImplementations(
1523 Params.textDocument.uri.file(), Params.position,
1524 [Reply = std::move(Reply)](
1525 llvm::Expected<std::vector<LocatedSymbol>> Overrides)
mutable {
1527 return Reply(Overrides.takeError());
1528 std::vector<Location> Impls;
1529 for (const LocatedSymbol &Sym : *Overrides)
1530 Impls.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration));
1531 return Reply(std::move(Impls));
1535void ClangdLSPServer::onSymbolInfo(
const TextDocumentPositionParams &Params,
1536 Callback<std::vector<SymbolDetails>> Reply) {
1537 Server->symbolInfo(Params.textDocument.uri.file(), Params.position,
1541void ClangdLSPServer::onSelectionRange(
1542 const SelectionRangeParams &Params,
1543 Callback<std::vector<SelectionRange>> Reply) {
1544 Server->semanticRanges(
1545 Params.textDocument.uri.file(), Params.positions,
1546 [Reply = std::move(Reply)](
1547 llvm::Expected<std::vector<SelectionRange>> Ranges)
mutable {
1549 return Reply(Ranges.takeError());
1550 return Reply(std::move(*Ranges));
1554void ClangdLSPServer::onDocumentLink(
1555 const DocumentLinkParams &Params,
1556 Callback<std::vector<DocumentLink>> Reply) {
1562 Server->documentLinks(
1563 Params.textDocument.uri.file(),
1564 [Reply = std::move(Reply)](
1565 llvm::Expected<std::vector<DocumentLink>> Links)
mutable {
1567 return Reply(Links.takeError());
1569 return Reply(std::move(Links));
1575 for (
char &C : llvm::reverse(S)) {
1582 S.insert(S.begin(),
'1');
1585void ClangdLSPServer::onSemanticTokens(
const SemanticTokensParams &Params,
1586 Callback<SemanticTokens> CB) {
1587 auto File = Params.textDocument.uri.file();
1588 Server->semanticHighlights(
1589 Params.textDocument.uri.file(),
1590 [
this, File(File.str()), CB(std::move(CB)), Code(Server->getDraft(File))](
1591 llvm::Expected<std::vector<HighlightingToken>> HT)
mutable {
1593 return CB(HT.takeError());
1594 SemanticTokens Result;
1595 Result.tokens = toSemanticTokens(*HT, *Code);
1597 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1598 auto &Last = LastSemanticTokens[File];
1600 Last.tokens = Result.tokens;
1601 increment(Last.resultId);
1602 Result.resultId = Last.resultId;
1604 CB(std::move(Result));
1608void ClangdLSPServer::onSemanticTokensDelta(
1609 const SemanticTokensDeltaParams &Params,
1610 Callback<SemanticTokensOrDelta> CB) {
1611 auto File = Params.textDocument.uri.file();
1612 Server->semanticHighlights(
1613 Params.textDocument.uri.file(),
1614 [
this, PrevResultID(Params.previousResultId),
File(
File.str()),
1615 CB(std::move(CB)), Code(Server->getDraft(File))](
1616 llvm::Expected<std::vector<HighlightingToken>> HT)
mutable {
1618 return CB(HT.takeError());
1619 std::vector<SemanticToken> Toks = toSemanticTokens(*HT, *Code);
1621 SemanticTokensOrDelta Result;
1623 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1624 auto &Last = LastSemanticTokens[File];
1626 if (PrevResultID == Last.resultId) {
1627 Result.edits = diffTokens(Last.tokens, Toks);
1629 vlog(
"semanticTokens/full/delta: wanted edits vs {0} but last "
1630 "result had ID {1}. Returning full token list.",
1631 PrevResultID, Last.resultId);
1632 Result.tokens = Toks;
1635 Last.tokens = std::move(Toks);
1636 increment(Last.resultId);
1637 Result.resultId = Last.resultId;
1640 CB(std::move(Result));
1644void ClangdLSPServer::onMemoryUsage(
const NoParams &,
1645 Callback<MemoryTree> Reply) {
1646 llvm::BumpPtrAllocator DetailAlloc;
1647 MemoryTree MT(&DetailAlloc);
1649 Reply(std::move(MT));
1652void ClangdLSPServer::onAST(
const ASTParams &Params,
1653 Callback<std::optional<ASTNode>> CB) {
1654 Server->getAST(Params.textDocument.uri.file(), Params.range, std::move(CB));
1659 : ShouldProfile(std::chrono::minutes(5),
1660 std::chrono::minutes(1)),
1661 ShouldCleanupMemory(std::chrono::minutes(1),
1662 std::chrono::minutes(1)),
1663 BackgroundContext(
Context::current().clone()), Transp(Transp),
1665 SupportedSymbolKinds(defaultSymbolKinds()),
1666 SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts) {
1667 if (Opts.ConfigProvider) {
1668 assert(!Opts.ContextProvider &&
1669 "Only one of ConfigProvider and ContextProvider allowed!");
1670 this->Opts.ContextProvider = ClangdServer::createConfiguredContextProvider(
1671 Opts.ConfigProvider, this);
1674 Bind.
method(
"initialize",
this, &ClangdLSPServer::onInitialize);
1677void ClangdLSPServer::bindMethods(
LSPBinder &Bind,
1680 Bind.
notification(
"initialized",
this, &ClangdLSPServer::onInitialized);
1681 Bind.
method(
"shutdown",
this, &ClangdLSPServer::onShutdown);
1682 Bind.
method(
"sync",
this, &ClangdLSPServer::onSync);
1683 Bind.
method(
"textDocument/rangeFormatting",
this, &ClangdLSPServer::onDocumentRangeFormatting);
1684 Bind.
method(
"textDocument/rangesFormatting",
this, &ClangdLSPServer::onDocumentRangesFormatting);
1685 Bind.
method(
"textDocument/onTypeFormatting",
this, &ClangdLSPServer::onDocumentOnTypeFormatting);
1686 Bind.
method(
"textDocument/formatting",
this, &ClangdLSPServer::onDocumentFormatting);
1687 Bind.
method(
"textDocument/codeAction",
this, &ClangdLSPServer::onCodeAction);
1688 Bind.
method(
"textDocument/completion",
this, &ClangdLSPServer::onCompletion);
1689 Bind.
method(
"textDocument/signatureHelp",
this, &ClangdLSPServer::onSignatureHelp);
1690 Bind.
method(
"textDocument/definition",
this, &ClangdLSPServer::onGoToDefinition);
1691 Bind.
method(
"textDocument/declaration",
this, &ClangdLSPServer::onGoToDeclaration);
1692 Bind.
method(
"textDocument/typeDefinition",
this, &ClangdLSPServer::onGoToType);
1693 Bind.
method(
"textDocument/implementation",
this, &ClangdLSPServer::onGoToImplementation);
1694 Bind.
method(
"textDocument/references",
this, &ClangdLSPServer::onReference);
1695 Bind.
method(
"textDocument/switchSourceHeader",
this, &ClangdLSPServer::onSwitchSourceHeader);
1696 Bind.
method(
"textDocument/prepareRename",
this, &ClangdLSPServer::onPrepareRename);
1697 Bind.
method(
"textDocument/rename",
this, &ClangdLSPServer::onRename);
1698 Bind.
method(
"textDocument/hover",
this, &ClangdLSPServer::onHover);
1699 Bind.
method(
"textDocument/documentSymbol",
this, &ClangdLSPServer::onDocumentSymbol);
1700 Bind.
method(
"workspace/executeCommand",
this, &ClangdLSPServer::onCommand);
1701 Bind.
method(
"textDocument/documentHighlight",
this, &ClangdLSPServer::onDocumentHighlight);
1702 Bind.
method(
"workspace/symbol",
this, &ClangdLSPServer::onWorkspaceSymbol);
1703 Bind.
method(
"textDocument/ast",
this, &ClangdLSPServer::onAST);
1704 Bind.
notification(
"textDocument/didOpen",
this, &ClangdLSPServer::onDocumentDidOpen);
1705 Bind.
notification(
"textDocument/didClose",
this, &ClangdLSPServer::onDocumentDidClose);
1706 Bind.
notification(
"textDocument/didChange",
this, &ClangdLSPServer::onDocumentDidChange);
1707 Bind.
notification(
"textDocument/didSave",
this, &ClangdLSPServer::onDocumentDidSave);
1708 Bind.
notification(
"workspace/didChangeWatchedFiles",
this, &ClangdLSPServer::onFileEvent);
1709 Bind.
notification(
"workspace/didChangeConfiguration",
this, &ClangdLSPServer::onChangeConfiguration);
1710 Bind.
method(
"textDocument/symbolInfo",
this, &ClangdLSPServer::onSymbolInfo);
1711 Bind.
method(
"textDocument/typeHierarchy",
this, &ClangdLSPServer::onTypeHierarchy);
1712 Bind.
method(
"typeHierarchy/resolve",
this, &ClangdLSPServer::onResolveTypeHierarchy);
1713 Bind.
method(
"textDocument/prepareTypeHierarchy",
this, &ClangdLSPServer::onPrepareTypeHierarchy);
1714 Bind.
method(
"typeHierarchy/supertypes",
this, &ClangdLSPServer::onSuperTypes);
1715 Bind.
method(
"typeHierarchy/subtypes",
this, &ClangdLSPServer::onSubTypes);
1716 Bind.
method(
"textDocument/prepareCallHierarchy",
this, &ClangdLSPServer::onPrepareCallHierarchy);
1717 Bind.
method(
"callHierarchy/incomingCalls",
this, &ClangdLSPServer::onCallHierarchyIncomingCalls);
1719 Bind.
method(
"callHierarchy/outgoingCalls",
this, &ClangdLSPServer::onCallHierarchyOutgoingCalls);
1720 Bind.
method(
"textDocument/selectionRange",
this, &ClangdLSPServer::onSelectionRange);
1721 Bind.
method(
"textDocument/documentLink",
this, &ClangdLSPServer::onDocumentLink);
1722 Bind.
method(
"textDocument/semanticTokens/full",
this, &ClangdLSPServer::onSemanticTokens);
1723 Bind.
method(
"textDocument/semanticTokens/full/delta",
this, &ClangdLSPServer::onSemanticTokensDelta);
1724 Bind.
method(
"clangd/inlayHints",
this, &ClangdLSPServer::onClangdInlayHints);
1725 Bind.
method(
"textDocument/inlayHint",
this, &ClangdLSPServer::onInlayHint);
1726 Bind.
method(
"$/memoryUsage",
this, &ClangdLSPServer::onMemoryUsage);
1727 Bind.
method(
"textDocument/foldingRange",
this, &ClangdLSPServer::onFoldingRange);
1728 Bind.
command(ApplyFixCommand,
this, &ClangdLSPServer::onCommandApplyEdit);
1729 Bind.
command(ApplyTweakCommand,
this, &ClangdLSPServer::onCommandApplyTweak);
1730 Bind.
command(ApplyRenameCommand,
this, &ClangdLSPServer::onCommandApplyRename);
1738 CreateWorkDoneProgress = Bind.
outgoingMethod(
"window/workDoneProgress/create");
1743 SemanticTokensRefresh = Bind.
outgoingMethod(
"workspace/semanticTokens/refresh");
1748 IsBeingDestroyed =
true;
1756 bool CleanExit =
true;
1757 if (
auto Err = Transp.loop(*MsgHandler)) {
1758 elog(
"Transport error: {0}", std::move(Err));
1762 return CleanExit && ShutdownRequestReceived;
1767 Server->profile(MT.
child(
"clangd_server"));
1770std::optional<ClangdServer::DiagRef>
1772 std::lock_guard<std::mutex> Lock(DiagRefMutex);
1773 auto DiagToDiagRefIter = DiagRefMap.find(
File);
1774 if (DiagToDiagRefIter == DiagRefMap.end())
1775 return std::nullopt;
1777 const auto &DiagToDiagRefMap = DiagToDiagRefIter->second;
1778 auto FixItsIter = DiagToDiagRefMap.find(toDiagKey(D));
1779 if (FixItsIter == DiagToDiagRefMap.end())
1780 return std::nullopt;
1782 return FixItsIter->second;
1791bool ClangdLSPServer::shouldRunCompletion(
1792 const CompletionParams &Params)
const {
1795 auto Code = Server->getDraft(Params.textDocument.uri.file());
1801 vlog(
"could not convert position '{0}' to offset for file '{1}'",
1802 Params.position, Params.textDocument.uri.file());
1808void ClangdLSPServer::onDiagnosticsReady(
PathRef File, llvm::StringRef Version,
1809 llvm::ArrayRef<Diag> Diagnostics) {
1810 PublishDiagnosticsParams Notification;
1811 Notification.version = decodeVersion(Version);
1813 DiagnosticToDiagRefMap LocalDiagMap;
1814 for (
auto &Diag : Diagnostics) {
1816 [&](clangd::Diagnostic LSPDiag, llvm::ArrayRef<Fix> Fixes) {
1817 if (DiagOpts.EmbedFixesInDiagnostics) {
1818 std::vector<CodeAction> CodeActions;
1819 for (const auto &Fix : Fixes)
1820 CodeActions.push_back(toCodeAction(
1821 Fix, Notification.uri, Notification.version,
1822 SupportsDocumentChanges, SupportsChangeAnnotation));
1823 LSPDiag.codeActions.emplace(std::move(CodeActions));
1824 if (LSPDiag.codeActions->size() == 1)
1825 LSPDiag.codeActions->front().isPreferred = true;
1827 LocalDiagMap[toDiagKey(LSPDiag)] = {Diag.Range, Diag.Message};
1828 Notification.diagnostics.push_back(std::move(LSPDiag));
1834 std::lock_guard<std::mutex> Lock(DiagRefMutex);
1835 DiagRefMap[
File] = LocalDiagMap;
1842void ClangdLSPServer::onInactiveRegionsReady(
1843 PathRef File, std::vector<Range> InactiveRegions) {
1844 InactiveRegionsParams Notification;
1846 Notification.InactiveRegions = std::move(InactiveRegions);
1848 PublishInactiveRegions(Notification);
1851void ClangdLSPServer::onBackgroundIndexProgress(
1853 static const char ProgressToken[] =
"backgroundIndexProgress";
1856 maybeCleanupMemory();
1858 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1860 auto NotifyProgress = [
this](
const BackgroundQueue::Stats &Stats) {
1861 if (BackgroundIndexProgressState != BackgroundIndexProgress::Live) {
1862 WorkDoneProgressBegin Begin;
1863 Begin.percentage =
true;
1864 Begin.title =
"indexing";
1865 BeginWorkDoneProgress({ProgressToken, std::move(Begin)});
1866 BackgroundIndexProgressState = BackgroundIndexProgress::Live;
1869 if (Stats.Completed < Stats.Enqueued) {
1870 assert(Stats.Enqueued > Stats.LastIdle);
1871 WorkDoneProgressReport Report;
1872 Report.percentage = 100 * (Stats.Completed - Stats.LastIdle) /
1873 (Stats.Enqueued - Stats.LastIdle);
1875 llvm::formatv(
"{0}/{1}", Stats.Completed - Stats.LastIdle,
1876 Stats.Enqueued - Stats.LastIdle);
1877 ReportWorkDoneProgress({ProgressToken, std::move(Report)});
1879 assert(Stats.Completed == Stats.Enqueued);
1880 EndWorkDoneProgress({ProgressToken, WorkDoneProgressEnd()});
1881 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
1885 switch (BackgroundIndexProgressState) {
1886 case BackgroundIndexProgress::Unsupported:
1888 case BackgroundIndexProgress::Creating:
1890 PendingBackgroundIndexProgress = Stats;
1892 case BackgroundIndexProgress::Empty: {
1893 if (BackgroundIndexSkipCreate) {
1894 NotifyProgress(Stats);
1898 PendingBackgroundIndexProgress = Stats;
1899 BackgroundIndexProgressState = BackgroundIndexProgress::Creating;
1900 WorkDoneProgressCreateParams CreateRequest;
1901 CreateRequest.token = ProgressToken;
1902 CreateWorkDoneProgress(
1904 [
this, NotifyProgress](llvm::Expected<std::nullptr_t> E) {
1905 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1907 NotifyProgress(this->PendingBackgroundIndexProgress);
1909 elog(
"Failed to create background index progress bar: {0}",
1912 BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported;
1917 case BackgroundIndexProgress::Live:
1918 NotifyProgress(Stats);
1924 if (!SupportFileStatus)
1934 NotifyFileStatus(Status.render(
File));
1937void ClangdLSPServer::onSemanticsMaybeChanged(
PathRef File) {
1938 if (SemanticTokensRefresh) {
1939 SemanticTokensRefresh(NoParams{}, [](llvm::Expected<std::nullptr_t> E) {
1942 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.