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;
558 std::make_unique<DirectoryBasedGlobalCompilationDatabase>(CDBOpts);
561 Mangler.SystemIncludeExtractor =
563 if (Opts.ResourceDir)
564 Mangler.ResourceDir = *Opts.ResourceDir;
565 CDB.emplace(BaseCDB.get(), Params.initializationOptions.fallbackFlags,
568 if (Opts.EnableExperimentalModulesSupport) {
569 ModulesManager.emplace(*CDB);
570 Opts.ModulesManager = &*ModulesManager;
577 WithContext MainContext(BackgroundContext.clone());
578 std::optional<WithContextValue> WithOffsetEncoding;
581 Server.emplace(*CDB, TFS, Opts,
582 static_cast<ClangdServer::Callbacks *
>(
this));
585 llvm::json::Object ServerCaps{
592 {
"documentFormattingProvider",
true},
593 {
"documentRangeFormattingProvider",
595 {
"rangesSupport",
true},
597 {
"documentOnTypeFormattingProvider",
599 {
"firstTriggerCharacter",
"\n"},
600 {
"moreTriggerCharacter", {}},
602 {
"completionProvider",
609 {
"resolveProvider",
false},
611 {
"triggerCharacters", {
".",
"<",
">",
":",
"\"",
"/",
"*"}},
613 {
"semanticTokensProvider",
615 {
"full", llvm::json::Object{{
"delta",
true}}},
621 {
"signatureHelpProvider",
623 {
"triggerCharacters", {
"(",
")",
"{",
"}",
"<",
">",
","}},
625 {
"declarationProvider",
true},
626 {
"definitionProvider",
true},
627 {
"implementationProvider",
true},
628 {
"typeDefinitionProvider",
true},
629 {
"documentHighlightProvider",
true},
630 {
"documentLinkProvider",
632 {
"resolveProvider",
false},
634 {
"hoverProvider",
true},
635 {
"selectionRangeProvider",
true},
636 {
"documentSymbolProvider",
true},
637 {
"workspaceSymbolProvider",
true},
638 {
"referencesProvider",
true},
639 {
"astProvider",
true},
640 {
"typeHierarchyProvider",
true},
644 {
"standardTypeHierarchyProvider",
true},
645 {
"memoryUsageProvider",
true},
646 {
"compilationDatabase",
647 llvm::json::Object{{
"automaticReload",
true}}},
648 {
"inactiveRegionsProvider",
true},
649 {
"callHierarchyProvider",
true},
650 {
"clangdInlayHintsProvider",
true},
651 {
"inlayHintProvider",
true},
652 {
"foldingRangeProvider",
true},
656 LSPBinder Binder(Handlers, *
this);
657 bindMethods(Binder, Params.capabilities);
658 if (Opts.FeatureModules)
659 for (
auto &Mod : *Opts.FeatureModules)
660 Mod.initializeLSP(Binder, Params.rawCapabilities, ServerCaps);
665 ServerCaps[
"renameProvider"] =
666 Params.capabilities.RenamePrepareSupport
667 ? llvm::json::Object{{
"prepareProvider",
true}}
668 : llvm::json::Value(
true);
673 ServerCaps[
"codeActionProvider"] =
674 Params.capabilities.CodeActionStructure
675 ? llvm::json::Object{{
"codeActionKinds",
679 : llvm::json::Value(
true);
681 std::vector<llvm::StringRef>
Commands;
682 for (llvm::StringRef Command : Handlers.CommandHandlers.keys())
685 ServerCaps[
"executeCommandProvider"] =
686 llvm::json::Object{{
"commands",
Commands}};
689 ServerCaps[
"positionEncoding"] = *Opts.Encoding;
691 llvm::json::Object Result{
697 {
"capabilities", std::move(ServerCaps)}}};
702 Result[
"offsetEncoding"] = *Opts.Encoding;
703 Reply(std::move(Result));
707 applyConfiguration(Params.initializationOptions.ConfigSettings);
712void ClangdLSPServer::onShutdown(
const NoParams &,
715 ShutdownRequestReceived =
true;
722 if (Server->blockUntilIdleForTest(60))
725 Reply(
error(
"Not idle after a minute"));
728void ClangdLSPServer::onDocumentDidOpen(
732 const std::string &Contents = Params.textDocument.text;
734 Server->addDocument(
File, Contents,
735 encodeVersion(Params.textDocument.version),
739void ClangdLSPServer::onDocumentDidChange(
742 if (Params.wantDiagnostics)
747 auto Code = Server->getDraft(
File);
749 log(
"Trying to incrementally change non-added document: {0}",
File);
752 std::string NewCode(*Code);
753 for (
const auto &Change : Params.contentChanges) {
758 Server->removeDocument(
File);
759 elog(
"Failed to update {0}: {1}",
File, std::move(Err));
763 Server->addDocument(
File, NewCode, encodeVersion(Params.textDocument.version),
764 WantDiags, Params.forceRebuild);
767void ClangdLSPServer::onDocumentDidSave(
769 Server->reparseOpenFilesIfNeeded([](llvm::StringRef) {
return true; });
777 Server->onFileEvent(Params);
786 auto It = Handlers.CommandHandlers.find(Params.command);
787 if (It == Handlers.CommandHandlers.end()) {
788 return Reply(llvm::make_error<LSPError>(
789 llvm::formatv(
"Unsupported command \"{0}\".", Params.command).str(),
792 It->second(Params.argument, std::move(Reply));
795void ClangdLSPServer::onCommandApplyEdit(
const WorkspaceEdit &WE,
805 applyEdit(WE,
"Fix applied.", std::move(Reply));
808void ClangdLSPServer::onCommandApplyTweak(
const TweakArgs &Args,
810 auto Action = [
this, Reply = std::move(Reply)](
811 llvm::Expected<Tweak::Effect> R)
mutable {
813 return Reply(R.takeError());
815 assert(R->ShowMessage || (!R->ApplyEdits.empty() &&
"tweak has no effect"));
817 if (R->ShowMessage) {
818 ShowMessageParams Msg;
819 Msg.message = *R->ShowMessage;
824 if (R->ApplyEdits.empty())
825 return Reply(
"Tweak applied.");
827 if (
auto Err = validateEdits(*Server, R->ApplyEdits))
828 return Reply(std::move(Err));
832 WE.changes.emplace();
833 for (
const auto &It : R->ApplyEdits) {
835 It.second.asTextEdits();
838 return applyEdit(std::move(WE),
"Tweak applied.", std::move(Reply));
840 Server->applyTweak(Args.file.file(), Args.selection, Args.tweakID,
844void ClangdLSPServer::onCommandApplyRename(
const RenameParams &R,
846 onRename(R, [
this, Reply = std::move(Reply)](
847 llvm::Expected<WorkspaceEdit> Edit)
mutable {
849 Reply(Edit.takeError());
850 applyEdit(std::move(*Edit),
"Rename applied.", std::move(Reply));
854void ClangdLSPServer::applyEdit(
WorkspaceEdit WE, llvm::json::Value Success,
856 ApplyWorkspaceEditParams Edit;
857 Edit.edit = std::move(WE);
859 Edit, [Reply = std::move(Reply), SuccessMessage = std::move(Success)](
860 llvm::Expected<ApplyWorkspaceEditResponse> Response)
mutable {
862 return Reply(Response.takeError());
863 if (!Response->applied) {
864 std::string Reason = Response->failureReason
865 ? *Response->failureReason
867 return Reply(
error(
"edits were not applied: {0}", Reason));
869 return Reply(SuccessMessage);
873void ClangdLSPServer::onWorkspaceSymbol(
875 Callback<std::vector<SymbolInformation>> Reply) {
876 Server->workspaceSymbols(
877 Params.query, Params.limit.value_or(Opts.CodeComplete.Limit),
878 [Reply = std::move(Reply),
879 this](llvm::Expected<std::vector<SymbolInformation>> Items)
mutable {
881 return Reply(Items.takeError());
882 for (auto &Sym : *Items)
883 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds);
885 Reply(std::move(*Items));
891 Server->prepareRename(
892 Params.textDocument.uri.file(), Params.position, std::nullopt,
894 [Reply = std::move(Reply)](llvm::Expected<RenameResult> Result)
mutable {
896 return Reply(Result.takeError());
897 PrepareRenameResult PrepareResult;
898 PrepareResult.range = Result->Target;
899 PrepareResult.placeholder = Result->Placeholder;
900 return Reply(std::move(PrepareResult));
904void ClangdLSPServer::onRename(
const RenameParams &Params,
906 Path File = std::string(Params.textDocument.uri.file());
907 if (!Server->getDraft(
File))
908 return Reply(llvm::make_error<LSPError>(
910 Server->rename(
File, Params.position, Params.newName, Opts.Rename,
911 [
File, Params, Reply = std::move(Reply),
912 this](llvm::Expected<RenameResult> R)
mutable {
914 return Reply(R.takeError());
915 if (auto Err = validateEdits(*Server, R->GlobalChanges))
916 return Reply(std::move(Err));
917 WorkspaceEdit Result;
920 Result.changes.emplace();
921 for (const auto &Rep : R->GlobalChanges) {
923 .changes)[URI::createFile(Rep.first()).toString()] =
924 Rep.second.asTextEdits();
930void ClangdLSPServer::onDocumentDidClose(
931 const DidCloseTextDocumentParams &Params) {
933 Server->removeDocument(File);
936 std::lock_guard<std::mutex> Lock(DiagRefMutex);
937 DiagRefMap.erase(File);
940 std::lock_guard<std::mutex> HLock(SemanticTokensMutex);
941 LastSemanticTokens.erase(File);
948 PublishDiagnosticsParams Notification;
949 Notification.uri = URIForFile::canonicalize(File, File);
950 PublishDiagnostics(Notification);
953void ClangdLSPServer::onDocumentOnTypeFormatting(
954 const DocumentOnTypeFormattingParams &Params,
955 Callback<std::vector<TextEdit>> Reply) {
956 auto File = Params.textDocument.uri.file();
957 Server->formatOnType(File, Params.position, Params.ch, std::move(Reply));
960void ClangdLSPServer::onDocumentRangeFormatting(
961 const DocumentRangeFormattingParams &Params,
962 Callback<std::vector<TextEdit>> Reply) {
963 onDocumentRangesFormatting(
964 DocumentRangesFormattingParams{Params.textDocument, {Params.range}},
968void ClangdLSPServer::onDocumentRangesFormatting(
969 const DocumentRangesFormattingParams &Params,
970 Callback<std::vector<TextEdit>> Reply) {
971 auto File = Params.textDocument.uri.file();
972 auto Code = Server->getDraft(File);
973 Server->formatFile(File, Params.ranges,
974 [Code = std::move(Code), Reply = std::move(Reply)](
975 llvm::Expected<tooling::Replacements> Result)
mutable {
977 Reply(replacementsToEdits(*Code, Result.get()));
979 Reply(Result.takeError());
983void ClangdLSPServer::onDocumentFormatting(
984 const DocumentFormattingParams &Params,
985 Callback<std::vector<TextEdit>> Reply) {
986 auto File = Params.textDocument.uri.file();
987 auto Code = Server->getDraft(File);
988 Server->formatFile(File,
990 [Code = std::move(Code), Reply = std::move(Reply)](
991 llvm::Expected<tooling::Replacements> Result)
mutable {
995 Reply(Result.takeError());
1001static std::vector<SymbolInformation>
1004 std::vector<SymbolInformation> Results;
1005 std::function<void(
const DocumentSymbol &, llvm::StringRef)> Process =
1006 [&](
const DocumentSymbol &S, std::optional<llvm::StringRef> ParentName) {
1008 SI.
containerName = std::string(ParentName ?
"" : *ParentName);
1014 Results.push_back(std::move(SI));
1015 std::string FullName =
1016 !ParentName ? S.
name : (ParentName->str() +
"::" + S.
name);
1018 Process(C, FullName);
1025void ClangdLSPServer::onDocumentSymbol(
const DocumentSymbolParams &Params,
1026 Callback<llvm::json::Value> Reply) {
1027 URIForFile FileURI = Params.textDocument.uri;
1028 Server->documentSymbols(
1029 Params.textDocument.uri.file(),
1030 [
this, FileURI, Reply = std::move(Reply)](
1031 llvm::Expected<std::vector<DocumentSymbol>> Items)
mutable {
1033 return Reply(Items.takeError());
1034 adjustSymbolKinds(*Items, SupportedSymbolKinds);
1035 if (SupportsHierarchicalDocumentSymbol)
1036 return Reply(std::move(*Items));
1037 return Reply(flattenSymbolHierarchy(*Items, FileURI));
1041void ClangdLSPServer::onFoldingRange(
1042 const FoldingRangeParams &Params,
1043 Callback<std::vector<FoldingRange>> Reply) {
1044 Server->foldingRanges(Params.textDocument.uri.file(), std::move(Reply));
1050 return std::nullopt;
1053 }
else if (Action.
edit) {
1054 Cmd.command = std::string(ApplyFixCommand);
1055 Cmd.argument = *Action.
edit;
1057 return std::nullopt;
1059 Cmd.title = Action.
title;
1061 Cmd.title =
"Apply fix: " + Cmd.title;
1065void ClangdLSPServer::onCodeAction(
const CodeActionParams &Params,
1066 Callback<llvm::json::Value> Reply) {
1067 URIForFile File = Params.textDocument.uri;
1068 std::map<ClangdServer::DiagRef, clangd::Diagnostic> ToLSPDiags;
1069 ClangdServer::CodeActionInputs Inputs;
1071 for (
const auto& LSPDiag : Params.context.diagnostics) {
1072 if (
auto DiagRef = getDiagRef(File.file(), LSPDiag)) {
1073 ToLSPDiags[*DiagRef] = LSPDiag;
1074 Inputs.Diagnostics.push_back(*DiagRef);
1077 Inputs.File =
File.file();
1078 Inputs.Selection = Params.range;
1079 Inputs.RequestedActionKinds = Params.context.only;
1080 Inputs.TweakFilter = [
this](
const Tweak &
T) {
1081 return Opts.TweakFilter(T);
1084 Reply = std::move(Reply),
1085 ToLSPDiags = std::move(ToLSPDiags),
File,
1086 Selection = Params.range](
1087 llvm::Expected<ClangdServer::CodeActionResult> Fixits)
mutable {
1089 return Reply(Fixits.takeError());
1090 std::vector<CodeAction> CAs;
1091 auto Version = decodeVersion(Fixits->Version);
1092 for (
const auto &QF : Fixits->QuickFixes) {
1093 CAs.push_back(toCodeAction(QF.F, File, Version, SupportsDocumentChanges,
1094 SupportsChangeAnnotation));
1095 if (
auto It = ToLSPDiags.find(QF.Diag);
1096 It != ToLSPDiags.end()) {
1097 CAs.back().diagnostics = {It->second};
1101 for (
const auto &R : Fixits->Renames)
1102 CAs.push_back(toCodeAction(R, File));
1104 for (
const auto &TR : Fixits->TweakRefs)
1105 CAs.push_back(toCodeAction(TR, File, Selection));
1109 CodeAction *OnlyFix =
nullptr;
1110 for (
auto &Action : CAs) {
1111 if (Action.kind && *Action.kind == CodeAction::QUICKFIX_KIND) {
1120 OnlyFix->isPreferred =
true;
1121 if (ToLSPDiags.size() == 1 &&
1122 ToLSPDiags.begin()->second.range == Selection)
1123 OnlyFix->diagnostics = {ToLSPDiags.begin()->second};
1126 if (SupportsCodeAction)
1127 return Reply(llvm::json::Array(CAs));
1129 for (
const auto &Action : CAs) {
1131 Commands.push_back(std::move(*Command));
1133 return Reply(llvm::json::Array(
Commands));
1135 Server->codeAction(Inputs, std::move(CB));
1138void ClangdLSPServer::onCompletion(
const CompletionParams &Params,
1139 Callback<CompletionList> Reply) {
1140 if (!shouldRunCompletion(Params)) {
1143 vlog(
"ignored auto-triggered completion, preceding char did not match");
1144 return Reply(CompletionList());
1146 auto Opts = this->Opts.CodeComplete;
1147 if (Params.limit && *Params.limit >= 0)
1148 Opts.Limit = *Params.limit;
1149 Server->codeComplete(Params.textDocument.uri.file(), Params.position, Opts,
1150 [Reply = std::move(Reply), Opts,
1151 this](llvm::Expected<CodeCompleteResult> List)
mutable {
1153 return Reply(List.takeError());
1154 CompletionList LSPList;
1155 LSPList.isIncomplete = List->HasMore;
1156 for (const auto &R : List->Completions) {
1157 CompletionItem C = R.render(Opts);
1158 C.kind = adjustKindToCapability(
1159 C.kind, SupportedCompletionItemKinds);
1160 if (!SupportsCompletionLabelDetails)
1161 removeCompletionLabelDetails(C);
1162 LSPList.items.push_back(std::move(C));
1164 return Reply(std::move(LSPList));
1168void ClangdLSPServer::onSignatureHelp(
const TextDocumentPositionParams &Params,
1169 Callback<SignatureHelp> Reply) {
1170 Server->signatureHelp(Params.textDocument.uri.file(), Params.position,
1171 Opts.SignatureHelpDocumentationFormat,
1172 [Reply = std::move(Reply),
this](
1173 llvm::Expected<SignatureHelp> Signature)
mutable {
1175 return Reply(Signature.takeError());
1176 if (SupportsOffsetsInSignatureHelp)
1177 return Reply(std::move(*Signature));
1180 for (auto &SigInfo : Signature->signatures) {
1181 for (auto &Param : SigInfo.parameters)
1182 Param.labelOffsets.reset();
1184 return Reply(std::move(*Signature));
1208void ClangdLSPServer::onGoToDefinition(
const TextDocumentPositionParams &Params,
1209 Callback<std::vector<Location>> Reply) {
1210 Server->locateSymbolAt(
1211 Params.textDocument.uri.file(), Params.position,
1212 [Params, Reply = std::move(Reply)](
1213 llvm::Expected<std::vector<LocatedSymbol>> Symbols)
mutable {
1215 return Reply(Symbols.takeError());
1216 std::vector<Location> Defs;
1217 for (auto &S : *Symbols) {
1218 if (Location *Toggle = getToggle(Params, S))
1219 return Reply(std::vector<Location>{std::move(*Toggle)});
1220 Defs.push_back(S.Definition.value_or(S.PreferredDeclaration));
1222 Reply(std::move(Defs));
1226void ClangdLSPServer::onGoToDeclaration(
1227 const TextDocumentPositionParams &Params,
1228 Callback<std::vector<Location>> Reply) {
1229 Server->locateSymbolAt(
1230 Params.textDocument.uri.file(), Params.position,
1231 [Params, Reply = std::move(Reply)](
1232 llvm::Expected<std::vector<LocatedSymbol>> Symbols)
mutable {
1234 return Reply(Symbols.takeError());
1235 std::vector<Location> Decls;
1236 for (auto &S : *Symbols) {
1237 if (Location *Toggle = getToggle(Params, S))
1238 return Reply(std::vector<Location>{std::move(*Toggle)});
1239 Decls.push_back(std::move(S.PreferredDeclaration));
1241 Reply(std::move(Decls));
1245void ClangdLSPServer::onSwitchSourceHeader(
1246 const TextDocumentIdentifier &Params,
1247 Callback<std::optional<URIForFile>> Reply) {
1248 Server->switchSourceHeader(
1250 [Reply = std::move(Reply),
1251 Params](llvm::Expected<std::optional<clangd::Path>> Path)
mutable {
1253 return Reply(Path.takeError());
1255 return Reply(URIForFile::canonicalize(**Path, Params.uri.file()));
1256 return Reply(std::nullopt);
1260void ClangdLSPServer::onDocumentHighlight(
1261 const TextDocumentPositionParams &Params,
1262 Callback<std::vector<DocumentHighlight>> Reply) {
1263 Server->findDocumentHighlights(Params.textDocument.uri.file(),
1264 Params.position, std::move(Reply));
1267void ClangdLSPServer::onHover(
const TextDocumentPositionParams &Params,
1268 Callback<std::optional<Hover>> Reply) {
1269 Server->findHover(Params.textDocument.uri.file(), Params.position,
1270 [Reply = std::move(Reply),
1271 this](llvm::Expected<std::optional<HoverInfo>> H)
mutable {
1273 return Reply(H.takeError());
1275 return Reply(std::nullopt);
1278 R.contents.kind = HoverContentFormat;
1279 R.range = (*H)->SymRange;
1280 switch (HoverContentFormat) {
1281 case MarkupKind::Markdown:
1282 case MarkupKind::PlainText:
1283 R.contents.value = (*H)->present(HoverContentFormat);
1284 return Reply(std::move(R));
1286 llvm_unreachable(
"unhandled MarkupKind");
1293 llvm::json::Object Result{{
1294 {
"name", std::move(THI.
name)},
1295 {
"kind",
static_cast<int>(THI.
kind)},
1296 {
"uri", std::move(THI.
uri)},
1297 {
"range", THI.
range},
1299 {
"data", std::move(THI.
data)},
1304 Result[
"detail"] = std::move(*THI.
detail);
1308 for (
auto &Parent : *THI.
parents)
1310 Result[
"parents"] = std::move(
Parents);
1317 Result[
"children"] = std::move(
Children);
1322void ClangdLSPServer::onTypeHierarchy(
const TypeHierarchyPrepareParams &Params,
1323 Callback<llvm::json::Value> Reply) {
1325 [Reply = std::move(Reply)](
1326 llvm::Expected<std::vector<TypeHierarchyItem>> Resp)
mutable {
1328 Reply(Resp.takeError());
1331 if (Resp->empty()) {
1337 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1338 Params.resolve, Params.direction, std::move(Serialize));
1341void ClangdLSPServer::onResolveTypeHierarchy(
1342 const ResolveTypeHierarchyItemParams &Params,
1343 Callback<llvm::json::Value> Reply) {
1345 [Reply = std::move(Reply)](
1346 llvm::Expected<std::optional<TypeHierarchyItem>> Resp)
mutable {
1348 Reply(Resp.takeError());
1352 Reply(std::move(*Resp));
1357 Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction,
1358 std::move(Serialize));
1361void ClangdLSPServer::onPrepareTypeHierarchy(
1362 const TypeHierarchyPrepareParams &Params,
1363 Callback<std::vector<TypeHierarchyItem>> Reply) {
1364 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1365 Params.resolve, Params.direction, std::move(Reply));
1368void ClangdLSPServer::onSuperTypes(
1369 const ResolveTypeHierarchyItemParams &Params,
1370 Callback<std::optional<std::vector<TypeHierarchyItem>>> Reply) {
1371 Server->superTypes(Params.item, std::move(Reply));
1374void ClangdLSPServer::onSubTypes(
1375 const ResolveTypeHierarchyItemParams &Params,
1376 Callback<std::vector<TypeHierarchyItem>> Reply) {
1377 Server->subTypes(Params.item, std::move(Reply));
1380void ClangdLSPServer::onPrepareCallHierarchy(
1381 const CallHierarchyPrepareParams &Params,
1382 Callback<std::vector<CallHierarchyItem>> Reply) {
1383 Server->prepareCallHierarchy(Params.textDocument.uri.file(), Params.position,
1387void ClangdLSPServer::onCallHierarchyIncomingCalls(
1388 const CallHierarchyIncomingCallsParams &Params,
1389 Callback<std::vector<CallHierarchyIncomingCall>> Reply) {
1390 Server->incomingCalls(Params.item, std::move(Reply));
1393void ClangdLSPServer::onClangdInlayHints(
const InlayHintsParams &Params,
1394 Callback<llvm::json::Value> Reply) {
1399 auto Serialize = [Reply = std::move(Reply)](
1400 llvm::Expected<std::vector<InlayHint>> Hints)
mutable {
1402 Reply(Hints.takeError());
1405 llvm::json::Array Result;
1406 Result.reserve(Hints->size());
1407 for (
auto &Hint : *Hints) {
1408 Result.emplace_back(llvm::json::Object{
1409 {
"kind", llvm::to_string(Hint.kind)},
1410 {
"range", Hint.range},
1411 {
"position", Hint.position},
1415 ((Hint.paddingLeft ?
" " :
"") + llvm::StringRef(Hint.joinLabels()) +
1416 (Hint.paddingRight ?
" " :
""))
1420 Reply(std::move(Result));
1422 Server->inlayHints(Params.textDocument.uri.file(), Params.range,
1423 std::move(Serialize));
1426void ClangdLSPServer::onInlayHint(
const InlayHintsParams &Params,
1427 Callback<std::vector<InlayHint>> Reply) {
1428 Server->inlayHints(Params.textDocument.uri.file(), Params.range,
1432void ClangdLSPServer::onCallHierarchyOutgoingCalls(
1433 const CallHierarchyOutgoingCallsParams &Params,
1434 Callback<std::vector<CallHierarchyOutgoingCall>> Reply) {
1435 Server->outgoingCalls(Params.item, std::move(Reply));
1438void ClangdLSPServer::applyConfiguration(
1439 const ConfigurationSettings &Settings) {
1441 llvm::StringSet<> ModifiedFiles;
1442 for (
auto &[File, Command] : Settings.compilationDatabaseChanges) {
1444 tooling::CompileCommand(std::move(Command.workingDirectory), File,
1445 std::move(Command.compilationCommand),
1447 if (CDB->setCompileCommand(File, std::move(Cmd))) {
1448 ModifiedFiles.insert(File);
1452 Server->reparseOpenFilesIfNeeded(
1453 [&](llvm::StringRef File) {
return ModifiedFiles.count(File) != 0; });
1456void ClangdLSPServer::maybeExportMemoryProfile() {
1457 if (!trace::enabled() || !ShouldProfile())
1460 static constexpr trace::Metric MemoryUsage(
1461 "memory_usage", trace::Metric::Value,
"component_name");
1462 trace::Span Tracer(
"ProfileBrief");
1465 record(MT,
"clangd_lsp_server", MemoryUsage);
1468void ClangdLSPServer::maybeCleanupMemory() {
1469 if (!Opts.MemoryCleanup || !ShouldCleanupMemory())
1471 Opts.MemoryCleanup();
1475void ClangdLSPServer::onChangeConfiguration(
1476 const DidChangeConfigurationParams &Params) {
1477 applyConfiguration(Params.settings);
1480void ClangdLSPServer::onReference(
1481 const ReferenceParams &Params,
1482 Callback<std::vector<ReferenceLocation>> Reply) {
1483 Server->findReferences(Params.textDocument.uri.file(), Params.position,
1484 Opts.ReferencesLimit, SupportsReferenceContainer,
1485 [Reply = std::move(Reply),
1486 IncludeDecl(Params.context.includeDeclaration)](
1487 llvm::Expected<ReferencesResult> Refs)
mutable {
1489 return Reply(Refs.takeError());
1491 std::vector<ReferenceLocation> Result;
1492 Result.reserve(Refs->References.size());
1493 for (auto &Ref : Refs->References) {
1495 Ref.Attributes & ReferencesResult::Declaration;
1496 if (IncludeDecl || !IsDecl)
1497 Result.push_back(std::move(Ref.Loc));
1499 return Reply(std::move(Result));
1503void ClangdLSPServer::onGoToType(
const TextDocumentPositionParams &Params,
1504 Callback<std::vector<Location>> Reply) {
1506 Params.textDocument.uri.file(), Params.position,
1507 [Reply = std::move(Reply)](
1508 llvm::Expected<std::vector<LocatedSymbol>> Types)
mutable {
1510 return Reply(Types.takeError());
1511 std::vector<Location> Response;
1512 for (const LocatedSymbol &Sym : *Types)
1513 Response.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration));
1514 return Reply(std::move(Response));
1518void ClangdLSPServer::onGoToImplementation(
1519 const TextDocumentPositionParams &Params,
1520 Callback<std::vector<Location>> Reply) {
1521 Server->findImplementations(
1522 Params.textDocument.uri.file(), Params.position,
1523 [Reply = std::move(Reply)](
1524 llvm::Expected<std::vector<LocatedSymbol>> Overrides)
mutable {
1526 return Reply(Overrides.takeError());
1527 std::vector<Location> Impls;
1528 for (const LocatedSymbol &Sym : *Overrides)
1529 Impls.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration));
1530 return Reply(std::move(Impls));
1534void ClangdLSPServer::onSymbolInfo(
const TextDocumentPositionParams &Params,
1535 Callback<std::vector<SymbolDetails>> Reply) {
1536 Server->symbolInfo(Params.textDocument.uri.file(), Params.position,
1540void ClangdLSPServer::onSelectionRange(
1541 const SelectionRangeParams &Params,
1542 Callback<std::vector<SelectionRange>> Reply) {
1543 Server->semanticRanges(
1544 Params.textDocument.uri.file(), Params.positions,
1545 [Reply = std::move(Reply)](
1546 llvm::Expected<std::vector<SelectionRange>> Ranges)
mutable {
1548 return Reply(Ranges.takeError());
1549 return Reply(std::move(*Ranges));
1553void ClangdLSPServer::onDocumentLink(
1554 const DocumentLinkParams &Params,
1555 Callback<std::vector<DocumentLink>> Reply) {
1561 Server->documentLinks(
1562 Params.textDocument.uri.file(),
1563 [Reply = std::move(Reply)](
1564 llvm::Expected<std::vector<DocumentLink>> Links)
mutable {
1566 return Reply(Links.takeError());
1568 return Reply(std::move(Links));
1574 for (
char &C : llvm::reverse(S)) {
1581 S.insert(S.begin(),
'1');
1584void ClangdLSPServer::onSemanticTokens(
const SemanticTokensParams &Params,
1585 Callback<SemanticTokens> CB) {
1586 auto File = Params.textDocument.uri.file();
1587 Server->semanticHighlights(
1588 Params.textDocument.uri.file(),
1589 [
this, File(File.str()), CB(std::move(CB)), Code(Server->getDraft(File))](
1590 llvm::Expected<std::vector<HighlightingToken>> HT)
mutable {
1592 return CB(HT.takeError());
1593 SemanticTokens Result;
1594 Result.tokens = toSemanticTokens(*HT, *Code);
1596 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1597 auto &Last = LastSemanticTokens[File];
1599 Last.tokens = Result.tokens;
1600 increment(Last.resultId);
1601 Result.resultId = Last.resultId;
1603 CB(std::move(Result));
1607void ClangdLSPServer::onSemanticTokensDelta(
1608 const SemanticTokensDeltaParams &Params,
1609 Callback<SemanticTokensOrDelta> CB) {
1610 auto File = Params.textDocument.uri.file();
1611 Server->semanticHighlights(
1612 Params.textDocument.uri.file(),
1613 [
this, PrevResultID(Params.previousResultId),
File(
File.str()),
1614 CB(std::move(CB)), Code(Server->getDraft(File))](
1615 llvm::Expected<std::vector<HighlightingToken>> HT)
mutable {
1617 return CB(HT.takeError());
1618 std::vector<SemanticToken> Toks = toSemanticTokens(*HT, *Code);
1620 SemanticTokensOrDelta Result;
1622 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1623 auto &Last = LastSemanticTokens[File];
1625 if (PrevResultID == Last.resultId) {
1626 Result.edits = diffTokens(Last.tokens, Toks);
1628 vlog(
"semanticTokens/full/delta: wanted edits vs {0} but last "
1629 "result had ID {1}. Returning full token list.",
1630 PrevResultID, Last.resultId);
1631 Result.tokens = Toks;
1634 Last.tokens = std::move(Toks);
1635 increment(Last.resultId);
1636 Result.resultId = Last.resultId;
1639 CB(std::move(Result));
1643void ClangdLSPServer::onMemoryUsage(
const NoParams &,
1644 Callback<MemoryTree> Reply) {
1645 llvm::BumpPtrAllocator DetailAlloc;
1646 MemoryTree MT(&DetailAlloc);
1648 Reply(std::move(MT));
1651void ClangdLSPServer::onAST(
const ASTParams &Params,
1652 Callback<std::optional<ASTNode>> CB) {
1653 Server->getAST(Params.textDocument.uri.file(), Params.range, std::move(CB));
1658 : ShouldProfile(std::chrono::minutes(5),
1659 std::chrono::minutes(1)),
1660 ShouldCleanupMemory(std::chrono::minutes(1),
1661 std::chrono::minutes(1)),
1662 BackgroundContext(
Context::current().clone()), Transp(Transp),
1664 SupportedSymbolKinds(defaultSymbolKinds()),
1665 SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts) {
1666 if (Opts.ConfigProvider) {
1667 assert(!Opts.ContextProvider &&
1668 "Only one of ConfigProvider and ContextProvider allowed!");
1669 this->Opts.ContextProvider = ClangdServer::createConfiguredContextProvider(
1670 Opts.ConfigProvider, this);
1673 Bind.
method(
"initialize",
this, &ClangdLSPServer::onInitialize);
1676void ClangdLSPServer::bindMethods(
LSPBinder &Bind,
1679 Bind.
notification(
"initialized",
this, &ClangdLSPServer::onInitialized);
1680 Bind.
method(
"shutdown",
this, &ClangdLSPServer::onShutdown);
1681 Bind.
method(
"sync",
this, &ClangdLSPServer::onSync);
1682 Bind.
method(
"textDocument/rangeFormatting",
this, &ClangdLSPServer::onDocumentRangeFormatting);
1683 Bind.
method(
"textDocument/rangesFormatting",
this, &ClangdLSPServer::onDocumentRangesFormatting);
1684 Bind.
method(
"textDocument/onTypeFormatting",
this, &ClangdLSPServer::onDocumentOnTypeFormatting);
1685 Bind.
method(
"textDocument/formatting",
this, &ClangdLSPServer::onDocumentFormatting);
1686 Bind.
method(
"textDocument/codeAction",
this, &ClangdLSPServer::onCodeAction);
1687 Bind.
method(
"textDocument/completion",
this, &ClangdLSPServer::onCompletion);
1688 Bind.
method(
"textDocument/signatureHelp",
this, &ClangdLSPServer::onSignatureHelp);
1689 Bind.
method(
"textDocument/definition",
this, &ClangdLSPServer::onGoToDefinition);
1690 Bind.
method(
"textDocument/declaration",
this, &ClangdLSPServer::onGoToDeclaration);
1691 Bind.
method(
"textDocument/typeDefinition",
this, &ClangdLSPServer::onGoToType);
1692 Bind.
method(
"textDocument/implementation",
this, &ClangdLSPServer::onGoToImplementation);
1693 Bind.
method(
"textDocument/references",
this, &ClangdLSPServer::onReference);
1694 Bind.
method(
"textDocument/switchSourceHeader",
this, &ClangdLSPServer::onSwitchSourceHeader);
1695 Bind.
method(
"textDocument/prepareRename",
this, &ClangdLSPServer::onPrepareRename);
1696 Bind.
method(
"textDocument/rename",
this, &ClangdLSPServer::onRename);
1697 Bind.
method(
"textDocument/hover",
this, &ClangdLSPServer::onHover);
1698 Bind.
method(
"textDocument/documentSymbol",
this, &ClangdLSPServer::onDocumentSymbol);
1699 Bind.
method(
"workspace/executeCommand",
this, &ClangdLSPServer::onCommand);
1700 Bind.
method(
"textDocument/documentHighlight",
this, &ClangdLSPServer::onDocumentHighlight);
1701 Bind.
method(
"workspace/symbol",
this, &ClangdLSPServer::onWorkspaceSymbol);
1702 Bind.
method(
"textDocument/ast",
this, &ClangdLSPServer::onAST);
1703 Bind.
notification(
"textDocument/didOpen",
this, &ClangdLSPServer::onDocumentDidOpen);
1704 Bind.
notification(
"textDocument/didClose",
this, &ClangdLSPServer::onDocumentDidClose);
1705 Bind.
notification(
"textDocument/didChange",
this, &ClangdLSPServer::onDocumentDidChange);
1706 Bind.
notification(
"textDocument/didSave",
this, &ClangdLSPServer::onDocumentDidSave);
1707 Bind.
notification(
"workspace/didChangeWatchedFiles",
this, &ClangdLSPServer::onFileEvent);
1708 Bind.
notification(
"workspace/didChangeConfiguration",
this, &ClangdLSPServer::onChangeConfiguration);
1709 Bind.
method(
"textDocument/symbolInfo",
this, &ClangdLSPServer::onSymbolInfo);
1710 Bind.
method(
"textDocument/typeHierarchy",
this, &ClangdLSPServer::onTypeHierarchy);
1711 Bind.
method(
"typeHierarchy/resolve",
this, &ClangdLSPServer::onResolveTypeHierarchy);
1712 Bind.
method(
"textDocument/prepareTypeHierarchy",
this, &ClangdLSPServer::onPrepareTypeHierarchy);
1713 Bind.
method(
"typeHierarchy/supertypes",
this, &ClangdLSPServer::onSuperTypes);
1714 Bind.
method(
"typeHierarchy/subtypes",
this, &ClangdLSPServer::onSubTypes);
1715 Bind.
method(
"textDocument/prepareCallHierarchy",
this, &ClangdLSPServer::onPrepareCallHierarchy);
1716 Bind.
method(
"callHierarchy/incomingCalls",
this, &ClangdLSPServer::onCallHierarchyIncomingCalls);
1718 Bind.
method(
"callHierarchy/outgoingCalls",
this, &ClangdLSPServer::onCallHierarchyOutgoingCalls);
1719 Bind.
method(
"textDocument/selectionRange",
this, &ClangdLSPServer::onSelectionRange);
1720 Bind.
method(
"textDocument/documentLink",
this, &ClangdLSPServer::onDocumentLink);
1721 Bind.
method(
"textDocument/semanticTokens/full",
this, &ClangdLSPServer::onSemanticTokens);
1722 Bind.
method(
"textDocument/semanticTokens/full/delta",
this, &ClangdLSPServer::onSemanticTokensDelta);
1723 Bind.
method(
"clangd/inlayHints",
this, &ClangdLSPServer::onClangdInlayHints);
1724 Bind.
method(
"textDocument/inlayHint",
this, &ClangdLSPServer::onInlayHint);
1725 Bind.
method(
"$/memoryUsage",
this, &ClangdLSPServer::onMemoryUsage);
1726 Bind.
method(
"textDocument/foldingRange",
this, &ClangdLSPServer::onFoldingRange);
1727 Bind.
command(ApplyFixCommand,
this, &ClangdLSPServer::onCommandApplyEdit);
1728 Bind.
command(ApplyTweakCommand,
this, &ClangdLSPServer::onCommandApplyTweak);
1729 Bind.
command(ApplyRenameCommand,
this, &ClangdLSPServer::onCommandApplyRename);
1737 CreateWorkDoneProgress = Bind.
outgoingMethod(
"window/workDoneProgress/create");
1742 SemanticTokensRefresh = Bind.
outgoingMethod(
"workspace/semanticTokens/refresh");
1747 IsBeingDestroyed =
true;
1755 bool CleanExit =
true;
1756 if (
auto Err = Transp.loop(*MsgHandler)) {
1757 elog(
"Transport error: {0}", std::move(Err));
1761 return CleanExit && ShutdownRequestReceived;
1766 Server->profile(MT.
child(
"clangd_server"));
1769std::optional<ClangdServer::DiagRef>
1771 std::lock_guard<std::mutex> Lock(DiagRefMutex);
1772 auto DiagToDiagRefIter = DiagRefMap.find(
File);
1773 if (DiagToDiagRefIter == DiagRefMap.end())
1774 return std::nullopt;
1776 const auto &DiagToDiagRefMap = DiagToDiagRefIter->second;
1777 auto FixItsIter = DiagToDiagRefMap.find(toDiagKey(D));
1778 if (FixItsIter == DiagToDiagRefMap.end())
1779 return std::nullopt;
1781 return FixItsIter->second;
1790bool ClangdLSPServer::shouldRunCompletion(
1791 const CompletionParams &Params)
const {
1794 auto Code = Server->getDraft(Params.textDocument.uri.file());
1800 vlog(
"could not convert position '{0}' to offset for file '{1}'",
1801 Params.position, Params.textDocument.uri.file());
1807void ClangdLSPServer::onDiagnosticsReady(
PathRef File, llvm::StringRef Version,
1808 llvm::ArrayRef<Diag> Diagnostics) {
1809 PublishDiagnosticsParams Notification;
1810 Notification.version = decodeVersion(Version);
1812 DiagnosticToDiagRefMap LocalDiagMap;
1813 for (
auto &Diag : Diagnostics) {
1815 [&](clangd::Diagnostic LSPDiag, llvm::ArrayRef<Fix> Fixes) {
1816 if (DiagOpts.EmbedFixesInDiagnostics) {
1817 std::vector<CodeAction> CodeActions;
1818 for (const auto &Fix : Fixes)
1819 CodeActions.push_back(toCodeAction(
1820 Fix, Notification.uri, Notification.version,
1821 SupportsDocumentChanges, SupportsChangeAnnotation));
1822 LSPDiag.codeActions.emplace(std::move(CodeActions));
1823 if (LSPDiag.codeActions->size() == 1)
1824 LSPDiag.codeActions->front().isPreferred = true;
1826 LocalDiagMap[toDiagKey(LSPDiag)] = {Diag.Range, Diag.Message};
1827 Notification.diagnostics.push_back(std::move(LSPDiag));
1833 std::lock_guard<std::mutex> Lock(DiagRefMutex);
1834 DiagRefMap[
File] = LocalDiagMap;
1841void ClangdLSPServer::onInactiveRegionsReady(
1842 PathRef File, std::vector<Range> InactiveRegions) {
1843 InactiveRegionsParams Notification;
1845 Notification.InactiveRegions = std::move(InactiveRegions);
1847 PublishInactiveRegions(Notification);
1850void ClangdLSPServer::onBackgroundIndexProgress(
1852 static const char ProgressToken[] =
"backgroundIndexProgress";
1855 maybeCleanupMemory();
1857 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1859 auto NotifyProgress = [
this](
const BackgroundQueue::Stats &Stats) {
1860 if (BackgroundIndexProgressState != BackgroundIndexProgress::Live) {
1861 WorkDoneProgressBegin Begin;
1862 Begin.percentage =
true;
1863 Begin.title =
"indexing";
1864 BeginWorkDoneProgress({ProgressToken, std::move(Begin)});
1865 BackgroundIndexProgressState = BackgroundIndexProgress::Live;
1868 if (Stats.Completed < Stats.Enqueued) {
1869 assert(Stats.Enqueued > Stats.LastIdle);
1870 WorkDoneProgressReport Report;
1871 Report.percentage = 100 * (Stats.Completed - Stats.LastIdle) /
1872 (Stats.Enqueued - Stats.LastIdle);
1874 llvm::formatv(
"{0}/{1}", Stats.Completed - Stats.LastIdle,
1875 Stats.Enqueued - Stats.LastIdle);
1876 ReportWorkDoneProgress({ProgressToken, std::move(Report)});
1878 assert(Stats.Completed == Stats.Enqueued);
1879 EndWorkDoneProgress({ProgressToken, WorkDoneProgressEnd()});
1880 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
1884 switch (BackgroundIndexProgressState) {
1885 case BackgroundIndexProgress::Unsupported:
1887 case BackgroundIndexProgress::Creating:
1889 PendingBackgroundIndexProgress = Stats;
1891 case BackgroundIndexProgress::Empty: {
1892 if (BackgroundIndexSkipCreate) {
1893 NotifyProgress(Stats);
1897 PendingBackgroundIndexProgress = Stats;
1898 BackgroundIndexProgressState = BackgroundIndexProgress::Creating;
1899 WorkDoneProgressCreateParams CreateRequest;
1900 CreateRequest.token = ProgressToken;
1901 CreateWorkDoneProgress(
1903 [
this, NotifyProgress](llvm::Expected<std::nullptr_t> E) {
1904 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1906 NotifyProgress(this->PendingBackgroundIndexProgress);
1908 elog(
"Failed to create background index progress bar: {0}",
1911 BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported;
1916 case BackgroundIndexProgress::Live:
1917 NotifyProgress(Stats);
1923 if (!SupportFileStatus)
1933 NotifyFileStatus(Status.render(
File));
1936void ClangdLSPServer::onSemanticsMaybeChanged(
PathRef File) {
1937 if (SemanticTokensRefresh) {
1938 SemanticTokensRefresh(NoParams{}, [](llvm::Expected<std::nullptr_t> E) {
1941 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.