22 #include "clang/Basic/Version.h"
23 #include "clang/Tooling/Core/Replacement.h"
24 #include "llvm/ADT/ArrayRef.h"
25 #include "llvm/ADT/Optional.h"
26 #include "llvm/ADT/ScopeExit.h"
27 #include "llvm/ADT/StringRef.h"
28 #include "llvm/ADT/iterator_range.h"
29 #include "llvm/Support/Errc.h"
30 #include "llvm/Support/Error.h"
31 #include "llvm/Support/FormatVariadic.h"
32 #include "llvm/Support/JSON.h"
33 #include "llvm/Support/Path.h"
34 #include "llvm/Support/SHA1.h"
35 #include "llvm/Support/ScopedPrinter.h"
53 std::string encodeVersion(int64_t LSPVersion) {
54 return llvm::to_string(LSPVersion);
56 llvm::Optional<int64_t> decodeVersion(llvm::StringRef Encoded) {
58 if (llvm::to_integer(Encoded, Result, 10))
60 else if (!Encoded.empty())
61 elog(
"unexpected non-numeric version {0}", Encoded);
67 CodeAction
toCodeAction(
const ClangdServer::TweakRef &T,
const URIForFile &File,
85 CA.command->title = T.Title;
86 CA.command->command = std::string(Command::CLANGD_APPLY_TWEAK);
87 CA.command->tweakArgs.emplace();
88 CA.command->tweakArgs->file = File;
89 CA.command->tweakArgs->tweakID = T.ID;
90 CA.command->tweakArgs->selection = Selection;
94 void adjustSymbolKinds(llvm::MutableArrayRef<DocumentSymbol> Syms,
96 for (
auto &S : Syms) {
98 adjustSymbolKinds(S.children, Kinds);
120 std::vector<std::vector<std::string>> buildHighlightScopeLookupTable() {
121 std::vector<std::vector<std::string>> LookupTable;
125 LookupTable.push_back(
126 {std::string(toTextMateScope((HighlightingKind)(KindValue)))});
134 size_t InvalidFileCount = 0;
135 llvm::StringRef LastInvalidFile;
136 for (
const auto &It : FE) {
137 if (
auto Draft = DraftMgr.getDraft(It.first())) {
141 if (!It.second.canApplyTo(Draft->Contents)) {
143 LastInvalidFile = It.first();
147 if (!InvalidFileCount)
148 return llvm::Error::success();
149 if (InvalidFileCount == 1)
150 return llvm::createStringError(llvm::inconvertibleErrorCode(),
151 "File must be saved first: " +
153 return llvm::createStringError(
154 llvm::inconvertibleErrorCode(),
155 "Files must be saved first: " + LastInvalidFile +
" (and " +
156 llvm::to_string(InvalidFileCount - 1) +
" others)");
178 elog(
"Notification {0} before initialization",
Method);
179 else if (
Method ==
"$/cancelRequest")
180 onCancel(std::move(Params));
181 else if (
auto Handler = Notifications.lookup(
Method))
182 Handler(std::move(Params));
184 log(
"unhandled notification {0}",
Method);
189 llvm::json::Value ID)
override {
192 WithContext WithCancel(cancelableRequestContext(ID));
197 if (!Server.Server &&
Method !=
"initialize") {
198 elog(
"Call {0} before initialization.",
Method);
199 Reply(llvm::make_error<LSPError>(
"server not initialized",
201 }
else if (
auto Handler = Calls.lookup(
Method))
202 Handler(std::move(Params), std::move(Reply));
204 Reply(llvm::make_error<LSPError>(
"method not found",
210 llvm::Expected<llvm::json::Value> Result)
override {
214 if (
auto IntID = ID.getAsInteger()) {
215 std::lock_guard<std::mutex> Mutex(CallMutex);
218 if (ReplyCallbacks[
Index].first == *IntID) {
219 ReplyHandler = std::move(ReplyCallbacks[
Index].second);
220 ReplyCallbacks.erase(ReplyCallbacks.begin() +
229 ReplyHandler = [&ID](llvm::Expected<llvm::json::Value> Result) {
230 elog(
"received a reply with ID {0}, but there was no such call", ID);
232 llvm::consumeError(Result.takeError());
238 log(
"<-- reply({0})", ID);
239 ReplyHandler(std::move(Result));
241 auto Err = Result.takeError();
242 log(
"<-- reply({0}) error: {1}", ID, Err);
243 ReplyHandler(std::move(Err));
249 template <
typename Param,
typename Result>
252 Calls[
Method] = [
Method, Handler,
this](llvm::json::Value RawParams,
256 (Server.*Handler)(P, std::move(Reply));
258 elog(
"Failed to decode {0} request.",
Method);
259 Reply(llvm::make_error<LSPError>(
"failed to decode request",
269 llvm::Optional<std::pair<int, Callback<llvm::json::Value>>> OldestCB;
272 std::lock_guard<std::mutex> Mutex(CallMutex);
274 ReplyCallbacks.emplace_back(ID, std::move(Reply));
279 if (ReplyCallbacks.size() > MaxReplayCallbacks) {
280 elog(
"more than {0} outstanding LSP calls, forgetting about {1}",
281 MaxReplayCallbacks, ReplyCallbacks.front().first);
282 OldestCB = std::move(ReplyCallbacks.front());
283 ReplyCallbacks.pop_front();
287 OldestCB->second(llvm::createStringError(
288 llvm::inconvertibleErrorCode(),
289 llvm::formatv(
"failed to receive a client reply for request ({0})",
295 template <
typename Param>
299 this](llvm::json::Value RawParams) {
302 elog(
"Failed to decode {0} request.",
Method);
307 (Server.*Handler)(P);
318 std::atomic<bool> Replied = {
false};
319 std::chrono::steady_clock::time_point Start;
320 llvm::json::Value ID;
323 llvm::json::Object *TraceArgs;
326 ReplyOnce(
const llvm::json::Value &ID, llvm::StringRef
Method,
328 : Start(std::chrono::steady_clock::now()), ID(ID),
Method(
Method),
329 Server(Server), TraceArgs(TraceArgs) {
332 ReplyOnce(ReplyOnce &&Other)
333 : Replied(Other.Replied.load()), Start(Other.Start),
334 ID(std::move(Other.ID)),
Method(std::move(Other.
Method)),
335 Server(Other.Server), TraceArgs(Other.TraceArgs) {
336 Other.Server =
nullptr;
338 ReplyOnce &operator=(ReplyOnce &&) =
delete;
339 ReplyOnce(
const ReplyOnce &) =
delete;
340 ReplyOnce &operator=(
const ReplyOnce &) =
delete;
349 if (Server && !Server->IsBeingDestroyed && !Replied) {
350 elog(
"No reply to message {0}({1})",
Method, ID);
351 assert(
false &&
"must reply to all calls!");
352 (*this)(llvm::make_error<LSPError>(
"server failed to reply",
357 void operator()(llvm::Expected<llvm::json::Value> Reply) {
358 assert(Server &&
"moved-from!");
359 if (Replied.exchange(
true)) {
360 elog(
"Replied twice to message {0}({1})",
Method, ID);
361 assert(
false &&
"must reply to each call only once!");
364 auto Duration = std::chrono::steady_clock::now() - Start;
366 log(
"--> reply:{0}({1}) {2:ms}",
Method, ID, Duration);
368 (*TraceArgs)[
"Reply"] = *Reply;
369 std::lock_guard<std::mutex> Lock(Server->TranspWriter);
370 Server->Transp.reply(std::move(ID), std::move(Reply));
373 log(
"--> reply:{0}({1}) {2:ms}, error: {3}",
Method, ID, Duration, Err);
375 (*TraceArgs)[
"Error"] = llvm::to_string(Err);
376 std::lock_guard<std::mutex> Lock(Server->TranspWriter);
377 Server->Transp.reply(std::move(ID), std::move(Err));
382 llvm::StringMap<std::function<void(llvm::json::Value)>> Notifications;
383 llvm::StringMap<std::function<void(llvm::json::Value, ReplyOnce)>> Calls;
388 mutable std::mutex RequestCancelersMutex;
389 llvm::StringMap<std::pair<
Canceler,
unsigned>> RequestCancelers;
390 unsigned NextRequestCookie = 0;
391 void onCancel(
const llvm::json::Value &Params) {
392 const llvm::json::Value *ID =
nullptr;
393 if (
auto *O = Params.getAsObject())
396 elog(
"Bad cancellation request: {0}", Params);
399 auto StrID = llvm::to_string(*ID);
400 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
401 auto It = RequestCancelers.find(StrID);
402 if (It != RequestCancelers.end())
406 Context handlerContext()
const {
416 Context cancelableRequestContext(
const llvm::json::Value &ID) {
419 auto StrID = llvm::to_string(ID);
420 auto Cookie = NextRequestCookie++;
422 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
423 RequestCancelers[StrID] = {std::move(Task.second), Cookie};
428 return Task.first.derive(llvm::make_scope_exit([
this, StrID, Cookie] {
429 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
430 auto It = RequestCancelers.find(StrID);
431 if (It != RequestCancelers.end() && It->second.second == Cookie)
432 RequestCancelers.erase(It);
442 static constexpr
int MaxReplayCallbacks = 100;
443 mutable std::mutex CallMutex;
445 std::deque<std::pair< int,
446 Callback<llvm::json::Value>>>
451 constexpr
int ClangdLSPServer::MessageHandler::MaxReplayCallbacks;
454 void ClangdLSPServer::callRaw(StringRef Method, llvm::json::Value Params,
455 Callback<llvm::json::Value> CB) {
456 auto ID = MsgHandler->bindReply(std::move(CB));
458 std::lock_guard<std::mutex> Lock(TranspWriter);
462 void ClangdLSPServer::notify(llvm::StringRef Method, llvm::json::Value Params) {
464 std::lock_guard<std::mutex> Lock(TranspWriter);
469 std::vector<llvm::StringRef> Types;
476 void ClangdLSPServer::onInitialize(
const InitializeParams &Params,
477 Callback<llvm::json::Value> Reply) {
479 if (Params.capabilities.offsetEncoding && !NegotiatedOffsetEncoding) {
481 for (
OffsetEncoding Supported : *Params.capabilities.offsetEncoding)
483 NegotiatedOffsetEncoding = Supported;
489 Params.capabilities.TheiaSemanticHighlighting;
490 if (Params.capabilities.TheiaSemanticHighlighting &&
491 Params.capabilities.SemanticTokens) {
492 log(
"Client supports legacy semanticHighlights notification and standard "
493 "semanticTokens request, choosing the latter (no notifications).");
497 if (Params.rootUri && *Params.rootUri)
498 ClangdServerOpts.
WorkspaceRoot = std::string(Params.rootUri->file());
499 else if (Params.rootPath && !Params.rootPath->empty())
502 return Reply(llvm::make_error<LSPError>(
"server already initialized",
504 if (
const auto &Dir = Params.initializationOptions.compilationDatabasePath)
505 CompileCommandsDir = Dir;
506 if (UseDirBasedCDB) {
507 BaseCDB = std::make_unique<DirectoryBasedGlobalCompilationDatabase>(
515 Mangler.ResourceDir = *ClangdServerOpts.
ResourceDir;
516 CDB.emplace(BaseCDB.get(), Params.initializationOptions.fallbackFlags,
517 tooling::ArgumentsAdjuster(std::move(Mangler)));
522 WithContext MainContext(BackgroundContext.
clone());
523 llvm::Optional<WithContextValue> WithOffsetEncoding;
524 if (NegotiatedOffsetEncoding)
526 *NegotiatedOffsetEncoding);
527 Server.emplace(*CDB, TFS, ClangdServerOpts,
528 static_cast<ClangdServer::Callbacks *>(
this));
530 applyConfiguration(Params.initializationOptions.ConfigSettings);
537 Params.capabilities.CompletionDocumentationFormat;
541 Params.capabilities.DiagnosticRelatedInformation;
542 if (Params.capabilities.WorkspaceSymbolKinds)
543 SupportedSymbolKinds |= *Params.capabilities.WorkspaceSymbolKinds;
544 if (Params.capabilities.CompletionItemKinds)
545 SupportedCompletionItemKinds |= *Params.capabilities.CompletionItemKinds;
546 SupportsCodeAction = Params.capabilities.CodeActionStructure;
547 SupportsHierarchicalDocumentSymbol =
548 Params.capabilities.HierarchicalDocumentSymbol;
549 SupportFileStatus = Params.initializationOptions.FileStatus;
550 HoverContentFormat = Params.capabilities.HoverContentFormat;
551 SupportsOffsetsInSignatureHelp = Params.capabilities.OffsetsInSignatureHelp;
552 if (Params.capabilities.WorkDoneProgress)
553 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
554 BackgroundIndexSkipCreate = Params.capabilities.ImplicitProgressCreation;
558 llvm::json::Value RenameProvider =
559 llvm::json::Object{{
"prepareProvider",
true}};
560 if (!Params.capabilities.RenamePrepareSupport)
561 RenameProvider =
true;
566 llvm::json::Value CodeActionProvider =
true;
567 if (Params.capabilities.CodeActionStructure)
568 CodeActionProvider = llvm::json::Object{
573 llvm::json::Object Result{
575 llvm::json::Object{{
"name",
"clangd"},
576 {
"version", getClangToolFullVersion(
"clangd")}}},
585 {
"documentFormattingProvider",
true},
586 {
"documentRangeFormattingProvider",
true},
587 {
"documentOnTypeFormattingProvider",
589 {
"firstTriggerCharacter",
"\n"},
590 {
"moreTriggerCharacter", {}},
592 {
"codeActionProvider", std::move(CodeActionProvider)},
593 {
"completionProvider",
595 {
"allCommitCharacters",
" \t()[]{}<>:;,+-/*%^&#?.=\"'|"},
596 {
"resolveProvider",
false},
598 {
"triggerCharacters", {
".",
"<",
">",
":",
"\"",
"/"}},
600 {
"semanticTokensProvider",
602 {
"full", llvm::json::Object{{
"delta",
true}}},
606 {
"tokenModifiers", llvm::json::Array()}}},
608 {
"signatureHelpProvider",
610 {
"triggerCharacters", {
"(",
","}},
612 {
"declarationProvider",
true},
613 {
"definitionProvider",
true},
614 {
"documentHighlightProvider",
true},
615 {
"documentLinkProvider",
617 {
"resolveProvider",
false},
619 {
"hoverProvider",
true},
620 {
"renameProvider", std::move(RenameProvider)},
621 {
"selectionRangeProvider",
true},
622 {
"documentSymbolProvider",
true},
623 {
"workspaceSymbolProvider",
true},
624 {
"referencesProvider",
true},
625 {
"executeCommandProvider",
631 {
"typeHierarchyProvider",
true},
633 if (NegotiatedOffsetEncoding)
634 Result[
"offsetEncoding"] = *NegotiatedOffsetEncoding;
636 Result.getObject(
"capabilities")
638 {
"semanticHighlighting",
639 llvm::json::Object{{
"scopes", buildHighlightScopeLookupTable()}}});
641 Result.getObject(
"capabilities")->insert({
"foldingRangeProvider",
true});
642 Reply(std::move(Result));
648 Callback<std::nullptr_t> Reply) {
650 ShutdownRequestReceived =
true;
656 void ClangdLSPServer::onSync(
const NoParams &Params,
657 Callback<std::nullptr_t> Reply) {
658 if (Server->blockUntilIdleForTest(60))
661 Reply(llvm::createStringError(llvm::inconvertibleErrorCode(),
662 "Not idle after a minute"));
665 void ClangdLSPServer::onDocumentDidOpen(
666 const DidOpenTextDocumentParams &Params) {
669 const std::string &
Contents = Params.textDocument.text;
671 auto Version = DraftMgr.addDraft(
File, Params.textDocument.version,
Contents);
672 Server->addDocument(
File,
Contents, encodeVersion(Version),
676 void ClangdLSPServer::onDocumentDidChange(
677 const DidChangeTextDocumentParams &Params) {
679 if (Params.wantDiagnostics.hasValue())
684 llvm::Expected<DraftStore::Draft> Draft = DraftMgr.updateDraft(
685 File, Params.textDocument.version, Params.contentChanges);
690 DraftMgr.removeDraft(
File);
691 Server->removeDocument(
File);
692 elog(
"Failed to update {0}: {1}",
File, Draft.takeError());
696 Server->addDocument(
File, Draft->Contents, encodeVersion(Draft->Version),
700 void ClangdLSPServer::onDocumentDidSave(
701 const DidSaveTextDocumentParams &Params) {
702 reparseOpenFilesIfNeeded([](llvm::StringRef) {
return true; });
705 void ClangdLSPServer::onFileEvent(
const DidChangeWatchedFilesParams &Params) {
710 Server->onFileEvent(Params);
713 void ClangdLSPServer::onCommand(
const ExecuteCommandParams &Params,
714 Callback<llvm::json::Value> Reply) {
715 auto ApplyEdit = [
this](WorkspaceEdit WE, std::string SuccessMessage,
716 decltype(Reply) Reply) {
717 ApplyWorkspaceEditParams Edit;
718 Edit.edit = std::move(WE);
719 call<ApplyWorkspaceEditResponse>(
720 "workspace/applyEdit", std::move(Edit),
721 [Reply = std::move(Reply), SuccessMessage = std::move(SuccessMessage)](
722 llvm::Expected<ApplyWorkspaceEditResponse> Response)
mutable {
724 return Reply(Response.takeError());
725 if (!Response->applied) {
726 std::string Reason = Response->failureReason
727 ? *Response->failureReason
729 return Reply(llvm::createStringError(
730 llvm::inconvertibleErrorCode(),
731 (
"edits were not applied: " + Reason).c_str()));
733 return Reply(SuccessMessage);
738 Params.workspaceEdit) {
747 ApplyEdit(*Params.workspaceEdit,
"Fix applied.", std::move(Reply));
750 auto Code = DraftMgr.getDraft(Params.tweakArgs->file.file());
752 return Reply(llvm::createStringError(
753 llvm::inconvertibleErrorCode(),
754 "trying to apply a code action for a non-added file"));
756 auto Action = [
this, ApplyEdit, Reply = std::move(Reply),
757 File = Params.tweakArgs->file,
Code = std::move(*
Code)](
758 llvm::Expected<Tweak::Effect> R)
mutable {
760 return Reply(R.takeError());
762 assert(R->ShowMessage ||
763 (!R->ApplyEdits.empty() &&
"tweak has no effect"));
765 if (R->ShowMessage) {
766 ShowMessageParams Msg;
767 Msg.message = *R->ShowMessage;
769 notify(
"window/showMessage", Msg);
772 if (R->ApplyEdits.empty())
773 return Reply(
"Tweak applied.");
775 if (
auto Err = validateEdits(DraftMgr, R->ApplyEdits))
776 return Reply(std::move(Err));
779 WE.changes.emplace();
780 for (
const auto &It : R->ApplyEdits) {
782 It.second.asTextEdits();
785 return ApplyEdit(std::move(WE),
"Tweak applied.", std::move(Reply));
787 Server->applyTweak(Params.tweakArgs->file.file(),
788 Params.tweakArgs->selection, Params.tweakArgs->tweakID,
794 Reply(llvm::make_error<LSPError>(
795 llvm::formatv(
"Unsupported command \"{0}\".", Params.command).str(),
800 void ClangdLSPServer::onWorkspaceSymbol(
801 const WorkspaceSymbolParams &Params,
802 Callback<std::vector<SymbolInformation>> Reply) {
803 Server->workspaceSymbols(
804 Params.query, CCOpts.
Limit,
805 [Reply = std::move(Reply),
806 this](
llvm::Expected<std::vector<SymbolInformation>> Items)
mutable {
808 return Reply(Items.takeError());
809 for (auto &Sym : *Items)
810 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds);
812 Reply(std::move(*Items));
816 void ClangdLSPServer::onPrepareRename(
const TextDocumentPositionParams &Params,
817 Callback<llvm::Optional<Range>> Reply) {
818 Server->prepareRename(Params.textDocument.uri.file(), Params.position,
819 RenameOpts, std::move(Reply));
822 void ClangdLSPServer::onRename(
const RenameParams &Params,
823 Callback<WorkspaceEdit> Reply) {
824 Path File = std::string(Params.textDocument.uri.file());
825 if (!DraftMgr.getDraft(
File))
826 return Reply(llvm::make_error<LSPError>(
829 File, Params.position, Params.newName, RenameOpts,
830 [
File, Params, Reply = std::move(Reply),
831 this](llvm::Expected<FileEdits> Edits)
mutable {
833 return Reply(Edits.takeError());
834 if (auto Err = validateEdits(DraftMgr, *Edits))
835 return Reply(std::move(Err));
836 WorkspaceEdit Result;
837 Result.changes.emplace();
838 for (const auto &Rep : *Edits) {
839 (*Result.changes)[URI::createFile(Rep.first()).toString()] =
840 Rep.second.asTextEdits();
846 void ClangdLSPServer::onDocumentDidClose(
847 const DidCloseTextDocumentParams &Params) {
848 PathRef File = Params.textDocument.uri.file();
849 DraftMgr.removeDraft(File);
850 Server->removeDocument(File);
853 std::lock_guard<std::mutex> Lock(FixItsMutex);
854 FixItsMap.erase(File);
857 std::lock_guard<std::mutex> HLock(HighlightingsMutex);
858 FileToHighlightings.erase(File);
861 std::lock_guard<std::mutex> HLock(SemanticTokensMutex);
862 LastSemanticTokens.erase(File);
869 PublishDiagnosticsParams Notification;
871 publishDiagnostics(Notification);
874 void ClangdLSPServer::onDocumentOnTypeFormatting(
875 const DocumentOnTypeFormattingParams &Params,
876 Callback<std::vector<TextEdit>> Reply) {
877 auto File = Params.textDocument.uri.file();
878 auto Code = DraftMgr.getDraft(File);
880 return Reply(llvm::make_error<LSPError>(
881 "onDocumentOnTypeFormatting called for non-added file",
882 ErrorCode::InvalidParams));
884 Server->formatOnType(File,
Code->Contents, Params.position, Params.ch,
888 void ClangdLSPServer::onDocumentRangeFormatting(
889 const DocumentRangeFormattingParams &Params,
890 Callback<std::vector<TextEdit>> Reply) {
891 auto File = Params.textDocument.uri.file();
892 auto Code = DraftMgr.getDraft(File);
894 return Reply(llvm::make_error<LSPError>(
895 "onDocumentRangeFormatting called for non-added file",
896 ErrorCode::InvalidParams));
899 File,
Code->Contents, Params.range,
900 [
Code =
Code->Contents, Reply = std::move(Reply)](
901 llvm::Expected<tooling::Replacements> Result)
mutable {
903 Reply(replacementsToEdits(Code, Result.get()));
905 Reply(Result.takeError());
909 void ClangdLSPServer::onDocumentFormatting(
910 const DocumentFormattingParams &Params,
911 Callback<std::vector<TextEdit>> Reply) {
912 auto File = Params.textDocument.uri.file();
913 auto Code = DraftMgr.getDraft(File);
915 return Reply(llvm::make_error<LSPError>(
916 "onDocumentFormatting called for non-added file",
917 ErrorCode::InvalidParams));
919 Server->formatFile(File,
Code->Contents,
920 [
Code =
Code->Contents, Reply = std::move(Reply)](
921 llvm::Expected<tooling::Replacements> Result)
mutable {
923 Reply(replacementsToEdits(Code, Result.get()));
925 Reply(Result.takeError());
931 static std::vector<SymbolInformation>
934 std::vector<SymbolInformation>
Results;
935 std::function<void(
const DocumentSymbol &, llvm::StringRef)> Process =
936 [&](
const DocumentSymbol &S, llvm::Optional<llvm::StringRef> ParentName) {
938 SI.
containerName = std::string(ParentName ?
"" : *ParentName);
944 Results.push_back(std::move(SI));
945 std::string FullName =
946 !ParentName ? S.
name : (ParentName->str() +
"::" + S.
name);
948 Process(C, FullName);
955 void ClangdLSPServer::onDocumentSymbol(
const DocumentSymbolParams &Params,
956 Callback<llvm::json::Value> Reply) {
957 URIForFile FileURI = Params.textDocument.uri;
958 Server->documentSymbols(
959 Params.textDocument.uri.file(),
960 [
this, FileURI, Reply = std::move(Reply)](
961 llvm::Expected<std::vector<DocumentSymbol>> Items)
mutable {
963 return Reply(Items.takeError());
964 adjustSymbolKinds(*Items, SupportedSymbolKinds);
965 if (SupportsHierarchicalDocumentSymbol)
966 return Reply(std::move(*Items));
972 void ClangdLSPServer::onFoldingRange(
973 const FoldingRangeParams &Params,
974 Callback<std::vector<FoldingRange>> Reply) {
975 Server->foldingRanges(Params.textDocument.uri.file(), std::move(Reply));
985 Cmd.command = std::string(Command::CLANGD_APPLY_FIX_COMMAND);
986 Cmd.workspaceEdit = *
Action.edit;
991 if (
Action.kind && *
Action.kind == CodeAction::QUICKFIX_KIND)
992 Cmd.title =
"Apply fix: " + Cmd.title;
996 void ClangdLSPServer::onCodeAction(
const CodeActionParams &Params,
997 Callback<llvm::json::Value> Reply) {
998 URIForFile File = Params.textDocument.uri;
999 auto Code = DraftMgr.getDraft(File.file());
1001 return Reply(llvm::make_error<LSPError>(
1002 "onCodeAction called for non-added file", ErrorCode::InvalidParams));
1004 std::vector<CodeAction> FixIts;
1005 for (
const Diagnostic &D : Params.context.diagnostics) {
1006 for (
auto &F : getFixes(File.file(), D)) {
1007 FixIts.push_back(
toCodeAction(F, Params.textDocument.uri));
1008 FixIts.back().diagnostics = {D};
1013 auto ConsumeActions =
1014 [Reply = std::move(Reply), File,
Code = std::move(*
Code),
1015 Selection = Params.range, FixIts = std::move(FixIts),
this](
1016 llvm::Expected<std::vector<ClangdServer::TweakRef>> Tweaks)
mutable {
1018 return Reply(Tweaks.takeError());
1020 std::vector<CodeAction> Actions = std::move(FixIts);
1021 Actions.reserve(Actions.size() + Tweaks->size());
1022 for (
const auto &T : *Tweaks)
1025 if (SupportsCodeAction)
1026 return Reply(llvm::json::Array(Actions));
1028 for (
const auto &
Action : Actions) {
1030 Commands.push_back(std::move(*Command));
1032 return Reply(llvm::json::Array(
Commands));
1035 Server->enumerateTweaks(File.file(), Params.range, std::move(ConsumeActions));
1038 void ClangdLSPServer::onCompletion(
const CompletionParams &Params,
1039 Callback<CompletionList> Reply) {
1040 if (!shouldRunCompletion(Params)) {
1043 vlog(
"ignored auto-triggered completion, preceding char did not match");
1044 return Reply(CompletionList());
1046 Server->codeComplete(Params.textDocument.uri.file(), Params.position,
CCOpts,
1047 [Reply = std::move(Reply),
1048 this](llvm::Expected<CodeCompleteResult> List)
mutable {
1050 return Reply(List.takeError());
1051 CompletionList LSPList;
1052 LSPList.isIncomplete = List->HasMore;
1053 for (
const auto &R : List->Completions) {
1054 CompletionItem C = R.render(
CCOpts);
1056 C.kind, SupportedCompletionItemKinds);
1057 LSPList.items.push_back(std::move(C));
1059 return Reply(std::move(LSPList));
1063 void ClangdLSPServer::onSignatureHelp(
const TextDocumentPositionParams &Params,
1064 Callback<SignatureHelp> Reply) {
1065 Server->signatureHelp(Params.textDocument.uri.file(), Params.position,
1066 [Reply = std::move(Reply),
this](
1067 llvm::Expected<SignatureHelp>
Signature)
mutable {
1070 if (SupportsOffsetsInSignatureHelp)
1074 for (
auto &SigInfo :
Signature->signatures) {
1075 for (
auto &Param : SigInfo.parameters)
1076 Param.labelOffsets.reset();
1102 void ClangdLSPServer::onGoToDefinition(
const TextDocumentPositionParams &Params,
1103 Callback<std::vector<Location>> Reply) {
1104 Server->locateSymbolAt(
1105 Params.textDocument.uri.file(), Params.position,
1106 [Params, Reply = std::move(Reply)](
1107 llvm::Expected<std::vector<LocatedSymbol>>
Symbols)
mutable {
1109 return Reply(
Symbols.takeError());
1110 std::vector<Location> Defs;
1113 return Reply(std::vector<Location>{std::move(*Toggle)});
1114 Defs.push_back(S.Definition.getValueOr(S.PreferredDeclaration));
1116 Reply(std::move(Defs));
1120 void ClangdLSPServer::onGoToDeclaration(
1121 const TextDocumentPositionParams &Params,
1122 Callback<std::vector<Location>> Reply) {
1123 Server->locateSymbolAt(
1124 Params.textDocument.uri.file(), Params.position,
1125 [Params, Reply = std::move(Reply)](
1126 llvm::Expected<std::vector<LocatedSymbol>>
Symbols)
mutable {
1128 return Reply(
Symbols.takeError());
1129 std::vector<Location> Decls;
1132 return Reply(std::vector<Location>{std::move(*Toggle)});
1133 Decls.push_back(std::move(S.PreferredDeclaration));
1135 Reply(std::move(Decls));
1139 void ClangdLSPServer::onSwitchSourceHeader(
1140 const TextDocumentIdentifier &Params,
1141 Callback<llvm::Optional<URIForFile>> Reply) {
1142 Server->switchSourceHeader(
1144 [Reply = std::move(Reply),
1145 Params](llvm::Expected<llvm::Optional<clangd::Path>>
Path)
mutable {
1147 return Reply(
Path.takeError());
1150 return Reply(llvm::None);
1154 void ClangdLSPServer::onDocumentHighlight(
1155 const TextDocumentPositionParams &Params,
1156 Callback<std::vector<DocumentHighlight>> Reply) {
1157 Server->findDocumentHighlights(Params.textDocument.uri.file(),
1158 Params.position, std::move(Reply));
1161 void ClangdLSPServer::onHover(
const TextDocumentPositionParams &Params,
1162 Callback<llvm::Optional<Hover>> Reply) {
1163 Server->findHover(Params.textDocument.uri.file(), Params.position,
1164 [Reply = std::move(Reply),
this](
1165 llvm::Expected<llvm::Optional<HoverInfo>> H)
mutable {
1167 return Reply(H.takeError());
1169 return Reply(llvm::None);
1172 R.contents.kind = HoverContentFormat;
1173 R.range = (*H)->SymRange;
1174 switch (HoverContentFormat) {
1175 case MarkupKind::PlainText:
1176 R.contents.value = (*H)->present().asPlainText();
1177 return Reply(std::move(R));
1178 case MarkupKind::Markdown:
1179 R.contents.value = (*H)->present().asMarkdown();
1180 return Reply(std::move(R));
1182 llvm_unreachable(
"unhandled MarkupKind");
1186 void ClangdLSPServer::onTypeHierarchy(
1187 const TypeHierarchyParams &Params,
1188 Callback<Optional<TypeHierarchyItem>> Reply) {
1189 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
1190 Params.resolve, Params.direction, std::move(Reply));
1193 void ClangdLSPServer::onResolveTypeHierarchy(
1194 const ResolveTypeHierarchyItemParams &Params,
1195 Callback<Optional<TypeHierarchyItem>> Reply) {
1196 Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction,
1200 void ClangdLSPServer::applyConfiguration(
1201 const ConfigurationSettings &Settings) {
1203 llvm::StringSet<> ModifiedFiles;
1204 for (
auto &
Entry : Settings.compilationDatabaseChanges) {
1206 auto Old = CDB->getCompileCommand(File);
1208 tooling::CompileCommand(std::move(
Entry.second.workingDirectory), File,
1209 std::move(
Entry.second.compilationCommand),
1212 CDB->setCompileCommand(File, std::move(New));
1213 ModifiedFiles.insert(File);
1217 reparseOpenFilesIfNeeded(
1218 [&](llvm::StringRef File) {
return ModifiedFiles.count(File) != 0; });
1221 void ClangdLSPServer::publishTheiaSemanticHighlighting(
1222 const TheiaSemanticHighlightingParams &Params) {
1223 notify(
"textDocument/semanticHighlighting", Params);
1226 void ClangdLSPServer::publishDiagnostics(
1227 const PublishDiagnosticsParams &Params) {
1228 notify(
"textDocument/publishDiagnostics", Params);
1232 void ClangdLSPServer::onChangeConfiguration(
1233 const DidChangeConfigurationParams &Params) {
1234 applyConfiguration(Params.settings);
1237 void ClangdLSPServer::onReference(
const ReferenceParams &Params,
1238 Callback<std::vector<Location>> Reply) {
1239 Server->findReferences(Params.textDocument.uri.file(), Params.position,
1241 [Reply = std::move(Reply)](
1242 llvm::Expected<ReferencesResult>
Refs)
mutable {
1244 return Reply(
Refs.takeError());
1245 return Reply(std::move(
Refs->References));
1249 void ClangdLSPServer::onSymbolInfo(
const TextDocumentPositionParams &Params,
1250 Callback<std::vector<SymbolDetails>> Reply) {
1251 Server->symbolInfo(Params.textDocument.uri.file(), Params.position,
1255 void ClangdLSPServer::onSelectionRange(
1256 const SelectionRangeParams &Params,
1257 Callback<std::vector<SelectionRange>> Reply) {
1258 Server->semanticRanges(
1259 Params.textDocument.uri.file(), Params.positions,
1260 [Reply = std::move(Reply)](
1261 llvm::Expected<std::vector<SelectionRange>> Ranges)
mutable {
1263 return Reply(Ranges.takeError());
1264 return Reply(std::move(*Ranges));
1268 void ClangdLSPServer::onDocumentLink(
1269 const DocumentLinkParams &Params,
1270 Callback<std::vector<DocumentLink>> Reply) {
1276 Server->documentLinks(
1277 Params.textDocument.uri.file(),
1278 [Reply = std::move(Reply)](
1279 llvm::Expected<std::vector<DocumentLink>> Links)
mutable {
1281 return Reply(Links.takeError());
1283 return Reply(std::move(Links));
1289 for (
char &C : llvm::reverse(S)) {
1296 S.insert(S.begin(),
'1');
1299 void ClangdLSPServer::onSemanticTokens(
const SemanticTokensParams &Params,
1300 Callback<SemanticTokens> CB) {
1301 Server->semanticHighlights(
1302 Params.textDocument.uri.file(),
1303 [
this, File(Params.textDocument.uri.file().str()), CB(std::move(CB))](
1306 return CB(HT.takeError());
1307 SemanticTokens Result;
1310 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1311 auto &Last = LastSemanticTokens[File];
1313 Last.tokens = Result.tokens;
1315 Result.resultId = Last.resultId;
1317 CB(std::move(Result));
1321 void ClangdLSPServer::onSemanticTokensDelta(
1322 const SemanticTokensDeltaParams &Params,
1323 Callback<SemanticTokensOrDelta> CB) {
1324 Server->semanticHighlights(
1325 Params.textDocument.uri.file(),
1326 [
this, PrevResultID(Params.previousResultId),
1327 File(Params.textDocument.uri.file().str()), CB(std::move(CB))](
1330 return CB(HT.takeError());
1333 SemanticTokensOrDelta Result;
1335 std::lock_guard<std::mutex> Lock(SemanticTokensMutex);
1336 auto &Last = LastSemanticTokens[File];
1338 if (PrevResultID == Last.resultId) {
1339 Result.edits =
diffTokens(Last.tokens, Toks);
1341 vlog(
"semanticTokens/full/delta: wanted edits vs {0} but last "
1342 "result had ID {1}. Returning full token list.",
1343 PrevResultID, Last.resultId);
1344 Result.tokens = Toks;
1347 Last.tokens = std::move(Toks);
1349 Result.resultId = Last.resultId;
1352 CB(std::move(Result));
1356 ClangdLSPServer::ClangdLSPServer(
1360 llvm::Optional<Path> CompileCommandsDir,
bool UseDirBasedCDB,
1361 llvm::Optional<OffsetEncoding> ForcedOffsetEncoding,
1363 : BackgroundContext(
Context::current().clone()), Transp(Transp),
1366 SupportedCompletionItemKinds(defaultCompletionItemKinds()),
1367 UseDirBasedCDB(UseDirBasedCDB),
1368 CompileCommandsDir(std::move(CompileCommandsDir)), ClangdServerOpts(Opts),
1369 NegotiatedOffsetEncoding(ForcedOffsetEncoding) {
1371 MsgHandler->bind(
"initialize", &ClangdLSPServer::onInitialize);
1372 MsgHandler->bind(
"initialized", &ClangdLSPServer::onInitialized);
1373 MsgHandler->bind(
"shutdown", &ClangdLSPServer::onShutdown);
1374 MsgHandler->bind(
"sync", &ClangdLSPServer::onSync);
1375 MsgHandler->bind(
"textDocument/rangeFormatting", &ClangdLSPServer::onDocumentRangeFormatting);
1376 MsgHandler->bind(
"textDocument/onTypeFormatting", &ClangdLSPServer::onDocumentOnTypeFormatting);
1377 MsgHandler->bind(
"textDocument/formatting", &ClangdLSPServer::onDocumentFormatting);
1378 MsgHandler->bind(
"textDocument/codeAction", &ClangdLSPServer::onCodeAction);
1379 MsgHandler->bind(
"textDocument/completion", &ClangdLSPServer::onCompletion);
1380 MsgHandler->bind(
"textDocument/signatureHelp", &ClangdLSPServer::onSignatureHelp);
1381 MsgHandler->bind(
"textDocument/definition", &ClangdLSPServer::onGoToDefinition);
1382 MsgHandler->bind(
"textDocument/declaration", &ClangdLSPServer::onGoToDeclaration);
1383 MsgHandler->bind(
"textDocument/references", &ClangdLSPServer::onReference);
1384 MsgHandler->bind(
"textDocument/switchSourceHeader", &ClangdLSPServer::onSwitchSourceHeader);
1385 MsgHandler->bind(
"textDocument/prepareRename", &ClangdLSPServer::onPrepareRename);
1386 MsgHandler->bind(
"textDocument/rename", &ClangdLSPServer::onRename);
1387 MsgHandler->bind(
"textDocument/hover", &ClangdLSPServer::onHover);
1388 MsgHandler->bind(
"textDocument/documentSymbol", &ClangdLSPServer::onDocumentSymbol);
1389 MsgHandler->bind(
"workspace/executeCommand", &ClangdLSPServer::onCommand);
1390 MsgHandler->bind(
"textDocument/documentHighlight", &ClangdLSPServer::onDocumentHighlight);
1391 MsgHandler->bind(
"workspace/symbol", &ClangdLSPServer::onWorkspaceSymbol);
1392 MsgHandler->bind(
"textDocument/didOpen", &ClangdLSPServer::onDocumentDidOpen);
1393 MsgHandler->bind(
"textDocument/didClose", &ClangdLSPServer::onDocumentDidClose);
1394 MsgHandler->bind(
"textDocument/didChange", &ClangdLSPServer::onDocumentDidChange);
1395 MsgHandler->bind(
"textDocument/didSave", &ClangdLSPServer::onDocumentDidSave);
1396 MsgHandler->bind(
"workspace/didChangeWatchedFiles", &ClangdLSPServer::onFileEvent);
1397 MsgHandler->bind(
"workspace/didChangeConfiguration", &ClangdLSPServer::onChangeConfiguration);
1398 MsgHandler->bind(
"textDocument/symbolInfo", &ClangdLSPServer::onSymbolInfo);
1399 MsgHandler->bind(
"textDocument/typeHierarchy", &ClangdLSPServer::onTypeHierarchy);
1400 MsgHandler->bind(
"typeHierarchy/resolve", &ClangdLSPServer::onResolveTypeHierarchy);
1401 MsgHandler->bind(
"textDocument/selectionRange", &ClangdLSPServer::onSelectionRange);
1402 MsgHandler->bind(
"textDocument/documentLink", &ClangdLSPServer::onDocumentLink);
1403 MsgHandler->bind(
"textDocument/semanticTokens/full", &ClangdLSPServer::onSemanticTokens);
1404 MsgHandler->bind(
"textDocument/semanticTokens/full/delta", &ClangdLSPServer::onSemanticTokensDelta);
1405 if (Opts.FoldingRanges)
1406 MsgHandler->bind(
"textDocument/foldingRange", &ClangdLSPServer::onFoldingRange);
1411 IsBeingDestroyed =
true;
1419 bool CleanExit =
true;
1420 if (
auto Err = Transp.
loop(*MsgHandler)) {
1421 elog(
"Transport error: {0}", std::move(Err));
1425 return CleanExit && ShutdownRequestReceived;
1428 std::vector<Fix> ClangdLSPServer::getFixes(llvm::StringRef File,
1430 std::lock_guard<std::mutex> Lock(FixItsMutex);
1431 auto DiagToFixItsIter = FixItsMap.find(
File);
1432 if (DiagToFixItsIter == FixItsMap.end())
1435 const auto &DiagToFixItsMap = DiagToFixItsIter->second;
1436 auto FixItsIter = DiagToFixItsMap.find(D);
1437 if (FixItsIter == DiagToFixItsMap.end())
1440 return FixItsIter->second;
1449 bool ClangdLSPServer::shouldRunCompletion(
1450 const CompletionParams &Params)
const {
1453 auto Code = DraftMgr.getDraft(Params.textDocument.uri.file());
1459 vlog(
"could not convert position '{0}' to offset for file '{1}'",
1460 Params.position, Params.textDocument.uri.file());
1466 void ClangdLSPServer::onHighlightingsReady(
1467 PathRef File, llvm::StringRef Version,
1468 std::vector<HighlightingToken> Highlightings) {
1469 std::vector<HighlightingToken> Old;
1470 std::vector<HighlightingToken> HighlightingsCopy = Highlightings;
1472 std::lock_guard<std::mutex> Lock(HighlightingsMutex);
1473 Old = std::move(FileToHighlightings[
File]);
1474 FileToHighlightings[
File] = std::move(HighlightingsCopy);
1479 TheiaSemanticHighlightingParams Notification;
1480 Notification.TextDocument.uri =
1482 Notification.TextDocument.version = decodeVersion(Version);
1484 publishTheiaSemanticHighlighting(Notification);
1487 void ClangdLSPServer::onDiagnosticsReady(
PathRef File, llvm::StringRef Version,
1488 std::vector<Diag> Diagnostics) {
1489 PublishDiagnosticsParams Notification;
1490 Notification.version = decodeVersion(Version);
1492 DiagnosticToReplacementMap LocalFixIts;
1493 for (
auto &Diag : Diagnostics) {
1496 auto &FixItsForDiagnostic = LocalFixIts[Diag];
1497 llvm::copy(Fixes, std::back_inserter(FixItsForDiagnostic));
1498 Notification.diagnostics.push_back(std::move(Diag));
1504 std::lock_guard<std::mutex> Lock(FixItsMutex);
1505 FixItsMap[
File] = LocalFixIts;
1509 publishDiagnostics(Notification);
1512 void ClangdLSPServer::onBackgroundIndexProgress(
1513 const BackgroundQueue::Stats &Stats) {
1514 static const char ProgressToken[] =
"backgroundIndexProgress";
1515 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1517 auto NotifyProgress = [
this](
const BackgroundQueue::Stats &Stats) {
1518 if (BackgroundIndexProgressState != BackgroundIndexProgress::Live) {
1519 WorkDoneProgressBegin Begin;
1520 Begin.percentage =
true;
1521 Begin.title =
"indexing";
1522 progress(ProgressToken, std::move(Begin));
1523 BackgroundIndexProgressState = BackgroundIndexProgress::Live;
1526 if (Stats.Completed < Stats.Enqueued) {
1527 assert(Stats.Enqueued > Stats.LastIdle);
1528 WorkDoneProgressReport Report;
1529 Report.percentage = 100.0 * (Stats.Completed - Stats.LastIdle) /
1530 (Stats.Enqueued - Stats.LastIdle);
1532 llvm::formatv(
"{0}/{1}", Stats.Completed - Stats.LastIdle,
1533 Stats.Enqueued - Stats.LastIdle);
1534 progress(ProgressToken, std::move(Report));
1536 assert(Stats.Completed == Stats.Enqueued);
1537 progress(ProgressToken, WorkDoneProgressEnd());
1538 BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
1542 switch (BackgroundIndexProgressState) {
1543 case BackgroundIndexProgress::Unsupported:
1545 case BackgroundIndexProgress::Creating:
1547 PendingBackgroundIndexProgress = Stats;
1549 case BackgroundIndexProgress::Empty: {
1550 if (BackgroundIndexSkipCreate) {
1551 NotifyProgress(Stats);
1555 PendingBackgroundIndexProgress = Stats;
1556 BackgroundIndexProgressState = BackgroundIndexProgress::Creating;
1557 WorkDoneProgressCreateParams CreateRequest;
1558 CreateRequest.token = ProgressToken;
1559 call<std::nullptr_t>(
1560 "window/workDoneProgress/create", CreateRequest,
1561 [
this, NotifyProgress](llvm::Expected<std::nullptr_t>
E) {
1562 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex);
1564 NotifyProgress(this->PendingBackgroundIndexProgress);
1566 elog(
"Failed to create background index progress bar: {0}",
1569 BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported;
1574 case BackgroundIndexProgress::Live:
1575 NotifyProgress(Stats);
1580 void ClangdLSPServer::onFileUpdated(
PathRef File,
const TUStatus &Status) {
1581 if (!SupportFileStatus)
1591 notify(
"textDocument/clangd.fileStatus", Status.render(
File));
1594 void ClangdLSPServer::reparseOpenFilesIfNeeded(
1595 llvm::function_ref<
bool(llvm::StringRef File)> Filter) {
1597 for (
const Path &FilePath : DraftMgr.getActiveFiles())
1598 if (Filter(FilePath))
1599 if (
auto Draft = DraftMgr.getDraft(FilePath))
1600 Server->addDocument(FilePath, std::move(Draft->Contents),
1601 encodeVersion(Draft->Version),