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";
79CodeAction toCodeAction(
const ClangdServer::TweakRef &T,
const URIForFile &
File,
83 CA.kind = T.Kind.str();
90 CA.command->title = T.Title;
91 CA.command->command = std::string(ApplyTweakCommand);
95 Args.selection = Selection;
96 CA.command->argument = std::move(
Args);
101CodeAction toCodeAction(
const Fix &F,
const URIForFile &
File,
102 const std::optional<int64_t> &Version,
103 bool SupportsDocumentChanges,
104 bool SupportChangeAnnotation) {
109 if (!SupportsDocumentChanges) {
110 Action.edit->changes.emplace();
112 for (
const auto &
E : F.Edits)
113 Changes.push_back({
E.range,
E.newText,
""});
115 Action.edit->documentChanges.emplace();
116 TextDocumentEdit &Edit =
Action.edit->documentChanges->emplace_back();
117 Edit.textDocument = VersionedTextDocumentIdentifier{{
File}, Version};
118 for (
const auto &
E : F.Edits)
119 Edit.edits.push_back(
121 SupportChangeAnnotation ?
E.annotationId :
""});
122 if (SupportChangeAnnotation) {
123 for (
const auto &[AID, Annotation]: F.Annotations)
124 Action.edit->changeAnnotations[AID] = Annotation;
130void adjustSymbolKinds(llvm::MutableArrayRef<DocumentSymbol> Syms,
132 for (
auto &S : Syms) {
134 adjustSymbolKinds(S.children, Kinds);
140 for (
size_t I =
SymbolKindMin; I <= static_cast<size_t>(SymbolKind::Array);
157llvm::Error validateEdits(
const ClangdServer &Server,
const FileEdits &FE) {
158 size_t InvalidFileCount = 0;
159 llvm::StringRef LastInvalidFile;
160 for (
const auto &It : FE) {
161 if (
auto Draft = Server.getDraft(It.first())) {
165 if (!It.second.canApplyTo(*Draft)) {
167 LastInvalidFile = It.first();
171 if (!InvalidFileCount)
172 return llvm::Error::success();
173 if (InvalidFileCount == 1)
174 return error(
"File must be saved first: {0}", LastInvalidFile);
175 return error(
"Files must be saved first: {0} (and {1} others)",
176 LastInvalidFile, InvalidFileCount - 1);
198 auto Handler = Server.Handlers.NotificationHandlers.find(
Method);
199 if (Handler != Server.Handlers.NotificationHandlers.end()) {
200 Handler->second(std::move(Params));
201 Server.maybeExportMemoryProfile();
202 Server.maybeCleanupMemory();
203 }
else if (!Server.Server) {
204 elog(
"Notification {0} before initialization",
Method);
205 }
else if (
Method ==
"$/cancelRequest") {
206 onCancel(std::move(Params));
208 log(
"unhandled notification {0}",
Method);
214 llvm::json::Value
ID)
override {
220 ReplyOnce Reply(
ID,
Method, &Server, Tracer.Args);
222 auto Handler = Server.Handlers.MethodHandlers.find(
Method);
223 if (Handler != Server.Handlers.MethodHandlers.end()) {
224 Handler->second(std::move(Params), std::move(Reply));
225 }
else if (!Server.Server) {
226 elog(
"Call {0} before initialization.",
Method);
227 Reply(llvm::make_error<LSPError>(
"server not initialized",
230 Reply(llvm::make_error<LSPError>(
"method not found",
237 llvm::Expected<llvm::json::Value> Result)
override {
241 if (
auto IntID =
ID.getAsInteger()) {
242 std::lock_guard<std::mutex> Mutex(CallMutex);
244 for (
size_t Index = 0; Index < ReplyCallbacks.size(); ++Index) {
245 if (ReplyCallbacks[Index].first == *IntID) {
246 ReplyHandler = std::move(ReplyCallbacks[Index].second);
247 ReplyCallbacks.erase(ReplyCallbacks.begin() +
256 ReplyHandler = [&
ID](llvm::Expected<llvm::json::Value> Result) {
257 elog(
"received a reply with ID {0}, but there was no such call",
ID);
259 llvm::consumeError(Result.takeError());
265 log(
"<-- reply({0})",
ID);
266 ReplyHandler(std::move(Result));
268 auto Err = Result.takeError();
269 log(
"<-- reply({0}) error: {1}",
ID, Err);
270 ReplyHandler(std::move(Err));
279 std::optional<std::pair<int, Callback<llvm::json::Value>>> OldestCB;
282 std::lock_guard<std::mutex> Mutex(CallMutex);
284 ReplyCallbacks.emplace_back(
ID, std::move(Reply));
289 if (ReplyCallbacks.size() > MaxReplayCallbacks) {
290 elog(
"more than {0} outstanding LSP calls, forgetting about {1}",
291 MaxReplayCallbacks, ReplyCallbacks.front().first);
292 OldestCB = std::move(ReplyCallbacks.front());
293 ReplyCallbacks.pop_front();
298 error(
"failed to receive a client reply for request ({0})",
310 std::atomic<bool> Replied = {
false};
311 std::chrono::steady_clock::time_point Start;
312 llvm::json::Value
ID;
315 llvm::json::Object *TraceArgs;
318 ReplyOnce(
const llvm::json::Value &ID, llvm::StringRef Method,
321 Server(Server), TraceArgs(TraceArgs) {
324 ReplyOnce(ReplyOnce &&Other)
325 : Replied(Other.Replied.load()), Start(Other.Start),
326 ID(std::move(Other.
ID)), Method(std::move(Other.Method)),
327 Server(Other.Server), TraceArgs(Other.TraceArgs) {
328 Other.Server =
nullptr;
330 ReplyOnce &operator=(ReplyOnce &&) =
delete;
331 ReplyOnce(
const ReplyOnce &) =
delete;
332 ReplyOnce &operator=(
const ReplyOnce &) =
delete;
341 if (Server && !Server->IsBeingDestroyed && !Replied) {
342 elog(
"No reply to message {0}({1})", Method, ID);
343 assert(
false &&
"must reply to all calls!");
344 (*this)(llvm::make_error<LSPError>(
"server failed to reply",
349 void operator()(llvm::Expected<llvm::json::Value> Reply) {
350 assert(Server &&
"moved-from!");
351 if (Replied.exchange(
true)) {
352 elog(
"Replied twice to message {0}({1})", Method, ID);
353 assert(
false &&
"must reply to each call only once!");
356 auto Duration = std::chrono::steady_clock::now() - Start;
358 log(
"--> reply:{0}({1}) {2:ms}", Method, ID, Duration);
360 (*TraceArgs)[
"Reply"] = *Reply;
361 std::lock_guard<std::mutex> Lock(Server->TranspWriter);
362 Server->Transp.reply(std::move(ID), std::move(Reply));
364 llvm::Error Err = Reply.takeError();
365 log(
"--> reply:{0}({1}) {2:ms}, error: {3}", Method, ID, Duration, Err);
367 (*TraceArgs)[
"Error"] = llvm::to_string(Err);
368 std::lock_guard<std::mutex> Lock(Server->TranspWriter);
369 Server->Transp.reply(std::move(ID), std::move(Err));
377 mutable std::mutex RequestCancelersMutex;
378 llvm::StringMap<std::pair<
Canceler,
unsigned>> RequestCancelers;
379 unsigned NextRequestCookie = 0;
380 void onCancel(
const llvm::json::Value &Params) {
381 const llvm::json::Value *
ID =
nullptr;
382 if (
auto *O = Params.getAsObject())
385 elog(
"Bad cancellation request: {0}", Params);
388 auto StrID = llvm::to_string(*
ID);
389 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
390 auto It = RequestCancelers.find(StrID);
391 if (It != RequestCancelers.end())
395 Context handlerContext()
const {
405 Context cancelableRequestContext(
const llvm::json::Value &
ID) {
408 auto StrID = llvm::to_string(
ID);
409 auto Cookie = NextRequestCookie++;
411 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
412 RequestCancelers[StrID] = {std::move(Task.second), Cookie};
417 return Task.first.derive(llvm::make_scope_exit([
this, StrID, Cookie] {
418 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
419 auto It = RequestCancelers.find(StrID);
420 if (It != RequestCancelers.end() && It->second.second == Cookie)
421 RequestCancelers.erase(It);
431 static constexpr int MaxReplayCallbacks = 100;
432 mutable std::mutex CallMutex;
434 std::deque<std::pair< int,
435 Callback<llvm::json::Value>>>
438 ClangdLSPServer &Server;
440constexpr int ClangdLSPServer::MessageHandler::MaxReplayCallbacks;
443void ClangdLSPServer::callMethod(StringRef
Method, llvm::json::Value Params,
444 Callback<llvm::json::Value> CB) {
445 auto ID = MsgHandler->bindReply(std::move(CB));
447 std::lock_guard<std::mutex> Lock(TranspWriter);
451void ClangdLSPServer::notify(llvm::StringRef
Method, llvm::json::Value Params) {
453 maybeCleanupMemory();
454 std::lock_guard<std::mutex> Lock(TranspWriter);
459 std::vector<llvm::StringRef> Types;
467 std::vector<llvm::StringRef> Modifiers;
475void ClangdLSPServer::onInitialize(
const InitializeParams &Params,
476 Callback<llvm::json::Value> Reply) {
478 if (Params.capabilities.offsetEncoding && !Opts.
Encoding) {
480 for (
OffsetEncoding Supported : *Params.capabilities.offsetEncoding)
487 if (Params.capabilities.TheiaSemanticHighlighting &&
488 !Params.capabilities.SemanticTokens) {
489 elog(
"Client requested legacy semanticHighlights notification, which is "
490 "no longer supported. Migrate to standard semanticTokens request");
493 if (Params.rootUri && *Params.rootUri)
495 else if (Params.rootPath && !Params.rootPath->empty())
498 return Reply(llvm::make_error<LSPError>(
"server already initialized",
506 Params.capabilities.CompletionDocumentationFormat;
508 Params.capabilities.SignatureHelpDocumentationFormat;
512 Params.capabilities.DiagnosticRelatedInformation;
513 if (Params.capabilities.WorkspaceSymbolKinds)
514 SupportedSymbolKinds |= *Params.capabilities.WorkspaceSymbolKinds;
515 if (Params.capabilities.CompletionItemKinds)
516 SupportedCompletionItemKinds |= *Params.capabilities.CompletionItemKinds;
517 SupportsCompletionLabelDetails = Params.capabilities.CompletionLabelDetail;
518 SupportsCodeAction = Params.capabilities.CodeActionStructure;
519 SupportsHierarchicalDocumentSymbol =
520 Params.capabilities.HierarchicalDocumentSymbol;
521 SupportsReferenceContainer = Params.capabilities.ReferenceContainer;
522 SupportFileStatus = Params.initializationOptions.FileStatus;
523 SupportsDocumentChanges = Params.capabilities.DocumentChanges;
524 SupportsChangeAnnotation = Params.capabilities.ChangeAnnotation;
525 HoverContentFormat = Params.capabilities.HoverContentFormat;
527 SupportsOffsetsInSignatureHelp = Params.capabilities.OffsetsInSignatureHelp;
528 if (Params.capabilities.WorkDoneProgress)
529 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
530 BackgroundIndexSkipCreate = Params.capabilities.ImplicitProgressCreation;
535 DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS);
536 if (
const auto &Dir = Params.initializationOptions.compilationDatabasePath)
537 CDBOpts.CompileCommandsDir = Dir;
540 std::make_unique<DirectoryBasedGlobalCompilationDatabase>(CDBOpts);
543 Mangler.SystemIncludeExtractor =
547 CDB.emplace(BaseCDB.get(), Params.initializationOptions.fallbackFlags,
553 WithContext MainContext(BackgroundContext.
clone());
554 std::optional<WithContextValue> WithOffsetEncoding;
557 Server.emplace(*CDB, TFS, Opts,
558 static_cast<ClangdServer::Callbacks *
>(
this));
561 llvm::json::Object ServerCaps{
568 {
"documentFormattingProvider",
true},
569 {
"documentRangeFormattingProvider",
true},
570 {
"documentOnTypeFormattingProvider",
572 {
"firstTriggerCharacter",
"\n"},
573 {
"moreTriggerCharacter", {}},
575 {
"completionProvider",
582 {
"resolveProvider",
false},
584 {
"triggerCharacters", {
".",
"<",
">",
":",
"\"",
"/",
"*"}},
586 {
"semanticTokensProvider",
588 {
"full", llvm::json::Object{{
"delta",
true}}},
594 {
"signatureHelpProvider",
596 {
"triggerCharacters", {
"(",
")",
"{",
"}",
"<",
">",
","}},
598 {
"declarationProvider",
true},
599 {
"definitionProvider",
true},
600 {
"implementationProvider",
true},
601 {
"typeDefinitionProvider",
true},
602 {
"documentHighlightProvider",
true},
603 {
"documentLinkProvider",
605 {
"resolveProvider",
false},
607 {
"hoverProvider",
true},
608 {
"selectionRangeProvider",
true},
609 {
"documentSymbolProvider",
true},
610 {
"workspaceSymbolProvider",
true},
611 {
"referencesProvider",
true},
612 {
"astProvider",
true},
613 {
"typeHierarchyProvider",
true},
617 {
"standardTypeHierarchyProvider",
true},
618 {
"memoryUsageProvider",
true},
619 {
"compilationDatabase",
620 llvm::json::Object{{
"automaticReload",
true}}},
621 {
"inactiveRegionsProvider",
true},
622 {
"callHierarchyProvider",
true},
623 {
"clangdInlayHintsProvider",
true},
624 {
"inlayHintProvider",
true},
625 {
"foldingRangeProvider",
true},
629 LSPBinder Binder(Handlers, *
this);
630 bindMethods(Binder, Params.capabilities);
633 Mod.initializeLSP(Binder, Params.rawCapabilities, ServerCaps);
638 ServerCaps[
"renameProvider"] =
639 Params.capabilities.RenamePrepareSupport
640 ? llvm::json::Object{{
"prepareProvider",
true}}
641 : llvm::json::Value(
true);
646 ServerCaps[
"codeActionProvider"] =
647 Params.capabilities.CodeActionStructure
648 ? llvm::json::Object{{
"codeActionKinds",
652 : llvm::json::Value(
true);
654 std::vector<llvm::StringRef>
Commands;
658 ServerCaps[
"executeCommandProvider"] =
659 llvm::json::Object{{
"commands",
Commands}};
661 llvm::json::Object Result{
667 {
"capabilities", std::move(ServerCaps)}}};
669 Result[
"offsetEncoding"] = *Opts.
Encoding;
670 Reply(std::move(Result));
674 applyConfiguration(Params.initializationOptions.ConfigSettings);
679void ClangdLSPServer::onShutdown(
const NoParams &,
680 Callback<std::nullptr_t> Reply) {
682 ShutdownRequestReceived =
true;
688void ClangdLSPServer::onSync(
const NoParams &, Callback<std::nullptr_t> Reply) {
689 if (Server->blockUntilIdleForTest(60))
692 Reply(
error(
"Not idle after a minute"));
695void ClangdLSPServer::onDocumentDidOpen(
696 const DidOpenTextDocumentParams &Params) {
699 const std::string &Contents = Params.textDocument.text;
701 Server->addDocument(
File, Contents,
702 encodeVersion(Params.textDocument.version),
706void ClangdLSPServer::onDocumentDidChange(
707 const DidChangeTextDocumentParams &Params) {
709 if (Params.wantDiagnostics)
716 log(
"Trying to incrementally change non-added document: {0}",
File);
719 std::string NewCode(*
Code);
720 for (
const auto &Change : Params.contentChanges) {
725 Server->removeDocument(
File);
726 elog(
"Failed to update {0}: {1}",
File, std::move(Err));
730 Server->addDocument(
File, NewCode, encodeVersion(Params.textDocument.version),
734void ClangdLSPServer::onDocumentDidSave(
735 const DidSaveTextDocumentParams &Params) {
736 Server->reparseOpenFilesIfNeeded([](llvm::StringRef) {
return true; });
739void ClangdLSPServer::onFileEvent(
const DidChangeWatchedFilesParams &Params) {
744 Server->onFileEvent(Params);
751void ClangdLSPServer::onCommand(
const ExecuteCommandParams &Params,
752 Callback<llvm::json::Value> Reply) {
755 return Reply(llvm::make_error<LSPError>(
756 llvm::formatv(
"Unsupported command \"{0}\".", Params.command).str(),
759 It->second(Params.argument, std::move(Reply));
762void ClangdLSPServer::onCommandApplyEdit(
const WorkspaceEdit &WE,
763 Callback<llvm::json::Value> Reply) {
772 applyEdit(WE,
"Fix applied.", std::move(Reply));
775void ClangdLSPServer::onCommandApplyTweak(
const TweakArgs &
Args,
776 Callback<llvm::json::Value> Reply) {
777 auto Action = [
this, Reply = std::move(Reply)](
778 llvm::Expected<Tweak::Effect> R)
mutable {
780 return Reply(R.takeError());
782 assert(R->ShowMessage || (!R->ApplyEdits.empty() &&
"tweak has no effect"));
784 if (R->ShowMessage) {
785 ShowMessageParams Msg;
786 Msg.message = *R->ShowMessage;
791 if (R->ApplyEdits.empty())
792 return Reply(
"Tweak applied.");
794 if (
auto Err = validateEdits(*Server, R->ApplyEdits))
795 return Reply(std::move(Err));
799 WE.changes.emplace();
800 for (
const auto &It : R->ApplyEdits) {
802 It.second.asTextEdits();
805 return applyEdit(std::move(WE),
"Tweak applied.", std::move(Reply));
807 Server->applyTweak(
Args.file.file(),
Args.selection,
Args.tweakID,
811void ClangdLSPServer::applyEdit(WorkspaceEdit WE, llvm::json::Value Success,
812 Callback<llvm::json::Value> Reply) {
813 ApplyWorkspaceEditParams Edit;
814 Edit.edit = std::move(WE);
816 Edit, [Reply = std::move(Reply), SuccessMessage = std::move(Success)](
817 llvm::Expected<ApplyWorkspaceEditResponse> Response)
mutable {
819 return Reply(Response.takeError());
820 if (!Response->applied) {
821 std::string Reason = Response->failureReason
822 ? *Response->failureReason
824 return Reply(error(
"edits were not applied: {0}", Reason));
826 return Reply(SuccessMessage);
830void ClangdLSPServer::onWorkspaceSymbol(
831 const WorkspaceSymbolParams &Params,
832 Callback<std::vector<SymbolInformation>> Reply) {
833 Server->workspaceSymbols(
835 [Reply = std::move(Reply),
836 this](llvm::Expected<std::vector<SymbolInformation>> Items)
mutable {
838 return Reply(Items.takeError());
839 for (auto &Sym : *Items)
840 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds);
842 Reply(std::move(*Items));
846void ClangdLSPServer::onPrepareRename(
const TextDocumentPositionParams &Params,
847 Callback<std::optional<Range>> Reply) {
848 Server->prepareRename(
849 Params.textDocument.uri.file(), Params.position, std::nullopt,
851 [Reply = std::move(Reply)](llvm::Expected<RenameResult> Result)
mutable {
853 return Reply(Result.takeError());
854 return Reply(std::move(Result->Target));
858void ClangdLSPServer::onRename(
const RenameParams &Params,
859 Callback<WorkspaceEdit> Reply) {
860 Path File = std::string(Params.textDocument.uri.file());
861 if (!Server->getDraft(
File))
862 return Reply(llvm::make_error<LSPError>(
864 Server->rename(
File, Params.position, Params.newName, Opts.
Rename,
865 [
File, Params, Reply = std::move(Reply),
866 this](llvm::Expected<RenameResult> R)
mutable {
868 return Reply(R.takeError());
869 if (auto Err = validateEdits(*Server, R->GlobalChanges))
870 return Reply(std::move(Err));
871 WorkspaceEdit Result;
874 Result.changes.emplace();
875 for (const auto &Rep : R->GlobalChanges) {
877 .changes)[URI::createFile(Rep.first()).toString()] =
878 Rep.second.asTextEdits();
884void ClangdLSPServer::onDocumentDidClose(
885 const DidCloseTextDocumentParams &Params) {
886 PathRef File = Params.textDocument.uri.file();
887 Server->removeDocument(File);
890 std::lock_guard<std::mutex> Lock(DiagRefMutex);
891 DiagRefMap.erase(File);
894 std::lock_guard<std::mutex> HLock(SemanticTokensMutex);
895 LastSemanticTokens.erase(File);
902 PublishDiagnosticsParams Notification;
903 Notification.uri = URIForFile::canonicalize(File, File);
904 PublishDiagnostics(Notification);
907void ClangdLSPServer::onDocumentOnTypeFormatting(
908 const DocumentOnTypeFormattingParams &Params,
909 Callback<std::vector<TextEdit>> Reply) {
910 auto File = Params.textDocument.uri.file();
911 Server->formatOnType(File, Params.position, Params.ch, std::move(Reply));
914void ClangdLSPServer::onDocumentRangeFormatting(
915 const DocumentRangeFormattingParams &Params,
916 Callback<std::vector<TextEdit>> Reply) {
917 auto File = Params.textDocument.uri.file();
918 auto Code = Server->getDraft(File);
919 Server->formatFile(File, Params.range,
920 [
Code = std::move(
Code), Reply = std::move(Reply)](
921 llvm::Expected<tooling::Replacements> Result)
mutable {
923 Reply(replacementsToEdits(*Code, Result.get()));
925 Reply(Result.takeError());
929void ClangdLSPServer::onDocumentFormatting(
930 const DocumentFormattingParams &Params,
931 Callback<std::vector<TextEdit>> Reply) {
932 auto File = Params.textDocument.uri.file();
933 auto Code = Server->getDraft(File);
934 Server->formatFile(File,
936 [
Code = std::move(
Code), Reply = std::move(Reply)](
937 llvm::Expected<tooling::Replacements> Result)
mutable {
941 Reply(Result.takeError());
947static std::vector<SymbolInformation>
950 std::vector<SymbolInformation>
Results;
951 std::function<void(
const DocumentSymbol &, llvm::StringRef)> Process =
952 [&](
const DocumentSymbol &S, std::optional<llvm::StringRef> ParentName) {
954 SI.
containerName = std::string(ParentName ?
"" : *ParentName);
960 Results.push_back(std::move(SI));
961 std::string FullName =
962 !ParentName ? S.
name : (ParentName->str() +
"::" + S.
name);
964 Process(
C, FullName);
966 for (
auto &S : Symbols)
971void ClangdLSPServer::onDocumentSymbol(
const DocumentSymbolParams &Params,
972 Callback<llvm::json::Value> Reply) {
973 URIForFile FileURI = Params.textDocument.uri;
974 Server->documentSymbols(
975 Params.textDocument.uri.file(),
976 [
this, FileURI, Reply = std::move(Reply)](
977 llvm::Expected<std::vector<DocumentSymbol>> Items)
mutable {
979 return Reply(Items.takeError());
980 adjustSymbolKinds(*Items, SupportedSymbolKinds);
981 if (SupportsHierarchicalDocumentSymbol)
982 return Reply(std::move(*Items));
983 return Reply(flattenSymbolHierarchy(*Items, FileURI));
987void ClangdLSPServer::onFoldingRange(
988 const FoldingRangeParams &Params,
989 Callback<std::vector<FoldingRange>> Reply) {
990 Server->foldingRanges(Params.textDocument.uri.file(), std::move(Reply));
1000 Cmd.command = std::string(ApplyFixCommand);
1001 Cmd.argument = *
Action.edit;
1003 return std::nullopt;
1005 Cmd.title =
Action.title;
1006 if (
Action.kind && *
Action.kind == CodeAction::QUICKFIX_KIND)
1007 Cmd.title =
"Apply fix: " + Cmd.title;
1011void ClangdLSPServer::onCodeAction(
const CodeActionParams &Params,
1012 Callback<llvm::json::Value> Reply) {
1013 URIForFile File = Params.textDocument.uri;
1014 std::map<ClangdServer::DiagRef, clangd::Diagnostic> ToLSPDiags;
1015 ClangdServer::CodeActionInputs Inputs;
1017 for (
const auto& LSPDiag : Params.context.diagnostics) {
1018 if (
auto DiagRef = getDiagRef(File.file(), LSPDiag)) {
1019 ToLSPDiags[*DiagRef] = LSPDiag;
1020 Inputs.Diagnostics.push_back(*DiagRef);
1023 Inputs.File = File.file();
1024 Inputs.Selection = Params.range;
1025 Inputs.RequestedActionKinds = Params.context.only;
1026 Inputs.TweakFilter = [
this](
const Tweak &T) {
1027 return Opts.TweakFilter(T);
1030 Reply = std::move(Reply),
1031 ToLSPDiags = std::move(ToLSPDiags), File,
1032 Selection = Params.range](
1033 llvm::Expected<ClangdServer::CodeActionResult> Fixits)
mutable {
1035 return Reply(Fixits.takeError());
1036 std::vector<CodeAction> CAs;
1037 auto Version = decodeVersion(Fixits->Version);
1038 for (
const auto &QF : Fixits->QuickFixes) {
1039 CAs.push_back(toCodeAction(QF.F, File, Version, SupportsDocumentChanges,
1040 SupportsChangeAnnotation));
1041 if (
auto It = ToLSPDiags.find(QF.Diag);
1042 It != ToLSPDiags.end()) {
1043 CAs.back().diagnostics = {It->second};
1046 for (
const auto &TR : Fixits->TweakRefs)
1047 CAs.push_back(toCodeAction(TR, File, Selection));
1051 CodeAction *OnlyFix =
nullptr;
1052 for (
auto &
Action : CAs) {
1053 if (
Action.kind && *
Action.kind == CodeAction::QUICKFIX_KIND) {
1062 OnlyFix->isPreferred =
true;
1063 if (ToLSPDiags.size() == 1 &&
1064 ToLSPDiags.begin()->second.range == Selection)
1065 OnlyFix->diagnostics = {ToLSPDiags.begin()->second};
1068 if (SupportsCodeAction)
1069 return Reply(llvm::json::Array(CAs));
1071 for (
const auto &
Action : CAs) {
1073 Commands.push_back(std::move(*Command));
1075 return Reply(llvm::json::Array(
Commands));
1077 Server->codeAction(Inputs, std::move(CB));
1080void ClangdLSPServer::onCompletion(
const CompletionParams &Params,
1081 Callback<CompletionList> Reply) {
1082 if (!shouldRunCompletion(Params)) {
1085 vlog(
"ignored auto-triggered completion, preceding char did not match");
1086 return Reply(CompletionList());
1088 auto Opts = this->Opts.CodeComplete;
1089 if (Params.limit && *Params.limit >= 0)
1090 Opts.Limit = *Params.limit;
1091 Server->codeComplete(Params.textDocument.uri.file(), Params.position, Opts,
1092 [Reply = std::move(Reply), Opts,
1093 this](llvm::Expected<CodeCompleteResult> List)
mutable {
1095 return Reply(List.takeError());
1096 CompletionList LSPList;
1097 LSPList.isIncomplete = List->HasMore;
1098 for (const auto &R : List->Completions) {
1099 CompletionItem C = R.render(Opts);
1100 C.kind = adjustKindToCapability(
1101 C.kind, SupportedCompletionItemKinds);
1102 if (!SupportsCompletionLabelDetails)
1103 removeCompletionLabelDetails(C);
1104 LSPList.items.push_back(std::move(C));
1106 return Reply(std::move(LSPList));
1110void ClangdLSPServer::onSignatureHelp(
const TextDocumentPositionParams &Params,
1111 Callback<SignatureHelp> Reply) {
1112 Server->signatureHelp(Params.textDocument.uri.file(), Params.position,
1113 Opts.SignatureHelpDocumentationFormat,
1114 [Reply = std::move(Reply),
this](
1115 llvm::Expected<SignatureHelp>
Signature)
mutable {
1117 return Reply(Signature.takeError());
1118 if (SupportsOffsetsInSignatureHelp)
1119 return Reply(std::move(*Signature));
1122 for (auto &SigInfo : Signature->signatures) {
1123 for (auto &Param : SigInfo.parameters)
1124 Param.labelOffsets.reset();
1150void ClangdLSPServer::onGoToDefinition(
const TextDocumentPositionParams &Params,
1151 Callback<std::vector<Location>> Reply) {
1152 Server->locateSymbolAt(
1153 Params.textDocument.uri.file(), Params.position,
1154 [Params, Reply = std::move(Reply)](
1155 llvm::Expected<std::vector<LocatedSymbol>> Symbols)
mutable {
1157 return Reply(Symbols.takeError());
1158 std::vector<Location> Defs;
1159 for (auto &S : *Symbols) {
1160 if (Location *Toggle = getToggle(Params, S))
1161 return Reply(std::vector<Location>{std::move(*Toggle)});
1162 Defs.push_back(S.Definition.value_or(S.PreferredDeclaration));
1164 Reply(std::move(Defs));
1168void ClangdLSPServer::onGoToDeclaration(
1169 const TextDocumentPositionParams &Params,
1170 Callback<std::vector<Location>> Reply) {
1171 Server->locateSymbolAt(
1172 Params.textDocument.uri.file(), Params.position,
1173 [Params, Reply = std::move(Reply)](
1174 llvm::Expected<std::vector<LocatedSymbol>> Symbols)
mutable {
1176 return Reply(Symbols.takeError());
1177 std::vector<Location> Decls;
1178 for (auto &S : *Symbols) {
1179 if (Location *Toggle = getToggle(Params, S))
1180 return Reply(std::vector<Location>{std::move(*Toggle)});
1181 Decls.push_back(std::move(S.PreferredDeclaration));
1183 Reply(std::move(Decls));
1187void ClangdLSPServer::onSwitchSourceHeader(
1188 const TextDocumentIdentifier &Params,
1189 Callback<std::optional<URIForFile>> Reply) {
1190 Server->switchSourceHeader(
1192 [Reply = std::move(Reply),
1193 Params](llvm::Expected<std::optional<clangd::Path>>
Path)
mutable {
1195 return Reply(Path.takeError());
1197 return Reply(URIForFile::canonicalize(**Path, Params.uri.file()));
1198 return Reply(std::nullopt);
1202void ClangdLSPServer::onDocumentHighlight(
1203 const TextDocumentPositionParams &Params,
1204 Callback<std::vector<DocumentHighlight>> Reply) {
1205 Server->findDocumentHighlights(Params.textDocument.uri.file(),
1206 Params.position, std::move(Reply));
1209void ClangdLSPServer::onHover(
const TextDocumentPositionParams &Params,
1210 Callback<std::optional<Hover>> Reply) {
1211 Server->findHover(Params.textDocument.uri.file(), Params.position,
1212 [Reply = std::move(Reply),
1213 this](llvm::Expected<std::optional<HoverInfo>> H)
mutable {
1215 return Reply(H.takeError());
1217 return Reply(std::nullopt);
1220 R.contents.kind = HoverContentFormat;
1221 R.range = (*H)->SymRange;
1222 switch (HoverContentFormat) {
1223 case MarkupKind::PlainText:
1224 R.contents.value = (*H)->present().asPlainText();
1225 return Reply(std::move(R));
1226 case MarkupKind::Markdown:
1227 R.contents.value = (*H)->present().asMarkdown();
1228 return Reply(std::move(R));
1230 llvm_unreachable(
"unhandled MarkupKind");
1237 llvm::json::Object Result{{
1238 {
"name", std::move(THI.
name)},
1239 {
"kind",
static_cast<int>(THI.
kind)},
1240 {
"uri", std::move(THI.
uri)},
1241 {
"range", THI.
range},
1243 {
"data", std::move(THI.
data)},
1248 Result[
"detail"] = std::move(*THI.
detail);
1254 Result[
"parents"] = std::move(
Parents);
1261 Result[
"children"] = std::move(
Children);
1266void ClangdLSPServer::onTypeHierarchy(
const TypeHierarchyPrepareParams &Params,
1267 Callback<llvm::json::Value> Reply) {
1269 [Reply = std::move(Reply)](
1270 llvm::Expected<std::vector<TypeHierarchyItem>> Resp)
mutable {
1272 Reply(Resp.takeError());
1275 if (Resp->empty()) {
1281 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1282 Params.resolve, Params.direction, std::move(Serialize));
1285void ClangdLSPServer::onResolveTypeHierarchy(
1286 const ResolveTypeHierarchyItemParams &Params,
1287 Callback<llvm::json::Value> Reply) {
1289 [Reply = std::move(Reply)](
1290 llvm::Expected<std::optional<TypeHierarchyItem>> Resp)
mutable {
1292 Reply(Resp.takeError());
1296 Reply(std::move(*Resp));
1301 Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction,
1302 std::move(Serialize));
1305void ClangdLSPServer::onPrepareTypeHierarchy(
1306 const TypeHierarchyPrepareParams &Params,
1307 Callback<std::vector<TypeHierarchyItem>> Reply) {
1308 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1309 Params.resolve, Params.direction, std::move(Reply));
1312void ClangdLSPServer::onSuperTypes(
1313 const ResolveTypeHierarchyItemParams &Params,
1314 Callback<std::optional<std::vector<TypeHierarchyItem>>> Reply) {
1315 Server->superTypes(Params.item, std::move(Reply));
1318void ClangdLSPServer::onSubTypes(
1319 const ResolveTypeHierarchyItemParams &Params,
1320 Callback<std::vector<TypeHierarchyItem>> Reply) {
1321 Server->subTypes(Params.item, std::move(Reply));
1324void ClangdLSPServer::onPrepareCallHierarchy(
1325 const CallHierarchyPrepareParams &Params,
1326 Callback<std::vector<CallHierarchyItem>> Reply) {
1327 Server->prepareCallHierarchy(Params.textDocument.uri.file(), Params.position,
1331void ClangdLSPServer::onCallHierarchyIncomingCalls(
1332 const CallHierarchyIncomingCallsParams &Params,
1333 Callback<std::vector<CallHierarchyIncomingCall>> Reply) {
1334 Server->incomingCalls(Params.item, std::move(Reply));
1337void ClangdLSPServer::onClangdInlayHints(
const InlayHintsParams &Params,
1338 Callback<llvm::json::Value> Reply) {
1343 auto Serialize = [Reply = std::move(Reply)](
1344 llvm::Expected<std::vector<InlayHint>>
Hints)
mutable {
1346 Reply(
Hints.takeError());
1349 llvm::json::Array Result;
1350 Result.reserve(
Hints->size());
1351 for (
auto &Hint : *
Hints) {
1352 Result.emplace_back(llvm::json::Object{
1353 {
"kind", llvm::to_string(Hint.kind)},
1354 {
"range", Hint.range},
1355 {
"position", Hint.position},
1359 ((Hint.paddingLeft ?
" " :
"") + llvm::StringRef(Hint.label) +
1360 (Hint.paddingRight ?
" " :
""))
1364 Reply(std::move(Result));
1366 Server->inlayHints(Params.textDocument.uri.file(), Params.range,
1367 std::move(Serialize));
1370void ClangdLSPServer::onInlayHint(
const InlayHintsParams &Params,
1371 Callback<std::vector<InlayHint>> Reply) {
1372 Server->inlayHints(Params.textDocument.uri.file(), Params.range,
1376void ClangdLSPServer::applyConfiguration(
1377 const ConfigurationSettings &Settings) {
1379 llvm::StringSet<> ModifiedFiles;
1380 for (
auto &
Entry : Settings.compilationDatabaseChanges) {
1382 auto Old = CDB->getCompileCommand(File);
1384 tooling::CompileCommand(std::move(
Entry.second.workingDirectory), File,
1385 std::move(
Entry.second.compilationCommand),
1388 CDB->setCompileCommand(File, std::move(New));
1389 ModifiedFiles.insert(File);
1393 Server->reparseOpenFilesIfNeeded(
1394 [&](llvm::StringRef File) {
return ModifiedFiles.count(File) != 0; });
1397void ClangdLSPServer::maybeExportMemoryProfile() {
1398 if (!trace::enabled() || !ShouldProfile())
1401 static constexpr trace::Metric MemoryUsage(
1402 "memory_usage", trace::Metric::Value,
"component_name");
1403 trace::Span Tracer(
"ProfileBrief");
1406 record(MT,
"clangd_lsp_server", MemoryUsage);
1409void ClangdLSPServer::maybeCleanupMemory() {
1410 if (!Opts.MemoryCleanup || !ShouldCleanupMemory())
1412 Opts.MemoryCleanup();
1416void ClangdLSPServer::onChangeConfiguration(
1417 const DidChangeConfigurationParams &Params) {
1418 applyConfiguration(Params.settings);
1421void ClangdLSPServer::onReference(
1422 const ReferenceParams &Params,
1423 Callback<std::vector<ReferenceLocation>> Reply) {
1424 Server->findReferences(Params.textDocument.uri.file(), Params.position,
1425 Opts.ReferencesLimit, SupportsReferenceContainer,
1426 [Reply = std::move(Reply),
1427 IncludeDecl(Params.context.includeDeclaration)](
1428 llvm::Expected<ReferencesResult> Refs)
mutable {
1430 return Reply(Refs.takeError());
1432 std::vector<ReferenceLocation> Result;
1433 Result.reserve(Refs->References.size());
1434 for (auto &Ref : Refs->References) {
1436 Ref.Attributes & ReferencesResult::Declaration;
1437 if (IncludeDecl || !IsDecl)
1438 Result.push_back(std::move(Ref.Loc));
1440 return Reply(std::move(Result));
1444void ClangdLSPServer::onGoToType(
const TextDocumentPositionParams &Params,
1445 Callback<std::vector<Location>> Reply) {
1447 Params.textDocument.uri.file(), Params.position,
1448 [Reply = std::move(Reply)](
1449 llvm::Expected<std::vector<LocatedSymbol>> Types)
mutable {
1451 return Reply(Types.takeError());
1452 std::vector<Location> Response;
1453 for (const LocatedSymbol &Sym : *Types)
1454 Response.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration));
1455 return Reply(std::move(Response));
1459void ClangdLSPServer::onGoToImplementation(
1460 const TextDocumentPositionParams &Params,
1461 Callback<std::vector<Location>> Reply) {
1462 Server->findImplementations(
1463 Params.textDocument.uri.file(), Params.position,
1464 [Reply = std::move(Reply)](
1465 llvm::Expected<std::vector<LocatedSymbol>> Overrides)
mutable {
1467 return Reply(Overrides.takeError());
1468 std::vector<Location> Impls;
1469 for (const LocatedSymbol &Sym : *Overrides)
1470 Impls.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration));
1471 return Reply(std::move(Impls));
1475void ClangdLSPServer::onSymbolInfo(
const TextDocumentPositionParams &Params,
1476 Callback<std::vector<SymbolDetails>> Reply) {
1477 Server->symbolInfo(Params.textDocument.uri.file(), Params.position,
1481void ClangdLSPServer::onSelectionRange(
1482 const SelectionRangeParams &Params,
1483 Callback<std::vector<SelectionRange>> Reply) {
1484 Server->semanticRanges(
1485 Params.textDocument.uri.file(), Params.positions,
1486 [Reply = std::move(Reply)](
1487 llvm::Expected<std::vector<SelectionRange>> Ranges)
mutable {
1489 return Reply(Ranges.takeError());
1490 return Reply(std::move(*Ranges));
1494void ClangdLSPServer::onDocumentLink(
1495 const DocumentLinkParams &Params,
1496 Callback<std::vector<DocumentLink>> Reply) {
1502 Server->documentLinks(
1503 Params.textDocument.uri.file(),
1504 [Reply = std::move(Reply)](
1505 llvm::Expected<std::vector<DocumentLink>> Links)
mutable {
1507 return Reply(Links.takeError());
1509 return Reply(std::move(Links));
1515 for (
char &
C : llvm::reverse(S)) {
1522 S.insert(S.begin(),
'1');
1525void ClangdLSPServer::onSemanticTokens(
const SemanticTokensParams &Params,
1526 Callback<SemanticTokens> CB) {
1527 auto File = Params.textDocument.uri.file();
1528 Server->semanticHighlights(
1529 Params.textDocument.uri.file(),
1530 [
this, File(File.str()), CB(std::move(CB)),
Code(Server->getDraft(File))](
1531 llvm::Expected<std::vector<HighlightingToken>> HT)
mutable {
1533 return CB(HT.takeError());
1534 SemanticTokens Result;
1535 Result.tokens = toSemanticTokens(*HT, *Code);
1537 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1538 auto &Last = LastSemanticTokens[File];
1540 Last.tokens = Result.tokens;
1541 increment(Last.resultId);
1542 Result.resultId = Last.resultId;
1544 CB(std::move(Result));
1548void ClangdLSPServer::onSemanticTokensDelta(
1549 const SemanticTokensDeltaParams &Params,
1550 Callback<SemanticTokensOrDelta> CB) {
1551 auto File = Params.textDocument.uri.file();
1552 Server->semanticHighlights(
1553 Params.textDocument.uri.file(),
1554 [
this, PrevResultID(Params.previousResultId), File(File.str()),
1555 CB(std::move(CB)),
Code(Server->getDraft(File))](
1556 llvm::Expected<std::vector<HighlightingToken>> HT)
mutable {
1558 return CB(HT.takeError());
1559 std::vector<SemanticToken> Toks = toSemanticTokens(*HT, *Code);
1561 SemanticTokensOrDelta Result;
1563 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1564 auto &Last = LastSemanticTokens[File];
1566 if (PrevResultID == Last.resultId) {
1567 Result.edits = diffTokens(Last.tokens, Toks);
1569 vlog(
"semanticTokens/full/delta: wanted edits vs {0} but last "
1570 "result had ID {1}. Returning full token list.",
1571 PrevResultID, Last.resultId);
1572 Result.tokens = Toks;
1575 Last.tokens = std::move(Toks);
1576 increment(Last.resultId);
1577 Result.resultId = Last.resultId;
1580 CB(std::move(Result));
1584void ClangdLSPServer::onMemoryUsage(
const NoParams &,
1585 Callback<MemoryTree> Reply) {
1586 llvm::BumpPtrAllocator DetailAlloc;
1587 MemoryTree MT(&DetailAlloc);
1589 Reply(std::move(MT));
1592void ClangdLSPServer::onAST(
const ASTParams &Params,
1593 Callback<std::optional<ASTNode>> CB) {
1594 Server->getAST(Params.textDocument.uri.file(), Params.range, std::move(CB));
1599 : ShouldProfile(std::chrono::minutes(5),
1600 std::chrono::minutes(1)),
1601 ShouldCleanupMemory(std::chrono::minutes(1),
1602 std::chrono::minutes(1)),
1603 BackgroundContext(
Context::current().clone()), Transp(Transp),
1605 SupportedSymbolKinds(defaultSymbolKinds()),
1606 SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts) {
1607 if (Opts.ConfigProvider) {
1608 assert(!Opts.ContextProvider &&
1609 "Only one of ConfigProvider and ContextProvider allowed!");
1611 Opts.ConfigProvider,
this);
1614 Bind.
method(
"initialize",
this, &ClangdLSPServer::onInitialize);
1617void ClangdLSPServer::bindMethods(
LSPBinder &Bind,
1620 Bind.
notification(
"initialized",
this, &ClangdLSPServer::onInitialized);
1621 Bind.
method(
"shutdown",
this, &ClangdLSPServer::onShutdown);
1622 Bind.
method(
"sync",
this, &ClangdLSPServer::onSync);
1623 Bind.
method(
"textDocument/rangeFormatting",
this, &ClangdLSPServer::onDocumentRangeFormatting);
1624 Bind.
method(
"textDocument/onTypeFormatting",
this, &ClangdLSPServer::onDocumentOnTypeFormatting);
1625 Bind.
method(
"textDocument/formatting",
this, &ClangdLSPServer::onDocumentFormatting);
1626 Bind.
method(
"textDocument/codeAction",
this, &ClangdLSPServer::onCodeAction);
1627 Bind.
method(
"textDocument/completion",
this, &ClangdLSPServer::onCompletion);
1628 Bind.
method(
"textDocument/signatureHelp",
this, &ClangdLSPServer::onSignatureHelp);
1629 Bind.
method(
"textDocument/definition",
this, &ClangdLSPServer::onGoToDefinition);
1630 Bind.
method(
"textDocument/declaration",
this, &ClangdLSPServer::onGoToDeclaration);
1631 Bind.
method(
"textDocument/typeDefinition",
this, &ClangdLSPServer::onGoToType);
1632 Bind.
method(
"textDocument/implementation",
this, &ClangdLSPServer::onGoToImplementation);
1633 Bind.
method(
"textDocument/references",
this, &ClangdLSPServer::onReference);
1634 Bind.
method(
"textDocument/switchSourceHeader",
this, &ClangdLSPServer::onSwitchSourceHeader);
1635 Bind.
method(
"textDocument/prepareRename",
this, &ClangdLSPServer::onPrepareRename);
1636 Bind.
method(
"textDocument/rename",
this, &ClangdLSPServer::onRename);
1637 Bind.
method(
"textDocument/hover",
this, &ClangdLSPServer::onHover);
1638 Bind.
method(
"textDocument/documentSymbol",
this, &ClangdLSPServer::onDocumentSymbol);
1639 Bind.
method(
"workspace/executeCommand",
this, &ClangdLSPServer::onCommand);
1640 Bind.
method(
"textDocument/documentHighlight",
this, &ClangdLSPServer::onDocumentHighlight);
1641 Bind.
method(
"workspace/symbol",
this, &ClangdLSPServer::onWorkspaceSymbol);
1642 Bind.
method(
"textDocument/ast",
this, &ClangdLSPServer::onAST);
1643 Bind.
notification(
"textDocument/didOpen",
this, &ClangdLSPServer::onDocumentDidOpen);
1644 Bind.
notification(
"textDocument/didClose",
this, &ClangdLSPServer::onDocumentDidClose);
1645 Bind.
notification(
"textDocument/didChange",
this, &ClangdLSPServer::onDocumentDidChange);
1646 Bind.
notification(
"textDocument/didSave",
this, &ClangdLSPServer::onDocumentDidSave);
1647 Bind.
notification(
"workspace/didChangeWatchedFiles",
this, &ClangdLSPServer::onFileEvent);
1648 Bind.
notification(
"workspace/didChangeConfiguration",
this, &ClangdLSPServer::onChangeConfiguration);
1649 Bind.
method(
"textDocument/symbolInfo",
this, &ClangdLSPServer::onSymbolInfo);
1650 Bind.
method(
"textDocument/typeHierarchy",
this, &ClangdLSPServer::onTypeHierarchy);
1651 Bind.
method(
"typeHierarchy/resolve",
this, &ClangdLSPServer::onResolveTypeHierarchy);
1652 Bind.
method(
"textDocument/prepareTypeHierarchy",
this, &ClangdLSPServer::onPrepareTypeHierarchy);
1653 Bind.
method(
"typeHierarchy/supertypes",
this, &ClangdLSPServer::onSuperTypes);
1654 Bind.
method(
"typeHierarchy/subtypes",
this, &ClangdLSPServer::onSubTypes);
1655 Bind.
method(
"textDocument/prepareCallHierarchy",
this, &ClangdLSPServer::onPrepareCallHierarchy);
1656 Bind.
method(
"callHierarchy/incomingCalls",
this, &ClangdLSPServer::onCallHierarchyIncomingCalls);
1657 Bind.
method(
"textDocument/selectionRange",
this, &ClangdLSPServer::onSelectionRange);
1658 Bind.
method(
"textDocument/documentLink",
this, &ClangdLSPServer::onDocumentLink);
1659 Bind.
method(
"textDocument/semanticTokens/full",
this, &ClangdLSPServer::onSemanticTokens);
1660 Bind.
method(
"textDocument/semanticTokens/full/delta",
this, &ClangdLSPServer::onSemanticTokensDelta);
1661 Bind.
method(
"clangd/inlayHints",
this, &ClangdLSPServer::onClangdInlayHints);
1662 Bind.
method(
"textDocument/inlayHint",
this, &ClangdLSPServer::onInlayHint);
1663 Bind.
method(
"$/memoryUsage",
this, &ClangdLSPServer::onMemoryUsage);
1664 Bind.
method(
"textDocument/foldingRange",
this, &ClangdLSPServer::onFoldingRange);
1665 Bind.
command(ApplyFixCommand,
this, &ClangdLSPServer::onCommandApplyEdit);
1666 Bind.
command(ApplyTweakCommand,
this, &ClangdLSPServer::onCommandApplyTweak);
1674 CreateWorkDoneProgress = Bind.
outgoingMethod(
"window/workDoneProgress/create");
1679 SemanticTokensRefresh = Bind.
outgoingMethod(
"workspace/semanticTokens/refresh");
1684 IsBeingDestroyed =
true;
1692 bool CleanExit =
true;
1693 if (
auto Err = Transp.
loop(*MsgHandler)) {
1694 elog(
"Transport error: {0}", std::move(Err));
1698 return CleanExit && ShutdownRequestReceived;
1703 Server->profile(MT.
child(
"clangd_server"));
1706std::optional<ClangdServer::DiagRef>
1708 std::lock_guard<std::mutex> Lock(DiagRefMutex);
1709 auto DiagToDiagRefIter = DiagRefMap.find(
File);
1710 if (DiagToDiagRefIter == DiagRefMap.end())
1711 return std::nullopt;
1713 const auto &DiagToDiagRefMap = DiagToDiagRefIter->second;
1714 auto FixItsIter = DiagToDiagRefMap.find(toDiagKey(D));
1715 if (FixItsIter == DiagToDiagRefMap.end())
1716 return std::nullopt;
1718 return FixItsIter->second;
1727bool ClangdLSPServer::shouldRunCompletion(
1728 const CompletionParams &Params)
const {
1731 auto Code = Server->getDraft(Params.textDocument.uri.file());
1737 vlog(
"could not convert position '{0}' to offset for file '{1}'",
1738 Params.position, Params.textDocument.uri.file());
1744void ClangdLSPServer::onDiagnosticsReady(
PathRef File, llvm::StringRef Version,
1746 PublishDiagnosticsParams Notification;
1747 Notification.version = decodeVersion(Version);
1749 DiagnosticToDiagRefMap LocalDiagMap;
1752 [&](clangd::Diagnostic LSPDiag, llvm::ArrayRef<Fix> Fixes) {
1753 if (DiagOpts.EmbedFixesInDiagnostics) {
1754 std::vector<CodeAction> CodeActions;
1755 for (const auto &Fix : Fixes)
1756 CodeActions.push_back(toCodeAction(
1757 Fix, Notification.uri, Notification.version,
1758 SupportsDocumentChanges, SupportsChangeAnnotation));
1759 LSPDiag.codeActions.emplace(std::move(CodeActions));
1760 if (LSPDiag.codeActions->size() == 1)
1761 LSPDiag.codeActions->front().isPreferred = true;
1763 LocalDiagMap[toDiagKey(LSPDiag)] = {Diag.Range, Diag.Message};
1764 Notification.diagnostics.push_back(std::move(LSPDiag));
1770 std::lock_guard<std::mutex> Lock(DiagRefMutex);
1771 DiagRefMap[
File] = LocalDiagMap;
1775 PublishDiagnostics(Notification);
1778void ClangdLSPServer::onInactiveRegionsReady(
1779 PathRef File, std::vector<Range> InactiveRegions) {
1780 InactiveRegionsParams Notification;
1782 Notification.InactiveRegions = std::move(InactiveRegions);
1784 PublishInactiveRegions(Notification);
1787void ClangdLSPServer::onBackgroundIndexProgress(
1788 const BackgroundQueue::Stats &Stats) {
1789 static const char ProgressToken[] =
"backgroundIndexProgress";
1792 maybeCleanupMemory();
1794 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1796 auto NotifyProgress = [
this](
const BackgroundQueue::Stats &Stats) {
1797 if (BackgroundIndexProgressState != BackgroundIndexProgress::Live) {
1798 WorkDoneProgressBegin Begin;
1799 Begin.percentage =
true;
1800 Begin.title =
"indexing";
1801 BeginWorkDoneProgress({ProgressToken, std::move(Begin)});
1802 BackgroundIndexProgressState = BackgroundIndexProgress::Live;
1805 if (Stats.Completed < Stats.Enqueued) {
1806 assert(Stats.Enqueued > Stats.LastIdle);
1807 WorkDoneProgressReport Report;
1808 Report.percentage = 100 * (Stats.Completed - Stats.LastIdle) /
1809 (Stats.Enqueued - Stats.LastIdle);
1811 llvm::formatv(
"{0}/{1}", Stats.Completed - Stats.LastIdle,
1812 Stats.Enqueued - Stats.LastIdle);
1813 ReportWorkDoneProgress({ProgressToken, std::move(Report)});
1815 assert(Stats.Completed == Stats.Enqueued);
1816 EndWorkDoneProgress({ProgressToken, WorkDoneProgressEnd()});
1817 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
1821 switch (BackgroundIndexProgressState) {
1822 case BackgroundIndexProgress::Unsupported:
1824 case BackgroundIndexProgress::Creating:
1826 PendingBackgroundIndexProgress = Stats;
1828 case BackgroundIndexProgress::Empty: {
1829 if (BackgroundIndexSkipCreate) {
1830 NotifyProgress(Stats);
1834 PendingBackgroundIndexProgress = Stats;
1835 BackgroundIndexProgressState = BackgroundIndexProgress::Creating;
1836 WorkDoneProgressCreateParams CreateRequest;
1837 CreateRequest.token = ProgressToken;
1838 CreateWorkDoneProgress(
1840 [
this, NotifyProgress](llvm::Expected<std::nullptr_t>
E) {
1841 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1843 NotifyProgress(this->PendingBackgroundIndexProgress);
1845 elog(
"Failed to create background index progress bar: {0}",
1848 BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported;
1853 case BackgroundIndexProgress::Live:
1854 NotifyProgress(Stats);
1859void ClangdLSPServer::onFileUpdated(
PathRef File,
const TUStatus &Status) {
1860 if (!SupportFileStatus)
1870 NotifyFileStatus(Status.render(
File));
1873void ClangdLSPServer::onSemanticsMaybeChanged(
PathRef File) {
1874 if (SemanticTokensRefresh) {
1875 SemanticTokensRefresh(NoParams{}, [](llvm::Expected<std::nullptr_t>
E) {
1878 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.
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.
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.