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"
63std::string encodeVersion(std::optional<int64_t> LSPVersion) {
64 return LSPVersion ? llvm::to_string(*LSPVersion) :
"";
66std::optional<int64_t> decodeVersion(llvm::StringRef Encoded) {
68 if (llvm::to_integer(Encoded, Result, 10))
71 elog(
"unexpected non-numeric version {0}", Encoded);
75const llvm::StringLiteral ApplyFixCommand =
"clangd.applyFix";
76const llvm::StringLiteral ApplyTweakCommand =
"clangd.applyTweak";
77const llvm::StringLiteral ApplyRenameCommand =
"clangd.applyRename";
82 CA.
title = R.FixMessage;
85 CA.command->title = R.FixMessage;
86 CA.command->command = std::string(ApplyRenameCommand);
89 Params.position = R.Diag.Range.start;
90 Params.newName = R.NewName;
91 CA.command->argument = Params;
101 CA.kind =
T.Kind.str();
107 CA.command.emplace();
108 CA.command->title =
T.Title;
109 CA.command->command = std::string(ApplyTweakCommand);
113 Args.selection = Selection;
114 CA.command->argument = std::move(Args);
120 const std::optional<int64_t> &Version,
121 bool SupportsDocumentChanges,
122 bool SupportChangeAnnotation) {
124 Action.
title = F.Message;
126 Action.edit.emplace();
127 if (!SupportsDocumentChanges) {
128 Action.edit->changes.emplace();
129 auto &Changes = (*Action.edit->changes)[
File.uri()];
130 for (
const auto &E : F.Edits)
131 Changes.push_back({E.range, E.newText,
""});
133 Action.edit->documentChanges.emplace();
136 for (
const auto &E : F.Edits)
137 Edit.edits.push_back(
139 SupportChangeAnnotation ? E.annotationId :
""});
140 if (SupportChangeAnnotation) {
141 for (
const auto &[AID, Annotation]: F.Annotations)
142 Action.edit->changeAnnotations[AID] = Annotation;
148void adjustSymbolKinds(llvm::MutableArrayRef<DocumentSymbol> Syms,
150 for (
auto &S : Syms) {
152 adjustSymbolKinds(S.children, Kinds);
158 for (
size_t I =
SymbolKindMin; I <= static_cast<size_t>(SymbolKind::Array);
176 size_t InvalidFileCount = 0;
177 llvm::StringRef LastInvalidFile;
178 for (
const auto &It : FE) {
179 if (
auto Draft = Server.getDraft(It.first())) {
183 if (!It.second.canApplyTo(*Draft)) {
185 LastInvalidFile = It.first();
189 if (!InvalidFileCount)
190 return llvm::Error::success();
191 if (InvalidFileCount == 1)
192 return error(
"File must be saved first: {0}", LastInvalidFile);
193 return error(
"Files must be saved first: {0} (and {1} others)",
194 LastInvalidFile, InvalidFileCount - 1);
216 auto Handler = Server.Handlers.NotificationHandlers.find(
Method);
217 if (Handler != Server.Handlers.NotificationHandlers.end()) {
218 Handler->second(std::move(Params));
219 Server.maybeExportMemoryProfile();
220 Server.maybeCleanupMemory();
221 }
else if (!Server.Server) {
222 elog(
"Notification {0} before initialization",
Method);
223 }
else if (
Method ==
"$/cancelRequest") {
224 onCancel(std::move(Params));
226 log(
"unhandled notification {0}",
Method);
232 llvm::json::Value ID)
override {
235 WithContext WithCancel(cancelableRequestContext(ID));
238 ReplyOnce Reply(ID,
Method, &Server, Tracer.
Args);
240 auto Handler = Server.Handlers.MethodHandlers.find(
Method);
241 if (Handler != Server.Handlers.MethodHandlers.end()) {
242 Handler->second(std::move(Params), std::move(Reply));
243 }
else if (!Server.Server) {
244 elog(
"Call {0} before initialization.",
Method);
245 Reply(llvm::make_error<LSPError>(
"server not initialized",
248 Reply(llvm::make_error<LSPError>(
"method not found",
255 llvm::Expected<llvm::json::Value> Result)
override {
259 if (
auto IntID = ID.getAsInteger()) {
260 std::lock_guard<std::mutex> Mutex(CallMutex);
262 for (
size_t Index = 0; Index < ReplyCallbacks.size(); ++Index) {
263 if (ReplyCallbacks[Index].first == *IntID) {
264 ReplyHandler = std::move(ReplyCallbacks[Index].second);
265 ReplyCallbacks.erase(ReplyCallbacks.begin() +
274 ReplyHandler = [&ID](llvm::Expected<llvm::json::Value> Result) {
275 elog(
"received a reply with ID {0}, but there was no such call", ID);
277 llvm::consumeError(Result.takeError());
283 log(
"<-- reply({0})", ID);
284 ReplyHandler(std::move(Result));
286 auto Err = Result.takeError();
287 log(
"<-- reply({0}) error: {1}", ID, Err);
288 ReplyHandler(std::move(Err));
297 std::optional<std::pair<int, Callback<llvm::json::Value>>> OldestCB;
300 std::lock_guard<std::mutex> Mutex(CallMutex);
302 ReplyCallbacks.emplace_back(ID, std::move(Reply));
307 if (ReplyCallbacks.size() > MaxReplayCallbacks) {
308 elog(
"more than {0} outstanding LSP calls, forgetting about {1}",
309 MaxReplayCallbacks, ReplyCallbacks.front().first);
310 OldestCB = std::move(ReplyCallbacks.front());
311 ReplyCallbacks.pop_front();
316 error(
"failed to receive a client reply for request ({0})",
328 std::atomic<bool> Replied = {
false};
329 std::chrono::steady_clock::time_point Start;
330 llvm::json::Value ID;
333 llvm::json::Object *TraceArgs;
336 ReplyOnce(
const llvm::json::Value &ID, llvm::StringRef
Method,
338 : Start(std::chrono::steady_clock::now()), ID(ID),
Method(
Method),
339 Server(Server), TraceArgs(TraceArgs) {
342 ReplyOnce(ReplyOnce &&Other)
343 : Replied(Other.Replied.load()), Start(Other.Start),
345 Server(Other.Server), TraceArgs(Other.TraceArgs) {
346 Other.Server =
nullptr;
348 ReplyOnce &operator=(ReplyOnce &&) =
delete;
349 ReplyOnce(
const ReplyOnce &) =
delete;
350 ReplyOnce &operator=(
const ReplyOnce &) =
delete;
359 if (Server && !Server->IsBeingDestroyed && !Replied) {
360 elog(
"No reply to message {0}({1})",
Method, ID);
361 assert(
false &&
"must reply to all calls!");
362 (*this)(llvm::make_error<LSPError>(
"server failed to reply",
367 void operator()(llvm::Expected<llvm::json::Value> Reply) {
368 assert(Server &&
"moved-from!");
369 if (Replied.exchange(
true)) {
370 elog(
"Replied twice to message {0}({1})", Method, ID);
371 assert(
false &&
"must reply to each call only once!");
374 auto Duration = std::chrono::steady_clock::now() - Start;
376 log(
"--> reply:{0}({1}) {2:ms}", Method, ID, Duration);
378 (*TraceArgs)[
"Reply"] = *Reply;
379 std::lock_guard<std::mutex> Lock(Server->TranspWriter);
380 Server->Transp.reply(std::move(ID), std::move(Reply));
382 llvm::Error Err = Reply.takeError();
383 log(
"--> reply:{0}({1}) {2:ms}, error: {3}", Method, ID, Duration, Err);
385 (*TraceArgs)[
"Error"] = llvm::to_string(Err);
386 std::lock_guard<std::mutex> Lock(Server->TranspWriter);
387 Server->Transp.reply(std::move(ID), std::move(Err));
395 mutable std::mutex RequestCancelersMutex;
396 llvm::StringMap<std::pair<
Canceler,
unsigned>> RequestCancelers;
397 unsigned NextRequestCookie = 0;
398 void onCancel(
const llvm::json::Value &Params) {
399 const llvm::json::Value *ID =
nullptr;
400 if (
auto *O = Params.getAsObject())
403 elog(
"Bad cancellation request: {0}", Params);
406 auto StrID = llvm::to_string(*ID);
407 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
408 auto It = RequestCancelers.find(StrID);
409 if (It != RequestCancelers.end())
413 Context handlerContext()
const {
423 Context cancelableRequestContext(
const llvm::json::Value &ID) {
426 auto StrID = llvm::to_string(ID);
427 auto Cookie = NextRequestCookie++;
429 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
430 RequestCancelers[StrID] = {std::move(Task.second), Cookie};
435 return Task.first.derive(llvm::scope_exit([
this, StrID, Cookie] {
436 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
437 auto It = RequestCancelers.find(StrID);
438 if (It != RequestCancelers.end() && It->second.second == Cookie)
439 RequestCancelers.erase(It);
449 static constexpr int MaxReplayCallbacks = 100;
450 mutable std::mutex CallMutex;
452 std::deque<std::pair< int,
460void ClangdLSPServer::callMethod(StringRef
Method, llvm::json::Value Params,
462 auto ID = MsgHandler->bindReply(std::move(CB));
464 std::lock_guard<std::mutex> Lock(TranspWriter);
465 Transp.call(
Method, std::move(Params), ID);
468void ClangdLSPServer::notify(llvm::StringRef
Method, llvm::json::Value Params) {
470 maybeCleanupMemory();
471 std::lock_guard<std::mutex> Lock(TranspWriter);
472 Transp.notify(
Method, std::move(Params));
476 std::vector<llvm::StringRef> Types;
484 std::vector<llvm::StringRef> Modifiers;
492void ClangdLSPServer::onInitialize(
const InitializeParams &Params,
495 if (Params.capabilities.PositionEncodings && !Opts.
Encoding) {
497 for (
OffsetEncoding Supported : *Params.capabilities.PositionEncodings)
504 if (Params.capabilities.TheiaSemanticHighlighting &&
505 !Params.capabilities.SemanticTokens) {
506 elog(
"Client requested legacy semanticHighlights notification, which is "
507 "no longer supported. Migrate to standard semanticTokens request");
510 if (Params.rootUri && *Params.rootUri)
511 Opts.WorkspaceRoot = std::string(Params.rootUri->file());
512 else if (Params.rootPath && !Params.rootPath->empty())
513 Opts.WorkspaceRoot = *Params.rootPath;
515 return Reply(llvm::make_error<LSPError>(
"server already initialized",
518 Opts.CodeComplete.EnableSnippets = Params.capabilities.CompletionSnippets;
519 Opts.CodeComplete.IncludeFixIts = Params.capabilities.CompletionFixes;
520 Opts.CodeComplete.EnableInsertReplace = Params.capabilities.InsertReplace;
521 Opts.CodeComplete.DocumentationFormat =
522 Params.capabilities.CompletionDocumentationFormat;
523 Opts.SignatureHelpDocumentationFormat =
524 Params.capabilities.SignatureHelpDocumentationFormat;
525 DiagOpts.EmbedFixesInDiagnostics = Params.capabilities.DiagnosticFixes;
526 DiagOpts.SendDiagnosticCategory = Params.capabilities.DiagnosticCategory;
527 DiagOpts.EmitRelatedLocations =
528 Params.capabilities.DiagnosticRelatedInformation;
529 if (Params.capabilities.WorkspaceSymbolKinds)
530 SupportedSymbolKinds |= *Params.capabilities.WorkspaceSymbolKinds;
531 if (Params.capabilities.CompletionItemKinds)
532 SupportedCompletionItemKinds |= *Params.capabilities.CompletionItemKinds;
533 SupportsCompletionLabelDetails = Params.capabilities.CompletionLabelDetail;
534 SupportsCodeAction = Params.capabilities.CodeActionStructure;
535 SupportsHierarchicalDocumentSymbol =
536 Params.capabilities.HierarchicalDocumentSymbol;
537 SupportsReferenceContainer = Params.capabilities.ReferenceContainer;
538 SupportFileStatus = Params.initializationOptions.FileStatus;
539 SupportsDocumentChanges = Params.capabilities.DocumentChanges;
540 SupportsChangeAnnotation = Params.capabilities.ChangeAnnotation;
541 HoverContentFormat = Params.capabilities.HoverContentFormat;
542 Opts.LineFoldingOnly = Params.capabilities.LineFoldingOnly;
543 SupportsOffsetsInSignatureHelp = Params.capabilities.OffsetsInSignatureHelp;
544 if (Params.capabilities.WorkDoneProgress)
545 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
546 BackgroundIndexSkipCreate = Params.capabilities.ImplicitProgressCreation;
547 Opts.ImplicitCancellation = !Params.capabilities.CancelsStaleRequests;
548 Opts.PublishInactiveRegions = Params.capabilities.InactiveRegions;
550 if (Opts.UseDirBasedCDB) {
551 DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS);
552 if (
const auto &Dir = Params.initializationOptions.compilationDatabasePath)
553 CDBOpts.CompileCommandsDir = Dir;
554 CDBOpts.ContextProvider = Opts.ContextProvider;
555 if (Opts.StrongWorkspaceMode)
556 CDBOpts.applyFallbackWorkingDirectory(Opts.WorkspaceRoot);
558 std::make_unique<DirectoryBasedGlobalCompilationDatabase>(CDBOpts);
561 Mangler.SystemIncludeExtractor =
563 if (Opts.ResourceDir)
564 Mangler.ResourceDir = *Opts.ResourceDir;
565 CDB.emplace(BaseCDB.get(), Params.initializationOptions.fallbackFlags,
568 if (Opts.EnableExperimentalModulesSupport) {
569 ModulesManager.emplace(*CDB);
570 Opts.ModulesManager = &*ModulesManager;
577 WithContext MainContext(BackgroundContext.clone());
578 std::optional<WithContextValue> WithOffsetEncoding;
581 Server.emplace(*CDB, TFS, Opts,
582 static_cast<ClangdServer::Callbacks *
>(
this));
585 llvm::json::Object ServerCaps{
592 {
"documentFormattingProvider",
true},
593 {
"documentRangeFormattingProvider",
595 {
"rangesSupport",
true},
597 {
"documentOnTypeFormattingProvider",
599 {
"firstTriggerCharacter",
"\n"},
600 {
"moreTriggerCharacter", {}},
602 {
"completionProvider",
609 {
"resolveProvider",
false},
611 {
"triggerCharacters", {
".",
"<",
">",
":",
"\"",
"/",
"*"}},
613 {
"semanticTokensProvider",
615 {
"full", llvm::json::Object{{
"delta",
true}}},
621 {
"signatureHelpProvider",
623 {
"triggerCharacters", {
"(",
")",
"{",
"}",
"<",
">",
","}},
625 {
"declarationProvider",
true},
626 {
"definitionProvider",
true},
627 {
"implementationProvider",
true},
628 {
"typeDefinitionProvider",
true},
629 {
"documentHighlightProvider",
true},
630 {
"documentLinkProvider",
632 {
"resolveProvider",
false},
634 {
"hoverProvider",
true},
635 {
"selectionRangeProvider",
true},
636 {
"documentSymbolProvider",
true},
637 {
"workspaceSymbolProvider",
true},
638 {
"referencesProvider",
true},
639 {
"astProvider",
true},
640 {
"typeHierarchyProvider",
true},
644 {
"standardTypeHierarchyProvider",
true},
645 {
"memoryUsageProvider",
true},
646 {
"compilationDatabase",
647 llvm::json::Object{{
"automaticReload",
true}}},
648 {
"inactiveRegionsProvider",
true},
649 {
"callHierarchyProvider",
true},
650 {
"clangdInlayHintsProvider",
true},
651 {
"inlayHintProvider",
true},
652 {
"foldingRangeProvider",
true},
656 LSPBinder Binder(Handlers, *
this);
657 bindMethods(Binder, Params.capabilities);
658 if (Opts.FeatureModules)
659 for (
auto &Mod : *Opts.FeatureModules)
660 Mod.initializeLSP(Binder, Params.rawCapabilities, ServerCaps);
665 ServerCaps[
"renameProvider"] =
666 Params.capabilities.RenamePrepareSupport
667 ? llvm::json::Object{{
"prepareProvider",
true}}
668 : llvm::json::Value(
true);
673 ServerCaps[
"codeActionProvider"] =
674 Params.capabilities.CodeActionStructure
675 ? llvm::json::Object{{
"codeActionKinds",
679 : llvm::json::Value(
true);
681 std::vector<llvm::StringRef>
Commands;
682 for (llvm::StringRef Command : Handlers.CommandHandlers.keys())
685 ServerCaps[
"executeCommandProvider"] =
686 llvm::json::Object{{
"commands",
Commands}};
689 ServerCaps[
"positionEncoding"] = *Opts.Encoding;
691 llvm::json::Object Result{
697 {
"capabilities", std::move(ServerCaps)}}};
702 Result[
"offsetEncoding"] = *Opts.Encoding;
703 Reply(std::move(Result));
707 applyConfiguration(Params.initializationOptions.ConfigSettings);
712void ClangdLSPServer::onShutdown(
const NoParams &,
715 ShutdownRequestReceived =
true;
722 if (Server->blockUntilIdleForTest(60))
725 Reply(
error(
"Not idle after a minute"));
728void ClangdLSPServer::onDocumentDidOpen(
732 const std::string &Contents = Params.textDocument.text;
734 Server->addDocument(
File, Contents,
735 encodeVersion(Params.textDocument.version),
739void ClangdLSPServer::onDocumentDidChange(
742 if (Params.wantDiagnostics)
747 auto Code = Server->getDraft(
File);
749 log(
"Trying to incrementally change non-added document: {0}",
File);
752 std::string NewCode(*Code);
753 for (
const auto &Change : Params.contentChanges) {
758 Server->removeDocument(
File);
759 elog(
"Failed to update {0}: {1}",
File, std::move(Err));
763 Server->addDocument(
File, NewCode, encodeVersion(Params.textDocument.version),
764 WantDiags, Params.forceRebuild);
767void ClangdLSPServer::onDocumentDidSave(
769 Server->reparseOpenFilesIfNeeded([](llvm::StringRef) {
return true; });
777 Server->onFileEvent(Params);
786 auto It = Handlers.CommandHandlers.find(Params.command);
787 if (It == Handlers.CommandHandlers.end()) {
788 return Reply(llvm::make_error<LSPError>(
789 llvm::formatv(
"Unsupported command \"{0}\".", Params.command).str(),
792 It->second(Params.argument, std::move(Reply));
795void ClangdLSPServer::onCommandApplyEdit(
const WorkspaceEdit &WE,
805 applyEdit(WE,
"Fix applied.", std::move(Reply));
808void ClangdLSPServer::onCommandApplyTweak(
const TweakArgs &Args,
810 auto Action = [
this, Reply = std::move(Reply), &ServerRef = *Server](
811 llvm::Expected<Tweak::Effect> R)
mutable {
813 return Reply(R.takeError());
815 assert(R->ShowMessage || (!R->ApplyEdits.empty() &&
"tweak has no effect"));
817 if (R->ShowMessage) {
818 ShowMessageParams Msg;
819 Msg.message = *R->ShowMessage;
824 if (R->ApplyEdits.empty())
825 return Reply(
"Tweak applied.");
827 if (
auto Err = validateEdits(ServerRef, R->ApplyEdits))
828 return Reply(std::move(Err));
832 WE.changes.emplace();
833 for (
const auto &It : R->ApplyEdits) {
835 It.second.asTextEdits();
838 return applyEdit(std::move(WE),
"Tweak applied.", std::move(Reply));
840 Server->applyTweak(Args.file.file(), Args.selection, Args.tweakID,
844void ClangdLSPServer::onCommandApplyRename(
const RenameParams &R,
846 onRename(R, [
this, Reply = std::move(Reply)](
847 llvm::Expected<WorkspaceEdit> Edit)
mutable {
849 Reply(Edit.takeError());
850 applyEdit(std::move(*Edit),
"Rename applied.", std::move(Reply));
854void ClangdLSPServer::applyEdit(
WorkspaceEdit WE, llvm::json::Value Success,
856 ApplyWorkspaceEditParams Edit;
857 Edit.edit = std::move(WE);
859 Edit, [Reply = std::move(Reply), SuccessMessage = std::move(Success)](
860 llvm::Expected<ApplyWorkspaceEditResponse> Response)
mutable {
862 return Reply(Response.takeError());
863 if (!Response->applied) {
864 std::string Reason = Response->failureReason
865 ? *Response->failureReason
867 return Reply(
error(
"edits were not applied: {0}", Reason));
869 return Reply(SuccessMessage);
873void ClangdLSPServer::onWorkspaceSymbol(
875 Callback<std::vector<SymbolInformation>> Reply) {
876 Server->workspaceSymbols(
877 Params.query, Params.limit.value_or(Opts.CodeComplete.Limit),
878 [Reply = std::move(Reply),
879 this](llvm::Expected<std::vector<SymbolInformation>> Items)
mutable {
881 return Reply(Items.takeError());
882 for (auto &Sym : *Items)
883 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds);
885 Reply(std::move(*Items));
891 Server->prepareRename(
892 Params.textDocument.uri.file(), Params.position, std::nullopt,
894 [Reply = std::move(Reply)](llvm::Expected<RenameResult> Result)
mutable {
896 return Reply(Result.takeError());
897 PrepareRenameResult PrepareResult;
898 PrepareResult.range = Result->Target;
899 PrepareResult.placeholder = Result->Placeholder;
900 return Reply(std::move(PrepareResult));
904void ClangdLSPServer::onRename(
const RenameParams &Params,
906 Path File = std::string(Params.textDocument.uri.file());
907 if (!Server->getDraft(
File))
908 return Reply(llvm::make_error<LSPError>(
910 Server->rename(
File, Params.position, Params.newName, Opts.Rename,
911 [
File, Params, Reply = std::move(Reply), &ServerRef = *Server](
912 llvm::Expected<RenameResult> R)
mutable {
914 return Reply(R.takeError());
915 if (auto Err = validateEdits(ServerRef, R->GlobalChanges))
916 return Reply(std::move(Err));
917 WorkspaceEdit Result;
920 Result.changes.emplace();
921 for (const auto &Rep : R->GlobalChanges) {
923 .changes)[URI::createFile(Rep.first()).toString()] =
924 Rep.second.asTextEdits();
930void ClangdLSPServer::onDocumentDidClose(
931 const DidCloseTextDocumentParams &Params) {
933 Server->removeDocument(File);
936 std::lock_guard<std::mutex> Lock(DiagRefMutex);
937 DiagRefMap.erase(File);
940 std::lock_guard<std::mutex> HLock(SemanticTokensMutex);
941 LastSemanticTokens.erase(File);
948 PublishDiagnosticsParams Notification;
949 Notification.uri = URIForFile::canonicalize(File, File);
950 PublishDiagnostics(Notification);
953void ClangdLSPServer::onDocumentOnTypeFormatting(
954 const DocumentOnTypeFormattingParams &Params,
955 Callback<std::vector<TextEdit>> Reply) {
956 auto File = Params.textDocument.uri.file();
957 Server->formatOnType(File, Params.position, Params.ch, std::move(Reply));
960void ClangdLSPServer::onDocumentRangeFormatting(
961 const DocumentRangeFormattingParams &Params,
962 Callback<std::vector<TextEdit>> Reply) {
963 onDocumentRangesFormatting(
964 DocumentRangesFormattingParams{Params.textDocument, {Params.range}},
968void ClangdLSPServer::onDocumentRangesFormatting(
969 const DocumentRangesFormattingParams &Params,
970 Callback<std::vector<TextEdit>> Reply) {
971 auto File = Params.textDocument.uri.file();
972 auto Code = Server->getDraft(File);
973 Server->formatFile(File, Params.ranges,
974 [Code = std::move(Code), Reply = std::move(Reply)](
975 llvm::Expected<tooling::Replacements> Result)
mutable {
977 Reply(replacementsToEdits(*Code, Result.get()));
979 Reply(Result.takeError());
983void ClangdLSPServer::onDocumentFormatting(
984 const DocumentFormattingParams &Params,
985 Callback<std::vector<TextEdit>> Reply) {
986 auto File = Params.textDocument.uri.file();
987 auto Code = Server->getDraft(File);
988 Server->formatFile(File,
990 [Code = std::move(Code), Reply = std::move(Reply)](
991 llvm::Expected<tooling::Replacements> Result)
mutable {
995 Reply(Result.takeError());
1001static std::vector<SymbolInformation>
1004 std::vector<SymbolInformation> Results;
1005 struct SymbolHierarchyFlattener {
1007 std::vector<SymbolInformation> &Results;
1010 std::optional<llvm::StringRef> ParentName) {
1012 SI.
containerName = std::string(ParentName ?
"" : *ParentName);
1018 Results.push_back(std::move(SI));
1019 std::string FullName =
1020 !ParentName ? S.
name : (ParentName->str() +
"::" + S.
name);
1022 append(C, FullName);
1025 SymbolHierarchyFlattener Flattener{FileURI, Results};
1027 Flattener.append(S,
"");
1031void ClangdLSPServer::onDocumentSymbol(
const DocumentSymbolParams &Params,
1032 Callback<llvm::json::Value> Reply) {
1033 URIForFile FileURI = Params.textDocument.uri;
1034 Server->documentSymbols(
1035 Params.textDocument.uri.file(),
1036 [
this, FileURI, Reply = std::move(Reply)](
1037 llvm::Expected<std::vector<DocumentSymbol>> Items)
mutable {
1039 return Reply(Items.takeError());
1040 adjustSymbolKinds(*Items, SupportedSymbolKinds);
1041 if (SupportsHierarchicalDocumentSymbol)
1042 return Reply(std::move(*Items));
1043 return Reply(flattenSymbolHierarchy(*Items, FileURI));
1047void ClangdLSPServer::onFoldingRange(
1048 const FoldingRangeParams &Params,
1049 Callback<std::vector<FoldingRange>> Reply) {
1050 Server->foldingRanges(Params.textDocument.uri.file(), std::move(Reply));
1056 return std::nullopt;
1059 }
else if (Action.
edit) {
1060 Cmd.command = std::string(ApplyFixCommand);
1061 Cmd.argument = *Action.
edit;
1063 return std::nullopt;
1065 Cmd.title = Action.
title;
1067 Cmd.title =
"Apply fix: " + Cmd.title;
1071void ClangdLSPServer::onCodeAction(
const CodeActionParams &Params,
1072 Callback<llvm::json::Value> Reply) {
1073 URIForFile File = Params.textDocument.uri;
1074 std::map<ClangdServer::DiagRef, clangd::Diagnostic> ToLSPDiags;
1075 ClangdServer::CodeActionInputs Inputs;
1077 for (
const auto& LSPDiag : Params.context.diagnostics) {
1078 if (
auto DiagRef = getDiagRef(File.file(), LSPDiag)) {
1079 ToLSPDiags[*DiagRef] = LSPDiag;
1080 Inputs.Diagnostics.push_back(*DiagRef);
1083 Inputs.File =
File.file();
1084 Inputs.Selection = Params.range;
1085 Inputs.RequestedActionKinds = Params.context.only;
1086 Inputs.TweakFilter = [
this](
const Tweak &
T) {
1087 return Opts.TweakFilter(T);
1090 Reply = std::move(Reply),
1091 ToLSPDiags = std::move(ToLSPDiags),
File,
1092 Selection = Params.range](
1093 llvm::Expected<ClangdServer::CodeActionResult> Fixits)
mutable {
1095 return Reply(Fixits.takeError());
1096 std::vector<CodeAction> CAs;
1097 auto Version = decodeVersion(Fixits->Version);
1098 for (
const auto &QF : Fixits->QuickFixes) {
1099 CAs.push_back(toCodeAction(QF.F, File, Version, SupportsDocumentChanges,
1100 SupportsChangeAnnotation));
1101 if (
auto It = ToLSPDiags.find(QF.Diag);
1102 It != ToLSPDiags.end()) {
1103 CAs.back().diagnostics = {It->second};
1107 for (
const auto &R : Fixits->Renames)
1108 CAs.push_back(toCodeAction(R, File));
1110 for (
const auto &TR : Fixits->TweakRefs)
1111 CAs.push_back(toCodeAction(TR, File, Selection));
1115 CodeAction *OnlyFix =
nullptr;
1116 for (
auto &Action : CAs) {
1117 if (Action.kind && *Action.kind == CodeAction::QUICKFIX_KIND) {
1126 OnlyFix->isPreferred =
true;
1127 if (ToLSPDiags.size() == 1 &&
1128 ToLSPDiags.begin()->second.range == Selection)
1129 OnlyFix->diagnostics = {ToLSPDiags.begin()->second};
1132 if (SupportsCodeAction)
1133 return Reply(llvm::json::Array(CAs));
1135 for (
const auto &Action : CAs) {
1137 Commands.push_back(std::move(*Command));
1139 return Reply(llvm::json::Array(
Commands));
1141 Server->codeAction(Inputs, std::move(CB));
1144void ClangdLSPServer::onCompletion(
const CompletionParams &Params,
1145 Callback<CompletionList> Reply) {
1146 if (!shouldRunCompletion(Params)) {
1149 vlog(
"ignored auto-triggered completion, preceding char did not match");
1150 return Reply(CompletionList());
1152 auto Opts = this->Opts.CodeComplete;
1153 if (Params.limit && *Params.limit >= 0)
1154 Opts.Limit = *Params.limit;
1155 Server->codeComplete(Params.textDocument.uri.file(), Params.position, Opts,
1156 [Reply = std::move(Reply), Opts,
1157 this](llvm::Expected<CodeCompleteResult> List)
mutable {
1159 return Reply(List.takeError());
1160 CompletionList LSPList;
1161 LSPList.isIncomplete = List->HasMore;
1162 for (const auto &R : List->Completions) {
1163 CompletionItem C = R.render(Opts);
1164 C.kind = adjustKindToCapability(
1165 C.kind, SupportedCompletionItemKinds);
1166 if (!SupportsCompletionLabelDetails)
1167 removeCompletionLabelDetails(C);
1168 LSPList.items.push_back(std::move(C));
1170 return Reply(std::move(LSPList));
1174void ClangdLSPServer::onSignatureHelp(
const TextDocumentPositionParams &Params,
1175 Callback<SignatureHelp> Reply) {
1176 Server->signatureHelp(Params.textDocument.uri.file(), Params.position,
1177 Opts.SignatureHelpDocumentationFormat,
1178 [Reply = std::move(Reply),
this](
1179 llvm::Expected<SignatureHelp> Signature)
mutable {
1181 return Reply(Signature.takeError());
1182 if (SupportsOffsetsInSignatureHelp)
1183 return Reply(std::move(*Signature));
1186 for (auto &SigInfo : Signature->signatures) {
1187 for (auto &Param : SigInfo.parameters)
1188 Param.labelOffsets.reset();
1190 return Reply(std::move(*Signature));
1214void ClangdLSPServer::onGoToDefinition(
const TextDocumentPositionParams &Params,
1215 Callback<std::vector<Location>> Reply) {
1216 Server->locateSymbolAt(
1217 Params.textDocument.uri.file(), Params.position,
1218 [Params, Reply = std::move(Reply)](
1219 llvm::Expected<std::vector<LocatedSymbol>> Symbols)
mutable {
1221 return Reply(Symbols.takeError());
1222 std::vector<Location> Defs;
1223 for (auto &S : *Symbols) {
1224 if (Location *Toggle = getToggle(Params, S))
1225 return Reply(std::vector<Location>{std::move(*Toggle)});
1226 Defs.push_back(S.Definition.value_or(S.PreferredDeclaration));
1228 Reply(std::move(Defs));
1232void ClangdLSPServer::onGoToDeclaration(
1233 const TextDocumentPositionParams &Params,
1234 Callback<std::vector<Location>> Reply) {
1235 Server->locateSymbolAt(
1236 Params.textDocument.uri.file(), Params.position,
1237 [Params, Reply = std::move(Reply)](
1238 llvm::Expected<std::vector<LocatedSymbol>> Symbols)
mutable {
1240 return Reply(Symbols.takeError());
1241 std::vector<Location> Decls;
1242 for (auto &S : *Symbols) {
1243 if (Location *Toggle = getToggle(Params, S))
1244 return Reply(std::vector<Location>{std::move(*Toggle)});
1245 Decls.push_back(std::move(S.PreferredDeclaration));
1247 Reply(std::move(Decls));
1251void ClangdLSPServer::onSwitchSourceHeader(
1252 const TextDocumentIdentifier &Params,
1253 Callback<std::optional<URIForFile>> Reply) {
1254 Server->switchSourceHeader(
1256 [Reply = std::move(Reply),
1257 Params](llvm::Expected<std::optional<clangd::Path>> Path)
mutable {
1259 return Reply(Path.takeError());
1261 return Reply(URIForFile::canonicalize(**Path, Params.uri.file()));
1262 return Reply(std::nullopt);
1266void ClangdLSPServer::onDocumentHighlight(
1267 const TextDocumentPositionParams &Params,
1268 Callback<std::vector<DocumentHighlight>> Reply) {
1269 Server->findDocumentHighlights(Params.textDocument.uri.file(),
1270 Params.position, std::move(Reply));
1273void ClangdLSPServer::onHover(
const TextDocumentPositionParams &Params,
1274 Callback<std::optional<Hover>> Reply) {
1275 Server->findHover(Params.textDocument.uri.file(), Params.position,
1276 [Reply = std::move(Reply),
1277 this](llvm::Expected<std::optional<HoverInfo>> H)
mutable {
1279 return Reply(H.takeError());
1281 return Reply(std::nullopt);
1284 R.contents.kind = HoverContentFormat;
1285 R.range = (*H)->SymRange;
1286 switch (HoverContentFormat) {
1287 case MarkupKind::Markdown:
1288 case MarkupKind::PlainText:
1289 R.contents.value = (*H)->present(HoverContentFormat);
1290 return Reply(std::move(R));
1292 llvm_unreachable(
"unhandled MarkupKind");
1299 llvm::json::Object Result{{
1300 {
"name", std::move(THI.
name)},
1301 {
"kind",
static_cast<int>(THI.
kind)},
1302 {
"uri", std::move(THI.
uri)},
1303 {
"range", THI.
range},
1305 {
"data", std::move(THI.
data)},
1310 Result[
"detail"] = std::move(*THI.
detail);
1314 for (
auto &Parent : *THI.
parents)
1316 Result[
"parents"] = std::move(
Parents);
1323 Result[
"children"] = std::move(
Children);
1328void ClangdLSPServer::onTypeHierarchy(
const TypeHierarchyPrepareParams &Params,
1329 Callback<llvm::json::Value> Reply) {
1331 [Reply = std::move(Reply)](
1332 llvm::Expected<std::vector<TypeHierarchyItem>> Resp)
mutable {
1334 Reply(Resp.takeError());
1337 if (Resp->empty()) {
1343 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1344 Params.resolve, Params.direction, std::move(Serialize));
1347void ClangdLSPServer::onResolveTypeHierarchy(
1348 const ResolveTypeHierarchyItemParams &Params,
1349 Callback<llvm::json::Value> Reply) {
1351 [Reply = std::move(Reply)](
1352 llvm::Expected<std::optional<TypeHierarchyItem>> Resp)
mutable {
1354 Reply(Resp.takeError());
1358 Reply(std::move(*Resp));
1363 Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction,
1364 std::move(Serialize));
1367void ClangdLSPServer::onPrepareTypeHierarchy(
1368 const TypeHierarchyPrepareParams &Params,
1369 Callback<std::vector<TypeHierarchyItem>> Reply) {
1370 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1371 Params.resolve, Params.direction, std::move(Reply));
1374void ClangdLSPServer::onSuperTypes(
1375 const ResolveTypeHierarchyItemParams &Params,
1376 Callback<std::optional<std::vector<TypeHierarchyItem>>> Reply) {
1377 Server->superTypes(Params.item, std::move(Reply));
1380void ClangdLSPServer::onSubTypes(
1381 const ResolveTypeHierarchyItemParams &Params,
1382 Callback<std::vector<TypeHierarchyItem>> Reply) {
1383 Server->subTypes(Params.item, std::move(Reply));
1386void ClangdLSPServer::onPrepareCallHierarchy(
1387 const CallHierarchyPrepareParams &Params,
1388 Callback<std::vector<CallHierarchyItem>> Reply) {
1389 Server->prepareCallHierarchy(Params.textDocument.uri.file(), Params.position,
1393void ClangdLSPServer::onCallHierarchyIncomingCalls(
1394 const CallHierarchyIncomingCallsParams &Params,
1395 Callback<std::vector<CallHierarchyIncomingCall>> Reply) {
1396 Server->incomingCalls(Params.item, std::move(Reply));
1399void ClangdLSPServer::onClangdInlayHints(
const InlayHintsParams &Params,
1400 Callback<llvm::json::Value> Reply) {
1405 auto Serialize = [Reply = std::move(Reply)](
1406 llvm::Expected<std::vector<InlayHint>> Hints)
mutable {
1408 Reply(Hints.takeError());
1411 llvm::json::Array Result;
1412 Result.reserve(Hints->size());
1413 for (
auto &Hint : *Hints) {
1414 Result.emplace_back(llvm::json::Object{
1415 {
"kind", llvm::to_string(Hint.kind)},
1416 {
"range", Hint.range},
1417 {
"position", Hint.position},
1421 ((Hint.paddingLeft ?
" " :
"") + llvm::StringRef(Hint.joinLabels()) +
1422 (Hint.paddingRight ?
" " :
""))
1426 Reply(std::move(Result));
1428 Server->inlayHints(Params.textDocument.uri.file(), Params.range,
1429 std::move(Serialize));
1432void ClangdLSPServer::onInlayHint(
const InlayHintsParams &Params,
1433 Callback<std::vector<InlayHint>> Reply) {
1434 Server->inlayHints(Params.textDocument.uri.file(), Params.range,
1438void ClangdLSPServer::onCallHierarchyOutgoingCalls(
1439 const CallHierarchyOutgoingCallsParams &Params,
1440 Callback<std::vector<CallHierarchyOutgoingCall>> Reply) {
1441 Server->outgoingCalls(Params.item, std::move(Reply));
1444void ClangdLSPServer::applyConfiguration(
1445 const ConfigurationSettings &Settings) {
1447 llvm::StringSet<> ModifiedFiles;
1448 for (
auto &[File, Command] : Settings.compilationDatabaseChanges) {
1450 tooling::CompileCommand(std::move(Command.workingDirectory), File,
1451 std::move(Command.compilationCommand),
1453 if (CDB->setCompileCommand(File, std::move(Cmd))) {
1454 ModifiedFiles.insert(File);
1458 Server->reparseOpenFilesIfNeeded(
1459 [&](llvm::StringRef File) {
return ModifiedFiles.count(File) != 0; });
1462void ClangdLSPServer::maybeExportMemoryProfile() {
1463 if (!trace::enabled() || !ShouldProfile())
1466 static constexpr trace::Metric MemoryUsage(
1467 "memory_usage", trace::Metric::Value,
"component_name");
1468 trace::Span Tracer(
"ProfileBrief");
1471 record(MT,
"clangd_lsp_server", MemoryUsage);
1474void ClangdLSPServer::maybeCleanupMemory() {
1475 if (!Opts.MemoryCleanup || !ShouldCleanupMemory())
1477 Opts.MemoryCleanup();
1481void ClangdLSPServer::onChangeConfiguration(
1482 const DidChangeConfigurationParams &Params) {
1483 applyConfiguration(Params.settings);
1486void ClangdLSPServer::onReference(
1487 const ReferenceParams &Params,
1488 Callback<std::vector<ReferenceLocation>> Reply) {
1489 Server->findReferences(Params.textDocument.uri.file(), Params.position,
1490 Opts.ReferencesLimit, SupportsReferenceContainer,
1491 [Reply = std::move(Reply),
1492 IncludeDecl(Params.context.includeDeclaration)](
1493 llvm::Expected<ReferencesResult> Refs)
mutable {
1495 return Reply(Refs.takeError());
1497 std::vector<ReferenceLocation> Result;
1498 Result.reserve(Refs->References.size());
1499 for (auto &Ref : Refs->References) {
1501 Ref.Attributes & ReferencesResult::Declaration;
1502 if (IncludeDecl || !IsDecl)
1503 Result.push_back(std::move(Ref.Loc));
1505 return Reply(std::move(Result));
1509void ClangdLSPServer::onGoToType(
const TextDocumentPositionParams &Params,
1510 Callback<std::vector<Location>> Reply) {
1512 Params.textDocument.uri.file(), Params.position,
1513 [Reply = std::move(Reply)](
1514 llvm::Expected<std::vector<LocatedSymbol>> Types)
mutable {
1516 return Reply(Types.takeError());
1517 std::vector<Location> Response;
1518 for (const LocatedSymbol &Sym : *Types)
1519 Response.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration));
1520 return Reply(std::move(Response));
1524void ClangdLSPServer::onGoToImplementation(
1525 const TextDocumentPositionParams &Params,
1526 Callback<std::vector<Location>> Reply) {
1527 Server->findImplementations(
1528 Params.textDocument.uri.file(), Params.position,
1529 [Reply = std::move(Reply)](
1530 llvm::Expected<std::vector<LocatedSymbol>> Overrides)
mutable {
1532 return Reply(Overrides.takeError());
1533 std::vector<Location> Impls;
1534 for (const LocatedSymbol &Sym : *Overrides)
1535 Impls.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration));
1536 return Reply(std::move(Impls));
1540void ClangdLSPServer::onSymbolInfo(
const TextDocumentPositionParams &Params,
1541 Callback<std::vector<SymbolDetails>> Reply) {
1542 Server->symbolInfo(Params.textDocument.uri.file(), Params.position,
1546void ClangdLSPServer::onSelectionRange(
1547 const SelectionRangeParams &Params,
1548 Callback<std::vector<SelectionRange>> Reply) {
1549 Server->semanticRanges(
1550 Params.textDocument.uri.file(), Params.positions,
1551 [Reply = std::move(Reply)](
1552 llvm::Expected<std::vector<SelectionRange>> Ranges)
mutable {
1554 return Reply(Ranges.takeError());
1555 return Reply(std::move(*Ranges));
1559void ClangdLSPServer::onDocumentLink(
1560 const DocumentLinkParams &Params,
1561 Callback<std::vector<DocumentLink>> Reply) {
1567 Server->documentLinks(
1568 Params.textDocument.uri.file(),
1569 [Reply = std::move(Reply)](
1570 llvm::Expected<std::vector<DocumentLink>> Links)
mutable {
1572 return Reply(Links.takeError());
1574 return Reply(std::move(Links));
1580 for (
char &C : llvm::reverse(S)) {
1587 S.insert(S.begin(),
'1');
1590void ClangdLSPServer::onSemanticTokens(
const SemanticTokensParams &Params,
1591 Callback<SemanticTokens> CB) {
1592 auto File = Params.textDocument.uri.file();
1593 Server->semanticHighlights(
1594 Params.textDocument.uri.file(),
1595 [
this, File(File.str()), CB(std::move(CB)), Code(Server->getDraft(File))](
1596 llvm::Expected<std::vector<HighlightingToken>> HT)
mutable {
1598 return CB(HT.takeError());
1599 SemanticTokens Result;
1600 Result.tokens = toSemanticTokens(*HT, *Code);
1602 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1603 auto &Last = LastSemanticTokens[File];
1605 Last.tokens = Result.tokens;
1606 increment(Last.resultId);
1607 Result.resultId = Last.resultId;
1609 CB(std::move(Result));
1613void ClangdLSPServer::onSemanticTokensDelta(
1614 const SemanticTokensDeltaParams &Params,
1615 Callback<SemanticTokensOrDelta> CB) {
1616 auto File = Params.textDocument.uri.file();
1617 Server->semanticHighlights(
1618 Params.textDocument.uri.file(),
1619 [
this, PrevResultID(Params.previousResultId),
File(
File.str()),
1620 CB(std::move(CB)), Code(Server->getDraft(File))](
1621 llvm::Expected<std::vector<HighlightingToken>> HT)
mutable {
1623 return CB(HT.takeError());
1624 std::vector<SemanticToken> Toks = toSemanticTokens(*HT, *Code);
1626 SemanticTokensOrDelta Result;
1628 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1629 auto &Last = LastSemanticTokens[File];
1631 if (PrevResultID == Last.resultId) {
1632 Result.edits = diffTokens(Last.tokens, Toks);
1634 vlog(
"semanticTokens/full/delta: wanted edits vs {0} but last "
1635 "result had ID {1}. Returning full token list.",
1636 PrevResultID, Last.resultId);
1637 Result.tokens = Toks;
1640 Last.tokens = std::move(Toks);
1641 increment(Last.resultId);
1642 Result.resultId = Last.resultId;
1645 CB(std::move(Result));
1649void ClangdLSPServer::onMemoryUsage(
const NoParams &,
1650 Callback<MemoryTree> Reply) {
1651 llvm::BumpPtrAllocator DetailAlloc;
1652 MemoryTree MT(&DetailAlloc);
1654 Reply(std::move(MT));
1657void ClangdLSPServer::onAST(
const ASTParams &Params,
1658 Callback<std::optional<ASTNode>> CB) {
1659 Server->getAST(Params.textDocument.uri.file(), Params.range, std::move(CB));
1664 : ShouldProfile(std::chrono::minutes(5),
1665 std::chrono::minutes(1)),
1666 ShouldCleanupMemory(std::chrono::minutes(1),
1667 std::chrono::minutes(1)),
1668 BackgroundContext(
Context::current().clone()), Transp(Transp),
1670 SupportedSymbolKinds(defaultSymbolKinds()),
1671 SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts) {
1672 if (Opts.ConfigProvider) {
1673 assert(!Opts.ContextProvider &&
1674 "Only one of ConfigProvider and ContextProvider allowed!");
1675 this->Opts.ContextProvider = ClangdServer::createConfiguredContextProvider(
1676 Opts.ConfigProvider, this);
1679 Bind.
method(
"initialize",
this, &ClangdLSPServer::onInitialize);
1682void ClangdLSPServer::bindMethods(
LSPBinder &Bind,
1685 Bind.
notification(
"initialized",
this, &ClangdLSPServer::onInitialized);
1686 Bind.
method(
"shutdown",
this, &ClangdLSPServer::onShutdown);
1687 Bind.
method(
"sync",
this, &ClangdLSPServer::onSync);
1688 Bind.
method(
"textDocument/rangeFormatting",
this, &ClangdLSPServer::onDocumentRangeFormatting);
1689 Bind.
method(
"textDocument/rangesFormatting",
this, &ClangdLSPServer::onDocumentRangesFormatting);
1690 Bind.
method(
"textDocument/onTypeFormatting",
this, &ClangdLSPServer::onDocumentOnTypeFormatting);
1691 Bind.
method(
"textDocument/formatting",
this, &ClangdLSPServer::onDocumentFormatting);
1692 Bind.
method(
"textDocument/codeAction",
this, &ClangdLSPServer::onCodeAction);
1693 Bind.
method(
"textDocument/completion",
this, &ClangdLSPServer::onCompletion);
1694 Bind.
method(
"textDocument/signatureHelp",
this, &ClangdLSPServer::onSignatureHelp);
1695 Bind.
method(
"textDocument/definition",
this, &ClangdLSPServer::onGoToDefinition);
1696 Bind.
method(
"textDocument/declaration",
this, &ClangdLSPServer::onGoToDeclaration);
1697 Bind.
method(
"textDocument/typeDefinition",
this, &ClangdLSPServer::onGoToType);
1698 Bind.
method(
"textDocument/implementation",
this, &ClangdLSPServer::onGoToImplementation);
1699 Bind.
method(
"textDocument/references",
this, &ClangdLSPServer::onReference);
1700 Bind.
method(
"textDocument/switchSourceHeader",
this, &ClangdLSPServer::onSwitchSourceHeader);
1701 Bind.
method(
"textDocument/prepareRename",
this, &ClangdLSPServer::onPrepareRename);
1702 Bind.
method(
"textDocument/rename",
this, &ClangdLSPServer::onRename);
1703 Bind.
method(
"textDocument/hover",
this, &ClangdLSPServer::onHover);
1704 Bind.
method(
"textDocument/documentSymbol",
this, &ClangdLSPServer::onDocumentSymbol);
1705 Bind.
method(
"workspace/executeCommand",
this, &ClangdLSPServer::onCommand);
1706 Bind.
method(
"textDocument/documentHighlight",
this, &ClangdLSPServer::onDocumentHighlight);
1707 Bind.
method(
"workspace/symbol",
this, &ClangdLSPServer::onWorkspaceSymbol);
1708 Bind.
method(
"textDocument/ast",
this, &ClangdLSPServer::onAST);
1709 Bind.
notification(
"textDocument/didOpen",
this, &ClangdLSPServer::onDocumentDidOpen);
1710 Bind.
notification(
"textDocument/didClose",
this, &ClangdLSPServer::onDocumentDidClose);
1711 Bind.
notification(
"textDocument/didChange",
this, &ClangdLSPServer::onDocumentDidChange);
1712 Bind.
notification(
"textDocument/didSave",
this, &ClangdLSPServer::onDocumentDidSave);
1713 Bind.
notification(
"workspace/didChangeWatchedFiles",
this, &ClangdLSPServer::onFileEvent);
1714 Bind.
notification(
"workspace/didChangeConfiguration",
this, &ClangdLSPServer::onChangeConfiguration);
1715 Bind.
method(
"textDocument/symbolInfo",
this, &ClangdLSPServer::onSymbolInfo);
1716 Bind.
method(
"textDocument/typeHierarchy",
this, &ClangdLSPServer::onTypeHierarchy);
1717 Bind.
method(
"typeHierarchy/resolve",
this, &ClangdLSPServer::onResolveTypeHierarchy);
1718 Bind.
method(
"textDocument/prepareTypeHierarchy",
this, &ClangdLSPServer::onPrepareTypeHierarchy);
1719 Bind.
method(
"typeHierarchy/supertypes",
this, &ClangdLSPServer::onSuperTypes);
1720 Bind.
method(
"typeHierarchy/subtypes",
this, &ClangdLSPServer::onSubTypes);
1721 Bind.
method(
"textDocument/prepareCallHierarchy",
this, &ClangdLSPServer::onPrepareCallHierarchy);
1722 Bind.
method(
"callHierarchy/incomingCalls",
this, &ClangdLSPServer::onCallHierarchyIncomingCalls);
1724 Bind.
method(
"callHierarchy/outgoingCalls",
this, &ClangdLSPServer::onCallHierarchyOutgoingCalls);
1725 Bind.
method(
"textDocument/selectionRange",
this, &ClangdLSPServer::onSelectionRange);
1726 Bind.
method(
"textDocument/documentLink",
this, &ClangdLSPServer::onDocumentLink);
1727 Bind.
method(
"textDocument/semanticTokens/full",
this, &ClangdLSPServer::onSemanticTokens);
1728 Bind.
method(
"textDocument/semanticTokens/full/delta",
this, &ClangdLSPServer::onSemanticTokensDelta);
1729 Bind.
method(
"clangd/inlayHints",
this, &ClangdLSPServer::onClangdInlayHints);
1730 Bind.
method(
"textDocument/inlayHint",
this, &ClangdLSPServer::onInlayHint);
1731 Bind.
method(
"$/memoryUsage",
this, &ClangdLSPServer::onMemoryUsage);
1732 Bind.
method(
"textDocument/foldingRange",
this, &ClangdLSPServer::onFoldingRange);
1733 Bind.
command(ApplyFixCommand,
this, &ClangdLSPServer::onCommandApplyEdit);
1734 Bind.
command(ApplyTweakCommand,
this, &ClangdLSPServer::onCommandApplyTweak);
1735 Bind.
command(ApplyRenameCommand,
this, &ClangdLSPServer::onCommandApplyRename);
1743 CreateWorkDoneProgress = Bind.
outgoingMethod(
"window/workDoneProgress/create");
1748 SemanticTokensRefresh = Bind.
outgoingMethod(
"workspace/semanticTokens/refresh");
1753 IsBeingDestroyed =
true;
1761 bool CleanExit =
true;
1762 if (
auto Err = Transp.loop(*MsgHandler)) {
1763 elog(
"Transport error: {0}", std::move(Err));
1767 return CleanExit && ShutdownRequestReceived;
1772 Server->profile(MT.
child(
"clangd_server"));
1775std::optional<ClangdServer::DiagRef>
1777 std::lock_guard<std::mutex> Lock(DiagRefMutex);
1778 auto DiagToDiagRefIter = DiagRefMap.find(
File);
1779 if (DiagToDiagRefIter == DiagRefMap.end())
1780 return std::nullopt;
1782 const auto &DiagToDiagRefMap = DiagToDiagRefIter->second;
1783 auto FixItsIter = DiagToDiagRefMap.find(toDiagKey(D));
1784 if (FixItsIter == DiagToDiagRefMap.end())
1785 return std::nullopt;
1787 return FixItsIter->second;
1796bool ClangdLSPServer::shouldRunCompletion(
1797 const CompletionParams &Params)
const {
1800 auto Code = Server->getDraft(Params.textDocument.uri.file());
1806 elog(
"could not convert position '{0}' to offset for file '{1}': {2}",
1807 Params.position, Params.textDocument.uri.file(), Offset.takeError());
1813void ClangdLSPServer::onDiagnosticsReady(
PathRef File, llvm::StringRef Version,
1814 llvm::ArrayRef<Diag> Diagnostics) {
1815 PublishDiagnosticsParams Notification;
1816 Notification.version = decodeVersion(Version);
1818 DiagnosticToDiagRefMap LocalDiagMap;
1819 for (
auto &Diag : Diagnostics) {
1821 [&](clangd::Diagnostic LSPDiag, llvm::ArrayRef<Fix> Fixes) {
1822 if (DiagOpts.EmbedFixesInDiagnostics) {
1823 std::vector<CodeAction> CodeActions;
1824 for (const auto &Fix : Fixes)
1825 CodeActions.push_back(toCodeAction(
1826 Fix, Notification.uri, Notification.version,
1827 SupportsDocumentChanges, SupportsChangeAnnotation));
1828 LSPDiag.codeActions.emplace(std::move(CodeActions));
1829 if (LSPDiag.codeActions->size() == 1)
1830 LSPDiag.codeActions->front().isPreferred = true;
1832 LocalDiagMap[toDiagKey(LSPDiag)] = {Diag.Range, Diag.Message};
1833 Notification.diagnostics.push_back(std::move(LSPDiag));
1839 std::lock_guard<std::mutex> Lock(DiagRefMutex);
1840 DiagRefMap[
File] = std::move(LocalDiagMap);
1847void ClangdLSPServer::onInactiveRegionsReady(
1848 PathRef File, std::vector<Range> InactiveRegions) {
1849 InactiveRegionsParams Notification;
1851 Notification.InactiveRegions = std::move(InactiveRegions);
1853 PublishInactiveRegions(Notification);
1856void ClangdLSPServer::onBackgroundIndexProgress(
1858 static const char ProgressToken[] =
"backgroundIndexProgress";
1861 maybeCleanupMemory();
1863 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1865 auto NotifyProgress = [
this](
const BackgroundQueue::Stats &Stats) {
1866 if (BackgroundIndexProgressState != BackgroundIndexProgress::Live) {
1867 WorkDoneProgressBegin Begin;
1868 Begin.percentage =
true;
1869 Begin.title =
"indexing";
1870 BeginWorkDoneProgress({ProgressToken, std::move(Begin)});
1871 BackgroundIndexProgressState = BackgroundIndexProgress::Live;
1874 if (Stats.Completed < Stats.Enqueued) {
1875 assert(Stats.Enqueued > Stats.LastIdle);
1876 WorkDoneProgressReport Report;
1877 Report.percentage = 100 * (Stats.Completed - Stats.LastIdle) /
1878 (Stats.Enqueued - Stats.LastIdle);
1880 llvm::formatv(
"{0}/{1}", Stats.Completed - Stats.LastIdle,
1881 Stats.Enqueued - Stats.LastIdle);
1882 ReportWorkDoneProgress({ProgressToken, std::move(Report)});
1884 assert(Stats.Completed == Stats.Enqueued);
1885 EndWorkDoneProgress({ProgressToken, WorkDoneProgressEnd()});
1886 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
1890 switch (BackgroundIndexProgressState) {
1891 case BackgroundIndexProgress::Unsupported:
1893 case BackgroundIndexProgress::Creating:
1895 PendingBackgroundIndexProgress = Stats;
1897 case BackgroundIndexProgress::Empty: {
1898 if (BackgroundIndexSkipCreate) {
1899 NotifyProgress(Stats);
1903 PendingBackgroundIndexProgress = Stats;
1904 BackgroundIndexProgressState = BackgroundIndexProgress::Creating;
1905 WorkDoneProgressCreateParams CreateRequest;
1906 CreateRequest.token = ProgressToken;
1907 CreateWorkDoneProgress(
1909 [
this, NotifyProgress](llvm::Expected<std::nullptr_t> E) {
1910 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1912 NotifyProgress(this->PendingBackgroundIndexProgress);
1914 elog(
"Failed to create background index progress bar: {0}",
1917 BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported;
1922 case BackgroundIndexProgress::Live:
1923 NotifyProgress(Stats);
1929 if (!SupportFileStatus)
1939 NotifyFileStatus(Status.render(
File));
1942void ClangdLSPServer::onSemanticsMaybeChanged(
PathRef File) {
1943 if (SemanticTokensRefresh) {
1944 SemanticTokensRefresh(NoParams{}, [](llvm::Expected<std::nullptr_t> E) {
1947 elog(
"Failed to refresh semantic tokens: {0}", E.takeError());
static cl::list< std::string > Commands("c", cl::desc("Specify command to run"), cl::value_desc("command"), cl::cat(ClangQueryCategory))
#define SPAN_ATTACH(S, Name, Expr)
Attach a key-value pair to a Span event.
bool onReply(llvm::json::Value ID, llvm::Expected< llvm::json::Value > Result) override
MessageHandler(ClangdLSPServer &Server)
bool onCall(llvm::StringRef Method, llvm::json::Value Params, llvm::json::Value ID) override
bool onNotify(llvm::StringRef Method, llvm::json::Value Params) override
llvm::json::Value bindReply(Callback< llvm::json::Value > Reply)
This class exposes ClangdServer's capabilities via Language Server Protocol.
~ClangdLSPServer()
The destructor blocks on any outstanding background tasks.
ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts)
void profile(MemoryTree &MT) const
Profiles resource-usage.
bool run()
Run LSP server loop, communicating with the Transport provided in the constructor.
Manages a collection of source files and derived data (ASTs, indexes), and provides language-aware fe...
A context is an immutable container for per-request data that must be propagated through layers that ...
Context derive(const Key< Type > &Key, std::decay_t< Type > Value) const &
Derives a child context It is safe to move or destroy a parent context after calling derive().
static const Context & current()
Returns the context for the current thread, creating it if needed.
LSPBinder collects a table of functions that handle LSP calls.
UntypedOutgoingMethod outgoingMethod(llvm::StringLiteral Method)
Bind a function object to be used for outgoing method calls.
void method(llvm::StringLiteral Method, ThisT *This, void(ThisT::*Handler)(const Param &, Callback< Result >))
Bind a handler for an LSP method.
void notification(llvm::StringLiteral Method, ThisT *This, void(ThisT::*Handler)(const Param &))
Bind a handler for an LSP notification.
UntypedOutgoingNotification outgoingNotification(llvm::StringLiteral Method)
Bind a function object to be used for outgoing notifications.
void command(llvm::StringLiteral Command, ThisT *This, void(ThisT::*Handler)(const Param &, Callback< Result >))
Bind a handler for an LSP command.
A threadsafe flag that is initially clear.
Wrapper for vfs::FileSystem for use in multithreaded programs like clangd.
static URI createFile(llvm::StringRef AbsolutePath)
This creates a file:// URI for AbsolutePath. The path must be absolute.
std::string toString() const
Returns a string URI with all components percent-encoded.
WithContext replaces Context::current() with a provided scope.
Records an event whose duration is the lifetime of the Span object.
llvm::json::Object *const Args
Mutable metadata, if this span is interested.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
@ Info
An information message.
llvm::StringRef toSemanticTokenModifier(HighlightingModifier Modifier)
constexpr auto CompletionItemKindMin
llvm::Error applyChange(std::string &Contents, const TextDocumentContentChangeEvent &Change)
Apply an incremental update to a text document.
llvm::StringRef toSemanticTokenType(HighlightingKind Kind)
SystemIncludeExtractorFn getSystemIncludeExtractor(llvm::ArrayRef< std::string > QueryDriverGlobs)
static std::vector< llvm::StringRef > semanticTokenModifiers()
constexpr auto SymbolKindMin
Key< OffsetEncoding > kCurrentOffsetEncoding
void toLSPDiags(const Diag &D, const URIForFile &File, const ClangdDiagnosticOptions &Opts, llvm::function_ref< void(clangd::Diagnostic, llvm::ArrayRef< Fix >)> OutFn)
Conversion to LSP diagnostics.
@ TriggerCharacter
Completion was triggered by a trigger character specified by the triggerCharacters properties of the ...
bool allowImplicitCompletion(llvm::StringRef Content, unsigned Offset)
void record(const MemoryTree &MT, std::string RootName, const trace::Metric &Out)
Records total memory usage of each node under Out.
void vlog(const char *Fmt, Ts &&... Vals)
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
std::string platformString()
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
llvm::json::Value serializeTHIForExtension(TypeHierarchyItem THI)
static std::vector< SymbolInformation > flattenSymbolHierarchy(llvm::ArrayRef< DocumentSymbol > Symbols, const URIForFile &FileURI)
The functions constructs a flattened view of the DocumentSymbol hierarchy.
std::bitset< SymbolKindMax+1 > SymbolKindBitset
static void increment(std::string &S)
std::string featureString()
llvm::StringMap< Edit > FileEdits
A mapping from absolute file path (the one used for accessing the underlying VFS) to edits.
SymbolKind adjustKindToCapability(SymbolKind Kind, SymbolKindBitset &SupportedSymbolKinds)
static std::optional< Command > asCommand(const CodeAction &Action)
std::pair< Context, Canceler > cancelableTask(int Reason)
Defines a new task whose cancellation may be requested.
NoParams InitializedParams
void log(const char *Fmt, Ts &&... Vals)
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
std::bitset< CompletionItemKindMax+1 > CompletionItemKindBitset
static std::vector< llvm::StringRef > semanticTokenTypes()
llvm::StringRef PathRef
A typedef to represent a ref to file path.
@ Auto
Diagnostics must not be generated for this snapshot.
@ No
Diagnostics must be generated for this snapshot.
static Location * getToggle(const TextDocumentPositionParams &Point, LocatedSymbol &Sym)
std::string Path
A typedef to represent a file path.
std::function< void()> Canceler
A canceller requests cancellation of a task, when called.
@ Incremental
Documents are synced by sending the full content on open.
void elog(const char *Fmt, Ts &&... Vals)
std::string versionString()
std::vector< TextEdit > replacementsToEdits(llvm::StringRef Code, const tooling::Replacements &Repls)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::optional< OffsetEncoding > Encoding
The offset-encoding to use, or std::nullopt to negotiate it over LSP.
bool EnableOutgoingCalls
Call hierarchy's outgoing calls feature requires additional index serving structures which increase m...
bool SemanticTokenRefreshSupport
Whether the client implementation supports a refresh request sent from the server to the client.
bool InactiveRegions
Whether the client supports the textDocument/inactiveRegions notification.
A code action represents a change that can be performed in code, e.g.
static const llvm::StringLiteral INFO_KIND
static const llvm::StringLiteral REFACTOR_KIND
static const llvm::StringLiteral QUICKFIX_KIND
std::optional< WorkspaceEdit > edit
The workspace edit this code action performs.
std::optional< Command > command
A command this code action executes.
std::optional< std::string > kind
The kind of the code action.
std::string title
A short, human-readable, title for this code action.
static CommandMangler detect()
Represents programming constructs like variables, classes, interfaces etc.
std::vector< DocumentSymbol > children
Children of this symbol, e.g. properties of a class.
std::string name
The name of this symbol.
Range range
The range enclosing this symbol not including leading/trailing whitespace but everything else like co...
SymbolKind kind
The kind of this symbol.
A set of edits generated for a single file.
Represents a single fix-it that editor can apply to fix the error.
Location PreferredDeclaration
std::optional< Location > Definition
URIForFile uri
The text document's URI.
A tree that can be used to represent memory usage of nested components while preserving the hierarchy...
MemoryTree & child(llvm::StringLiteral Name)
No copy of the Name.
bool contains(Position Pos) const
TextDocumentIdentifier textDocument
The document that was opened.
URIForFile uri
The text document's URI.
TextDocumentIdentifier textDocument
The text document.
Position position
The position inside the text document.
Arguments for the 'applyTweak' command.
URIForFile file
A file provided by the client on a textDocument/codeAction request.
Range range
The range enclosing this symbol not including leading/trailing whitespace but everything else,...
URIForFile uri
The resource identifier of this item.
Range selectionRange
The range that should be selected and revealed when this symbol is being picked, e....
SymbolKind kind
The kind of this item.
std::optional< std::vector< TypeHierarchyItem > > children
If this type hierarchy item is resolved, it contains the direct children of the current item.
std::optional< std::vector< TypeHierarchyItem > > parents
This is a clangd exntesion.
bool deprecated
true if the hierarchy item is deprecated.
std::optional< std::string > detail
More detail for this item, e.g. the signature of a function.
ResolveParams data
A data entry field that is preserved between a type hierarchy prepare and supertypes or subtypes requ...
std::string name
The name of this item.
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Canonicalizes AbsPath via URI.
llvm::StringRef file() const
Retrieves absolute path to the file.
The edit should either provide changes or documentChanges.
The parameters of a Workspace Symbol Request.
Represents measurements of clangd events, e.g.
@ Distribution
A distribution of values with a meaningful mean and count.