summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clangd/ClangdServer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clangd/ClangdServer.cpp')
-rw-r--r--clang-tools-extra/clangd/ClangdServer.cpp71
1 files changed, 49 insertions, 22 deletions
diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp
index 40fa28ae140..90bdc68e374 100644
--- a/clang-tools-extra/clangd/ClangdServer.cpp
+++ b/clang-tools-extra/clangd/ClangdServer.cpp
@@ -374,7 +374,8 @@ void ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName,
WorkScheduler.runWithAST("Rename", File, std::move(Action));
}
-static llvm::Expected<Tweak::Selection>
+// May generate several candidate selections, due to SelectionTree ambiguity.
+static llvm::Expected<std::vector<Tweak::Selection>>
tweakSelection(const Range &Sel, const InputsAndAST &AST) {
auto Begin = positionToOffset(AST.Inputs.Contents, Sel.start);
if (!Begin)
@@ -382,7 +383,15 @@ tweakSelection(const Range &Sel, const InputsAndAST &AST) {
auto End = positionToOffset(AST.Inputs.Contents, Sel.end);
if (!End)
return End.takeError();
- return Tweak::Selection(AST.Inputs.Index, AST.AST, *Begin, *End);
+ std::vector<Tweak::Selection> Result;
+ SelectionTree::createEach(AST.AST.getASTContext(), AST.AST.getTokens(),
+ *Begin, *End, [&](SelectionTree T) {
+ Result.emplace_back(AST.Inputs.Index, AST.AST,
+ *Begin, *End, std::move(T));
+ return false;
+ });
+ assert(!Result.empty() && "Expected at least one SelectionTree");
+ return Result;
}
void ClangdServer::enumerateTweaks(PathRef File, Range Sel,
@@ -391,12 +400,21 @@ void ClangdServer::enumerateTweaks(PathRef File, Range Sel,
this](Expected<InputsAndAST> InpAST) mutable {
if (!InpAST)
return CB(InpAST.takeError());
- auto Selection = tweakSelection(Sel, *InpAST);
- if (!Selection)
- return CB(Selection.takeError());
+ auto Selections = tweakSelection(Sel, *InpAST);
+ if (!Selections)
+ return CB(Selections.takeError());
std::vector<TweakRef> Res;
- for (auto &T : prepareTweaks(*Selection, TweakFilter))
- Res.push_back({T->id(), T->title(), T->intent()});
+ // Don't allow a tweak to fire more than once across ambiguous selections.
+ llvm::DenseSet<llvm::StringRef> PreparedTweaks;
+ auto Filter = [&](const Tweak &T) {
+ return TweakFilter(T) && !PreparedTweaks.count(T.id());
+ };
+ for (const auto &Sel : *Selections) {
+ for (auto &T : prepareTweaks(Sel, Filter)) {
+ Res.push_back({T->id(), T->title(), T->intent()});
+ PreparedTweaks.insert(T->id());
+ }
+ }
CB(std::move(Res));
};
@@ -411,21 +429,30 @@ void ClangdServer::applyTweak(PathRef File, Range Sel, StringRef TweakID,
FS = FSProvider.getFileSystem()](Expected<InputsAndAST> InpAST) mutable {
if (!InpAST)
return CB(InpAST.takeError());
- auto Selection = tweakSelection(Sel, *InpAST);
- if (!Selection)
- return CB(Selection.takeError());
- auto A = prepareTweak(TweakID, *Selection);
- if (!A)
- return CB(A.takeError());
- auto Effect = (*A)->apply(*Selection);
- if (!Effect)
- return CB(Effect.takeError());
- for (auto &It : Effect->ApplyEdits) {
- Edit &E = It.second;
- format::FormatStyle Style =
- getFormatStyleForFile(File, E.InitialCode, FS.get());
- if (llvm::Error Err = reformatEdit(E, Style))
- elog("Failed to format {0}: {1}", It.first(), std::move(Err));
+ auto Selections = tweakSelection(Sel, *InpAST);
+ if (!Selections)
+ return CB(Selections.takeError());
+ llvm::Optional<llvm::Expected<Tweak::Effect>> Effect;
+ // 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);
+ if (T) {
+ Effect = (*T)->apply(Selection);
+ break;
+ }
+ Effect = T.takeError();
+ }
+ assert(Effect.hasValue() && "Expected at least one selection");
+ if (*Effect) {
+ // Tweaks don't apply clang-format, do that centrally here.
+ for (auto &It : (*Effect)->ApplyEdits) {
+ Edit &E = It.second;
+ format::FormatStyle Style =
+ getFormatStyleForFile(File, E.InitialCode, FS.get());
+ if (llvm::Error Err = reformatEdit(E, Style))
+ elog("Failed to format {0}: {1}", It.first(), std::move(Err));
+ }
}
return CB(std::move(*Effect));
};
OpenPOWER on IntegriCloud