27#include "clang/Tooling/Core/Replacement.h"
28#include "llvm/ADT/ArrayRef.h"
29#include "llvm/ADT/FunctionExtras.h"
30#include "llvm/ADT/ScopeExit.h"
31#include "llvm/ADT/StringRef.h"
32#include "llvm/ADT/Twine.h"
33#include "llvm/Support/Allocator.h"
34#include "llvm/Support/Error.h"
35#include "llvm/Support/FormatVariadic.h"
36#include "llvm/Support/JSON.h"
37#include "llvm/Support/SHA1.h"
38#include "llvm/Support/ScopedPrinter.h"
39#include "llvm/Support/raw_ostream.h"
62std::string encodeVersion(std::optional<int64_t> LSPVersion) {
63 return LSPVersion ? llvm::to_string(*LSPVersion) :
"";
65std::optional<int64_t> decodeVersion(llvm::StringRef Encoded) {
67 if (llvm::to_integer(Encoded, Result, 10))
70 elog(
"unexpected non-numeric version {0}", Encoded);
74const llvm::StringLiteral ApplyFixCommand =
"clangd.applyFix";
75const llvm::StringLiteral ApplyTweakCommand =
"clangd.applyTweak";
76const llvm::StringLiteral ApplyRenameCommand =
"clangd.applyRename";
78CodeAction toCodeAction(
const ClangdServer::CodeActionResult::Rename &R,
79 const URIForFile &
File) {
81 CA.title = R.FixMessage;
84 CA.command->title = R.FixMessage;
85 CA.command->command = std::string(ApplyRenameCommand);
87 Params.textDocument = TextDocumentIdentifier{
File};
88 Params.position = R.Diag.Range.start;
89 Params.newName = R.NewName;
90 CA.command->argument = Params;
96CodeAction toCodeAction(
const ClangdServer::TweakRef &T,
const URIForFile &
File,
100 CA.kind = T.Kind.str();
106 CA.command.emplace();
107 CA.command->title = T.Title;
108 CA.command->command = std::string(ApplyTweakCommand);
112 Args.selection = Selection;
113 CA.command->argument = std::move(
Args);
118CodeAction toCodeAction(
const Fix &F,
const URIForFile &
File,
119 const std::optional<int64_t> &Version,
120 bool SupportsDocumentChanges,
121 bool SupportChangeAnnotation) {
126 if (!SupportsDocumentChanges) {
127 Action.edit->changes.emplace();
129 for (
const auto &
E : F.Edits)
130 Changes.push_back({
E.range,
E.newText,
""});
132 Action.edit->documentChanges.emplace();
133 TextDocumentEdit &Edit =
Action.edit->documentChanges->emplace_back();
134 Edit.textDocument = VersionedTextDocumentIdentifier{{
File}, Version};
135 for (
const auto &
E : F.Edits)
136 Edit.edits.push_back(
138 SupportChangeAnnotation ?
E.annotationId :
""});
139 if (SupportChangeAnnotation) {
140 for (
const auto &[AID, Annotation]: F.Annotations)
141 Action.edit->changeAnnotations[AID] = Annotation;
147void adjustSymbolKinds(llvm::MutableArrayRef<DocumentSymbol> Syms,
149 for (
auto &S : Syms) {
151 adjustSymbolKinds(S.children, Kinds);
157 for (
size_t I =
SymbolKindMin; I <= static_cast<size_t>(SymbolKind::Array);
174llvm::Error validateEdits(
const ClangdServer &Server,
const FileEdits &FE) {
175 size_t InvalidFileCount = 0;
176 llvm::StringRef LastInvalidFile;
177 for (
const auto &It : FE) {
178 if (
auto Draft = Server.getDraft(It.first())) {
182 if (!It.second.canApplyTo(*Draft)) {
184 LastInvalidFile = It.first();
188 if (!InvalidFileCount)
189 return llvm::Error::success();
190 if (InvalidFileCount == 1)
191 return error(
"File must be saved first: {0}", LastInvalidFile);
192 return error(
"Files must be saved first: {0} (and {1} others)",
193 LastInvalidFile, InvalidFileCount - 1);
215 auto Handler = Server.Handlers.NotificationHandlers.find(
Method);
216 if (Handler != Server.Handlers.NotificationHandlers.end()) {
217 Handler->second(std::move(Params));
218 Server.maybeExportMemoryProfile();
219 Server.maybeCleanupMemory();
220 }
else if (!Server.Server) {
221 elog(
"Notification {0} before initialization",
Method);
222 }
else if (
Method ==
"$/cancelRequest") {
223 onCancel(std::move(Params));
225 log(
"unhandled notification {0}",
Method);
231 llvm::json::Value
ID)
override {
237 ReplyOnce Reply(
ID,
Method, &Server, Tracer.Args);
239 auto Handler = Server.Handlers.MethodHandlers.find(
Method);
240 if (Handler != Server.Handlers.MethodHandlers.end()) {
241 Handler->second(std::move(Params), std::move(Reply));
242 }
else if (!Server.Server) {
243 elog(
"Call {0} before initialization.",
Method);
244 Reply(llvm::make_error<LSPError>(
"server not initialized",
247 Reply(llvm::make_error<LSPError>(
"method not found",
254 llvm::Expected<llvm::json::Value> Result)
override {
258 if (
auto IntID =
ID.getAsInteger()) {
259 std::lock_guard<std::mutex> Mutex(CallMutex);
261 for (
size_t Index = 0; Index < ReplyCallbacks.size(); ++Index) {
262 if (ReplyCallbacks[Index].first == *IntID) {
263 ReplyHandler = std::move(ReplyCallbacks[Index].second);
264 ReplyCallbacks.erase(ReplyCallbacks.begin() +
273 ReplyHandler = [&
ID](llvm::Expected<llvm::json::Value> Result) {
274 elog(
"received a reply with ID {0}, but there was no such call",
ID);
276 llvm::consumeError(Result.takeError());
282 log(
"<-- reply({0})",
ID);
283 ReplyHandler(std::move(Result));
285 auto Err = Result.takeError();
286 log(
"<-- reply({0}) error: {1}",
ID, Err);
287 ReplyHandler(std::move(Err));
296 std::optional<std::pair<int, Callback<llvm::json::Value>>> OldestCB;
299 std::lock_guard<std::mutex> Mutex(CallMutex);
301 ReplyCallbacks.emplace_back(
ID, std::move(Reply));
306 if (ReplyCallbacks.size() > MaxReplayCallbacks) {
307 elog(
"more than {0} outstanding LSP calls, forgetting about {1}",
308 MaxReplayCallbacks, ReplyCallbacks.front().first);
309 OldestCB = std::move(ReplyCallbacks.front());
310 ReplyCallbacks.pop_front();
315 error(
"failed to receive a client reply for request ({0})",
327 std::atomic<bool> Replied = {
false};
328 std::chrono::steady_clock::time_point Start;
329 llvm::json::Value
ID;
332 llvm::json::Object *TraceArgs;
335 ReplyOnce(
const llvm::json::Value &ID, llvm::StringRef Method,
338 Server(Server), TraceArgs(TraceArgs) {
341 ReplyOnce(ReplyOnce &&Other)
342 : Replied(Other.Replied.load()), Start(Other.Start),
343 ID(std::move(Other.
ID)), Method(std::move(Other.Method)),
344 Server(Other.Server), TraceArgs(Other.TraceArgs) {
345 Other.Server =
nullptr;
347 ReplyOnce &operator=(ReplyOnce &&) =
delete;
348 ReplyOnce(
const ReplyOnce &) =
delete;
349 ReplyOnce &operator=(
const ReplyOnce &) =
delete;
358 if (Server && !Server->IsBeingDestroyed && !Replied) {
359 elog(
"No reply to message {0}({1})", Method, ID);
360 assert(
false &&
"must reply to all calls!");
361 (*this)(llvm::make_error<LSPError>(
"server failed to reply",
366 void operator()(llvm::Expected<llvm::json::Value> Reply) {
367 assert(Server &&
"moved-from!");
368 if (Replied.exchange(
true)) {
369 elog(
"Replied twice to message {0}({1})", Method, ID);
370 assert(
false &&
"must reply to each call only once!");
373 auto Duration = std::chrono::steady_clock::now() - Start;
375 log(
"--> reply:{0}({1}) {2:ms}", Method, ID, Duration);
377 (*TraceArgs)[
"Reply"] = *Reply;
378 std::lock_guard<std::mutex> Lock(Server->TranspWriter);
379 Server->Transp.reply(std::move(ID), std::move(Reply));
381 llvm::Error Err = Reply.takeError();
382 log(
"--> reply:{0}({1}) {2:ms}, error: {3}", Method, ID, Duration, Err);
384 (*TraceArgs)[
"Error"] = llvm::to_string(Err);
385 std::lock_guard<std::mutex> Lock(Server->TranspWriter);
386 Server->Transp.reply(std::move(ID), std::move(Err));
394 mutable std::mutex RequestCancelersMutex;
395 llvm::StringMap<std::pair<
Canceler,
unsigned>> RequestCancelers;
396 unsigned NextRequestCookie = 0;
397 void onCancel(
const llvm::json::Value &Params) {
398 const llvm::json::Value *
ID =
nullptr;
399 if (
auto *O = Params.getAsObject())
402 elog(
"Bad cancellation request: {0}", Params);
405 auto StrID = llvm::to_string(*
ID);
406 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
407 auto It = RequestCancelers.find(StrID);
408 if (It != RequestCancelers.end())
412 Context handlerContext()
const {
422 Context cancelableRequestContext(
const llvm::json::Value &
ID) {
425 auto StrID = llvm::to_string(
ID);
426 auto Cookie = NextRequestCookie++;
428 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
429 RequestCancelers[StrID] = {std::move(Task.second), Cookie};
434 return Task.first.derive(llvm::make_scope_exit([
this, StrID, Cookie] {
435 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
436 auto It = RequestCancelers.find(StrID);
437 if (It != RequestCancelers.end() && It->second.second == Cookie)
438 RequestCancelers.erase(It);
448 static constexpr int MaxReplayCallbacks = 100;
449 mutable std::mutex CallMutex;
451 std::deque<std::pair< int,
452 Callback<llvm::json::Value>>>
455 ClangdLSPServer &Server;
457constexpr int ClangdLSPServer::MessageHandler::MaxReplayCallbacks;
460void ClangdLSPServer::callMethod(StringRef
Method, llvm::json::Value Params,
461 Callback<llvm::json::Value> CB) {
462 auto ID = MsgHandler->bindReply(std::move(CB));
464 std::lock_guard<std::mutex> Lock(TranspWriter);
468void ClangdLSPServer::notify(llvm::StringRef
Method, llvm::json::Value Params) {
470 maybeCleanupMemory();
471 std::lock_guard<std::mutex> Lock(TranspWriter);
476 std::vector<llvm::StringRef> Types;
484 std::vector<llvm::StringRef> Modifiers;
492void ClangdLSPServer::onInitialize(
const InitializeParams &Params,
493 Callback<llvm::json::Value> Reply) {
495 if (Params.capabilities.offsetEncoding && !Opts.
Encoding) {
497 for (
OffsetEncoding Supported : *Params.capabilities.offsetEncoding)
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)
512 else if (Params.rootPath && !Params.rootPath->empty())
515 return Reply(llvm::make_error<LSPError>(
"server already initialized",
523 Params.capabilities.CompletionDocumentationFormat;
525 Params.capabilities.SignatureHelpDocumentationFormat;
529 Params.capabilities.DiagnosticRelatedInformation;
530 if (Params.capabilities.WorkspaceSymbolKinds)
531 SupportedSymbolKinds |= *Params.capabilities.WorkspaceSymbolKinds;
532 if (Params.capabilities.CompletionItemKinds)
533 SupportedCompletionItemKinds |= *Params.capabilities.CompletionItemKinds;
534 SupportsCompletionLabelDetails = Params.capabilities.CompletionLabelDetail;
535 SupportsCodeAction = Params.capabilities.CodeActionStructure;
536 SupportsHierarchicalDocumentSymbol =
537 Params.capabilities.HierarchicalDocumentSymbol;
538 SupportsReferenceContainer = Params.capabilities.ReferenceContainer;
539 SupportFileStatus = Params.initializationOptions.FileStatus;
540 SupportsDocumentChanges = Params.capabilities.DocumentChanges;
541 SupportsChangeAnnotation = Params.capabilities.ChangeAnnotation;
542 HoverContentFormat = Params.capabilities.HoverContentFormat;
544 SupportsOffsetsInSignatureHelp = Params.capabilities.OffsetsInSignatureHelp;
545 if (Params.capabilities.WorkDoneProgress)
546 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
547 BackgroundIndexSkipCreate = Params.capabilities.ImplicitProgressCreation;
552 DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS);
553 if (
const auto &Dir = Params.initializationOptions.compilationDatabasePath)
554 CDBOpts.CompileCommandsDir = Dir;
557 std::make_unique<DirectoryBasedGlobalCompilationDatabase>(CDBOpts);
560 Mangler.SystemIncludeExtractor =
564 CDB.emplace(BaseCDB.get(), Params.initializationOptions.fallbackFlags,
570 WithContext MainContext(BackgroundContext.
clone());
571 std::optional<WithContextValue> WithOffsetEncoding;
574 Server.emplace(*CDB, TFS, Opts,
575 static_cast<ClangdServer::Callbacks *
>(
this));
578 llvm::json::Object ServerCaps{
585 {
"documentFormattingProvider",
true},
586 {
"documentRangeFormattingProvider",
true},
587 {
"documentOnTypeFormattingProvider",
589 {
"firstTriggerCharacter",
"\n"},
590 {
"moreTriggerCharacter", {}},
592 {
"completionProvider",
599 {
"resolveProvider",
false},
601 {
"triggerCharacters", {
".",
"<",
">",
":",
"\"",
"/",
"*"}},
603 {
"semanticTokensProvider",
605 {
"full", llvm::json::Object{{
"delta",
true}}},
611 {
"signatureHelpProvider",
613 {
"triggerCharacters", {
"(",
")",
"{",
"}",
"<",
">",
","}},
615 {
"declarationProvider",
true},
616 {
"definitionProvider",
true},
617 {
"implementationProvider",
true},
618 {
"typeDefinitionProvider",
true},
619 {
"documentHighlightProvider",
true},
620 {
"documentLinkProvider",
622 {
"resolveProvider",
false},
624 {
"hoverProvider",
true},
625 {
"selectionRangeProvider",
true},
626 {
"documentSymbolProvider",
true},
627 {
"workspaceSymbolProvider",
true},
628 {
"referencesProvider",
true},
629 {
"astProvider",
true},
630 {
"typeHierarchyProvider",
true},
634 {
"standardTypeHierarchyProvider",
true},
635 {
"memoryUsageProvider",
true},
636 {
"compilationDatabase",
637 llvm::json::Object{{
"automaticReload",
true}}},
638 {
"inactiveRegionsProvider",
true},
639 {
"callHierarchyProvider",
true},
640 {
"clangdInlayHintsProvider",
true},
641 {
"inlayHintProvider",
true},
642 {
"foldingRangeProvider",
true},
646 LSPBinder Binder(Handlers, *
this);
647 bindMethods(Binder, Params.capabilities);
650 Mod.initializeLSP(Binder, Params.rawCapabilities, ServerCaps);
655 ServerCaps[
"renameProvider"] =
656 Params.capabilities.RenamePrepareSupport
657 ? llvm::json::Object{{
"prepareProvider",
true}}
658 : llvm::json::Value(
true);
663 ServerCaps[
"codeActionProvider"] =
664 Params.capabilities.CodeActionStructure
665 ? llvm::json::Object{{
"codeActionKinds",
669 : llvm::json::Value(
true);
671 std::vector<llvm::StringRef>
Commands;
675 ServerCaps[
"executeCommandProvider"] =
676 llvm::json::Object{{
"commands",
Commands}};
678 llvm::json::Object Result{
684 {
"capabilities", std::move(ServerCaps)}}};
686 Result[
"offsetEncoding"] = *Opts.
Encoding;
687 Reply(std::move(Result));
691 applyConfiguration(Params.initializationOptions.ConfigSettings);
696void ClangdLSPServer::onShutdown(
const NoParams &,
697 Callback<std::nullptr_t> Reply) {
699 ShutdownRequestReceived =
true;
705void ClangdLSPServer::onSync(
const NoParams &, Callback<std::nullptr_t> Reply) {
706 if (Server->blockUntilIdleForTest(60))
709 Reply(
error(
"Not idle after a minute"));
712void ClangdLSPServer::onDocumentDidOpen(
713 const DidOpenTextDocumentParams &Params) {
716 const std::string &Contents = Params.textDocument.text;
718 Server->addDocument(
File, Contents,
719 encodeVersion(Params.textDocument.version),
723void ClangdLSPServer::onDocumentDidChange(
724 const DidChangeTextDocumentParams &Params) {
726 if (Params.wantDiagnostics)
733 log(
"Trying to incrementally change non-added document: {0}",
File);
736 std::string NewCode(*
Code);
737 for (
const auto &Change : Params.contentChanges) {
742 Server->removeDocument(
File);
743 elog(
"Failed to update {0}: {1}",
File, std::move(Err));
747 Server->addDocument(
File, NewCode, encodeVersion(Params.textDocument.version),
751void ClangdLSPServer::onDocumentDidSave(
752 const DidSaveTextDocumentParams &Params) {
753 Server->reparseOpenFilesIfNeeded([](llvm::StringRef) {
return true; });
756void ClangdLSPServer::onFileEvent(
const DidChangeWatchedFilesParams &Params) {
761 Server->onFileEvent(Params);
768void ClangdLSPServer::onCommand(
const ExecuteCommandParams &Params,
769 Callback<llvm::json::Value> Reply) {
772 return Reply(llvm::make_error<LSPError>(
773 llvm::formatv(
"Unsupported command \"{0}\".", Params.command).str(),
776 It->second(Params.argument, std::move(Reply));
779void ClangdLSPServer::onCommandApplyEdit(
const WorkspaceEdit &WE,
780 Callback<llvm::json::Value> Reply) {
789 applyEdit(WE,
"Fix applied.", std::move(Reply));
792void ClangdLSPServer::onCommandApplyTweak(
const TweakArgs &
Args,
793 Callback<llvm::json::Value> Reply) {
794 auto Action = [
this, Reply = std::move(Reply)](
795 llvm::Expected<Tweak::Effect> R)
mutable {
797 return Reply(R.takeError());
799 assert(R->ShowMessage || (!R->ApplyEdits.empty() &&
"tweak has no effect"));
801 if (R->ShowMessage) {
802 ShowMessageParams Msg;
803 Msg.message = *R->ShowMessage;
808 if (R->ApplyEdits.empty())
809 return Reply(
"Tweak applied.");
811 if (
auto Err = validateEdits(*Server, R->ApplyEdits))
812 return Reply(std::move(Err));
816 WE.changes.emplace();
817 for (
const auto &It : R->ApplyEdits) {
819 It.second.asTextEdits();
822 return applyEdit(std::move(WE),
"Tweak applied.", std::move(Reply));
824 Server->applyTweak(
Args.file.file(),
Args.selection,
Args.tweakID,
828void ClangdLSPServer::onCommandApplyRename(
const RenameParams &R,
829 Callback<llvm::json::Value> Reply) {
830 onRename(R, [
this, Reply = std::move(Reply)](
831 llvm::Expected<WorkspaceEdit> Edit)
mutable {
833 Reply(Edit.takeError());
834 applyEdit(std::move(*Edit),
"Rename applied.", std::move(Reply));
838void ClangdLSPServer::applyEdit(WorkspaceEdit WE, llvm::json::Value Success,
839 Callback<llvm::json::Value> Reply) {
840 ApplyWorkspaceEditParams Edit;
841 Edit.edit = std::move(WE);
843 Edit, [Reply = std::move(Reply), SuccessMessage = std::move(Success)](
844 llvm::Expected<ApplyWorkspaceEditResponse> Response)
mutable {
846 return Reply(Response.takeError());
847 if (!Response->applied) {
848 std::string Reason = Response->failureReason
849 ? *Response->failureReason
851 return Reply(error(
"edits were not applied: {0}", Reason));
853 return Reply(SuccessMessage);
857void ClangdLSPServer::onWorkspaceSymbol(
858 const WorkspaceSymbolParams &Params,
859 Callback<std::vector<SymbolInformation>> Reply) {
860 Server->workspaceSymbols(
862 [Reply = std::move(Reply),
863 this](llvm::Expected<std::vector<SymbolInformation>> Items)
mutable {
865 return Reply(Items.takeError());
866 for (auto &Sym : *Items)
867 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds);
869 Reply(std::move(*Items));
873void ClangdLSPServer::onPrepareRename(
const TextDocumentPositionParams &Params,
874 Callback<PrepareRenameResult> Reply) {
875 Server->prepareRename(
876 Params.textDocument.uri.file(), Params.position, std::nullopt,
878 [Reply = std::move(Reply)](llvm::Expected<RenameResult> Result)
mutable {
880 return Reply(Result.takeError());
881 PrepareRenameResult PrepareResult;
882 PrepareResult.range = Result->Target;
883 PrepareResult.placeholder = Result->Placeholder;
884 return Reply(std::move(PrepareResult));
888void ClangdLSPServer::onRename(
const RenameParams &Params,
889 Callback<WorkspaceEdit> Reply) {
890 Path File = std::string(Params.textDocument.uri.file());
891 if (!Server->getDraft(
File))
892 return Reply(llvm::make_error<LSPError>(
894 Server->rename(
File, Params.position, Params.newName, Opts.
Rename,
895 [
File, Params, Reply = std::move(Reply),
896 this](llvm::Expected<RenameResult> R)
mutable {
898 return Reply(R.takeError());
899 if (auto Err = validateEdits(*Server, R->GlobalChanges))
900 return Reply(std::move(Err));
901 WorkspaceEdit Result;
904 Result.changes.emplace();
905 for (const auto &Rep : R->GlobalChanges) {
907 .changes)[URI::createFile(Rep.first()).toString()] =
908 Rep.second.asTextEdits();
914void ClangdLSPServer::onDocumentDidClose(
915 const DidCloseTextDocumentParams &Params) {
916 PathRef File = Params.textDocument.uri.file();
917 Server->removeDocument(File);
920 std::lock_guard<std::mutex> Lock(DiagRefMutex);
921 DiagRefMap.erase(File);
924 std::lock_guard<std::mutex> HLock(SemanticTokensMutex);
925 LastSemanticTokens.erase(File);
932 PublishDiagnosticsParams Notification;
933 Notification.uri = URIForFile::canonicalize(File, File);
934 PublishDiagnostics(Notification);
937void ClangdLSPServer::onDocumentOnTypeFormatting(
938 const DocumentOnTypeFormattingParams &Params,
939 Callback<std::vector<TextEdit>> Reply) {
940 auto File = Params.textDocument.uri.file();
941 Server->formatOnType(File, Params.position, Params.ch, std::move(Reply));
944void ClangdLSPServer::onDocumentRangeFormatting(
945 const DocumentRangeFormattingParams &Params,
946 Callback<std::vector<TextEdit>> Reply) {
947 auto File = Params.textDocument.uri.file();
948 auto Code = Server->getDraft(File);
949 Server->formatFile(File, Params.range,
950 [
Code = std::move(
Code), Reply = std::move(Reply)](
951 llvm::Expected<tooling::Replacements> Result)
mutable {
953 Reply(replacementsToEdits(*Code, Result.get()));
955 Reply(Result.takeError());
959void ClangdLSPServer::onDocumentFormatting(
960 const DocumentFormattingParams &Params,
961 Callback<std::vector<TextEdit>> Reply) {
962 auto File = Params.textDocument.uri.file();
963 auto Code = Server->getDraft(File);
964 Server->formatFile(File,
966 [
Code = std::move(
Code), Reply = std::move(Reply)](
967 llvm::Expected<tooling::Replacements> Result)
mutable {
971 Reply(Result.takeError());
977static std::vector<SymbolInformation>
980 std::vector<SymbolInformation>
Results;
981 std::function<void(
const DocumentSymbol &, llvm::StringRef)> Process =
982 [&](
const DocumentSymbol &S, std::optional<llvm::StringRef> ParentName) {
984 SI.
containerName = std::string(ParentName ?
"" : *ParentName);
990 Results.push_back(std::move(SI));
991 std::string FullName =
992 !ParentName ? S.name : (ParentName->str() +
"::" + S.name);
993 for (
auto &
C : S.children)
994 Process(
C, FullName);
996 for (
auto &S : Symbols)
1001void ClangdLSPServer::onDocumentSymbol(
const DocumentSymbolParams &Params,
1002 Callback<llvm::json::Value> Reply) {
1003 URIForFile FileURI = Params.textDocument.uri;
1004 Server->documentSymbols(
1005 Params.textDocument.uri.file(),
1006 [
this, FileURI, Reply = std::move(Reply)](
1007 llvm::Expected<std::vector<DocumentSymbol>> Items)
mutable {
1009 return Reply(Items.takeError());
1010 adjustSymbolKinds(*Items, SupportedSymbolKinds);
1011 if (SupportsHierarchicalDocumentSymbol)
1012 return Reply(std::move(*Items));
1013 return Reply(flattenSymbolHierarchy(*Items, FileURI));
1017void ClangdLSPServer::onFoldingRange(
1018 const FoldingRangeParams &Params,
1019 Callback<std::vector<FoldingRange>> Reply) {
1020 Server->foldingRanges(Params.textDocument.uri.file(), std::move(Reply));
1026 return std::nullopt;
1029 }
else if (
Action.edit) {
1030 Cmd.command = std::string(ApplyFixCommand);
1031 Cmd.argument = *
Action.edit;
1033 return std::nullopt;
1035 Cmd.title =
Action.title;
1036 if (
Action.kind && *
Action.kind == CodeAction::QUICKFIX_KIND)
1037 Cmd.title =
"Apply fix: " + Cmd.title;
1041void ClangdLSPServer::onCodeAction(
const CodeActionParams &Params,
1042 Callback<llvm::json::Value> Reply) {
1043 URIForFile File = Params.textDocument.uri;
1044 std::map<ClangdServer::DiagRef, clangd::Diagnostic> ToLSPDiags;
1045 ClangdServer::CodeActionInputs Inputs;
1047 for (
const auto& LSPDiag : Params.context.diagnostics) {
1048 if (
auto DiagRef = getDiagRef(File.file(), LSPDiag)) {
1049 ToLSPDiags[*DiagRef] = LSPDiag;
1050 Inputs.Diagnostics.push_back(*DiagRef);
1053 Inputs.File = File.file();
1054 Inputs.Selection = Params.range;
1055 Inputs.RequestedActionKinds = Params.context.only;
1056 Inputs.TweakFilter = [
this](
const Tweak &T) {
1057 return Opts.TweakFilter(T);
1060 Reply = std::move(Reply),
1061 ToLSPDiags = std::move(ToLSPDiags), File,
1062 Selection = Params.range](
1063 llvm::Expected<ClangdServer::CodeActionResult> Fixits)
mutable {
1065 return Reply(Fixits.takeError());
1066 std::vector<CodeAction> CAs;
1067 auto Version = decodeVersion(Fixits->Version);
1068 for (
const auto &QF : Fixits->QuickFixes) {
1069 CAs.push_back(toCodeAction(QF.F, File, Version, SupportsDocumentChanges,
1070 SupportsChangeAnnotation));
1071 if (
auto It = ToLSPDiags.find(QF.Diag);
1072 It != ToLSPDiags.end()) {
1073 CAs.back().diagnostics = {It->second};
1077 for (
const auto &R : Fixits->Renames)
1078 CAs.push_back(toCodeAction(R, File));
1080 for (
const auto &TR : Fixits->TweakRefs)
1081 CAs.push_back(toCodeAction(TR, File, Selection));
1085 CodeAction *OnlyFix =
nullptr;
1086 for (
auto &
Action : CAs) {
1087 if (
Action.kind && *
Action.kind == CodeAction::QUICKFIX_KIND) {
1096 OnlyFix->isPreferred =
true;
1097 if (ToLSPDiags.size() == 1 &&
1098 ToLSPDiags.begin()->second.range == Selection)
1099 OnlyFix->diagnostics = {ToLSPDiags.begin()->second};
1102 if (SupportsCodeAction)
1103 return Reply(llvm::json::Array(CAs));
1105 for (
const auto &
Action : CAs) {
1107 Commands.push_back(std::move(*Command));
1109 return Reply(llvm::json::Array(
Commands));
1111 Server->codeAction(Inputs, std::move(CB));
1114void ClangdLSPServer::onCompletion(
const CompletionParams &Params,
1115 Callback<CompletionList> Reply) {
1116 if (!shouldRunCompletion(Params)) {
1119 vlog(
"ignored auto-triggered completion, preceding char did not match");
1120 return Reply(CompletionList());
1122 auto Opts = this->Opts.CodeComplete;
1123 if (Params.limit && *Params.limit >= 0)
1124 Opts.Limit = *Params.limit;
1125 Server->codeComplete(Params.textDocument.uri.file(), Params.position, Opts,
1126 [Reply = std::move(Reply), Opts,
1127 this](llvm::Expected<CodeCompleteResult> List)
mutable {
1129 return Reply(List.takeError());
1130 CompletionList LSPList;
1131 LSPList.isIncomplete = List->HasMore;
1132 for (const auto &R : List->Completions) {
1133 CompletionItem C = R.render(Opts);
1134 C.kind = adjustKindToCapability(
1135 C.kind, SupportedCompletionItemKinds);
1136 if (!SupportsCompletionLabelDetails)
1137 removeCompletionLabelDetails(C);
1138 LSPList.items.push_back(std::move(C));
1140 return Reply(std::move(LSPList));
1144void ClangdLSPServer::onSignatureHelp(
const TextDocumentPositionParams &Params,
1145 Callback<SignatureHelp> Reply) {
1146 Server->signatureHelp(Params.textDocument.uri.file(), Params.position,
1147 Opts.SignatureHelpDocumentationFormat,
1148 [Reply = std::move(Reply),
this](
1149 llvm::Expected<SignatureHelp>
Signature)
mutable {
1151 return Reply(Signature.takeError());
1152 if (SupportsOffsetsInSignatureHelp)
1153 return Reply(std::move(*Signature));
1156 for (auto &SigInfo : Signature->signatures) {
1157 for (auto &Param : SigInfo.parameters)
1158 Param.labelOffsets.reset();
1184void ClangdLSPServer::onGoToDefinition(
const TextDocumentPositionParams &Params,
1185 Callback<std::vector<Location>> Reply) {
1186 Server->locateSymbolAt(
1187 Params.textDocument.uri.file(), Params.position,
1188 [Params, Reply = std::move(Reply)](
1189 llvm::Expected<std::vector<LocatedSymbol>> Symbols)
mutable {
1191 return Reply(Symbols.takeError());
1192 std::vector<Location> Defs;
1193 for (auto &S : *Symbols) {
1194 if (Location *Toggle = getToggle(Params, S))
1195 return Reply(std::vector<Location>{std::move(*Toggle)});
1196 Defs.push_back(S.Definition.value_or(S.PreferredDeclaration));
1198 Reply(std::move(Defs));
1202void ClangdLSPServer::onGoToDeclaration(
1203 const TextDocumentPositionParams &Params,
1204 Callback<std::vector<Location>> Reply) {
1205 Server->locateSymbolAt(
1206 Params.textDocument.uri.file(), Params.position,
1207 [Params, Reply = std::move(Reply)](
1208 llvm::Expected<std::vector<LocatedSymbol>> Symbols)
mutable {
1210 return Reply(Symbols.takeError());
1211 std::vector<Location> Decls;
1212 for (auto &S : *Symbols) {
1213 if (Location *Toggle = getToggle(Params, S))
1214 return Reply(std::vector<Location>{std::move(*Toggle)});
1215 Decls.push_back(std::move(S.PreferredDeclaration));
1217 Reply(std::move(Decls));
1221void ClangdLSPServer::onSwitchSourceHeader(
1222 const TextDocumentIdentifier &Params,
1223 Callback<std::optional<URIForFile>> Reply) {
1224 Server->switchSourceHeader(
1226 [Reply = std::move(Reply),
1227 Params](llvm::Expected<std::optional<clangd::Path>>
Path)
mutable {
1229 return Reply(Path.takeError());
1231 return Reply(URIForFile::canonicalize(**Path, Params.uri.file()));
1232 return Reply(std::nullopt);
1236void ClangdLSPServer::onDocumentHighlight(
1237 const TextDocumentPositionParams &Params,
1238 Callback<std::vector<DocumentHighlight>> Reply) {
1239 Server->findDocumentHighlights(Params.textDocument.uri.file(),
1240 Params.position, std::move(Reply));
1243void ClangdLSPServer::onHover(
const TextDocumentPositionParams &Params,
1244 Callback<std::optional<Hover>> Reply) {
1245 Server->findHover(Params.textDocument.uri.file(), Params.position,
1246 [Reply = std::move(Reply),
1247 this](llvm::Expected<std::optional<HoverInfo>> H)
mutable {
1249 return Reply(H.takeError());
1251 return Reply(std::nullopt);
1254 R.contents.kind = HoverContentFormat;
1255 R.range = (*H)->SymRange;
1256 switch (HoverContentFormat) {
1257 case MarkupKind::PlainText:
1258 R.contents.value = (*H)->present().asPlainText();
1259 return Reply(std::move(R));
1260 case MarkupKind::Markdown:
1261 R.contents.value = (*H)->present().asMarkdown();
1262 return Reply(std::move(R));
1264 llvm_unreachable(
"unhandled MarkupKind");
1271 llvm::json::Object Result{{
1272 {
"name", std::move(THI.
name)},
1273 {
"kind",
static_cast<int>(THI.
kind)},
1274 {
"uri", std::move(THI.
uri)},
1275 {
"range", THI.
range},
1277 {
"data", std::move(THI.
data)},
1282 Result[
"detail"] = std::move(*THI.
detail);
1288 Result[
"parents"] = std::move(
Parents);
1295 Result[
"children"] = std::move(
Children);
1300void ClangdLSPServer::onTypeHierarchy(
const TypeHierarchyPrepareParams &Params,
1301 Callback<llvm::json::Value> Reply) {
1303 [Reply = std::move(Reply)](
1304 llvm::Expected<std::vector<TypeHierarchyItem>> Resp)
mutable {
1306 Reply(Resp.takeError());
1309 if (Resp->empty()) {
1315 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1316 Params.resolve, Params.direction, std::move(Serialize));
1319void ClangdLSPServer::onResolveTypeHierarchy(
1320 const ResolveTypeHierarchyItemParams &Params,
1321 Callback<llvm::json::Value> Reply) {
1323 [Reply = std::move(Reply)](
1324 llvm::Expected<std::optional<TypeHierarchyItem>> Resp)
mutable {
1326 Reply(Resp.takeError());
1330 Reply(std::move(*Resp));
1335 Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction,
1336 std::move(Serialize));
1339void ClangdLSPServer::onPrepareTypeHierarchy(
1340 const TypeHierarchyPrepareParams &Params,
1341 Callback<std::vector<TypeHierarchyItem>> Reply) {
1342 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1343 Params.resolve, Params.direction, std::move(Reply));
1346void ClangdLSPServer::onSuperTypes(
1347 const ResolveTypeHierarchyItemParams &Params,
1348 Callback<std::optional<std::vector<TypeHierarchyItem>>> Reply) {
1349 Server->superTypes(Params.item, std::move(Reply));
1352void ClangdLSPServer::onSubTypes(
1353 const ResolveTypeHierarchyItemParams &Params,
1354 Callback<std::vector<TypeHierarchyItem>> Reply) {
1355 Server->subTypes(Params.item, std::move(Reply));
1358void ClangdLSPServer::onPrepareCallHierarchy(
1359 const CallHierarchyPrepareParams &Params,
1360 Callback<std::vector<CallHierarchyItem>> Reply) {
1361 Server->prepareCallHierarchy(Params.textDocument.uri.file(), Params.position,
1365void ClangdLSPServer::onCallHierarchyIncomingCalls(
1366 const CallHierarchyIncomingCallsParams &Params,
1367 Callback<std::vector<CallHierarchyIncomingCall>> Reply) {
1368 Server->incomingCalls(Params.item, std::move(Reply));
1371void ClangdLSPServer::onClangdInlayHints(
const InlayHintsParams &Params,
1372 Callback<llvm::json::Value> Reply) {
1377 auto Serialize = [Reply = std::move(Reply)](
1378 llvm::Expected<std::vector<InlayHint>>
Hints)
mutable {
1380 Reply(
Hints.takeError());
1383 llvm::json::Array Result;
1384 Result.reserve(
Hints->size());
1385 for (
auto &Hint : *
Hints) {
1386 Result.emplace_back(llvm::json::Object{
1387 {
"kind", llvm::to_string(Hint.kind)},
1388 {
"range", Hint.range},
1389 {
"position", Hint.position},
1393 ((Hint.paddingLeft ?
" " :
"") + llvm::StringRef(Hint.joinLabels()) +
1394 (Hint.paddingRight ?
" " :
""))
1398 Reply(std::move(Result));
1400 Server->inlayHints(Params.textDocument.uri.file(), Params.range,
1401 std::move(Serialize));
1404void ClangdLSPServer::onInlayHint(
const InlayHintsParams &Params,
1405 Callback<std::vector<InlayHint>> Reply) {
1406 Server->inlayHints(Params.textDocument.uri.file(), Params.range,
1410void ClangdLSPServer::applyConfiguration(
1411 const ConfigurationSettings &Settings) {
1413 llvm::StringSet<> ModifiedFiles;
1414 for (
auto &
Entry : Settings.compilationDatabaseChanges) {
1416 auto Old = CDB->getCompileCommand(File);
1418 tooling::CompileCommand(std::move(
Entry.second.workingDirectory), File,
1419 std::move(
Entry.second.compilationCommand),
1422 CDB->setCompileCommand(File, std::move(New));
1423 ModifiedFiles.insert(File);
1427 Server->reparseOpenFilesIfNeeded(
1428 [&](llvm::StringRef File) {
return ModifiedFiles.count(File) != 0; });
1431void ClangdLSPServer::maybeExportMemoryProfile() {
1432 if (!trace::enabled() || !ShouldProfile())
1435 static constexpr trace::Metric MemoryUsage(
1436 "memory_usage", trace::Metric::Value,
"component_name");
1437 trace::Span Tracer(
"ProfileBrief");
1440 record(MT,
"clangd_lsp_server", MemoryUsage);
1443void ClangdLSPServer::maybeCleanupMemory() {
1444 if (!Opts.MemoryCleanup || !ShouldCleanupMemory())
1446 Opts.MemoryCleanup();
1450void ClangdLSPServer::onChangeConfiguration(
1451 const DidChangeConfigurationParams &Params) {
1452 applyConfiguration(Params.settings);
1455void ClangdLSPServer::onReference(
1456 const ReferenceParams &Params,
1457 Callback<std::vector<ReferenceLocation>> Reply) {
1458 Server->findReferences(Params.textDocument.uri.file(), Params.position,
1459 Opts.ReferencesLimit, SupportsReferenceContainer,
1460 [Reply = std::move(Reply),
1461 IncludeDecl(Params.context.includeDeclaration)](
1462 llvm::Expected<ReferencesResult> Refs)
mutable {
1464 return Reply(Refs.takeError());
1466 std::vector<ReferenceLocation> Result;
1467 Result.reserve(Refs->References.size());
1468 for (auto &Ref : Refs->References) {
1470 Ref.Attributes & ReferencesResult::Declaration;
1471 if (IncludeDecl || !IsDecl)
1472 Result.push_back(std::move(Ref.Loc));
1474 return Reply(std::move(Result));
1478void ClangdLSPServer::onGoToType(
const TextDocumentPositionParams &Params,
1479 Callback<std::vector<Location>> Reply) {
1481 Params.textDocument.uri.file(), Params.position,
1482 [Reply = std::move(Reply)](
1483 llvm::Expected<std::vector<LocatedSymbol>> Types)
mutable {
1485 return Reply(Types.takeError());
1486 std::vector<Location> Response;
1487 for (const LocatedSymbol &Sym : *Types)
1488 Response.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration));
1489 return Reply(std::move(Response));
1493void ClangdLSPServer::onGoToImplementation(
1494 const TextDocumentPositionParams &Params,
1495 Callback<std::vector<Location>> Reply) {
1496 Server->findImplementations(
1497 Params.textDocument.uri.file(), Params.position,
1498 [Reply = std::move(Reply)](
1499 llvm::Expected<std::vector<LocatedSymbol>> Overrides)
mutable {
1501 return Reply(Overrides.takeError());
1502 std::vector<Location> Impls;
1503 for (const LocatedSymbol &Sym : *Overrides)
1504 Impls.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration));
1505 return Reply(std::move(Impls));
1509void ClangdLSPServer::onSymbolInfo(
const TextDocumentPositionParams &Params,
1510 Callback<std::vector<SymbolDetails>> Reply) {
1511 Server->symbolInfo(Params.textDocument.uri.file(), Params.position,
1515void ClangdLSPServer::onSelectionRange(
1516 const SelectionRangeParams &Params,
1517 Callback<std::vector<SelectionRange>> Reply) {
1518 Server->semanticRanges(
1519 Params.textDocument.uri.file(), Params.positions,
1520 [Reply = std::move(Reply)](
1521 llvm::Expected<std::vector<SelectionRange>> Ranges)
mutable {
1523 return Reply(Ranges.takeError());
1524 return Reply(std::move(*Ranges));
1528void ClangdLSPServer::onDocumentLink(
1529 const DocumentLinkParams &Params,
1530 Callback<std::vector<DocumentLink>> Reply) {
1536 Server->documentLinks(
1537 Params.textDocument.uri.file(),
1538 [Reply = std::move(Reply)](
1539 llvm::Expected<std::vector<DocumentLink>> Links)
mutable {
1541 return Reply(Links.takeError());
1543 return Reply(std::move(Links));
1549 for (
char &
C : llvm::reverse(S)) {
1556 S.insert(S.begin(),
'1');
1559void ClangdLSPServer::onSemanticTokens(
const SemanticTokensParams &Params,
1560 Callback<SemanticTokens> CB) {
1561 auto File = Params.textDocument.uri.file();
1562 Server->semanticHighlights(
1563 Params.textDocument.uri.file(),
1564 [
this, File(File.str()), CB(std::move(CB)),
Code(Server->getDraft(File))](
1565 llvm::Expected<std::vector<HighlightingToken>> HT)
mutable {
1567 return CB(HT.takeError());
1568 SemanticTokens Result;
1569 Result.tokens = toSemanticTokens(*HT, *Code);
1571 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1572 auto &Last = LastSemanticTokens[File];
1574 Last.tokens = Result.tokens;
1575 increment(Last.resultId);
1576 Result.resultId = Last.resultId;
1578 CB(std::move(Result));
1582void ClangdLSPServer::onSemanticTokensDelta(
1583 const SemanticTokensDeltaParams &Params,
1584 Callback<SemanticTokensOrDelta> CB) {
1585 auto File = Params.textDocument.uri.file();
1586 Server->semanticHighlights(
1587 Params.textDocument.uri.file(),
1588 [
this, PrevResultID(Params.previousResultId), File(File.str()),
1589 CB(std::move(CB)),
Code(Server->getDraft(File))](
1590 llvm::Expected<std::vector<HighlightingToken>> HT)
mutable {
1592 return CB(HT.takeError());
1593 std::vector<SemanticToken> Toks = toSemanticTokens(*HT, *Code);
1595 SemanticTokensOrDelta Result;
1597 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1598 auto &Last = LastSemanticTokens[File];
1600 if (PrevResultID == Last.resultId) {
1601 Result.edits = diffTokens(Last.tokens, Toks);
1603 vlog(
"semanticTokens/full/delta: wanted edits vs {0} but last "
1604 "result had ID {1}. Returning full token list.",
1605 PrevResultID, Last.resultId);
1606 Result.tokens = Toks;
1609 Last.tokens = std::move(Toks);
1610 increment(Last.resultId);
1611 Result.resultId = Last.resultId;
1614 CB(std::move(Result));
1618void ClangdLSPServer::onMemoryUsage(
const NoParams &,
1619 Callback<MemoryTree> Reply) {
1620 llvm::BumpPtrAllocator DetailAlloc;
1621 MemoryTree MT(&DetailAlloc);
1623 Reply(std::move(MT));
1626void ClangdLSPServer::onAST(
const ASTParams &Params,
1627 Callback<std::optional<ASTNode>> CB) {
1628 Server->getAST(Params.textDocument.uri.file(), Params.range, std::move(CB));
1633 : ShouldProfile(std::chrono::minutes(5),
1634 std::chrono::minutes(1)),
1635 ShouldCleanupMemory(std::chrono::minutes(1),
1636 std::chrono::minutes(1)),
1637 BackgroundContext(
Context::current().clone()), Transp(Transp),
1639 SupportedSymbolKinds(defaultSymbolKinds()),
1640 SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts) {
1641 if (Opts.ConfigProvider) {
1642 assert(!Opts.ContextProvider &&
1643 "Only one of ConfigProvider and ContextProvider allowed!");
1645 Opts.ConfigProvider,
this);
1648 Bind.
method(
"initialize",
this, &ClangdLSPServer::onInitialize);
1651void ClangdLSPServer::bindMethods(
LSPBinder &Bind,
1654 Bind.
notification(
"initialized",
this, &ClangdLSPServer::onInitialized);
1655 Bind.
method(
"shutdown",
this, &ClangdLSPServer::onShutdown);
1656 Bind.
method(
"sync",
this, &ClangdLSPServer::onSync);
1657 Bind.
method(
"textDocument/rangeFormatting",
this, &ClangdLSPServer::onDocumentRangeFormatting);
1658 Bind.
method(
"textDocument/onTypeFormatting",
this, &ClangdLSPServer::onDocumentOnTypeFormatting);
1659 Bind.
method(
"textDocument/formatting",
this, &ClangdLSPServer::onDocumentFormatting);
1660 Bind.
method(
"textDocument/codeAction",
this, &ClangdLSPServer::onCodeAction);
1661 Bind.
method(
"textDocument/completion",
this, &ClangdLSPServer::onCompletion);
1662 Bind.
method(
"textDocument/signatureHelp",
this, &ClangdLSPServer::onSignatureHelp);
1663 Bind.
method(
"textDocument/definition",
this, &ClangdLSPServer::onGoToDefinition);
1664 Bind.
method(
"textDocument/declaration",
this, &ClangdLSPServer::onGoToDeclaration);
1665 Bind.
method(
"textDocument/typeDefinition",
this, &ClangdLSPServer::onGoToType);
1666 Bind.
method(
"textDocument/implementation",
this, &ClangdLSPServer::onGoToImplementation);
1667 Bind.
method(
"textDocument/references",
this, &ClangdLSPServer::onReference);
1668 Bind.
method(
"textDocument/switchSourceHeader",
this, &ClangdLSPServer::onSwitchSourceHeader);
1669 Bind.
method(
"textDocument/prepareRename",
this, &ClangdLSPServer::onPrepareRename);
1670 Bind.
method(
"textDocument/rename",
this, &ClangdLSPServer::onRename);
1671 Bind.
method(
"textDocument/hover",
this, &ClangdLSPServer::onHover);
1672 Bind.
method(
"textDocument/documentSymbol",
this, &ClangdLSPServer::onDocumentSymbol);
1673 Bind.
method(
"workspace/executeCommand",
this, &ClangdLSPServer::onCommand);
1674 Bind.
method(
"textDocument/documentHighlight",
this, &ClangdLSPServer::onDocumentHighlight);
1675 Bind.
method(
"workspace/symbol",
this, &ClangdLSPServer::onWorkspaceSymbol);
1676 Bind.
method(
"textDocument/ast",
this, &ClangdLSPServer::onAST);
1677 Bind.
notification(
"textDocument/didOpen",
this, &ClangdLSPServer::onDocumentDidOpen);
1678 Bind.
notification(
"textDocument/didClose",
this, &ClangdLSPServer::onDocumentDidClose);
1679 Bind.
notification(
"textDocument/didChange",
this, &ClangdLSPServer::onDocumentDidChange);
1680 Bind.
notification(
"textDocument/didSave",
this, &ClangdLSPServer::onDocumentDidSave);
1681 Bind.
notification(
"workspace/didChangeWatchedFiles",
this, &ClangdLSPServer::onFileEvent);
1682 Bind.
notification(
"workspace/didChangeConfiguration",
this, &ClangdLSPServer::onChangeConfiguration);
1683 Bind.
method(
"textDocument/symbolInfo",
this, &ClangdLSPServer::onSymbolInfo);
1684 Bind.
method(
"textDocument/typeHierarchy",
this, &ClangdLSPServer::onTypeHierarchy);
1685 Bind.
method(
"typeHierarchy/resolve",
this, &ClangdLSPServer::onResolveTypeHierarchy);
1686 Bind.
method(
"textDocument/prepareTypeHierarchy",
this, &ClangdLSPServer::onPrepareTypeHierarchy);
1687 Bind.
method(
"typeHierarchy/supertypes",
this, &ClangdLSPServer::onSuperTypes);
1688 Bind.
method(
"typeHierarchy/subtypes",
this, &ClangdLSPServer::onSubTypes);
1689 Bind.
method(
"textDocument/prepareCallHierarchy",
this, &ClangdLSPServer::onPrepareCallHierarchy);
1690 Bind.
method(
"callHierarchy/incomingCalls",
this, &ClangdLSPServer::onCallHierarchyIncomingCalls);
1691 Bind.
method(
"textDocument/selectionRange",
this, &ClangdLSPServer::onSelectionRange);
1692 Bind.
method(
"textDocument/documentLink",
this, &ClangdLSPServer::onDocumentLink);
1693 Bind.
method(
"textDocument/semanticTokens/full",
this, &ClangdLSPServer::onSemanticTokens);
1694 Bind.
method(
"textDocument/semanticTokens/full/delta",
this, &ClangdLSPServer::onSemanticTokensDelta);
1695 Bind.
method(
"clangd/inlayHints",
this, &ClangdLSPServer::onClangdInlayHints);
1696 Bind.
method(
"textDocument/inlayHint",
this, &ClangdLSPServer::onInlayHint);
1697 Bind.
method(
"$/memoryUsage",
this, &ClangdLSPServer::onMemoryUsage);
1698 Bind.
method(
"textDocument/foldingRange",
this, &ClangdLSPServer::onFoldingRange);
1699 Bind.
command(ApplyFixCommand,
this, &ClangdLSPServer::onCommandApplyEdit);
1700 Bind.
command(ApplyTweakCommand,
this, &ClangdLSPServer::onCommandApplyTweak);
1701 Bind.
command(ApplyRenameCommand,
this, &ClangdLSPServer::onCommandApplyRename);
1709 CreateWorkDoneProgress = Bind.
outgoingMethod(
"window/workDoneProgress/create");
1714 SemanticTokensRefresh = Bind.
outgoingMethod(
"workspace/semanticTokens/refresh");
1719 IsBeingDestroyed =
true;
1727 bool CleanExit =
true;
1728 if (
auto Err = Transp.
loop(*MsgHandler)) {
1729 elog(
"Transport error: {0}", std::move(Err));
1733 return CleanExit && ShutdownRequestReceived;
1738 Server->profile(MT.
child(
"clangd_server"));
1741std::optional<ClangdServer::DiagRef>
1743 std::lock_guard<std::mutex> Lock(DiagRefMutex);
1744 auto DiagToDiagRefIter = DiagRefMap.find(
File);
1745 if (DiagToDiagRefIter == DiagRefMap.end())
1746 return std::nullopt;
1748 const auto &DiagToDiagRefMap = DiagToDiagRefIter->second;
1749 auto FixItsIter = DiagToDiagRefMap.find(toDiagKey(D));
1750 if (FixItsIter == DiagToDiagRefMap.end())
1751 return std::nullopt;
1753 return FixItsIter->second;
1762bool ClangdLSPServer::shouldRunCompletion(
1763 const CompletionParams &Params)
const {
1766 auto Code = Server->getDraft(Params.textDocument.uri.file());
1772 vlog(
"could not convert position '{0}' to offset for file '{1}'",
1773 Params.position, Params.textDocument.uri.file());
1779void ClangdLSPServer::onDiagnosticsReady(
PathRef File, llvm::StringRef Version,
1781 PublishDiagnosticsParams Notification;
1782 Notification.version = decodeVersion(Version);
1784 DiagnosticToDiagRefMap LocalDiagMap;
1787 [&](clangd::Diagnostic LSPDiag, llvm::ArrayRef<Fix> Fixes) {
1788 if (DiagOpts.EmbedFixesInDiagnostics) {
1789 std::vector<CodeAction> CodeActions;
1790 for (const auto &Fix : Fixes)
1791 CodeActions.push_back(toCodeAction(
1792 Fix, Notification.uri, Notification.version,
1793 SupportsDocumentChanges, SupportsChangeAnnotation));
1794 LSPDiag.codeActions.emplace(std::move(CodeActions));
1795 if (LSPDiag.codeActions->size() == 1)
1796 LSPDiag.codeActions->front().isPreferred = true;
1798 LocalDiagMap[toDiagKey(LSPDiag)] = {Diag.Range, Diag.Message};
1799 Notification.diagnostics.push_back(std::move(LSPDiag));
1805 std::lock_guard<std::mutex> Lock(DiagRefMutex);
1806 DiagRefMap[
File] = LocalDiagMap;
1810 PublishDiagnostics(Notification);
1813void ClangdLSPServer::onInactiveRegionsReady(
1814 PathRef File, std::vector<Range> InactiveRegions) {
1815 InactiveRegionsParams Notification;
1817 Notification.InactiveRegions = std::move(InactiveRegions);
1819 PublishInactiveRegions(Notification);
1822void ClangdLSPServer::onBackgroundIndexProgress(
1823 const BackgroundQueue::Stats &Stats) {
1824 static const char ProgressToken[] =
"backgroundIndexProgress";
1827 maybeCleanupMemory();
1829 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1831 auto NotifyProgress = [
this](
const BackgroundQueue::Stats &Stats) {
1832 if (BackgroundIndexProgressState != BackgroundIndexProgress::Live) {
1833 WorkDoneProgressBegin Begin;
1834 Begin.percentage =
true;
1835 Begin.title =
"indexing";
1836 BeginWorkDoneProgress({ProgressToken, std::move(Begin)});
1837 BackgroundIndexProgressState = BackgroundIndexProgress::Live;
1840 if (Stats.Completed < Stats.Enqueued) {
1841 assert(Stats.Enqueued > Stats.LastIdle);
1842 WorkDoneProgressReport Report;
1843 Report.percentage = 100 * (Stats.Completed - Stats.LastIdle) /
1844 (Stats.Enqueued - Stats.LastIdle);
1846 llvm::formatv(
"{0}/{1}", Stats.Completed - Stats.LastIdle,
1847 Stats.Enqueued - Stats.LastIdle);
1848 ReportWorkDoneProgress({ProgressToken, std::move(Report)});
1850 assert(Stats.Completed == Stats.Enqueued);
1851 EndWorkDoneProgress({ProgressToken, WorkDoneProgressEnd()});
1852 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
1856 switch (BackgroundIndexProgressState) {
1857 case BackgroundIndexProgress::Unsupported:
1859 case BackgroundIndexProgress::Creating:
1861 PendingBackgroundIndexProgress = Stats;
1863 case BackgroundIndexProgress::Empty: {
1864 if (BackgroundIndexSkipCreate) {
1865 NotifyProgress(Stats);
1869 PendingBackgroundIndexProgress = Stats;
1870 BackgroundIndexProgressState = BackgroundIndexProgress::Creating;
1871 WorkDoneProgressCreateParams CreateRequest;
1872 CreateRequest.token = ProgressToken;
1873 CreateWorkDoneProgress(
1875 [
this, NotifyProgress](llvm::Expected<std::nullptr_t>
E) {
1876 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1878 NotifyProgress(this->PendingBackgroundIndexProgress);
1880 elog(
"Failed to create background index progress bar: {0}",
1883 BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported;
1888 case BackgroundIndexProgress::Live:
1889 NotifyProgress(Stats);
1894void ClangdLSPServer::onFileUpdated(
PathRef File,
const TUStatus &Status) {
1895 if (!SupportFileStatus)
1905 NotifyFileStatus(Status.render(
File));
1908void ClangdLSPServer::onSemanticsMaybeChanged(
PathRef File) {
1909 if (SemanticTokensRefresh) {
1910 SemanticTokensRefresh(NoParams{}, [](llvm::Expected<std::nullptr_t>
E) {
1913 elog(
"Failed to refresh semantic tokens: {0}",
E.takeError());
static cl::list< std::string > Commands("c", cl::desc("Specify command to run"), cl::value_desc("command"), cl::cat(ClangQueryCategory))
std::vector< CodeCompletionResult > Results
std::vector< std::unique_ptr< HTMLNode > > Children
std::vector< HeaderHandle > Path
std::vector< FixItHint > Hints
WantDiagnostics Diagnostics
WantDiagnostics WantDiags
#define SPAN_ATTACH(S, Name, Expr)
Attach a key-value pair to a Span event.
bool onReply(llvm::json::Value ID, llvm::Expected< llvm::json::Value > Result) override
MessageHandler(ClangdLSPServer &Server)
bool onCall(llvm::StringRef Method, llvm::json::Value Params, llvm::json::Value ID) override
bool onNotify(llvm::StringRef Method, llvm::json::Value Params) override
llvm::json::Value bindReply(Callback< llvm::json::Value > Reply)
This class exposes ClangdServer's capabilities via Language Server Protocol.
~ClangdLSPServer()
The destructor blocks on any outstanding background tasks.
void profile(MemoryTree &MT) const
Profiles resource-usage.
bool run()
Run LSP server loop, communicating with the Transport provided in the constructor.
static std::function< Context(PathRef)> createConfiguredContextProvider(const config::Provider *Provider, ClangdServer::Callbacks *)
Creates a context provider that loads and installs config.
A context is an immutable container for per-request data that must be propagated through layers that ...
Context derive(const Key< Type > &Key, std::decay_t< Type > Value) const &
Derives a child context It is safe to move or destroy a parent context after calling derive().
Context clone() const
Clone this context object.
static const Context & current()
Returns the context for the current thread, creating it if needed.
LSPBinder collects a table of functions that handle LSP calls.
UntypedOutgoingMethod outgoingMethod(llvm::StringLiteral Method)
Bind a function object to be used for outgoing method calls.
void method(llvm::StringLiteral Method, ThisT *This, void(ThisT::*Handler)(const Param &, Callback< Result >))
Bind a handler for an LSP method.
void notification(llvm::StringLiteral Method, ThisT *This, void(ThisT::*Handler)(const Param &))
Bind a handler for an LSP notification.
UntypedOutgoingNotification outgoingNotification(llvm::StringLiteral Method)
Bind a function object to be used for outgoing notifications.
void command(llvm::StringLiteral Command, ThisT *This, void(ThisT::*Handler)(const Param &, Callback< Result >))
Bind a handler for an LSP command.
Wrapper for vfs::FileSystem for use in multithreaded programs like clangd.
virtual void call(llvm::StringRef Method, llvm::json::Value Params, llvm::json::Value ID)=0
virtual void notify(llvm::StringRef Method, llvm::json::Value Params)=0
virtual llvm::Error loop(MessageHandler &)=0
static URI createFile(llvm::StringRef AbsolutePath)
This creates a file:// URI for AbsolutePath. The path must be absolute.
WithContext replaces Context::current() with a provided scope.
Records an event whose duration is the lifetime of the Span object.
@ Info
An information message.
llvm::StringRef toSemanticTokenModifier(HighlightingModifier Modifier)
std::bitset< SymbolKindMax+1 > SymbolKindBitset
std::function< void()> Canceler
A canceller requests cancellation of a task, when called.
constexpr auto CompletionItemKindMin
llvm::Error applyChange(std::string &Contents, const TextDocumentContentChangeEvent &Change)
Apply an incremental update to a text document.
llvm::StringRef toSemanticTokenType(HighlightingKind Kind)
SystemIncludeExtractorFn getSystemIncludeExtractor(llvm::ArrayRef< std::string > QueryDriverGlobs)
std::string Path
A typedef to represent a file path.
static std::vector< llvm::StringRef > semanticTokenModifiers()
constexpr auto SymbolKindMin
Key< OffsetEncoding > kCurrentOffsetEncoding
void toLSPDiags(const Diag &D, const URIForFile &File, const ClangdDiagnosticOptions &Opts, llvm::function_ref< void(clangd::Diagnostic, llvm::ArrayRef< Fix >)> OutFn)
Conversion to LSP diagnostics.
@ TriggerCharacter
Completion was triggered by a trigger character specified by the triggerCharacters properties of the ...
bool allowImplicitCompletion(llvm::StringRef Content, unsigned Offset)
void record(const MemoryTree &MT, std::string RootName, const trace::Metric &Out)
Records total memory usage of each node under Out.
llvm::StringMap< Edit > FileEdits
A mapping from absolute file path (the one used for accessing the underlying VFS) to edits.
void vlog(const char *Fmt, Ts &&... Vals)
static const char * toString(OffsetEncoding OE)
std::string platformString()
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
llvm::json::Value serializeTHIForExtension(TypeHierarchyItem THI)
static std::vector< SymbolInformation > flattenSymbolHierarchy(llvm::ArrayRef< DocumentSymbol > Symbols, const URIForFile &FileURI)
The functions constructs a flattened view of the DocumentSymbol hierarchy.
static void increment(std::string &S)
std::string featureString()
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
SymbolKind adjustKindToCapability(SymbolKind Kind, SymbolKindBitset &SupportedSymbolKinds)
static std::optional< Command > asCommand(const CodeAction &Action)
std::pair< Context, Canceler > cancelableTask(int Reason)
Defines a new task whose cancellation may be requested.
void log(const char *Fmt, Ts &&... Vals)
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
NoParams InitializedParams
static std::vector< llvm::StringRef > semanticTokenTypes()
@ Auto
Diagnostics must not be generated for this snapshot.
@ No
Diagnostics must be generated for this snapshot.
static Location * getToggle(const TextDocumentPositionParams &Point, LocatedSymbol &Sym)
std::bitset< CompletionItemKindMax+1 > CompletionItemKindBitset
@ Incremental
Documents are synced by sending the full content on open.
llvm::StringRef PathRef
A typedef to represent a ref to file path.
void elog(const char *Fmt, Ts &&... Vals)
std::string versionString()
std::vector< TextEdit > replacementsToEdits(llvm::StringRef Code, const tooling::Replacements &Repls)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
bool EmbedFixesInDiagnostics
If true, Clangd uses an LSP extension to embed the fixes with the diagnostics that are sent to the cl...
bool SendDiagnosticCategory
If true, Clangd uses an LSP extension to send the diagnostic's category to the client.
bool EmitRelatedLocations
If true, Clangd uses the relatedInformation field to include other locations (in particular attached ...
bool 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 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.