[clangd] Support multifile edits as output of Tweaks
Summary:
First patch for propogating multifile changes from tweak outputs to LSP
WorkspaceEdits.
Uses SM to convert tooling::Replacements to TextEdits.
Errors out if there are any inconsistencies between the draft version and the
version generated the edits.
Reviewers: sammccall
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D66637
llvm-svn: 371392
diff --git a/clang-tools-extra/clangd/refactor/Tweak.cpp b/clang-tools-extra/clangd/refactor/Tweak.cpp
index 64e1323..4f3c40d 100644
--- a/clang-tools-extra/clangd/refactor/Tweak.cpp
+++ b/clang-tools-extra/clangd/refactor/Tweak.cpp
@@ -7,12 +7,18 @@
//===----------------------------------------------------------------------===//
#include "Tweak.h"
#include "Logger.h"
+#include "Path.h"
+#include "SourceCode.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Registry.h"
#include <functional>
#include <memory>
+#include <utility>
LLVM_INSTANTIATE_REGISTRY(llvm::Registry<clang::clangd::Tweak>)
@@ -81,5 +87,28 @@
return std::move(T);
}
+llvm::Expected<std::pair<Path, Edit>>
+Tweak::Effect::fileEdit(const SourceManager &SM, FileID FID,
+ tooling::Replacements Replacements) {
+ Edit Ed(SM.getBufferData(FID), std::move(Replacements));
+ if (auto FilePath = getCanonicalPath(SM.getFileEntryForID(FID), SM))
+ return std::make_pair(*FilePath, std::move(Ed));
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Failed to get absolute path for edited file: " +
+ SM.getFileEntryForID(FID)->getName());
+}
+
+llvm::Expected<Tweak::Effect>
+Tweak::Effect::mainFileEdit(const SourceManager &SM,
+ tooling::Replacements Replacements) {
+ auto PathAndEdit = fileEdit(SM, SM.getMainFileID(), std::move(Replacements));
+ if (!PathAndEdit)
+ return PathAndEdit.takeError();
+ Tweak::Effect E;
+ E.ApplyEdits.try_emplace(PathAndEdit->first, PathAndEdit->second);
+ return E;
+}
+
} // namespace clangd
} // namespace clang
diff --git a/clang-tools-extra/clangd/refactor/Tweak.h b/clang-tools-extra/clangd/refactor/Tweak.h
index 1728988..42b7fb0 100644
--- a/clang-tools-extra/clangd/refactor/Tweak.h
+++ b/clang-tools-extra/clangd/refactor/Tweak.h
@@ -20,11 +20,17 @@
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_REFACTOR_ACTIONS_TWEAK_H
#include "ParsedAST.h"
+#include "Path.h"
#include "Protocol.h"
#include "Selection.h"
+#include "SourceCode.h"
#include "clang/Tooling/Core/Replacement.h"
#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <string>
+
namespace clang {
namespace clangd {
@@ -67,19 +73,27 @@
struct Effect {
/// A message to be displayed to the user.
llvm::Optional<std::string> ShowMessage;
- /// An edit to apply to the input file.
- llvm::Optional<tooling::Replacements> ApplyEdit;
+ /// A mapping from file path(the one used for accessing the underlying VFS)
+ /// to edits.
+ llvm::StringMap<Edit> ApplyEdits;
- static Effect applyEdit(tooling::Replacements R) {
- Effect E;
- E.ApplyEdit = std::move(R);
- return E;
- }
static Effect showMessage(StringRef S) {
Effect E;
E.ShowMessage = S;
return E;
}
+
+ /// Path is the absolute, symlink-resolved path for the file pointed by FID
+ /// in SM. Edit is generated from Replacements.
+ /// Fails if cannot figure out absolute path for FID.
+ static llvm::Expected<std::pair<Path, Edit>>
+ fileEdit(const SourceManager &SM, FileID FID,
+ tooling::Replacements Replacements);
+
+ /// Creates an effect with an Edit for the main file.
+ /// Fails if cannot figure out absolute path for main file.
+ static llvm::Expected<Tweak::Effect>
+ mainFileEdit(const SourceManager &SM, tooling::Replacements Replacements);
};
virtual ~Tweak() = default;
@@ -126,7 +140,6 @@
// If prepare() returns true, returns the corresponding tweak.
llvm::Expected<std::unique_ptr<Tweak>> prepareTweak(StringRef TweakID,
const Tweak::Selection &S);
-
} // namespace clangd
} // namespace clang
diff --git a/clang-tools-extra/clangd/refactor/tweaks/AnnotateHighlightings.cpp b/clang-tools-extra/clangd/refactor/tweaks/AnnotateHighlightings.cpp
index 7fbd28a..2d4d2ac 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/AnnotateHighlightings.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/AnnotateHighlightings.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "SemanticHighlighting.h"
#include "refactor/Tweak.h"
+#include "llvm/ADT/StringRef.h"
namespace clang {
namespace clangd {
@@ -56,6 +57,7 @@
}
auto &SM = Inputs.AST.getSourceManager();
tooling::Replacements Result;
+ llvm::StringRef FilePath = SM.getFilename(Inputs.Cursor);
for (const auto &Token : HighlightingTokens) {
assert(Token.R.start.line == Token.R.end.line &&
"Token must be at the same line");
@@ -64,12 +66,12 @@
return InsertOffset.takeError();
auto InsertReplacement = tooling::Replacement(
- SM.getFileEntryForID(SM.getMainFileID())->getName(), *InsertOffset, 0,
+ FilePath, *InsertOffset, 0,
("/* " + toTextMateScope(Token.Kind) + " */").str());
if (auto Err = Result.add(InsertReplacement))
return std::move(Err);
}
- return Effect::applyEdit(Result);
+ return Effect::mainFileEdit(SM, std::move(Result));
}
} // namespace
diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp
index d14cd0f..eaab40b 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp
@@ -101,7 +101,7 @@
Expansion(SrcMgr, CharSourceRange(CachedLocation->getSourceRange(), true),
PrettyTypeName);
- return Tweak::Effect::applyEdit(tooling::Replacements(Expansion));
+ return Effect::mainFileEdit(SrcMgr, tooling::Replacements(Expansion));
}
llvm::Error ExpandAutoType::createErrorMessage(const std::string& Message,
diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExpandMacro.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExpandMacro.cpp
index 7aa9bf3..8902527 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/ExpandMacro.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/ExpandMacro.cpp
@@ -104,7 +104,7 @@
}
Expected<Tweak::Effect> ExpandMacro::apply(const Selection &Inputs) {
- auto &SM = Inputs.AST.getASTContext().getSourceManager();
+ auto &SM = Inputs.AST.getSourceManager();
std::string Replacement;
for (const syntax::Token &T : Expansion.Expanded) {
@@ -120,11 +120,9 @@
CharSourceRange::getCharRange(Expansion.Spelled.front().location(),
Expansion.Spelled.back().endLocation());
- Tweak::Effect E;
- E.ApplyEdit.emplace();
- llvm::cantFail(
- E.ApplyEdit->add(tooling::Replacement(SM, MacroRange, Replacement)));
- return E;
+ tooling::Replacements Reps;
+ llvm::cantFail(Reps.add(tooling::Replacement(SM, MacroRange, Replacement)));
+ return Effect::mainFileEdit(SM, std::move(Reps));
}
std::string ExpandMacro::title() const {
diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
index 93c272d..1532c95 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
@@ -645,7 +645,7 @@
return std::move(Err);
if (auto Err = Result.add(replaceWithFuncCall(*ExtractedFunc, SM, LangOpts)))
return std::move(Err);
- return Effect::applyEdit(Result);
+ return Effect::mainFileEdit(SM, std::move(Result));
}
} // namespace
diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
index 85a9686..b758e69 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
@@ -468,7 +468,7 @@
// replace expression with variable name
if (auto Err = Result.add(Target->replaceWithVar(Range, VarName)))
return std::move(Err);
- return Effect::applyEdit(Result);
+ return Effect::mainFileEdit(Inputs.AST.getSourceManager(), std::move(Result));
}
} // namespace
diff --git a/clang-tools-extra/clangd/refactor/tweaks/RawStringLiteral.cpp b/clang-tools-extra/clangd/refactor/tweaks/RawStringLiteral.cpp
index e38cf63..17eed4b 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/RawStringLiteral.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/RawStringLiteral.cpp
@@ -88,10 +88,12 @@
}
Expected<Tweak::Effect> RawStringLiteral::apply(const Selection &Inputs) {
- return Effect::applyEdit(tooling::Replacements(
+ auto &SM = Inputs.AST.getSourceManager();
+ auto Reps = tooling::Replacements(
tooling::Replacement(Inputs.AST.getSourceManager(), Str,
("R\"(" + Str->getBytes() + ")\"").str(),
- Inputs.AST.getASTContext().getLangOpts())));
+ Inputs.AST.getASTContext().getLangOpts()));
+ return Effect::mainFileEdit(SM, std::move(Reps));
}
} // namespace
diff --git a/clang-tools-extra/clangd/refactor/tweaks/SwapIfBranches.cpp b/clang-tools-extra/clangd/refactor/tweaks/SwapIfBranches.cpp
index 82617f4..a63b71b 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/SwapIfBranches.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/SwapIfBranches.cpp
@@ -90,7 +90,7 @@
ElseRng->getBegin(),
ElseCode.size(), ThenCode)))
return std::move(Err);
- return Effect::applyEdit(Result);
+ return Effect::mainFileEdit(SrcMgr, std::move(Result));
}
} // namespace