21 #include "clang/Tooling/Core/Replacement.h" 22 #include "llvm/ADT/ArrayRef.h" 23 #include "llvm/ADT/Optional.h" 24 #include "llvm/ADT/ScopeExit.h" 25 #include "llvm/ADT/StringRef.h" 26 #include "llvm/ADT/iterator_range.h" 27 #include "llvm/Support/Errc.h" 28 #include "llvm/Support/Error.h" 29 #include "llvm/Support/FormatVariadic.h" 30 #include "llvm/Support/JSON.h" 31 #include "llvm/Support/Path.h" 32 #include "llvm/Support/SHA1.h" 33 #include "llvm/Support/ScopedPrinter.h" 44 CodeAction
toCodeAction(
const ClangdServer::TweakRef &T,
const URIForFile &File,
62 CA.command->title = T.Title;
63 CA.command->command = Command::CLANGD_APPLY_TWEAK;
64 CA.command->tweakArgs.emplace();
65 CA.command->tweakArgs->file = File;
66 CA.command->tweakArgs->tweakID = T.ID;
67 CA.command->tweakArgs->selection = Selection;
71 void adjustSymbolKinds(llvm::MutableArrayRef<DocumentSymbol> Syms,
73 for (
auto &S : Syms) {
75 adjustSymbolKinds(S.children, Kinds);
97 std::vector<std::vector<std::string>> buildHighlightScopeLookupTable() {
98 std::vector<std::vector<std::string>> LookupTable;
102 LookupTable.push_back({toTextMateScope((HighlightingKind)(KindValue))});
110 size_t InvalidFileCount = 0;
111 llvm::StringRef LastInvalidFile;
112 for (
const auto &It : FE) {
113 if (
auto Draft = DraftMgr.getDraft(It.first())) {
117 if (!It.second.canApplyTo(*Draft)) {
119 LastInvalidFile = It.first();
123 if (!InvalidFileCount)
124 return llvm::Error::success();
125 if (InvalidFileCount == 1)
126 return llvm::createStringError(llvm::inconvertibleErrorCode(),
127 "File must be saved first: " +
129 return llvm::createStringError(
130 llvm::inconvertibleErrorCode(),
131 "Files must be saved first: " + LastInvalidFile +
" (and " +
132 llvm::to_string(InvalidFileCount - 1) +
" others)");
136 SelectionRange render(
const std::vector<Range> &Ranges) {
139 SelectionRange Result;
140 Result.range = Ranges[0];
141 auto *Next = &Result.parent;
142 for (
const auto &R : llvm::make_range(Ranges.begin() + 1, Ranges.end())) {
143 *Next = std::make_unique<SelectionRange>();
144 Next->get()->range = R;
145 Next = &Next->get()->parent;
165 log(
"<-- {0}", Method);
166 if (Method ==
"exit")
169 elog(
"Notification {0} before initialization", Method);
170 else if (Method ==
"$/cancelRequest")
171 onCancel(std::move(Params));
172 else if (
auto Handler = Notifications.lookup(Method))
173 Handler(std::move(Params));
175 log(
"unhandled notification {0}", Method);
180 llvm::json::Value ID)
override {
183 WithContext WithCancel(cancelableRequestContext(ID));
186 ReplyOnce Reply(ID, Method, &Server, Tracer.
Args);
187 log(
"<-- {0}({1})", Method, ID);
188 if (!Server.Server && Method !=
"initialize") {
189 elog(
"Call {0} before initialization.", Method);
190 Reply(llvm::make_error<LSPError>(
"server not initialized",
192 }
else if (
auto Handler = Calls.lookup(Method))
193 Handler(std::move(Params), std::move(Reply));
195 Reply(llvm::make_error<LSPError>(
"method not found",
201 llvm::Expected<llvm::json::Value> Result)
override {
205 if (
auto IntID = ID.getAsInteger()) {
206 std::lock_guard<std::mutex> Mutex(CallMutex);
209 if (ReplyCallbacks[
Index].first == *IntID) {
210 ReplyHandler = std::move(ReplyCallbacks[
Index].second);
211 ReplyCallbacks.erase(ReplyCallbacks.begin() +
220 ReplyHandler = [&ID](llvm::Expected<llvm::json::Value> Result) {
221 elog(
"received a reply with ID {0}, but there was no such call", ID);
223 llvm::consumeError(Result.takeError());
229 log(
"<-- reply({0})", ID);
230 ReplyHandler(std::move(Result));
232 auto Err = Result.takeError();
233 log(
"<-- reply({0}) error: {1}", ID, Err);
234 ReplyHandler(std::move(Err));
240 template <
typename Param,
typename Result>
243 Calls[
Method] = [
Method, Handler,
this](llvm::json::Value RawParams,
247 (Server.*Handler)(P, std::move(Reply));
249 elog(
"Failed to decode {0} request.", Method);
250 Reply(llvm::make_error<LSPError>(
"failed to decode request",
260 llvm::Optional<std::pair<int, Callback<llvm::json::Value>>> OldestCB;
263 std::lock_guard<std::mutex> Mutex(CallMutex);
265 ReplyCallbacks.emplace_back(ID, std::move(Reply));
270 if (ReplyCallbacks.size() > MaxReplayCallbacks) {
271 elog(
"more than {0} outstanding LSP calls, forgetting about {1}",
272 MaxReplayCallbacks, ReplyCallbacks.front().first);
273 OldestCB = std::move(ReplyCallbacks.front());
274 ReplyCallbacks.pop_front();
278 OldestCB->second(llvm::createStringError(
279 llvm::inconvertibleErrorCode(),
280 llvm::formatv(
"failed to receive a client reply for request ({0})",
286 template <
typename Param>
290 this](llvm::json::Value RawParams) {
293 elog(
"Failed to decode {0} request.", Method);
298 (Server.*Handler)(P);
309 std::atomic<bool> Replied = {
false};
310 std::chrono::steady_clock::time_point Start;
311 llvm::json::Value ID;
314 llvm::json::Object *TraceArgs;
317 ReplyOnce(
const llvm::json::Value &ID, llvm::StringRef Method,
319 : Start(std::chrono::steady_clock::now()), ID(ID),
Method(Method),
320 Server(Server), TraceArgs(TraceArgs) {
323 ReplyOnce(ReplyOnce &&Other)
324 : Replied(Other.Replied.load()), Start(Other.Start),
325 ID(std::move(Other.ID)),
Method(std::move(Other.Method)),
326 Server(Other.Server), TraceArgs(Other.TraceArgs) {
327 Other.Server =
nullptr;
329 ReplyOnce &operator=(ReplyOnce &&) =
delete;
330 ReplyOnce(
const ReplyOnce &) =
delete;
331 ReplyOnce &operator=(
const ReplyOnce &) =
delete;
340 if (Server && !Server->IsBeingDestroyed && !Replied) {
341 elog(
"No reply to message {0}({1})", Method, ID);
342 assert(
false &&
"must reply to all calls!");
343 (*this)(llvm::make_error<LSPError>(
"server failed to reply",
348 void operator()(llvm::Expected<llvm::json::Value> Reply) {
349 assert(Server &&
"moved-from!");
350 if (Replied.exchange(
true)) {
351 elog(
"Replied twice to message {0}({1})", Method, ID);
352 assert(
false &&
"must reply to each call only once!");
355 auto Duration = std::chrono::steady_clock::now() - Start;
357 log(
"--> reply:{0}({1}) {2:ms}", Method, ID, Duration);
359 (*TraceArgs)[
"Reply"] = *Reply;
360 std::lock_guard<std::mutex> Lock(Server->TranspWriter);
361 Server->Transp.
reply(std::move(ID), std::move(Reply));
364 log(
"--> reply:{0}({1}) {2:ms}, error: {3}", Method, ID, Duration, Err);
366 (*TraceArgs)[
"Error"] = llvm::to_string(Err);
367 std::lock_guard<std::mutex> Lock(Server->TranspWriter);
368 Server->Transp.
reply(std::move(ID), std::move(Err));
373 llvm::StringMap<std::function<void(llvm::json::Value)>> Notifications;
374 llvm::StringMap<std::function<void(llvm::json::Value, ReplyOnce)>> Calls;
379 mutable std::mutex RequestCancelersMutex;
380 llvm::StringMap<std::pair<
Canceler,
unsigned>> RequestCancelers;
381 unsigned NextRequestCookie = 0;
382 void onCancel(
const llvm::json::Value &Params) {
383 const llvm::json::Value *ID =
nullptr;
384 if (
auto *O = Params.getAsObject())
387 elog(
"Bad cancellation request: {0}", Params);
390 auto StrID = llvm::to_string(*ID);
391 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
392 auto It = RequestCancelers.find(StrID);
393 if (It != RequestCancelers.end())
397 Context handlerContext()
const {
407 Context cancelableRequestContext(
const llvm::json::Value &ID) {
409 auto StrID = llvm::to_string(ID);
410 auto Cookie = NextRequestCookie++;
412 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
413 RequestCancelers[StrID] = {std::move(Task.second), Cookie};
418 return Task.first.derive(llvm::make_scope_exit([
this, StrID, Cookie] {
419 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
420 auto It = RequestCancelers.find(StrID);
421 if (It != RequestCancelers.end() && It->second.second == Cookie)
422 RequestCancelers.erase(It);
432 static constexpr
int MaxReplayCallbacks = 100;
433 mutable std::mutex CallMutex;
435 std::deque<std::pair< int,
441 constexpr
int ClangdLSPServer::MessageHandler::MaxReplayCallbacks;
444 void ClangdLSPServer::callRaw(StringRef Method, llvm::json::Value Params,
446 auto ID = MsgHandler->bindReply(std::move(CB));
447 log(
"--> {0}({1})", Method, ID);
448 std::lock_guard<std::mutex> Lock(TranspWriter);
449 Transp.call(Method, std::move(Params), ID);
452 void ClangdLSPServer::notify(llvm::StringRef Method, llvm::json::Value Params) {
453 log(
"--> {0}", Method);
454 std::lock_guard<std::mutex> Lock(TranspWriter);
455 Transp.notify(Method, std::move(Params));
465 NegotiatedOffsetEncoding = Supported;
470 ClangdServerOpts.SemanticHighlighting =
473 ClangdServerOpts.WorkspaceRoot = Params.
rootUri->file();
475 ClangdServerOpts.WorkspaceRoot = *Params.
rootPath;
477 return Reply(llvm::make_error<LSPError>(
"server already initialized",
480 CompileCommandsDir = Dir;
481 if (UseDirBasedCDB) {
482 BaseCDB = std::make_unique<DirectoryBasedGlobalCompilationDatabase>(
485 llvm::makeArrayRef(ClangdServerOpts.QueryDriverGlobs),
489 if (ClangdServerOpts.ResourceDir)
490 Mangler.ResourceDir = *ClangdServerOpts.ResourceDir;
492 tooling::ArgumentsAdjuster(Mangler));
497 WithContext MainContext(BackgroundContext.clone());
498 llvm::Optional<WithContextValue> WithOffsetEncoding;
499 if (NegotiatedOffsetEncoding)
501 *NegotiatedOffsetEncoding);
509 if (!CCOpts.BundleOverloads.hasValue())
513 DiagOpts.EmitRelatedLocations =
520 SupportsHierarchicalDocumentSymbol =
528 llvm::json::Value RenameProvider =
529 llvm::json::Object{{
"prepareProvider",
true}};
531 RenameProvider =
true;
536 llvm::json::Value CodeActionProvider =
true;
538 CodeActionProvider = llvm::json::Object{
543 llvm::json::Object Result{
547 {
"documentFormattingProvider",
true},
548 {
"documentRangeFormattingProvider",
true},
549 {
"documentOnTypeFormattingProvider",
551 {
"firstTriggerCharacter",
"\n"},
552 {
"moreTriggerCharacter", {}},
554 {
"codeActionProvider", std::move(CodeActionProvider)},
555 {
"completionProvider",
557 {
"resolveProvider",
false},
560 {
"triggerCharacters", {
".",
">",
":"}},
562 {
"signatureHelpProvider",
564 {
"triggerCharacters", {
"(",
","}},
566 {
"declarationProvider",
true},
567 {
"definitionProvider",
true},
568 {
"documentHighlightProvider",
true},
569 {
"documentLinkProvider",
571 {
"resolveProvider",
false},
573 {
"hoverProvider",
true},
574 {
"renameProvider", std::move(RenameProvider)},
575 {
"selectionRangeProvider",
true},
576 {
"documentSymbolProvider",
true},
577 {
"workspaceSymbolProvider",
true},
578 {
"referencesProvider",
true},
579 {
"executeCommandProvider",
585 {
"typeHierarchyProvider",
true},
587 if (NegotiatedOffsetEncoding)
588 Result[
"offsetEncoding"] = *NegotiatedOffsetEncoding;
590 Result.getObject(
"capabilities")
592 {
"semanticHighlighting",
593 llvm::json::Object{{
"scopes", buildHighlightScopeLookupTable()}}});
594 Reply(std::move(Result));
600 ShutdownRequestReceived =
true;
606 void ClangdLSPServer::onSync(
const NoParams &Params,
608 if (
Server->blockUntilIdleForTest(60))
611 Reply(llvm::createStringError(llvm::inconvertibleErrorCode(),
612 "Not idle after a minute"));
615 void ClangdLSPServer::onDocumentDidOpen(
621 DraftMgr.addDraft(File, Contents);
625 void ClangdLSPServer::onDocumentDidChange(
633 llvm::Expected<std::string>
Contents =
639 DraftMgr.removeDraft(File);
640 Server->removeDocument(File);
641 elog(
"Failed to update {0}: {1}", File, Contents.takeError());
645 Server->addDocument(File, *Contents, WantDiags);
649 Server->onFileEvent(Params);
654 auto ApplyEdit = [
this](
WorkspaceEdit WE, std::string SuccessMessage,
655 decltype(Reply) Reply) {
657 Edit.
edit = std::move(WE);
658 call<ApplyWorkspaceEditResponse>(
659 "workspace/applyEdit", std::move(Edit),
660 [Reply = std::move(Reply), SuccessMessage = std::move(SuccessMessage)](
661 llvm::Expected<ApplyWorkspaceEditResponse> Response)
mutable {
663 return Reply(Response.takeError());
664 if (!Response->applied) {
665 std::string Reason = Response->failureReason
666 ? *Response->failureReason
668 return Reply(llvm::createStringError(
669 llvm::inconvertibleErrorCode(),
670 (
"edits were not applied: " + Reason).c_str()));
672 return Reply(SuccessMessage);
686 ApplyEdit(*Params.
workspaceEdit,
"Fix applied.", std::move(Reply));
689 auto Code = DraftMgr.getDraft(Params.
tweakArgs->file.file());
691 return Reply(llvm::createStringError(
692 llvm::inconvertibleErrorCode(),
693 "trying to apply a code action for a non-added file"));
695 auto Action = [
this, ApplyEdit, Reply = std::move(Reply),
697 llvm::Expected<Tweak::Effect> R)
mutable {
699 return Reply(R.takeError());
701 assert(R->ShowMessage ||
702 (!R->ApplyEdits.empty() &&
"tweak has no effect"));
704 if (R->ShowMessage) {
708 notify(
"window/showMessage", Msg);
711 if (R->ApplyEdits.empty())
712 return Reply(
"Tweak applied.");
714 if (
auto Err = validateEdits(DraftMgr, R->ApplyEdits))
715 return Reply(std::move(Err));
719 for (
const auto &It : R->ApplyEdits) {
721 It.second.asTextEdits();
724 return ApplyEdit(std::move(WE),
"Tweak applied.", std::move(Reply));
733 Reply(llvm::make_error<LSPError>(
734 llvm::formatv(
"Unsupported command \"{0}\".", Params.
command).str(),
739 void ClangdLSPServer::onWorkspaceSymbol(
741 Callback<std::vector<SymbolInformation>> Reply) {
743 Params.
query, CCOpts.Limit,
744 [Reply = std::move(Reply),
745 this](
llvm::Expected<std::vector<SymbolInformation>> Items)
mutable {
747 return Reply(Items.takeError());
748 for (auto &Sym : *Items)
749 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds);
751 Reply(std::move(*Items));
756 Callback<llvm::Optional<Range>> Reply) {
761 void ClangdLSPServer::onRename(
const RenameParams &Params,
764 llvm::Optional<std::string>
Code = DraftMgr.getDraft(File);
766 return Reply(llvm::make_error<LSPError>(
771 [File, Params, Reply = std::move(Reply),
772 this](llvm::Expected<FileEdits> Edits)
mutable {
774 return Reply(Edits.takeError());
775 if (auto Err = validateEdits(DraftMgr, *Edits))
776 return Reply(std::move(Err));
777 WorkspaceEdit Result;
778 Result.changes.emplace();
779 for (const auto &Rep : *Edits) {
780 (*Result.changes)[URI::createFile(Rep.first()).toString()] =
781 Rep.second.asTextEdits();
787 void ClangdLSPServer::onDocumentDidClose(
790 DraftMgr.removeDraft(File);
791 Server->removeDocument(File);
794 std::lock_guard<std::mutex> Lock(FixItsMutex);
795 FixItsMap.erase(File);
798 std::lock_guard<std::mutex> HLock(HighlightingsMutex);
799 FileToHighlightings.erase(File);
809 void ClangdLSPServer::onDocumentOnTypeFormatting(
811 Callback<std::vector<TextEdit>> Reply) {
813 auto Code = DraftMgr.getDraft(File);
815 return Reply(llvm::make_error<LSPError>(
816 "onDocumentOnTypeFormatting called for non-added file",
822 void ClangdLSPServer::onDocumentRangeFormatting(
824 Callback<std::vector<TextEdit>> Reply) {
826 auto Code = DraftMgr.getDraft(File);
828 return Reply(llvm::make_error<LSPError>(
829 "onDocumentRangeFormatting called for non-added file",
832 auto ReplacementsOrError =
Server->formatRange(*
Code, File, Params.
range);
833 if (ReplacementsOrError)
836 Reply(ReplacementsOrError.takeError());
839 void ClangdLSPServer::onDocumentFormatting(
841 Callback<std::vector<TextEdit>> Reply) {
843 auto Code = DraftMgr.getDraft(File);
845 return Reply(llvm::make_error<LSPError>(
846 "onDocumentFormatting called for non-added file",
849 auto ReplacementsOrError =
Server->formatFile(*
Code, File);
850 if (ReplacementsOrError)
853 Reply(ReplacementsOrError.takeError());
858 static std::vector<SymbolInformation>
862 std::vector<SymbolInformation>
Results;
863 std::function<void(const DocumentSymbol &, llvm::StringRef)> Process =
864 [&](
const DocumentSymbol &S, llvm::Optional<llvm::StringRef> ParentName) {
872 Results.push_back(std::move(SI));
873 std::string FullName =
874 !ParentName ? S.
name : (ParentName->str() +
"::" + S.
name);
876 Process(C, FullName);
878 for (
auto &S : Symbols)
888 [
this, FileURI, Reply = std::move(Reply)](
889 llvm::Expected<std::vector<DocumentSymbol>> Items)
mutable {
891 return Reply(Items.takeError());
892 adjustSymbolKinds(*Items, SupportedSymbolKinds);
893 if (SupportsHierarchicalDocumentSymbol)
894 return Reply(std::move(*Items));
906 }
else if (Action.
edit) {
907 Cmd.command = Command::CLANGD_APPLY_FIX_COMMAND;
908 Cmd.workspaceEdit = *Action.
edit;
912 Cmd.title = Action.
title;
914 Cmd.title =
"Apply fix: " + Cmd.title;
921 auto Code = DraftMgr.getDraft(File.
file());
923 return Reply(llvm::make_error<LSPError>(
926 std::vector<CodeAction> FixIts;
928 for (
auto &F : getFixes(File.
file(), D)) {
930 FixIts.back().diagnostics = {D};
935 auto ConsumeActions =
936 [Reply = std::move(Reply),
File,
Code = std::move(*Code),
937 Selection = Params.
range, FixIts = std::move(FixIts),
this](
938 llvm::Expected<std::vector<ClangdServer::TweakRef>> Tweaks)
mutable {
940 return Reply(Tweaks.takeError());
942 std::vector<CodeAction> Actions = std::move(FixIts);
943 Actions.reserve(Actions.size() + Tweaks->size());
944 for (
const auto &T : *Tweaks)
947 if (SupportsCodeAction)
948 return Reply(llvm::json::Array(Actions));
950 for (
const auto &
Action : Actions) {
952 Commands.push_back(std::move(*
Command));
954 return Reply(llvm::json::Array(Commands));
957 Server->enumerateTweaks(File.
file(), Params.
range, std::move(ConsumeActions));
962 if (!shouldRunCompletion(Params)) {
965 vlog(
"ignored auto-triggered completion, preceding char did not match");
969 [Reply = std::move(Reply),
970 this](llvm::Expected<CodeCompleteResult> List)
mutable {
972 return Reply(List.takeError());
975 for (
const auto &R : List->Completions) {
978 C.
kind, SupportedCompletionItemKinds);
979 LSPList.items.push_back(std::move(C));
981 return Reply(std::move(LSPList));
988 [Reply = std::move(Reply),
this](
989 llvm::Expected<SignatureHelp>
Signature)
mutable {
992 if (SupportsOffsetsInSignatureHelp)
996 for (
auto &SigInfo :
Signature->signatures) {
997 for (
auto &Param : SigInfo.parameters)
998 Param.labelOffsets.reset();
1024 void ClangdLSPServer::onGoToDefinition(
const TextDocumentPositionParams &Params,
1025 Callback<std::vector<Location>> Reply) {
1027 Params.textDocument.uri.file(), Params.position,
1028 [Params, Reply = std::move(Reply)](
1029 llvm::Expected<std::vector<LocatedSymbol>>
Symbols)
mutable {
1031 return Reply(
Symbols.takeError());
1032 std::vector<Location> Defs;
1035 return Reply(std::vector<Location>{std::move(*Toggle)});
1036 Defs.push_back(S.Definition.getValueOr(S.PreferredDeclaration));
1038 Reply(std::move(Defs));
1042 void ClangdLSPServer::onGoToDeclaration(
1043 const TextDocumentPositionParams &Params,
1044 Callback<std::vector<Location>> Reply) {
1046 Params.textDocument.uri.file(), Params.position,
1047 [Params, Reply = std::move(Reply)](
1048 llvm::Expected<std::vector<LocatedSymbol>>
Symbols)
mutable {
1050 return Reply(Symbols.takeError());
1051 std::vector<Location>
Decls;
1052 for (
auto &S : *Symbols) {
1054 return Reply(std::vector<Location>{std::move(*Toggle)});
1055 Decls.push_back(std::move(S.PreferredDeclaration));
1057 Reply(std::move(Decls));
1061 void ClangdLSPServer::onSwitchSourceHeader(
1062 const TextDocumentIdentifier &Params,
1063 Callback<llvm::Optional<URIForFile>> Reply) {
1064 Server->switchSourceHeader(
1066 [Reply = std::move(Reply),
1067 Params](llvm::Expected<llvm::Optional<clangd::Path>>
Path)
mutable {
1069 return Reply(
Path.takeError());
1076 void ClangdLSPServer::onDocumentHighlight(
1077 const TextDocumentPositionParams &Params,
1078 Callback<std::vector<DocumentHighlight>> Reply) {
1079 Server->findDocumentHighlights(Params.textDocument.uri.file(),
1080 Params.position, std::move(Reply));
1083 void ClangdLSPServer::onHover(
const TextDocumentPositionParams &Params,
1084 Callback<llvm::Optional<Hover>> Reply) {
1085 Server->findHover(Params.textDocument.uri.file(), Params.position,
1086 [Reply = std::move(Reply),
this](
1087 llvm::Expected<llvm::Optional<HoverInfo>> H)
mutable {
1089 return Reply(H.takeError());
1094 R.contents.kind = HoverContentFormat;
1095 R.range = (*H)->SymRange;
1096 switch (HoverContentFormat) {
1098 R.contents.value = (*H)->present().asPlainText();
1099 return Reply(std::move(R));
1101 R.contents.value = (*H)->present().asMarkdown();
1102 return Reply(std::move(R));
1104 llvm_unreachable(
"unhandled MarkupKind");
1108 void ClangdLSPServer::onTypeHierarchy(
1109 const TypeHierarchyParams &Params,
1110 Callback<Optional<TypeHierarchyItem>> Reply) {
1111 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1112 Params.resolve, Params.direction, std::move(Reply));
1115 void ClangdLSPServer::onResolveTypeHierarchy(
1116 const ResolveTypeHierarchyItemParams &Params,
1117 Callback<Optional<TypeHierarchyItem>> Reply) {
1118 Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction,
1122 void ClangdLSPServer::applyConfiguration(
1123 const ConfigurationSettings &Settings) {
1125 bool ShouldReparseOpenFiles =
false;
1126 for (
auto &
Entry : Settings.compilationDatabaseChanges) {
1130 auto Old = CDB->getCompileCommand(File);
1132 tooling::CompileCommand(std::move(
Entry.second.workingDirectory), File,
1133 std::move(
Entry.second.compilationCommand),
1136 CDB->setCompileCommand(File, std::move(New));
1137 ShouldReparseOpenFiles =
true;
1140 if (ShouldReparseOpenFiles)
1141 reparseOpenedFiles();
1144 void ClangdLSPServer::publishSemanticHighlighting(
1145 SemanticHighlightingParams Params) {
1146 notify(
"textDocument/semanticHighlighting", Params);
1149 void ClangdLSPServer::publishDiagnostics(
1150 const URIForFile &File, std::vector<clangd::Diagnostic> Diagnostics) {
1152 notify(
"textDocument/publishDiagnostics",
1155 {
"diagnostics", std::move(Diagnostics)},
1160 void ClangdLSPServer::onChangeConfiguration(
1161 const DidChangeConfigurationParams &Params) {
1162 applyConfiguration(Params.settings);
1165 void ClangdLSPServer::onReference(
const ReferenceParams &Params,
1166 Callback<std::vector<Location>> Reply) {
1167 Server->findReferences(Params.textDocument.uri.file(), Params.position,
1169 [Reply = std::move(Reply)](
1170 llvm::Expected<ReferencesResult>
Refs)
mutable {
1172 return Reply(
Refs.takeError());
1173 return Reply(std::move(
Refs->References));
1177 void ClangdLSPServer::onSymbolInfo(
const TextDocumentPositionParams &Params,
1178 Callback<std::vector<SymbolDetails>> Reply) {
1179 Server->symbolInfo(Params.textDocument.uri.file(), Params.position,
1183 void ClangdLSPServer::onSelectionRange(
1184 const SelectionRangeParams &Params,
1185 Callback<std::vector<SelectionRange>> Reply) {
1186 if (Params.positions.size() != 1) {
1187 elog(
"{0} positions provided to SelectionRange. Supports exactly one " 1189 Params.positions.size());
1190 return Reply(llvm::make_error<LSPError>(
1191 "SelectionRange supports exactly one position",
1195 Params.textDocument.uri.file(), Params.positions[0],
1196 [Reply = std::move(Reply)](
1197 llvm::Expected<std::vector<Range>> Ranges)
mutable {
1199 return Reply(Ranges.takeError());
1201 std::vector<SelectionRange> Result;
1202 Result.emplace_back(render(std::move(*Ranges)));
1203 return Reply(std::move(Result));
1207 void ClangdLSPServer::onDocumentLink(
1208 const DocumentLinkParams &Params,
1209 Callback<std::vector<DocumentLink>> Reply) {
1216 Params.textDocument.uri.file(),
1217 [Reply = std::move(Reply)](
1218 llvm::Expected<std::vector<DocumentLink>> Links)
mutable {
1220 return Reply(Links.takeError());
1222 return Reply(std::move(Links));
1229 llvm::Optional<Path> CompileCommandsDir,
bool UseDirBasedCDB,
1230 llvm::Optional<OffsetEncoding> ForcedOffsetEncoding,
1232 : BackgroundContext(
Context::current().clone()), Transp(Transp),
1234 CCOpts(CCOpts), SupportedSymbolKinds(defaultSymbolKinds()),
1235 SupportedCompletionItemKinds(defaultCompletionItemKinds()),
1236 UseDirBasedCDB(UseDirBasedCDB),
1237 CompileCommandsDir(std::move(CompileCommandsDir)), ClangdServerOpts(Opts),
1238 NegotiatedOffsetEncoding(ForcedOffsetEncoding) {
1240 MsgHandler->bind(
"initialize", &ClangdLSPServer::onInitialize);
1241 MsgHandler->bind(
"shutdown", &ClangdLSPServer::onShutdown);
1242 MsgHandler->bind(
"sync", &ClangdLSPServer::onSync);
1243 MsgHandler->bind(
"textDocument/rangeFormatting", &ClangdLSPServer::onDocumentRangeFormatting);
1244 MsgHandler->bind(
"textDocument/onTypeFormatting", &ClangdLSPServer::onDocumentOnTypeFormatting);
1245 MsgHandler->bind(
"textDocument/formatting", &ClangdLSPServer::onDocumentFormatting);
1246 MsgHandler->bind(
"textDocument/codeAction", &ClangdLSPServer::onCodeAction);
1247 MsgHandler->bind(
"textDocument/completion", &ClangdLSPServer::onCompletion);
1248 MsgHandler->bind(
"textDocument/signatureHelp", &ClangdLSPServer::onSignatureHelp);
1249 MsgHandler->bind(
"textDocument/definition", &ClangdLSPServer::onGoToDefinition);
1250 MsgHandler->bind(
"textDocument/declaration", &ClangdLSPServer::onGoToDeclaration);
1251 MsgHandler->bind(
"textDocument/references", &ClangdLSPServer::onReference);
1252 MsgHandler->bind(
"textDocument/switchSourceHeader", &ClangdLSPServer::onSwitchSourceHeader);
1253 MsgHandler->bind(
"textDocument/prepareRename", &ClangdLSPServer::onPrepareRename);
1254 MsgHandler->bind(
"textDocument/rename", &ClangdLSPServer::onRename);
1255 MsgHandler->bind(
"textDocument/hover", &ClangdLSPServer::onHover);
1256 MsgHandler->bind(
"textDocument/documentSymbol", &ClangdLSPServer::onDocumentSymbol);
1257 MsgHandler->bind(
"workspace/executeCommand", &ClangdLSPServer::onCommand);
1258 MsgHandler->bind(
"textDocument/documentHighlight", &ClangdLSPServer::onDocumentHighlight);
1259 MsgHandler->bind(
"workspace/symbol", &ClangdLSPServer::onWorkspaceSymbol);
1260 MsgHandler->bind(
"textDocument/didOpen", &ClangdLSPServer::onDocumentDidOpen);
1261 MsgHandler->bind(
"textDocument/didClose", &ClangdLSPServer::onDocumentDidClose);
1262 MsgHandler->bind(
"textDocument/didChange", &ClangdLSPServer::onDocumentDidChange);
1263 MsgHandler->bind(
"workspace/didChangeWatchedFiles", &ClangdLSPServer::onFileEvent);
1264 MsgHandler->bind(
"workspace/didChangeConfiguration", &ClangdLSPServer::onChangeConfiguration);
1265 MsgHandler->bind(
"textDocument/symbolInfo", &ClangdLSPServer::onSymbolInfo);
1266 MsgHandler->bind(
"textDocument/typeHierarchy", &ClangdLSPServer::onTypeHierarchy);
1267 MsgHandler->bind(
"typeHierarchy/resolve", &ClangdLSPServer::onResolveTypeHierarchy);
1268 MsgHandler->bind(
"textDocument/selectionRange", &ClangdLSPServer::onSelectionRange);
1269 MsgHandler->bind(
"textDocument/documentLink", &ClangdLSPServer::onDocumentLink);
1281 bool CleanExit =
true;
1282 if (
auto Err = Transp.
loop(*MsgHandler)) {
1283 elog(
"Transport error: {0}", std::move(Err));
1287 return CleanExit && ShutdownRequestReceived;
1290 std::vector<Fix> ClangdLSPServer::getFixes(llvm::StringRef File,
1292 std::lock_guard<std::mutex> Lock(FixItsMutex);
1293 auto DiagToFixItsIter = FixItsMap.find(File);
1294 if (DiagToFixItsIter == FixItsMap.end())
1297 const auto &DiagToFixItsMap = DiagToFixItsIter->second;
1298 auto FixItsIter = DiagToFixItsMap.find(D);
1299 if (FixItsIter == DiagToFixItsMap.end())
1302 return FixItsIter->second;
1305 bool ClangdLSPServer::shouldRunCompletion(
1309 (Trigger !=
">" && Trigger !=
":"))
1325 vlog(
"could not convert position '{0}' to offset for file '{1}'",
1336 assert(
false &&
"unhandled trigger character");
1340 void ClangdLSPServer::onHighlightingsReady(
1341 PathRef File, std::vector<HighlightingToken> Highlightings) {
1342 std::vector<HighlightingToken> Old;
1343 std::vector<HighlightingToken> HighlightingsCopy = Highlightings;
1345 std::lock_guard<std::mutex> Lock(HighlightingsMutex);
1346 Old = std::move(FileToHighlightings[File]);
1347 FileToHighlightings[
File] = std::move(HighlightingsCopy);
1352 publishSemanticHighlighting(
1357 void ClangdLSPServer::onDiagnosticsReady(
PathRef File,
1358 std::vector<Diag> Diagnostics) {
1360 std::vector<Diagnostic> LSPDiagnostics;
1361 DiagnosticToReplacementMap LocalFixIts;
1362 for (
auto &
Diag : Diagnostics) {
1365 auto &FixItsForDiagnostic = LocalFixIts[Diag];
1366 llvm::copy(Fixes, std::back_inserter(FixItsForDiagnostic));
1367 LSPDiagnostics.push_back(std::move(Diag));
1373 std::lock_guard<std::mutex> Lock(FixItsMutex);
1374 FixItsMap[
File] = LocalFixIts;
1378 publishDiagnostics(
URI, std::move(LSPDiagnostics));
1381 void ClangdLSPServer::onFileUpdated(
PathRef File,
const TUStatus &Status) {
1382 if (!SupportFileStatus)
1391 notify(
"textDocument/clangd.fileStatus", Status.
render(File));
1394 void ClangdLSPServer::reparseOpenedFiles() {
1395 for (
const Path &FilePath : DraftMgr.getActiveFiles())
1396 Server->addDocument(FilePath, *DraftMgr.getDraft(FilePath),
const tooling::CompileCommand & Command
Exact commands are not specified in the protocol so we define the ones supported by Clangd here...
llvm::Optional< SymbolKindBitset > WorkspaceSymbolKinds
The supported set of SymbolKinds for workspace/symbol.
llvm::Optional< URIForFile > rootUri
The rootUri of the workspace.
std::unique_ptr< GlobalCompilationDatabase > getQueryDriverDatabase(llvm::ArrayRef< std::string > QueryDriverGlobs, std::unique_ptr< GlobalCompilationDatabase > Base)
Extracts system include search path from drivers matching QueryDriverGlobs and adds them to the compi...
static std::vector< SymbolInformation > flattenSymbolHierarchy(llvm::ArrayRef< DocumentSymbol > Symbols, const URIForFile &FileURI)
The functions constructs a flattened view of the DocumentSymbol hierarchy.
Represents a collection of completion items to be presented in the editor.
Diagnostics must be generated for this snapshot.
llvm::Optional< bool > wantDiagnostics
Forces diagnostics to be generated, or to not be generated, for this version of the file...
Range range
The range for which the command was invoked.
std::function< void()> Canceler
A canceller requests cancellation of a task, when called.
FileStatus render(PathRef File) const
Serialize this to an LSP file status item.
CodeActionContext context
Context carrying additional information.
bool onReply(llvm::json::Value ID, llvm::Expected< llvm::json::Value > Result) override
static const llvm::StringLiteral CLANGD_APPLY_FIX_COMMAND
CompletionItemKind kind
The kind of this completion item.
bool CompletionSnippets
Client supports snippets as insert text.
Apply changes that preserve the behavior of the code.
void bind(const char *Method, void(ClangdLSPServer::*Handler)(const Param &))
CodeAction toCodeAction(const Fix &F, const URIForFile &File)
Convert from Fix to LSP CodeAction.
Documents are synced by sending the full content on open.
llvm::Optional< std::map< std::string, std::vector< TextEdit > > > changes
Holds changes to existing resources.
static cl::list< std::string > Commands("c", cl::desc("Specify command to run"), cl::value_desc("command"), cl::cat(ClangQueryCategory))
llvm::Optional< std::string > kind
The kind of the code action.
std::string title
A short, human-readable, title for this code action.
Provide information to the user.
TextDocumentIdentifier textDocument
The document that was closed.
bool run()
Run LSP server loop, communicating with the Transport provided in the constructor.
llvm::Optional< Location > Definition
A code action represents a change that can be performed in code, e.g.
URIForFile uri
The text document's URI.
llvm::StringRef PathRef
A typedef to represent a ref to file path.
llvm::Optional< WorkspaceEdit > edit
The workspace edit this code action performs.
std::vector< CodeCompletionResult > Results
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
constexpr auto SymbolKindMin
The show message notification is sent from a server to a client to ask the client to display a partic...
llvm::Optional< std::string > compilationDatabasePath
constexpr auto CompletionItemKindMin
std::bitset< CompletionItemKindMax+1 > CompletionItemKindBitset
Documents should not be synced at all.
bool isIncomplete
The list is not complete.
Range range
The range enclosing this symbol not including leading/trailing whitespace but everything else like co...
void vlog(const char *Fmt, Ts &&... Vals)
void elog(const char *Fmt, Ts &&... Vals)
Represents programming constructs like variables, classes, interfaces etc.
static const llvm::StringLiteral CLANGD_APPLY_TWEAK
bool onCall(llvm::StringRef Method, llvm::json::Value Params, llvm::json::Value ID) override
MarkupKind HoverContentFormat
The content format that should be used for Hover requests.
MockFSProvider FSProvider
ConfigurationSettings ConfigSettings
A top-level diagnostic that may have Notes and Fixes.
bool OffsetsInSignatureHelp
Client supports processing label offsets instead of a simple label string.
URIForFile uri
The text document's URI.
bool CompletionFixes
Client supports completions with additionalTextEdit near the cursor.
llvm::Optional< TweakArgs > tweakArgs
static URI createFile(llvm::StringRef AbsolutePath)
This creates a file:// URI for AbsolutePath. The path must be absolute.
TextDocumentIdentifier textDocument
The document that was opened.
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.
bool DiagnosticCategory
Whether the client accepts diagnostics with category attached to it using the "category" extension...
std::string newName
The new name of the symbol.
std::vector< SemanticHighlightingInformation > toSemanticHighlightingInformation(llvm::ArrayRef< LineHighlightings > Tokens)
Convert to LSP's semantic highlighting information.
std::string command
The command identifier, e.g. CLANGD_APPLY_FIX_COMMAND.
InitializationOptions initializationOptions
User-provided initialization options.
TextDocumentIdentifier textDocument
MessageHandler(ClangdLSPServer &Server)
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
TextDocumentIdentifier textDocument
The document in which the command was invoked.
static Location * getToggle(const TextDocumentPositionParams &Point, LocatedSymbol &Sym)
llvm::unique_function< void()> Action
std::vector< std::string > fallbackFlags
void log(const char *Fmt, Ts &&... Vals)
MessageType type
The message type.
static const char * toString(OffsetEncoding OE)
std::string Path
A typedef to represent a file path.
static const Context & current()
Returns the context for the current thread, creating it if needed.
CompletionTriggerKind triggerKind
How the completion was triggered.
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Canonicalizes AbsPath via URI.
Position position
The position inside the text document.
enum clang::clangd::@771::NamespaceEvent::@3 Trigger
Key< OffsetEncoding > kCurrentOffsetEncoding
bool SemanticHighlighting
Client supports semantic highlighting.
Location PreferredDeclaration
std::vector< LineHighlightings > diffHighlightings(ArrayRef< HighlightingToken > New, ArrayRef< HighlightingToken > Old)
Return a line-by-line diff between two highlightings.
std::vector< DocumentSymbol > children
Children of this symbol, e.g. properties of a class.
static const llvm::StringLiteral REFACTOR_KIND
llvm::json::Value bindReply(Callback< llvm::json::Value > Reply)
bool FileStatus
Clients supports show file status for textDocument/clangd.fileStatus.
std::string name
The name of this symbol.
std::pair< Context, Canceler > cancelableTask()
Defines a new task whose cancellation may be requested.
static llvm::Optional< Command > asCommand(const CodeAction &Action)
bool DiagnosticFixes
Whether the client accepts diagnostics with codeActions attached inline.
ClientCapabilities capabilities
The capabilities provided by the client (editor or tool)
TextDocumentItem textDocument
The document that was opened.
A context is an immutable container for per-request data that must be propagated through layers that ...
TextDocumentIdentifier textDocument
The document that did change.
Completion was triggered by a trigger character specified by the triggerCharacters properties of the ...
bool onNotify(llvm::StringRef Method, llvm::json::Value Params) override
llvm::Optional< CompletionItemKindBitset > CompletionItemKinds
The supported set of CompletionItemKinds for textDocument/completion.
~ClangdLSPServer()
The destructor blocks on any outstanding background tasks.
llvm::Optional< std::string > rootPath
The rootPath of the workspace.
virtual llvm::Error loop(MessageHandler &)=0
bool CodeActionStructure
Client supports CodeAction return value for textDocument/codeAction.
WithContext replaces Context::current() with a provided scope.
llvm::StringMap< Edit > FileEdits
A mapping from absolute file path (the one used for accessing the underlying VFS) to edits...
bool fromJSON(const llvm::json::Value &Parameters, FuzzyFindRequest &Request)
void bind(const char *Method, void(ClangdLSPServer::*Handler)(const Param &, Callback< Result >))
std::vector< TextDocumentContentChangeEvent > contentChanges
The actual content changes.
CompletionContext context
bool contains(Position Pos) const
std::string query
A non-empty query string.
SymbolKind kind
The kind of this symbol.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::bitset< SymbolKindMax+1 > SymbolKindBitset
std::string triggerCharacter
The trigger character (a single character) that has trigger code complete.
TextDocumentIdentifier textDocument
The text document.
static const llvm::StringLiteral INFO_KIND
Context derive(const Key< Type > &Key, typename std::decay< Type >::type Value) const &
Derives a child context It is safe to move or destroy a parent context after calling derive()...
virtual void reply(llvm::json::Value ID, llvm::Expected< llvm::json::Value > Result)=0
CharSourceRange Range
SourceRange for the file name.
SymbolKind adjustKindToCapability(SymbolKind Kind, SymbolKindBitset &SupportedSymbolKinds)
static const llvm::StringLiteral QUICKFIX_KIND
A URI describes the location of a source file.
std::vector< Diagnostic > diagnostics
An array of diagnostics.
llvm::Optional< Command > command
A command this code action executes.
std::vector< TextEdit > replacementsToEdits(llvm::StringRef Code, const tooling::Replacements &Repls)
The parameters of a Workspace Symbol Request.
std::vector< const char * > Expected
llvm::SmallDenseMap< const NamedDecl *, RelSet > Decls
std::string text
The content of the opened text document.
Position position
The position at which this request was sent.
URIForFile uri
The text document's URI.
std::string message
The actual message.
bool HasSignatureHelp
Client supports signature help.
bool DiagnosticRelatedInformation
Whether the client accepts diagnostics with related locations.
llvm::Optional< WorkspaceEdit > workspaceEdit
This class exposes ClangdServer's capabilities via Language Server Protocol.
llvm::json::Object *const Args
Mutable metadata, if this span is interested.
static CommandMangler detect()
Records an event whose duration is the lifetime of the Span object.
bool RenamePrepareSupport
The client supports testing for validity of rename operations before execution.
ClangdLSPServer(Transport &Transp, const FileSystemProvider &FSProvider, const clangd::CodeCompleteOptions &CCOpts, llvm::Optional< Path > CompileCommandsDir, bool UseDirBasedCDB, llvm::Optional< OffsetEncoding > ForcedOffsetEncoding, const ClangdServer::Options &Opts)
If CompileCommandsDir has a value, compile_commands.json will be loaded only from CompileCommandsDir...
#define SPAN_ATTACH(S, Name, Expr)
Attach a key-value pair to a Span event.
Diagnostics must not be generated for this snapshot.
A set of edits generated for a single file.
llvm::Optional< std::vector< OffsetEncoding > > offsetEncoding
Supported encodings for LSP character offsets. (clangd extension).
bool HierarchicalDocumentSymbol
Client supports hierarchical document symbols.
llvm::StringRef file() const
Retrieves absolute path to the file.
const SymbolIndex * Index