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";
80CodeAction toCodeAction(
const ClangdServer::CodeActionResult::Rename &R,
81 const URIForFile &
File) {
83 CA.title = R.FixMessage;
86 CA.command->title = R.FixMessage;
87 CA.command->command = std::string(ApplyRenameCommand);
89 Params.textDocument = TextDocumentIdentifier{
File};
90 Params.position = R.Diag.Range.start;
91 Params.newName = R.NewName;
92 CA.command->argument = Params;
98CodeAction toCodeAction(
const ClangdServer::TweakRef &T,
const URIForFile &
File,
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);
120CodeAction toCodeAction(
const Fix &F,
const URIForFile &
File,
121 const std::optional<int64_t> &Version,
122 bool SupportsDocumentChanges,
123 bool SupportChangeAnnotation) {
128 if (!SupportsDocumentChanges) {
129 Action.edit->changes.emplace();
131 for (
const auto &
E : F.Edits)
132 Changes.push_back({
E.range,
E.newText,
""});
134 Action.edit->documentChanges.emplace();
135 TextDocumentEdit &Edit =
Action.edit->documentChanges->emplace_back();
136 Edit.textDocument = VersionedTextDocumentIdentifier{{
File}, Version};
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);
176llvm::Error validateEdits(
const ClangdServer &Server,
const FileEdits &FE) {
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 {
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,
340 Server(Server), TraceArgs(TraceArgs) {
343 ReplyOnce(ReplyOnce &&Other)
344 : Replied(Other.Replied.load()), Start(Other.Start),
345 ID(std::move(Other.
ID)), Method(std::move(Other.Method)),
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,
454 Callback<llvm::json::Value>>>
457 ClangdLSPServer &Server;
459constexpr int ClangdLSPServer::MessageHandler::MaxReplayCallbacks;
462void ClangdLSPServer::callMethod(StringRef
Method, llvm::json::Value Params,
463 Callback<llvm::json::Value> CB) {
464 auto ID = MsgHandler->bindReply(std::move(CB));
466 std::lock_guard<std::mutex> Lock(TranspWriter);
470void ClangdLSPServer::notify(llvm::StringRef
Method, llvm::json::Value Params) {
472 maybeCleanupMemory();
473 std::lock_guard<std::mutex> Lock(TranspWriter);
478 std::vector<llvm::StringRef> Types;
486 std::vector<llvm::StringRef> Modifiers;
494void ClangdLSPServer::onInitialize(
const InitializeParams &Params,
495 Callback<llvm::json::Value> Reply) {
497 if (Params.capabilities.offsetEncoding && !Opts.
Encoding) {
499 for (
OffsetEncoding Supported : *Params.capabilities.offsetEncoding)
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)
514 else if (Params.rootPath && !Params.rootPath->empty())
517 return Reply(llvm::make_error<LSPError>(
"server already initialized",
525 Params.capabilities.CompletionDocumentationFormat;
527 Params.capabilities.SignatureHelpDocumentationFormat;
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;
546 SupportsOffsetsInSignatureHelp = Params.capabilities.OffsetsInSignatureHelp;
547 if (Params.capabilities.WorkDoneProgress)
548 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
549 BackgroundIndexSkipCreate = Params.capabilities.ImplicitProgressCreation;
554 DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS);
555 if (
const auto &Dir = Params.initializationOptions.compilationDatabasePath)
556 CDBOpts.CompileCommandsDir = Dir;
559 std::make_unique<DirectoryBasedGlobalCompilationDatabase>(CDBOpts);
562 Mangler.SystemIncludeExtractor =
566 CDB.emplace(BaseCDB.get(), Params.initializationOptions.fallbackFlags,
570 ModulesManager.emplace(*CDB);
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",
true},
595 {
"documentOnTypeFormattingProvider",
597 {
"firstTriggerCharacter",
"\n"},
598 {
"moreTriggerCharacter", {}},
600 {
"completionProvider",
607 {
"resolveProvider",
false},
609 {
"triggerCharacters", {
".",
"<",
">",
":",
"\"",
"/",
"*"}},
611 {
"semanticTokensProvider",
613 {
"full", llvm::json::Object{{
"delta",
true}}},
619 {
"signatureHelpProvider",
621 {
"triggerCharacters", {
"(",
")",
"{",
"}",
"<",
">",
","}},
623 {
"declarationProvider",
true},
624 {
"definitionProvider",
true},
625 {
"implementationProvider",
true},
626 {
"typeDefinitionProvider",
true},
627 {
"documentHighlightProvider",
true},
628 {
"documentLinkProvider",
630 {
"resolveProvider",
false},
632 {
"hoverProvider",
true},
633 {
"selectionRangeProvider",
true},
634 {
"documentSymbolProvider",
true},
635 {
"workspaceSymbolProvider",
true},
636 {
"referencesProvider",
true},
637 {
"astProvider",
true},
638 {
"typeHierarchyProvider",
true},
642 {
"standardTypeHierarchyProvider",
true},
643 {
"memoryUsageProvider",
true},
644 {
"compilationDatabase",
645 llvm::json::Object{{
"automaticReload",
true}}},
646 {
"inactiveRegionsProvider",
true},
647 {
"callHierarchyProvider",
true},
648 {
"clangdInlayHintsProvider",
true},
649 {
"inlayHintProvider",
true},
650 {
"foldingRangeProvider",
true},
654 LSPBinder Binder(Handlers, *
this);
655 bindMethods(Binder, Params.capabilities);
658 Mod.initializeLSP(Binder, Params.rawCapabilities, ServerCaps);
663 ServerCaps[
"renameProvider"] =
664 Params.capabilities.RenamePrepareSupport
665 ? llvm::json::Object{{
"prepareProvider",
true}}
666 : llvm::json::Value(
true);
671 ServerCaps[
"codeActionProvider"] =
672 Params.capabilities.CodeActionStructure
673 ? llvm::json::Object{{
"codeActionKinds",
677 : llvm::json::Value(
true);
679 std::vector<llvm::StringRef>
Commands;
683 ServerCaps[
"executeCommandProvider"] =
684 llvm::json::Object{{
"commands",
Commands}};
686 llvm::json::Object Result{
692 {
"capabilities", std::move(ServerCaps)}}};
694 Result[
"offsetEncoding"] = *Opts.
Encoding;
695 Reply(std::move(Result));
699 applyConfiguration(Params.initializationOptions.ConfigSettings);
704void ClangdLSPServer::onShutdown(
const NoParams &,
705 Callback<std::nullptr_t> Reply) {
707 ShutdownRequestReceived =
true;
713void ClangdLSPServer::onSync(
const NoParams &, Callback<std::nullptr_t> Reply) {
714 if (Server->blockUntilIdleForTest(60))
717 Reply(
error(
"Not idle after a minute"));
720void ClangdLSPServer::onDocumentDidOpen(
721 const DidOpenTextDocumentParams &Params) {
724 const std::string &Contents = Params.textDocument.text;
726 Server->addDocument(
File, Contents,
727 encodeVersion(Params.textDocument.version),
731void ClangdLSPServer::onDocumentDidChange(
732 const DidChangeTextDocumentParams &Params) {
734 if (Params.wantDiagnostics)
741 log(
"Trying to incrementally change non-added document: {0}",
File);
744 std::string NewCode(*
Code);
745 for (
const auto &Change : Params.contentChanges) {
750 Server->removeDocument(
File);
751 elog(
"Failed to update {0}: {1}",
File, std::move(Err));
755 Server->addDocument(
File, NewCode, encodeVersion(Params.textDocument.version),
759void ClangdLSPServer::onDocumentDidSave(
760 const DidSaveTextDocumentParams &Params) {
761 Server->reparseOpenFilesIfNeeded([](llvm::StringRef) {
return true; });
764void ClangdLSPServer::onFileEvent(
const DidChangeWatchedFilesParams &Params) {
769 Server->onFileEvent(Params);
776void ClangdLSPServer::onCommand(
const ExecuteCommandParams &Params,
777 Callback<llvm::json::Value> Reply) {
780 return Reply(llvm::make_error<LSPError>(
781 llvm::formatv(
"Unsupported command \"{0}\".", Params.command).str(),
784 It->second(Params.argument, std::move(Reply));
787void ClangdLSPServer::onCommandApplyEdit(
const WorkspaceEdit &WE,
788 Callback<llvm::json::Value> Reply) {
797 applyEdit(WE,
"Fix applied.", std::move(Reply));
800void ClangdLSPServer::onCommandApplyTweak(
const TweakArgs &
Args,
801 Callback<llvm::json::Value> Reply) {
802 auto Action = [
this, Reply = std::move(Reply)](
803 llvm::Expected<Tweak::Effect> R)
mutable {
805 return Reply(R.takeError());
807 assert(R->ShowMessage || (!R->ApplyEdits.empty() &&
"tweak has no effect"));
809 if (R->ShowMessage) {
810 ShowMessageParams Msg;
811 Msg.message = *R->ShowMessage;
816 if (R->ApplyEdits.empty())
817 return Reply(
"Tweak applied.");
819 if (
auto Err = validateEdits(*Server, R->ApplyEdits))
820 return Reply(std::move(Err));
824 WE.changes.emplace();
825 for (
const auto &It : R->ApplyEdits) {
827 It.second.asTextEdits();
830 return applyEdit(std::move(WE),
"Tweak applied.", std::move(Reply));
832 Server->applyTweak(
Args.file.file(),
Args.selection,
Args.tweakID,
836void ClangdLSPServer::onCommandApplyRename(
const RenameParams &R,
837 Callback<llvm::json::Value> Reply) {
838 onRename(R, [
this, Reply = std::move(Reply)](
839 llvm::Expected<WorkspaceEdit> Edit)
mutable {
841 Reply(Edit.takeError());
842 applyEdit(std::move(*Edit),
"Rename applied.", std::move(Reply));
846void ClangdLSPServer::applyEdit(WorkspaceEdit WE, llvm::json::Value Success,
847 Callback<llvm::json::Value> Reply) {
848 ApplyWorkspaceEditParams Edit;
849 Edit.edit = std::move(WE);
851 Edit, [Reply = std::move(Reply), SuccessMessage = std::move(Success)](
852 llvm::Expected<ApplyWorkspaceEditResponse> Response)
mutable {
854 return Reply(Response.takeError());
855 if (!Response->applied) {
856 std::string Reason = Response->failureReason
857 ? *Response->failureReason
859 return Reply(error(
"edits were not applied: {0}", Reason));
861 return Reply(SuccessMessage);
865void ClangdLSPServer::onWorkspaceSymbol(
866 const WorkspaceSymbolParams &Params,
867 Callback<std::vector<SymbolInformation>> Reply) {
868 Server->workspaceSymbols(
870 [Reply = std::move(Reply),
871 this](llvm::Expected<std::vector<SymbolInformation>> Items)
mutable {
873 return Reply(Items.takeError());
874 for (auto &Sym : *Items)
875 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds);
877 Reply(std::move(*Items));
881void ClangdLSPServer::onPrepareRename(
const TextDocumentPositionParams &Params,
882 Callback<PrepareRenameResult> Reply) {
883 Server->prepareRename(
884 Params.textDocument.uri.file(), Params.position, std::nullopt,
886 [Reply = std::move(Reply)](llvm::Expected<RenameResult> Result)
mutable {
888 return Reply(Result.takeError());
889 PrepareRenameResult PrepareResult;
890 PrepareResult.range = Result->Target;
891 PrepareResult.placeholder = Result->Placeholder;
892 return Reply(std::move(PrepareResult));
896void ClangdLSPServer::onRename(
const RenameParams &Params,
897 Callback<WorkspaceEdit> Reply) {
898 Path File = std::string(Params.textDocument.uri.file());
899 if (!Server->getDraft(
File))
900 return Reply(llvm::make_error<LSPError>(
902 Server->rename(
File, Params.position, Params.newName, Opts.
Rename,
903 [
File, Params, Reply = std::move(Reply),
904 this](llvm::Expected<RenameResult> R)
mutable {
906 return Reply(R.takeError());
907 if (auto Err = validateEdits(*Server, R->GlobalChanges))
908 return Reply(std::move(Err));
909 WorkspaceEdit Result;
912 Result.changes.emplace();
913 for (const auto &Rep : R->GlobalChanges) {
915 .changes)[URI::createFile(Rep.first()).toString()] =
916 Rep.second.asTextEdits();
922void ClangdLSPServer::onDocumentDidClose(
923 const DidCloseTextDocumentParams &Params) {
924 PathRef File = Params.textDocument.uri.file();
925 Server->removeDocument(File);
928 std::lock_guard<std::mutex> Lock(DiagRefMutex);
929 DiagRefMap.erase(File);
932 std::lock_guard<std::mutex> HLock(SemanticTokensMutex);
933 LastSemanticTokens.erase(File);
940 PublishDiagnosticsParams Notification;
941 Notification.uri = URIForFile::canonicalize(File, File);
942 PublishDiagnostics(Notification);
945void ClangdLSPServer::onDocumentOnTypeFormatting(
946 const DocumentOnTypeFormattingParams &Params,
947 Callback<std::vector<TextEdit>> Reply) {
948 auto File = Params.textDocument.uri.file();
949 Server->formatOnType(File, Params.position, Params.ch, std::move(Reply));
952void ClangdLSPServer::onDocumentRangeFormatting(
953 const DocumentRangeFormattingParams &Params,
954 Callback<std::vector<TextEdit>> Reply) {
955 auto File = Params.textDocument.uri.file();
956 auto Code = Server->getDraft(File);
957 Server->formatFile(File, Params.range,
958 [
Code = std::move(
Code), Reply = std::move(Reply)](
959 llvm::Expected<tooling::Replacements> Result)
mutable {
961 Reply(replacementsToEdits(*Code, Result.get()));
963 Reply(Result.takeError());
967void ClangdLSPServer::onDocumentFormatting(
968 const DocumentFormattingParams &Params,
969 Callback<std::vector<TextEdit>> Reply) {
970 auto File = Params.textDocument.uri.file();
971 auto Code = Server->getDraft(File);
972 Server->formatFile(File,
974 [
Code = std::move(
Code), Reply = std::move(Reply)](
975 llvm::Expected<tooling::Replacements> Result)
mutable {
979 Reply(Result.takeError());
985static std::vector<SymbolInformation>
988 std::vector<SymbolInformation>
Results;
989 std::function<void(
const DocumentSymbol &, llvm::StringRef)> Process =
990 [&](
const DocumentSymbol &S, std::optional<llvm::StringRef> ParentName) {
992 SI.
containerName = std::string(ParentName ?
"" : *ParentName);
998 Results.push_back(std::move(SI));
999 std::string FullName =
1000 !ParentName ? S.name : (ParentName->str() +
"::" + S.name);
1001 for (
auto &
C : S.children)
1002 Process(
C, FullName);
1004 for (
auto &S : Symbols)
1009void ClangdLSPServer::onDocumentSymbol(
const DocumentSymbolParams &Params,
1010 Callback<llvm::json::Value> Reply) {
1011 URIForFile FileURI = Params.textDocument.uri;
1012 Server->documentSymbols(
1013 Params.textDocument.uri.file(),
1014 [
this, FileURI, Reply = std::move(Reply)](
1015 llvm::Expected<std::vector<DocumentSymbol>> Items)
mutable {
1017 return Reply(Items.takeError());
1018 adjustSymbolKinds(*Items, SupportedSymbolKinds);
1019 if (SupportsHierarchicalDocumentSymbol)
1020 return Reply(std::move(*Items));
1021 return Reply(flattenSymbolHierarchy(*Items, FileURI));
1025void ClangdLSPServer::onFoldingRange(
1026 const FoldingRangeParams &Params,
1027 Callback<std::vector<FoldingRange>> Reply) {
1028 Server->foldingRanges(Params.textDocument.uri.file(), std::move(Reply));
1034 return std::nullopt;
1037 }
else if (
Action.edit) {
1038 Cmd.command = std::string(ApplyFixCommand);
1039 Cmd.argument = *
Action.edit;
1041 return std::nullopt;
1043 Cmd.title =
Action.title;
1044 if (
Action.kind && *
Action.kind == CodeAction::QUICKFIX_KIND)
1045 Cmd.title =
"Apply fix: " + Cmd.title;
1049void ClangdLSPServer::onCodeAction(
const CodeActionParams &Params,
1050 Callback<llvm::json::Value> Reply) {
1051 URIForFile File = Params.textDocument.uri;
1052 std::map<ClangdServer::DiagRef, clangd::Diagnostic> ToLSPDiags;
1053 ClangdServer::CodeActionInputs Inputs;
1055 for (
const auto& LSPDiag : Params.context.diagnostics) {
1056 if (
auto DiagRef = getDiagRef(File.file(), LSPDiag)) {
1057 ToLSPDiags[*DiagRef] = LSPDiag;
1058 Inputs.Diagnostics.push_back(*DiagRef);
1061 Inputs.File = File.file();
1062 Inputs.Selection = Params.range;
1063 Inputs.RequestedActionKinds = Params.context.only;
1064 Inputs.TweakFilter = [
this](
const Tweak &
T) {
1065 return Opts.TweakFilter(T);
1068 Reply = std::move(Reply),
1069 ToLSPDiags = std::move(ToLSPDiags), File,
1070 Selection = Params.range](
1071 llvm::Expected<ClangdServer::CodeActionResult> Fixits)
mutable {
1073 return Reply(Fixits.takeError());
1074 std::vector<CodeAction> CAs;
1075 auto Version = decodeVersion(Fixits->Version);
1076 for (
const auto &QF : Fixits->QuickFixes) {
1077 CAs.push_back(toCodeAction(QF.F, File, Version, SupportsDocumentChanges,
1078 SupportsChangeAnnotation));
1079 if (
auto It = ToLSPDiags.find(QF.Diag);
1080 It != ToLSPDiags.end()) {
1081 CAs.back().diagnostics = {It->second};
1085 for (
const auto &R : Fixits->Renames)
1086 CAs.push_back(toCodeAction(R, File));
1088 for (
const auto &TR : Fixits->TweakRefs)
1089 CAs.push_back(toCodeAction(TR, File, Selection));
1093 CodeAction *OnlyFix =
nullptr;
1094 for (
auto &
Action : CAs) {
1095 if (
Action.kind && *
Action.kind == CodeAction::QUICKFIX_KIND) {
1104 OnlyFix->isPreferred =
true;
1105 if (ToLSPDiags.size() == 1 &&
1106 ToLSPDiags.begin()->second.range == Selection)
1107 OnlyFix->diagnostics = {ToLSPDiags.begin()->second};
1110 if (SupportsCodeAction)
1111 return Reply(llvm::json::Array(CAs));
1113 for (
const auto &
Action : CAs) {
1115 Commands.push_back(std::move(*Command));
1117 return Reply(llvm::json::Array(
Commands));
1119 Server->codeAction(Inputs, std::move(CB));
1122void ClangdLSPServer::onCompletion(
const CompletionParams &Params,
1123 Callback<CompletionList> Reply) {
1124 if (!shouldRunCompletion(Params)) {
1127 vlog(
"ignored auto-triggered completion, preceding char did not match");
1128 return Reply(CompletionList());
1130 auto Opts = this->Opts.CodeComplete;
1131 if (Params.limit && *Params.limit >= 0)
1132 Opts.Limit = *Params.limit;
1133 Server->codeComplete(Params.textDocument.uri.file(), Params.position, Opts,
1134 [Reply = std::move(Reply), Opts,
1135 this](llvm::Expected<CodeCompleteResult> List)
mutable {
1137 return Reply(List.takeError());
1138 CompletionList LSPList;
1139 LSPList.isIncomplete = List->HasMore;
1140 for (const auto &R : List->Completions) {
1141 CompletionItem C = R.render(Opts);
1142 C.kind = adjustKindToCapability(
1143 C.kind, SupportedCompletionItemKinds);
1144 if (!SupportsCompletionLabelDetails)
1145 removeCompletionLabelDetails(C);
1146 LSPList.items.push_back(std::move(C));
1148 return Reply(std::move(LSPList));
1152void ClangdLSPServer::onSignatureHelp(
const TextDocumentPositionParams &Params,
1153 Callback<SignatureHelp> Reply) {
1154 Server->signatureHelp(Params.textDocument.uri.file(), Params.position,
1155 Opts.SignatureHelpDocumentationFormat,
1156 [Reply = std::move(Reply),
this](
1157 llvm::Expected<SignatureHelp>
Signature)
mutable {
1159 return Reply(Signature.takeError());
1160 if (SupportsOffsetsInSignatureHelp)
1161 return Reply(std::move(*Signature));
1164 for (auto &SigInfo : Signature->signatures) {
1165 for (auto &Param : SigInfo.parameters)
1166 Param.labelOffsets.reset();
1192void ClangdLSPServer::onGoToDefinition(
const TextDocumentPositionParams &Params,
1193 Callback<std::vector<Location>> Reply) {
1194 Server->locateSymbolAt(
1195 Params.textDocument.uri.file(), Params.position,
1196 [Params, Reply = std::move(Reply)](
1197 llvm::Expected<std::vector<LocatedSymbol>> Symbols)
mutable {
1199 return Reply(Symbols.takeError());
1200 std::vector<Location> Defs;
1201 for (auto &S : *Symbols) {
1202 if (Location *Toggle = getToggle(Params, S))
1203 return Reply(std::vector<Location>{std::move(*Toggle)});
1204 Defs.push_back(S.Definition.value_or(S.PreferredDeclaration));
1206 Reply(std::move(Defs));
1210void ClangdLSPServer::onGoToDeclaration(
1211 const TextDocumentPositionParams &Params,
1212 Callback<std::vector<Location>> Reply) {
1213 Server->locateSymbolAt(
1214 Params.textDocument.uri.file(), Params.position,
1215 [Params, Reply = std::move(Reply)](
1216 llvm::Expected<std::vector<LocatedSymbol>> Symbols)
mutable {
1218 return Reply(Symbols.takeError());
1219 std::vector<Location> Decls;
1220 for (auto &S : *Symbols) {
1221 if (Location *Toggle = getToggle(Params, S))
1222 return Reply(std::vector<Location>{std::move(*Toggle)});
1223 Decls.push_back(std::move(S.PreferredDeclaration));
1225 Reply(std::move(Decls));
1229void ClangdLSPServer::onSwitchSourceHeader(
1230 const TextDocumentIdentifier &Params,
1231 Callback<std::optional<URIForFile>> Reply) {
1232 Server->switchSourceHeader(
1234 [Reply = std::move(Reply),
1235 Params](llvm::Expected<std::optional<clangd::Path>>
Path)
mutable {
1237 return Reply(Path.takeError());
1239 return Reply(URIForFile::canonicalize(**Path, Params.uri.file()));
1240 return Reply(std::nullopt);
1244void ClangdLSPServer::onDocumentHighlight(
1245 const TextDocumentPositionParams &Params,
1246 Callback<std::vector<DocumentHighlight>> Reply) {
1247 Server->findDocumentHighlights(Params.textDocument.uri.file(),
1248 Params.position, std::move(Reply));
1251void ClangdLSPServer::onHover(
const TextDocumentPositionParams &Params,
1252 Callback<std::optional<Hover>> Reply) {
1253 Server->findHover(Params.textDocument.uri.file(), Params.position,
1254 [Reply = std::move(Reply),
1255 this](llvm::Expected<std::optional<HoverInfo>> H)
mutable {
1257 return Reply(H.takeError());
1259 return Reply(std::nullopt);
1262 R.contents.kind = HoverContentFormat;
1263 R.range = (*H)->SymRange;
1264 switch (HoverContentFormat) {
1265 case MarkupKind::PlainText:
1266 R.contents.value = (*H)->present().asPlainText();
1267 return Reply(std::move(R));
1268 case MarkupKind::Markdown:
1269 R.contents.value = (*H)->present().asMarkdown();
1270 return Reply(std::move(R));
1272 llvm_unreachable(
"unhandled MarkupKind");
1279 llvm::json::Object Result{{
1280 {
"name", std::move(THI.
name)},
1281 {
"kind",
static_cast<int>(THI.
kind)},
1282 {
"uri", std::move(THI.
uri)},
1283 {
"range", THI.
range},
1285 {
"data", std::move(THI.
data)},
1290 Result[
"detail"] = std::move(*THI.
detail);
1296 Result[
"parents"] = std::move(
Parents);
1303 Result[
"children"] = std::move(
Children);
1308void ClangdLSPServer::onTypeHierarchy(
const TypeHierarchyPrepareParams &Params,
1309 Callback<llvm::json::Value> Reply) {
1311 [Reply = std::move(Reply)](
1312 llvm::Expected<std::vector<TypeHierarchyItem>> Resp)
mutable {
1314 Reply(Resp.takeError());
1317 if (Resp->empty()) {
1323 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1324 Params.resolve, Params.direction, std::move(Serialize));
1327void ClangdLSPServer::onResolveTypeHierarchy(
1328 const ResolveTypeHierarchyItemParams &Params,
1329 Callback<llvm::json::Value> Reply) {
1331 [Reply = std::move(Reply)](
1332 llvm::Expected<std::optional<TypeHierarchyItem>> Resp)
mutable {
1334 Reply(Resp.takeError());
1338 Reply(std::move(*Resp));
1343 Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction,
1344 std::move(Serialize));
1347void ClangdLSPServer::onPrepareTypeHierarchy(
1348 const TypeHierarchyPrepareParams &Params,
1349 Callback<std::vector<TypeHierarchyItem>> Reply) {
1350 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1351 Params.resolve, Params.direction, std::move(Reply));
1354void ClangdLSPServer::onSuperTypes(
1355 const ResolveTypeHierarchyItemParams &Params,
1356 Callback<std::optional<std::vector<TypeHierarchyItem>>> Reply) {
1357 Server->superTypes(Params.item, std::move(Reply));
1360void ClangdLSPServer::onSubTypes(
1361 const ResolveTypeHierarchyItemParams &Params,
1362 Callback<std::vector<TypeHierarchyItem>> Reply) {
1363 Server->subTypes(Params.item, std::move(Reply));
1366void ClangdLSPServer::onPrepareCallHierarchy(
1367 const CallHierarchyPrepareParams &Params,
1368 Callback<std::vector<CallHierarchyItem>> Reply) {
1369 Server->prepareCallHierarchy(Params.textDocument.uri.file(), Params.position,
1373void ClangdLSPServer::onCallHierarchyIncomingCalls(
1374 const CallHierarchyIncomingCallsParams &Params,
1375 Callback<std::vector<CallHierarchyIncomingCall>> Reply) {
1376 Server->incomingCalls(Params.item, std::move(Reply));
1379void ClangdLSPServer::onClangdInlayHints(
const InlayHintsParams &Params,
1380 Callback<llvm::json::Value> Reply) {
1385 auto Serialize = [Reply = std::move(Reply)](
1386 llvm::Expected<std::vector<InlayHint>>
Hints)
mutable {
1388 Reply(
Hints.takeError());
1391 llvm::json::Array Result;
1392 Result.reserve(
Hints->size());
1393 for (
auto &Hint : *
Hints) {
1394 Result.emplace_back(llvm::json::Object{
1395 {
"kind", llvm::to_string(Hint.kind)},
1396 {
"range", Hint.range},
1397 {
"position", Hint.position},
1401 ((Hint.paddingLeft ?
" " :
"") + llvm::StringRef(Hint.joinLabels()) +
1402 (Hint.paddingRight ?
" " :
""))
1406 Reply(std::move(Result));
1408 Server->inlayHints(Params.textDocument.uri.file(), Params.range,
1409 std::move(Serialize));
1412void ClangdLSPServer::onInlayHint(
const InlayHintsParams &Params,
1413 Callback<std::vector<InlayHint>> Reply) {
1414 Server->inlayHints(Params.textDocument.uri.file(), Params.range,
1418void ClangdLSPServer::applyConfiguration(
1419 const ConfigurationSettings &Settings) {
1421 llvm::StringSet<> ModifiedFiles;
1422 for (
auto &
Entry : Settings.compilationDatabaseChanges) {
1424 auto Old = CDB->getCompileCommand(File);
1426 tooling::CompileCommand(std::move(
Entry.second.workingDirectory), File,
1427 std::move(
Entry.second.compilationCommand),
1430 CDB->setCompileCommand(File, std::move(New));
1431 ModifiedFiles.insert(File);
1435 Server->reparseOpenFilesIfNeeded(
1436 [&](llvm::StringRef File) {
return ModifiedFiles.count(File) != 0; });
1439void ClangdLSPServer::maybeExportMemoryProfile() {
1440 if (!trace::enabled() || !ShouldProfile())
1443 static constexpr trace::Metric MemoryUsage(
1444 "memory_usage", trace::Metric::Value,
"component_name");
1445 trace::Span Tracer(
"ProfileBrief");
1448 record(MT,
"clangd_lsp_server", MemoryUsage);
1451void ClangdLSPServer::maybeCleanupMemory() {
1452 if (!Opts.MemoryCleanup || !ShouldCleanupMemory())
1454 Opts.MemoryCleanup();
1458void ClangdLSPServer::onChangeConfiguration(
1459 const DidChangeConfigurationParams &Params) {
1460 applyConfiguration(Params.settings);
1463void ClangdLSPServer::onReference(
1464 const ReferenceParams &Params,
1465 Callback<std::vector<ReferenceLocation>> Reply) {
1466 Server->findReferences(Params.textDocument.uri.file(), Params.position,
1467 Opts.ReferencesLimit, SupportsReferenceContainer,
1468 [Reply = std::move(Reply),
1469 IncludeDecl(Params.context.includeDeclaration)](
1470 llvm::Expected<ReferencesResult> Refs)
mutable {
1472 return Reply(Refs.takeError());
1474 std::vector<ReferenceLocation> Result;
1475 Result.reserve(Refs->References.size());
1476 for (auto &Ref : Refs->References) {
1478 Ref.Attributes & ReferencesResult::Declaration;
1479 if (IncludeDecl || !IsDecl)
1480 Result.push_back(std::move(Ref.Loc));
1482 return Reply(std::move(Result));
1486void ClangdLSPServer::onGoToType(
const TextDocumentPositionParams &Params,
1487 Callback<std::vector<Location>> Reply) {
1489 Params.textDocument.uri.file(), Params.position,
1490 [Reply = std::move(Reply)](
1491 llvm::Expected<std::vector<LocatedSymbol>> Types)
mutable {
1493 return Reply(Types.takeError());
1494 std::vector<Location> Response;
1495 for (const LocatedSymbol &Sym : *Types)
1496 Response.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration));
1497 return Reply(std::move(Response));
1501void ClangdLSPServer::onGoToImplementation(
1502 const TextDocumentPositionParams &Params,
1503 Callback<std::vector<Location>> Reply) {
1504 Server->findImplementations(
1505 Params.textDocument.uri.file(), Params.position,
1506 [Reply = std::move(Reply)](
1507 llvm::Expected<std::vector<LocatedSymbol>> Overrides)
mutable {
1509 return Reply(Overrides.takeError());
1510 std::vector<Location> Impls;
1511 for (const LocatedSymbol &Sym : *Overrides)
1512 Impls.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration));
1513 return Reply(std::move(Impls));
1517void ClangdLSPServer::onSymbolInfo(
const TextDocumentPositionParams &Params,
1518 Callback<std::vector<SymbolDetails>> Reply) {
1519 Server->symbolInfo(Params.textDocument.uri.file(), Params.position,
1523void ClangdLSPServer::onSelectionRange(
1524 const SelectionRangeParams &Params,
1525 Callback<std::vector<SelectionRange>> Reply) {
1526 Server->semanticRanges(
1527 Params.textDocument.uri.file(), Params.positions,
1528 [Reply = std::move(Reply)](
1529 llvm::Expected<std::vector<SelectionRange>> Ranges)
mutable {
1531 return Reply(Ranges.takeError());
1532 return Reply(std::move(*Ranges));
1536void ClangdLSPServer::onDocumentLink(
1537 const DocumentLinkParams &Params,
1538 Callback<std::vector<DocumentLink>> Reply) {
1544 Server->documentLinks(
1545 Params.textDocument.uri.file(),
1546 [Reply = std::move(Reply)](
1547 llvm::Expected<std::vector<DocumentLink>> Links)
mutable {
1549 return Reply(Links.takeError());
1551 return Reply(std::move(Links));
1557 for (
char &
C : llvm::reverse(S)) {
1564 S.insert(S.begin(),
'1');
1567void ClangdLSPServer::onSemanticTokens(
const SemanticTokensParams &Params,
1568 Callback<SemanticTokens> CB) {
1569 auto File = Params.textDocument.uri.file();
1570 Server->semanticHighlights(
1571 Params.textDocument.uri.file(),
1572 [
this, File(File.str()), CB(std::move(CB)),
Code(Server->getDraft(File))](
1573 llvm::Expected<std::vector<HighlightingToken>> HT)
mutable {
1575 return CB(HT.takeError());
1576 SemanticTokens Result;
1577 Result.tokens = toSemanticTokens(*HT, *Code);
1579 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1580 auto &Last = LastSemanticTokens[File];
1582 Last.tokens = Result.tokens;
1583 increment(Last.resultId);
1584 Result.resultId = Last.resultId;
1586 CB(std::move(Result));
1590void ClangdLSPServer::onSemanticTokensDelta(
1591 const SemanticTokensDeltaParams &Params,
1592 Callback<SemanticTokensOrDelta> CB) {
1593 auto File = Params.textDocument.uri.file();
1594 Server->semanticHighlights(
1595 Params.textDocument.uri.file(),
1596 [
this, PrevResultID(Params.previousResultId), File(File.str()),
1597 CB(std::move(CB)),
Code(Server->getDraft(File))](
1598 llvm::Expected<std::vector<HighlightingToken>> HT)
mutable {
1600 return CB(HT.takeError());
1601 std::vector<SemanticToken> Toks = toSemanticTokens(*HT, *Code);
1603 SemanticTokensOrDelta Result;
1605 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1606 auto &Last = LastSemanticTokens[File];
1608 if (PrevResultID == Last.resultId) {
1609 Result.edits = diffTokens(Last.tokens, Toks);
1611 vlog(
"semanticTokens/full/delta: wanted edits vs {0} but last "
1612 "result had ID {1}. Returning full token list.",
1613 PrevResultID, Last.resultId);
1614 Result.tokens = Toks;
1617 Last.tokens = std::move(Toks);
1618 increment(Last.resultId);
1619 Result.resultId = Last.resultId;
1622 CB(std::move(Result));
1626void ClangdLSPServer::onMemoryUsage(
const NoParams &,
1627 Callback<MemoryTree> Reply) {
1628 llvm::BumpPtrAllocator DetailAlloc;
1629 MemoryTree MT(&DetailAlloc);
1631 Reply(std::move(MT));
1634void ClangdLSPServer::onAST(
const ASTParams &Params,
1635 Callback<std::optional<ASTNode>> CB) {
1636 Server->getAST(Params.textDocument.uri.file(), Params.range, std::move(CB));
1641 : ShouldProfile(std::chrono::minutes(5),
1642 std::chrono::minutes(1)),
1643 ShouldCleanupMemory(std::chrono::minutes(1),
1644 std::chrono::minutes(1)),
1645 BackgroundContext(
Context::current().clone()), Transp(Transp),
1647 SupportedSymbolKinds(defaultSymbolKinds()),
1648 SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts) {
1649 if (Opts.ConfigProvider) {
1650 assert(!Opts.ContextProvider &&
1651 "Only one of ConfigProvider and ContextProvider allowed!");
1653 Opts.ConfigProvider,
this);
1656 Bind.
method(
"initialize",
this, &ClangdLSPServer::onInitialize);
1659void ClangdLSPServer::bindMethods(
LSPBinder &Bind,
1662 Bind.
notification(
"initialized",
this, &ClangdLSPServer::onInitialized);
1663 Bind.
method(
"shutdown",
this, &ClangdLSPServer::onShutdown);
1664 Bind.
method(
"sync",
this, &ClangdLSPServer::onSync);
1665 Bind.
method(
"textDocument/rangeFormatting",
this, &ClangdLSPServer::onDocumentRangeFormatting);
1666 Bind.
method(
"textDocument/onTypeFormatting",
this, &ClangdLSPServer::onDocumentOnTypeFormatting);
1667 Bind.
method(
"textDocument/formatting",
this, &ClangdLSPServer::onDocumentFormatting);
1668 Bind.
method(
"textDocument/codeAction",
this, &ClangdLSPServer::onCodeAction);
1669 Bind.
method(
"textDocument/completion",
this, &ClangdLSPServer::onCompletion);
1670 Bind.
method(
"textDocument/signatureHelp",
this, &ClangdLSPServer::onSignatureHelp);
1671 Bind.
method(
"textDocument/definition",
this, &ClangdLSPServer::onGoToDefinition);
1672 Bind.
method(
"textDocument/declaration",
this, &ClangdLSPServer::onGoToDeclaration);
1673 Bind.
method(
"textDocument/typeDefinition",
this, &ClangdLSPServer::onGoToType);
1674 Bind.
method(
"textDocument/implementation",
this, &ClangdLSPServer::onGoToImplementation);
1675 Bind.
method(
"textDocument/references",
this, &ClangdLSPServer::onReference);
1676 Bind.
method(
"textDocument/switchSourceHeader",
this, &ClangdLSPServer::onSwitchSourceHeader);
1677 Bind.
method(
"textDocument/prepareRename",
this, &ClangdLSPServer::onPrepareRename);
1678 Bind.
method(
"textDocument/rename",
this, &ClangdLSPServer::onRename);
1679 Bind.
method(
"textDocument/hover",
this, &ClangdLSPServer::onHover);
1680 Bind.
method(
"textDocument/documentSymbol",
this, &ClangdLSPServer::onDocumentSymbol);
1681 Bind.
method(
"workspace/executeCommand",
this, &ClangdLSPServer::onCommand);
1682 Bind.
method(
"textDocument/documentHighlight",
this, &ClangdLSPServer::onDocumentHighlight);
1683 Bind.
method(
"workspace/symbol",
this, &ClangdLSPServer::onWorkspaceSymbol);
1684 Bind.
method(
"textDocument/ast",
this, &ClangdLSPServer::onAST);
1685 Bind.
notification(
"textDocument/didOpen",
this, &ClangdLSPServer::onDocumentDidOpen);
1686 Bind.
notification(
"textDocument/didClose",
this, &ClangdLSPServer::onDocumentDidClose);
1687 Bind.
notification(
"textDocument/didChange",
this, &ClangdLSPServer::onDocumentDidChange);
1688 Bind.
notification(
"textDocument/didSave",
this, &ClangdLSPServer::onDocumentDidSave);
1689 Bind.
notification(
"workspace/didChangeWatchedFiles",
this, &ClangdLSPServer::onFileEvent);
1690 Bind.
notification(
"workspace/didChangeConfiguration",
this, &ClangdLSPServer::onChangeConfiguration);
1691 Bind.
method(
"textDocument/symbolInfo",
this, &ClangdLSPServer::onSymbolInfo);
1692 Bind.
method(
"textDocument/typeHierarchy",
this, &ClangdLSPServer::onTypeHierarchy);
1693 Bind.
method(
"typeHierarchy/resolve",
this, &ClangdLSPServer::onResolveTypeHierarchy);
1694 Bind.
method(
"textDocument/prepareTypeHierarchy",
this, &ClangdLSPServer::onPrepareTypeHierarchy);
1695 Bind.
method(
"typeHierarchy/supertypes",
this, &ClangdLSPServer::onSuperTypes);
1696 Bind.
method(
"typeHierarchy/subtypes",
this, &ClangdLSPServer::onSubTypes);
1697 Bind.
method(
"textDocument/prepareCallHierarchy",
this, &ClangdLSPServer::onPrepareCallHierarchy);
1698 Bind.
method(
"callHierarchy/incomingCalls",
this, &ClangdLSPServer::onCallHierarchyIncomingCalls);
1699 Bind.
method(
"textDocument/selectionRange",
this, &ClangdLSPServer::onSelectionRange);
1700 Bind.
method(
"textDocument/documentLink",
this, &ClangdLSPServer::onDocumentLink);
1701 Bind.
method(
"textDocument/semanticTokens/full",
this, &ClangdLSPServer::onSemanticTokens);
1702 Bind.
method(
"textDocument/semanticTokens/full/delta",
this, &ClangdLSPServer::onSemanticTokensDelta);
1703 Bind.
method(
"clangd/inlayHints",
this, &ClangdLSPServer::onClangdInlayHints);
1704 Bind.
method(
"textDocument/inlayHint",
this, &ClangdLSPServer::onInlayHint);
1705 Bind.
method(
"$/memoryUsage",
this, &ClangdLSPServer::onMemoryUsage);
1706 Bind.
method(
"textDocument/foldingRange",
this, &ClangdLSPServer::onFoldingRange);
1707 Bind.
command(ApplyFixCommand,
this, &ClangdLSPServer::onCommandApplyEdit);
1708 Bind.
command(ApplyTweakCommand,
this, &ClangdLSPServer::onCommandApplyTweak);
1709 Bind.
command(ApplyRenameCommand,
this, &ClangdLSPServer::onCommandApplyRename);
1717 CreateWorkDoneProgress = Bind.
outgoingMethod(
"window/workDoneProgress/create");
1722 SemanticTokensRefresh = Bind.
outgoingMethod(
"workspace/semanticTokens/refresh");
1727 IsBeingDestroyed =
true;
1735 bool CleanExit =
true;
1736 if (
auto Err = Transp.
loop(*MsgHandler)) {
1737 elog(
"Transport error: {0}", std::move(Err));
1741 return CleanExit && ShutdownRequestReceived;
1746 Server->profile(MT.
child(
"clangd_server"));
1749std::optional<ClangdServer::DiagRef>
1751 std::lock_guard<std::mutex> Lock(DiagRefMutex);
1752 auto DiagToDiagRefIter = DiagRefMap.find(
File);
1753 if (DiagToDiagRefIter == DiagRefMap.end())
1754 return std::nullopt;
1756 const auto &DiagToDiagRefMap = DiagToDiagRefIter->second;
1757 auto FixItsIter = DiagToDiagRefMap.find(toDiagKey(D));
1758 if (FixItsIter == DiagToDiagRefMap.end())
1759 return std::nullopt;
1761 return FixItsIter->second;
1770bool ClangdLSPServer::shouldRunCompletion(
1771 const CompletionParams &Params)
const {
1774 auto Code = Server->getDraft(Params.textDocument.uri.file());
1780 vlog(
"could not convert position '{0}' to offset for file '{1}'",
1781 Params.position, Params.textDocument.uri.file());
1787void ClangdLSPServer::onDiagnosticsReady(
PathRef File, llvm::StringRef Version,
1789 PublishDiagnosticsParams Notification;
1790 Notification.version = decodeVersion(Version);
1792 DiagnosticToDiagRefMap LocalDiagMap;
1795 [&](clangd::Diagnostic LSPDiag, llvm::ArrayRef<Fix> Fixes) {
1796 if (DiagOpts.EmbedFixesInDiagnostics) {
1797 std::vector<CodeAction> CodeActions;
1798 for (const auto &Fix : Fixes)
1799 CodeActions.push_back(toCodeAction(
1800 Fix, Notification.uri, Notification.version,
1801 SupportsDocumentChanges, SupportsChangeAnnotation));
1802 LSPDiag.codeActions.emplace(std::move(CodeActions));
1803 if (LSPDiag.codeActions->size() == 1)
1804 LSPDiag.codeActions->front().isPreferred = true;
1806 LocalDiagMap[toDiagKey(LSPDiag)] = {Diag.Range, Diag.Message};
1807 Notification.diagnostics.push_back(std::move(LSPDiag));
1813 std::lock_guard<std::mutex> Lock(DiagRefMutex);
1814 DiagRefMap[
File] = LocalDiagMap;
1818 PublishDiagnostics(Notification);
1821void ClangdLSPServer::onInactiveRegionsReady(
1822 PathRef File, std::vector<Range> InactiveRegions) {
1823 InactiveRegionsParams Notification;
1825 Notification.InactiveRegions = std::move(InactiveRegions);
1827 PublishInactiveRegions(Notification);
1830void ClangdLSPServer::onBackgroundIndexProgress(
1831 const BackgroundQueue::Stats &Stats) {
1832 static const char ProgressToken[] =
"backgroundIndexProgress";
1835 maybeCleanupMemory();
1837 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1839 auto NotifyProgress = [
this](
const BackgroundQueue::Stats &Stats) {
1840 if (BackgroundIndexProgressState != BackgroundIndexProgress::Live) {
1841 WorkDoneProgressBegin Begin;
1842 Begin.percentage =
true;
1843 Begin.title =
"indexing";
1844 BeginWorkDoneProgress({ProgressToken, std::move(Begin)});
1845 BackgroundIndexProgressState = BackgroundIndexProgress::Live;
1848 if (Stats.Completed < Stats.Enqueued) {
1849 assert(Stats.Enqueued > Stats.LastIdle);
1850 WorkDoneProgressReport Report;
1851 Report.percentage = 100 * (Stats.Completed - Stats.LastIdle) /
1852 (Stats.Enqueued - Stats.LastIdle);
1854 llvm::formatv(
"{0}/{1}", Stats.Completed - Stats.LastIdle,
1855 Stats.Enqueued - Stats.LastIdle);
1856 ReportWorkDoneProgress({ProgressToken, std::move(Report)});
1858 assert(Stats.Completed == Stats.Enqueued);
1859 EndWorkDoneProgress({ProgressToken, WorkDoneProgressEnd()});
1860 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
1864 switch (BackgroundIndexProgressState) {
1865 case BackgroundIndexProgress::Unsupported:
1867 case BackgroundIndexProgress::Creating:
1869 PendingBackgroundIndexProgress = Stats;
1871 case BackgroundIndexProgress::Empty: {
1872 if (BackgroundIndexSkipCreate) {
1873 NotifyProgress(Stats);
1877 PendingBackgroundIndexProgress = Stats;
1878 BackgroundIndexProgressState = BackgroundIndexProgress::Creating;
1879 WorkDoneProgressCreateParams CreateRequest;
1880 CreateRequest.token = ProgressToken;
1881 CreateWorkDoneProgress(
1883 [
this, NotifyProgress](llvm::Expected<std::nullptr_t>
E) {
1884 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1886 NotifyProgress(this->PendingBackgroundIndexProgress);
1888 elog(
"Failed to create background index progress bar: {0}",
1891 BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported;
1896 case BackgroundIndexProgress::Live:
1897 NotifyProgress(Stats);
1902void ClangdLSPServer::onFileUpdated(
PathRef File,
const TUStatus &Status) {
1903 if (!SupportFileStatus)
1913 NotifyFileStatus(Status.render(
File));
1916void ClangdLSPServer::onSemanticsMaybeChanged(
PathRef File) {
1917 if (SemanticTokensRefresh) {
1918 SemanticTokensRefresh(NoParams{}, [](llvm::Expected<std::nullptr_t>
E) {
1921 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))
std::vector< CodeCompletionResult > Results
std::vector< std::unique_ptr< HTMLNode > > Children
std::vector< HeaderHandle > Path
std::vector< FixItHint > Hints
WantDiagnostics Diagnostics
WantDiagnostics WantDiags
#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.
void profile(MemoryTree &MT) const
Profiles resource-usage.
bool run()
Run LSP server loop, communicating with the Transport provided in the constructor.
static std::function< Context(PathRef)> createConfiguredContextProvider(const config::Provider *Provider, ClangdServer::Callbacks *)
Creates a context provider that loads and installs config.
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().
Context clone() const
Clone this context object.
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.
Wrapper for vfs::FileSystem for use in multithreaded programs like clangd.
virtual void call(llvm::StringRef Method, llvm::json::Value Params, llvm::json::Value ID)=0
virtual void notify(llvm::StringRef Method, llvm::json::Value Params)=0
virtual llvm::Error loop(MessageHandler &)=0
static URI createFile(llvm::StringRef AbsolutePath)
This creates a file:// URI for AbsolutePath. The path must be absolute.
WithContext replaces Context::current() with a provided scope.
Records an event whose duration is the lifetime of the Span object.
@ Info
An information message.
llvm::StringRef toSemanticTokenModifier(HighlightingModifier Modifier)
std::bitset< SymbolKindMax+1 > SymbolKindBitset
std::function< void()> Canceler
A canceller requests cancellation of a task, when called.
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)
std::string Path
A typedef to represent a file path.
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.
llvm::StringMap< Edit > FileEdits
A mapping from absolute file path (the one used for accessing the underlying VFS) to edits.
void vlog(const char *Fmt, Ts &&... Vals)
static const char * toString(OffsetEncoding OE)
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.
static void increment(std::string &S)
std::string featureString()
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
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.
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.
NoParams InitializedParams
static std::vector< llvm::StringRef > semanticTokenTypes()
@ 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::bitset< CompletionItemKindMax+1 > CompletionItemKindBitset
@ Incremental
Documents are synced by sending the full content on open.
llvm::StringRef PathRef
A typedef to represent a ref to file path.
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++ -*-===//
bool EmbedFixesInDiagnostics
If true, Clangd uses an LSP extension to embed the fixes with the diagnostics that are sent to the cl...
bool SendDiagnosticCategory
If true, Clangd uses an LSP extension to send the diagnostic's category to the client.
bool EmitRelatedLocations
If true, Clangd uses the relatedInformation field to include other locations (in particular attached ...
bool EnableExperimentalModulesSupport
Flag to hint the experimental modules support is enabled.
bool UseDirBasedCDB
Look for compilation databases, rather than using compile commands set via LSP (extensions) only.
clangd::RenameOptions Rename
clangd::CodeCompleteOptions CodeComplete
Per-feature options.
MarkupKind SignatureHelpDocumentationFormat
std::optional< OffsetEncoding > Encoding
The offset-encoding to use, or std::nullopt to negotiate it over LSP.
std::function< Context(PathRef)> ContextProvider
If set, queried to derive a processing context for some work.
std::vector< std::string > QueryDriverGlobs
Clangd will execute compiler drivers matching one of these globs to fetch system include path.
std::optional< std::string > WorkspaceRoot
Clangd's workspace root.
bool PublishInactiveRegions
Whether to collect and publish information about inactive preprocessor regions in the document.
std::optional< std::string > ResourceDir
The resource directory is used to find internal headers, overriding defaults and -resource-dir compil...
FeatureModuleSet * FeatureModules
ModulesBuilder * ModulesManager
Manages to build module files.
bool ImplicitCancellation
Cancel certain requests if the file changes before they begin running.
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
MarkupKind DocumentationFormat
Whether to present doc comments as plain-text or markdown.
size_t Limit
Limit the number of results returned (0 means no limit).
std::optional< bool > BundleOverloads
Combine overloads into a single completion item where possible.
bool IncludeFixIts
Include completions that require small corrections, e.g.
bool EnableSnippets
When true, completion items will contain expandable code snippets in completion (e....
static CommandMangler detect()
Represents programming constructs like variables, classes, interfaces etc.
HandlerMap< void(JSON, Callback< JSON >)> CommandHandlers
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
URIForFile uri
The text document's URI.
TextDocumentIdentifier textDocument
The text document.
Position position
The position inside the text document.
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.
@ Distribution
A distribution of values with a meaningful mean and count.