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)
739 auto Code = Server->getDraft(
File);
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::onCallHierarchyOutgoingCalls(
1419 const CallHierarchyOutgoingCallsParams &Params,
1420 Callback<std::vector<CallHierarchyOutgoingCall>> Reply) {
1421 Server->outgoingCalls(Params.item, std::move(Reply));
1424void ClangdLSPServer::applyConfiguration(
1425 const ConfigurationSettings &Settings) {
1427 llvm::StringSet<> ModifiedFiles;
1428 for (
auto &[File, Command] : Settings.compilationDatabaseChanges) {
1430 tooling::CompileCommand(std::move(Command.workingDirectory), File,
1431 std::move(Command.compilationCommand),
1433 if (CDB->setCompileCommand(File, std::move(Cmd))) {
1434 ModifiedFiles.insert(File);
1438 Server->reparseOpenFilesIfNeeded(
1439 [&](llvm::StringRef File) {
return ModifiedFiles.count(File) != 0; });
1442void ClangdLSPServer::maybeExportMemoryProfile() {
1443 if (!trace::enabled() || !ShouldProfile())
1446 static constexpr trace::Metric MemoryUsage(
1447 "memory_usage", trace::Metric::Value,
"component_name");
1448 trace::Span Tracer(
"ProfileBrief");
1451 record(MT,
"clangd_lsp_server", MemoryUsage);
1454void ClangdLSPServer::maybeCleanupMemory() {
1455 if (!Opts.MemoryCleanup || !ShouldCleanupMemory())
1457 Opts.MemoryCleanup();
1461void ClangdLSPServer::onChangeConfiguration(
1462 const DidChangeConfigurationParams &Params) {
1463 applyConfiguration(Params.settings);
1466void ClangdLSPServer::onReference(
1467 const ReferenceParams &Params,
1468 Callback<std::vector<ReferenceLocation>> Reply) {
1469 Server->findReferences(Params.textDocument.uri.file(), Params.position,
1470 Opts.ReferencesLimit, SupportsReferenceContainer,
1471 [Reply = std::move(Reply),
1472 IncludeDecl(Params.context.includeDeclaration)](
1473 llvm::Expected<ReferencesResult> Refs)
mutable {
1475 return Reply(Refs.takeError());
1477 std::vector<ReferenceLocation> Result;
1478 Result.reserve(Refs->References.size());
1479 for (auto &Ref : Refs->References) {
1481 Ref.Attributes & ReferencesResult::Declaration;
1482 if (IncludeDecl || !IsDecl)
1483 Result.push_back(std::move(Ref.Loc));
1485 return Reply(std::move(Result));
1489void ClangdLSPServer::onGoToType(
const TextDocumentPositionParams &Params,
1490 Callback<std::vector<Location>> Reply) {
1492 Params.textDocument.uri.file(), Params.position,
1493 [Reply = std::move(Reply)](
1494 llvm::Expected<std::vector<LocatedSymbol>> Types)
mutable {
1496 return Reply(Types.takeError());
1497 std::vector<Location> Response;
1498 for (const LocatedSymbol &Sym : *Types)
1499 Response.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration));
1500 return Reply(std::move(Response));
1504void ClangdLSPServer::onGoToImplementation(
1505 const TextDocumentPositionParams &Params,
1506 Callback<std::vector<Location>> Reply) {
1507 Server->findImplementations(
1508 Params.textDocument.uri.file(), Params.position,
1509 [Reply = std::move(Reply)](
1510 llvm::Expected<std::vector<LocatedSymbol>> Overrides)
mutable {
1512 return Reply(Overrides.takeError());
1513 std::vector<Location> Impls;
1514 for (const LocatedSymbol &Sym : *Overrides)
1515 Impls.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration));
1516 return Reply(std::move(Impls));
1520void ClangdLSPServer::onSymbolInfo(
const TextDocumentPositionParams &Params,
1521 Callback<std::vector<SymbolDetails>> Reply) {
1522 Server->symbolInfo(Params.textDocument.uri.file(), Params.position,
1526void ClangdLSPServer::onSelectionRange(
1527 const SelectionRangeParams &Params,
1528 Callback<std::vector<SelectionRange>> Reply) {
1529 Server->semanticRanges(
1530 Params.textDocument.uri.file(), Params.positions,
1531 [Reply = std::move(Reply)](
1532 llvm::Expected<std::vector<SelectionRange>> Ranges)
mutable {
1534 return Reply(Ranges.takeError());
1535 return Reply(std::move(*Ranges));
1539void ClangdLSPServer::onDocumentLink(
1540 const DocumentLinkParams &Params,
1541 Callback<std::vector<DocumentLink>> Reply) {
1547 Server->documentLinks(
1548 Params.textDocument.uri.file(),
1549 [Reply = std::move(Reply)](
1550 llvm::Expected<std::vector<DocumentLink>> Links)
mutable {
1552 return Reply(Links.takeError());
1554 return Reply(std::move(Links));
1560 for (
char &
C : llvm::reverse(S)) {
1567 S.insert(S.begin(),
'1');
1570void ClangdLSPServer::onSemanticTokens(
const SemanticTokensParams &Params,
1571 Callback<SemanticTokens> CB) {
1572 auto File = Params.textDocument.uri.file();
1573 Server->semanticHighlights(
1574 Params.textDocument.uri.file(),
1575 [
this, File(File.str()), CB(std::move(CB)), Code(Server->getDraft(File))](
1576 llvm::Expected<std::vector<HighlightingToken>> HT)
mutable {
1578 return CB(HT.takeError());
1579 SemanticTokens Result;
1580 Result.tokens = toSemanticTokens(*HT, *Code);
1582 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1583 auto &Last = LastSemanticTokens[File];
1585 Last.tokens = Result.tokens;
1586 increment(Last.resultId);
1587 Result.resultId = Last.resultId;
1589 CB(std::move(Result));
1593void ClangdLSPServer::onSemanticTokensDelta(
1594 const SemanticTokensDeltaParams &Params,
1595 Callback<SemanticTokensOrDelta> CB) {
1596 auto File = Params.textDocument.uri.file();
1597 Server->semanticHighlights(
1598 Params.textDocument.uri.file(),
1599 [
this, PrevResultID(Params.previousResultId), File(File.str()),
1600 CB(std::move(CB)), Code(Server->getDraft(File))](
1601 llvm::Expected<std::vector<HighlightingToken>> HT)
mutable {
1603 return CB(HT.takeError());
1604 std::vector<SemanticToken> Toks = toSemanticTokens(*HT, *Code);
1606 SemanticTokensOrDelta Result;
1608 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1609 auto &Last = LastSemanticTokens[File];
1611 if (PrevResultID == Last.resultId) {
1612 Result.edits = diffTokens(Last.tokens, Toks);
1614 vlog(
"semanticTokens/full/delta: wanted edits vs {0} but last "
1615 "result had ID {1}. Returning full token list.",
1616 PrevResultID, Last.resultId);
1617 Result.tokens = Toks;
1620 Last.tokens = std::move(Toks);
1621 increment(Last.resultId);
1622 Result.resultId = Last.resultId;
1625 CB(std::move(Result));
1629void ClangdLSPServer::onMemoryUsage(
const NoParams &,
1630 Callback<MemoryTree> Reply) {
1631 llvm::BumpPtrAllocator DetailAlloc;
1632 MemoryTree MT(&DetailAlloc);
1634 Reply(std::move(MT));
1637void ClangdLSPServer::onAST(
const ASTParams &Params,
1638 Callback<std::optional<ASTNode>> CB) {
1639 Server->getAST(Params.textDocument.uri.file(), Params.range, std::move(CB));
1644 : ShouldProfile(std::chrono::minutes(5),
1645 std::chrono::minutes(1)),
1646 ShouldCleanupMemory(std::chrono::minutes(1),
1647 std::chrono::minutes(1)),
1648 BackgroundContext(
Context::current().clone()), Transp(Transp),
1650 SupportedSymbolKinds(defaultSymbolKinds()),
1651 SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts) {
1652 if (Opts.ConfigProvider) {
1653 assert(!Opts.ContextProvider &&
1654 "Only one of ConfigProvider and ContextProvider allowed!");
1656 Opts.ConfigProvider,
this);
1659 Bind.
method(
"initialize",
this, &ClangdLSPServer::onInitialize);
1662void ClangdLSPServer::bindMethods(
LSPBinder &Bind,
1665 Bind.
notification(
"initialized",
this, &ClangdLSPServer::onInitialized);
1666 Bind.
method(
"shutdown",
this, &ClangdLSPServer::onShutdown);
1667 Bind.
method(
"sync",
this, &ClangdLSPServer::onSync);
1668 Bind.
method(
"textDocument/rangeFormatting",
this, &ClangdLSPServer::onDocumentRangeFormatting);
1669 Bind.
method(
"textDocument/onTypeFormatting",
this, &ClangdLSPServer::onDocumentOnTypeFormatting);
1670 Bind.
method(
"textDocument/formatting",
this, &ClangdLSPServer::onDocumentFormatting);
1671 Bind.
method(
"textDocument/codeAction",
this, &ClangdLSPServer::onCodeAction);
1672 Bind.
method(
"textDocument/completion",
this, &ClangdLSPServer::onCompletion);
1673 Bind.
method(
"textDocument/signatureHelp",
this, &ClangdLSPServer::onSignatureHelp);
1674 Bind.
method(
"textDocument/definition",
this, &ClangdLSPServer::onGoToDefinition);
1675 Bind.
method(
"textDocument/declaration",
this, &ClangdLSPServer::onGoToDeclaration);
1676 Bind.
method(
"textDocument/typeDefinition",
this, &ClangdLSPServer::onGoToType);
1677 Bind.
method(
"textDocument/implementation",
this, &ClangdLSPServer::onGoToImplementation);
1678 Bind.
method(
"textDocument/references",
this, &ClangdLSPServer::onReference);
1679 Bind.
method(
"textDocument/switchSourceHeader",
this, &ClangdLSPServer::onSwitchSourceHeader);
1680 Bind.
method(
"textDocument/prepareRename",
this, &ClangdLSPServer::onPrepareRename);
1681 Bind.
method(
"textDocument/rename",
this, &ClangdLSPServer::onRename);
1682 Bind.
method(
"textDocument/hover",
this, &ClangdLSPServer::onHover);
1683 Bind.
method(
"textDocument/documentSymbol",
this, &ClangdLSPServer::onDocumentSymbol);
1684 Bind.
method(
"workspace/executeCommand",
this, &ClangdLSPServer::onCommand);
1685 Bind.
method(
"textDocument/documentHighlight",
this, &ClangdLSPServer::onDocumentHighlight);
1686 Bind.
method(
"workspace/symbol",
this, &ClangdLSPServer::onWorkspaceSymbol);
1687 Bind.
method(
"textDocument/ast",
this, &ClangdLSPServer::onAST);
1688 Bind.
notification(
"textDocument/didOpen",
this, &ClangdLSPServer::onDocumentDidOpen);
1689 Bind.
notification(
"textDocument/didClose",
this, &ClangdLSPServer::onDocumentDidClose);
1690 Bind.
notification(
"textDocument/didChange",
this, &ClangdLSPServer::onDocumentDidChange);
1691 Bind.
notification(
"textDocument/didSave",
this, &ClangdLSPServer::onDocumentDidSave);
1692 Bind.
notification(
"workspace/didChangeWatchedFiles",
this, &ClangdLSPServer::onFileEvent);
1693 Bind.
notification(
"workspace/didChangeConfiguration",
this, &ClangdLSPServer::onChangeConfiguration);
1694 Bind.
method(
"textDocument/symbolInfo",
this, &ClangdLSPServer::onSymbolInfo);
1695 Bind.
method(
"textDocument/typeHierarchy",
this, &ClangdLSPServer::onTypeHierarchy);
1696 Bind.
method(
"typeHierarchy/resolve",
this, &ClangdLSPServer::onResolveTypeHierarchy);
1697 Bind.
method(
"textDocument/prepareTypeHierarchy",
this, &ClangdLSPServer::onPrepareTypeHierarchy);
1698 Bind.
method(
"typeHierarchy/supertypes",
this, &ClangdLSPServer::onSuperTypes);
1699 Bind.
method(
"typeHierarchy/subtypes",
this, &ClangdLSPServer::onSubTypes);
1700 Bind.
method(
"textDocument/prepareCallHierarchy",
this, &ClangdLSPServer::onPrepareCallHierarchy);
1701 Bind.
method(
"callHierarchy/incomingCalls",
this, &ClangdLSPServer::onCallHierarchyIncomingCalls);
1703 Bind.
method(
"callHierarchy/outgoingCalls",
this, &ClangdLSPServer::onCallHierarchyOutgoingCalls);
1704 Bind.
method(
"textDocument/selectionRange",
this, &ClangdLSPServer::onSelectionRange);
1705 Bind.
method(
"textDocument/documentLink",
this, &ClangdLSPServer::onDocumentLink);
1706 Bind.
method(
"textDocument/semanticTokens/full",
this, &ClangdLSPServer::onSemanticTokens);
1707 Bind.
method(
"textDocument/semanticTokens/full/delta",
this, &ClangdLSPServer::onSemanticTokensDelta);
1708 Bind.
method(
"clangd/inlayHints",
this, &ClangdLSPServer::onClangdInlayHints);
1709 Bind.
method(
"textDocument/inlayHint",
this, &ClangdLSPServer::onInlayHint);
1710 Bind.
method(
"$/memoryUsage",
this, &ClangdLSPServer::onMemoryUsage);
1711 Bind.
method(
"textDocument/foldingRange",
this, &ClangdLSPServer::onFoldingRange);
1712 Bind.
command(ApplyFixCommand,
this, &ClangdLSPServer::onCommandApplyEdit);
1713 Bind.
command(ApplyTweakCommand,
this, &ClangdLSPServer::onCommandApplyTweak);
1714 Bind.
command(ApplyRenameCommand,
this, &ClangdLSPServer::onCommandApplyRename);
1722 CreateWorkDoneProgress = Bind.
outgoingMethod(
"window/workDoneProgress/create");
1727 SemanticTokensRefresh = Bind.
outgoingMethod(
"workspace/semanticTokens/refresh");
1732 IsBeingDestroyed =
true;
1740 bool CleanExit =
true;
1741 if (
auto Err = Transp.
loop(*MsgHandler)) {
1742 elog(
"Transport error: {0}", std::move(Err));
1746 return CleanExit && ShutdownRequestReceived;
1751 Server->profile(MT.
child(
"clangd_server"));
1754std::optional<ClangdServer::DiagRef>
1756 std::lock_guard<std::mutex> Lock(DiagRefMutex);
1757 auto DiagToDiagRefIter = DiagRefMap.find(
File);
1758 if (DiagToDiagRefIter == DiagRefMap.end())
1759 return std::nullopt;
1761 const auto &DiagToDiagRefMap = DiagToDiagRefIter->second;
1762 auto FixItsIter = DiagToDiagRefMap.find(toDiagKey(D));
1763 if (FixItsIter == DiagToDiagRefMap.end())
1764 return std::nullopt;
1766 return FixItsIter->second;
1775bool ClangdLSPServer::shouldRunCompletion(
1776 const CompletionParams &Params)
const {
1779 auto Code = Server->getDraft(Params.textDocument.uri.file());
1785 vlog(
"could not convert position '{0}' to offset for file '{1}'",
1786 Params.position, Params.textDocument.uri.file());
1792void ClangdLSPServer::onDiagnosticsReady(
PathRef File, llvm::StringRef Version,
1794 PublishDiagnosticsParams Notification;
1795 Notification.version = decodeVersion(Version);
1797 DiagnosticToDiagRefMap LocalDiagMap;
1800 [&](clangd::Diagnostic LSPDiag, llvm::ArrayRef<Fix> Fixes) {
1801 if (DiagOpts.EmbedFixesInDiagnostics) {
1802 std::vector<CodeAction> CodeActions;
1803 for (const auto &Fix : Fixes)
1804 CodeActions.push_back(toCodeAction(
1805 Fix, Notification.uri, Notification.version,
1806 SupportsDocumentChanges, SupportsChangeAnnotation));
1807 LSPDiag.codeActions.emplace(std::move(CodeActions));
1808 if (LSPDiag.codeActions->size() == 1)
1809 LSPDiag.codeActions->front().isPreferred = true;
1811 LocalDiagMap[toDiagKey(LSPDiag)] = {Diag.Range, Diag.Message};
1812 Notification.diagnostics.push_back(std::move(LSPDiag));
1818 std::lock_guard<std::mutex> Lock(DiagRefMutex);
1819 DiagRefMap[
File] = LocalDiagMap;
1823 PublishDiagnostics(Notification);
1826void ClangdLSPServer::onInactiveRegionsReady(
1827 PathRef File, std::vector<Range> InactiveRegions) {
1828 InactiveRegionsParams Notification;
1830 Notification.InactiveRegions = std::move(InactiveRegions);
1832 PublishInactiveRegions(Notification);
1835void ClangdLSPServer::onBackgroundIndexProgress(
1836 const BackgroundQueue::Stats &Stats) {
1837 static const char ProgressToken[] =
"backgroundIndexProgress";
1840 maybeCleanupMemory();
1842 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1844 auto NotifyProgress = [
this](
const BackgroundQueue::Stats &Stats) {
1845 if (BackgroundIndexProgressState != BackgroundIndexProgress::Live) {
1846 WorkDoneProgressBegin Begin;
1847 Begin.percentage =
true;
1848 Begin.title =
"indexing";
1849 BeginWorkDoneProgress({ProgressToken, std::move(Begin)});
1850 BackgroundIndexProgressState = BackgroundIndexProgress::Live;
1853 if (Stats.Completed < Stats.Enqueued) {
1854 assert(Stats.Enqueued > Stats.LastIdle);
1855 WorkDoneProgressReport Report;
1856 Report.percentage = 100 * (Stats.Completed - Stats.LastIdle) /
1857 (Stats.Enqueued - Stats.LastIdle);
1859 llvm::formatv(
"{0}/{1}", Stats.Completed - Stats.LastIdle,
1860 Stats.Enqueued - Stats.LastIdle);
1861 ReportWorkDoneProgress({ProgressToken, std::move(Report)});
1863 assert(Stats.Completed == Stats.Enqueued);
1864 EndWorkDoneProgress({ProgressToken, WorkDoneProgressEnd()});
1865 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
1869 switch (BackgroundIndexProgressState) {
1870 case BackgroundIndexProgress::Unsupported:
1872 case BackgroundIndexProgress::Creating:
1874 PendingBackgroundIndexProgress = Stats;
1876 case BackgroundIndexProgress::Empty: {
1877 if (BackgroundIndexSkipCreate) {
1878 NotifyProgress(Stats);
1882 PendingBackgroundIndexProgress = Stats;
1883 BackgroundIndexProgressState = BackgroundIndexProgress::Creating;
1884 WorkDoneProgressCreateParams CreateRequest;
1885 CreateRequest.token = ProgressToken;
1886 CreateWorkDoneProgress(
1888 [
this, NotifyProgress](llvm::Expected<std::nullptr_t>
E) {
1889 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1891 NotifyProgress(this->PendingBackgroundIndexProgress);
1893 elog(
"Failed to create background index progress bar: {0}",
1896 BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported;
1901 case BackgroundIndexProgress::Live:
1902 NotifyProgress(Stats);
1907void ClangdLSPServer::onFileUpdated(
PathRef File,
const TUStatus &Status) {
1908 if (!SupportFileStatus)
1918 NotifyFileStatus(Status.render(
File));
1921void ClangdLSPServer::onSemanticsMaybeChanged(
PathRef File) {
1922 if (SemanticTokensRefresh) {
1923 SemanticTokensRefresh(NoParams{}, [](llvm::Expected<std::nullptr_t>
E) {
1926 elog(
"Failed to refresh semantic tokens: {0}",
E.takeError());
enum clang::clangd::@1062::Bracket::Direction Dir
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
bool EnableOutgoingCalls
Call hierarchy's outgoing calls feature requires additional index serving structures which increase m...
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.