diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -568,8 +568,9 @@ static constexpr trace::Metric TweakAvailable( "tweak_available", trace::Metric::Counter, "tweak_id"); auto Action = [File = File.str(), Sel, CB = std::move(CB), - Filter = - std::move(Filter)](Expected InpAST) mutable { + Filter = std::move(Filter), + FeatureModules(this->FeatureModules)]( + Expected InpAST) mutable { if (!InpAST) return CB(InpAST.takeError()); auto Selections = tweakSelection(Sel, *InpAST); @@ -582,7 +583,7 @@ return Filter(T) && !PreparedTweaks.count(T.id()); }; for (const auto &Sel : *Selections) { - for (auto &T : prepareTweaks(*Sel, DeduplicatingFilter)) { + for (auto &T : prepareTweaks(FeatureModules, *Sel, DeduplicatingFilter)) { Res.push_back({T->id(), T->title(), T->kind()}); PreparedTweaks.insert(T->id()); TweakAvailable.record(1, T->id()); @@ -617,7 +618,7 @@ // Try each selection, take the first one that prepare()s. // If they all fail, Effect will hold get the last error. for (const auto &Selection : *Selections) { - auto T = prepareTweak(TweakID, *Selection); + auto T = prepareTweak(TweakID, *Selection, FeatureModules); if (T) { Effect = (*T)->apply(*Selection); break; diff --git a/clang-tools-extra/clangd/FeatureModule.h b/clang-tools-extra/clangd/FeatureModule.h --- a/clang-tools-extra/clangd/FeatureModule.h +++ b/clang-tools-extra/clangd/FeatureModule.h @@ -91,6 +91,11 @@ /// Called by the server when shutting down, and also by tests. virtual bool blockUntilIdle(Deadline) { return true; } + /// Tweaks implemented by this module. Can be called asynchronously when + /// enumerating or applying code actions. + virtual void + contributeTweaks(std::vector> &Out) {} + protected: /// Accessors for modules to access shared server facilities they depend on. Facilities &facilities(); diff --git a/clang-tools-extra/clangd/refactor/Tweak.h b/clang-tools-extra/clangd/refactor/Tweak.h --- a/clang-tools-extra/clangd/refactor/Tweak.h +++ b/clang-tools-extra/clangd/refactor/Tweak.h @@ -127,14 +127,15 @@ /// Calls prepare() on all tweaks that satisfy the filter, returning those that /// can run on the selection. std::vector> -prepareTweaks(const Tweak::Selection &S, +prepareTweaks(const class FeatureModuleSet *Modules, const Tweak::Selection &S, llvm::function_ref Filter); // Calls prepare() on the tweak with a given ID. // If prepare() returns false, returns an error. // If prepare() returns true, returns the corresponding tweak. -llvm::Expected> prepareTweak(StringRef TweakID, - const Tweak::Selection &S); +llvm::Expected> +prepareTweak(StringRef ID, const Tweak::Selection &S, + const class FeatureModuleSet *Modules); } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/refactor/Tweak.cpp b/clang-tools-extra/clangd/refactor/Tweak.cpp --- a/clang-tools-extra/clangd/refactor/Tweak.cpp +++ b/clang-tools-extra/clangd/refactor/Tweak.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// #include "Tweak.h" +#include "FeatureModule.h" #include "SourceCode.h" #include "index/Index.h" #include "support/Logger.h" @@ -20,6 +21,7 @@ #include #include #include +#include LLVM_INSTANTIATE_REGISTRY(llvm::Registry) @@ -43,6 +45,18 @@ } #endif } + +std::vector> +getAllTweaks(const FeatureModuleSet *Modules) { + std::vector> All; + for (const auto &E : TweakRegistry::entries()) + All.emplace_back(E.instantiate()); + if (!Modules) + return All; + for (auto &M : *Modules) + M.contributeTweaks(All); + return All; +} } // namespace Tweak::Selection::Selection(const SymbolIndex *Index, ParsedAST &AST, @@ -56,13 +70,12 @@ } std::vector> -prepareTweaks(const Tweak::Selection &S, +prepareTweaks(const FeatureModuleSet *Modules, const Tweak::Selection &S, llvm::function_ref Filter) { validateRegistry(); std::vector> Available; - for (const auto &E : TweakRegistry::entries()) { - std::unique_ptr T = E.instantiate(); + for (auto &T : getAllTweaks(Modules)) { if (!Filter(*T) || !T->prepare(S)) continue; Available.push_back(std::move(T)); @@ -74,17 +87,17 @@ return Available; } -llvm::Expected> prepareTweak(StringRef ID, - const Tweak::Selection &S) { - auto It = llvm::find_if( - TweakRegistry::entries(), - [ID](const TweakRegistry::entry &E) { return E.getName() == ID; }); - if (It == TweakRegistry::end()) - return error("tweak ID {0} is invalid", ID); - std::unique_ptr T = It->instantiate(); - if (!T->prepare(S)) - return error("failed to prepare() tweak {0}", ID); - return std::move(T); +llvm::Expected> +prepareTweak(StringRef ID, const Tweak::Selection &S, + const FeatureModuleSet *Modules) { + for (auto &T : getAllTweaks(Modules)) { + if (T->id() != ID) + continue; + if (!T->prepare(S)) + return error("failed to prepare() tweak {0}", ID); + return std::move(T); + } + return error("tweak ID {0} is invalid", ID); } llvm::Expected> diff --git a/clang-tools-extra/clangd/tool/Check.cpp b/clang-tools-extra/clangd/tool/Check.cpp --- a/clang-tools-extra/clangd/tool/Check.cpp +++ b/clang-tools-extra/clangd/tool/Check.cpp @@ -206,7 +206,8 @@ auto Tree = SelectionTree::createRight(AST->getASTContext(), AST->getTokens(), Start, End); Tweak::Selection Selection(&Index, *AST, Start, End, std::move(Tree)); - for (const auto &T : prepareTweaks(Selection, Opts.TweakFilter)) { + for (const auto &T : + prepareTweaks(Opts.FeatureModules, Selection, Opts.TweakFilter)) { auto Result = T->apply(Selection); if (!Result) { elog(" tweak: {0} ==> FAIL: {1}", T->id(), Result.takeError()); diff --git a/clang-tools-extra/clangd/unittests/tweaks/TweakTesting.cpp b/clang-tools-extra/clangd/unittests/tweaks/TweakTesting.cpp --- a/clang-tools-extra/clangd/unittests/tweaks/TweakTesting.cpp +++ b/clang-tools-extra/clangd/unittests/tweaks/TweakTesting.cpp @@ -75,7 +75,7 @@ Range.second, [&](SelectionTree ST) { Tweak::Selection S(Index, AST, Range.first, Range.second, std::move(ST)); - if (auto T = prepareTweak(TweakID, S)) { + if (auto T = prepareTweak(TweakID, S, nullptr)) { Result = (*T)->apply(S); return true; } else {