diff options
Diffstat (limited to 'clang-tools-extra/clangd/ClangdServer.cpp')
-rw-r--r-- | clang-tools-extra/clangd/ClangdServer.cpp | 71 |
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)); }; |