Refactor cross file rename to use a Filesystem instead of a function for getting buffer contents of open files.
Depends on D94554
Reviewed By: sammccall
Differential Revision: https://reviews.llvm.org/D95043
return CB(InpAST.takeError());
// prepareRename is latency-sensitive: we don't query the index, as we
// only need main-file references
- auto Results = clangd::rename(
- {Pos, NewName.getValueOr("__clangd_rename_dummy"), InpAST->AST, File,
- /*Index=*/nullptr, RenameOpts});
+ auto Results =
+ clangd::rename({Pos, NewName.getValueOr("__clangd_rename_dummy"),
+ InpAST->AST, File, /*FS=*/nullptr,
+ /*Index=*/nullptr, RenameOpts});
if (!Results) {
// LSP says to return null on failure, but that will result in a generic
// failure message. If we send an LSP error response, clients can surface
void ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName,
const RenameOptions &Opts,
Callback<RenameResult> CB) {
- // A snapshot of all file dirty buffers.
- llvm::StringMap<std::string> Snapshot = WorkScheduler->getAllFileContents();
auto Action = [File = File.str(), NewName = NewName.str(), Pos, Opts,
- CB = std::move(CB), Snapshot = std::move(Snapshot),
+ CB = std::move(CB),
this](llvm::Expected<InputsAndAST> InpAST) mutable {
// Tracks number of files edited per invocation.
static constexpr trace::Metric RenameFiles("rename_files",
trace::Metric::Distribution);
if (!InpAST)
return CB(InpAST.takeError());
- auto GetDirtyBuffer =
- [&Snapshot](PathRef AbsPath) -> llvm::Optional<std::string> {
- auto It = Snapshot.find(AbsPath);
- if (It == Snapshot.end())
- return llvm::None;
- return It->second;
- };
- auto R = clangd::rename(
- {Pos, NewName, InpAST->AST, File, Index, Opts, GetDirtyBuffer});
+ auto R = clangd::rename({Pos, NewName, InpAST->AST, File,
+ DirtyFS->view(llvm::None), Index, Opts});
if (!R)
return CB(R.takeError());
// Store of the current versions of the open documents.
// Only written from the main thread (despite being threadsafe).
- // FIXME: TUScheduler also keeps these, unify?
DraftStore DraftMgr;
std::unique_ptr<ThreadsafeFS> DirtyFS;
// preamble of several open headers.
}
-llvm::StringMap<std::string> TUScheduler::getAllFileContents() const {
- llvm::StringMap<std::string> Results;
- for (auto &It : Files)
- Results.try_emplace(It.getKey(), It.getValue()->Contents);
- return Results;
-}
-
void TUScheduler::run(llvm::StringRef Name, llvm::StringRef Path,
llvm::unique_function<void()> Action) {
runWithSemaphore(Name, Path, std::move(Action), Barrier);
/// if requested with WantDiags::Auto or WantDiags::Yes.
void remove(PathRef File);
- /// Returns a snapshot of all file buffer contents, per last update().
- llvm::StringMap<std::string> getAllFileContents() const;
-
/// Schedule an async task with no dependencies.
/// Path may be empty (it is used only to set the Context).
void run(llvm::StringRef Name, llvm::StringRef Path,
// index (background index) is relatively stale. We choose the dirty buffers
// as the file content we rename on, and fallback to file content on disk if
// there is no dirty buffer.
-llvm::Expected<FileEdits> renameOutsideFile(
- const NamedDecl &RenameDecl, llvm::StringRef MainFilePath,
- llvm::StringRef NewName, const SymbolIndex &Index, size_t MaxLimitFiles,
- llvm::function_ref<llvm::Expected<std::string>(PathRef)> GetFileContent) {
+llvm::Expected<FileEdits>
+renameOutsideFile(const NamedDecl &RenameDecl, llvm::StringRef MainFilePath,
+ llvm::StringRef NewName, const SymbolIndex &Index,
+ size_t MaxLimitFiles, llvm::vfs::FileSystem &FS) {
trace::Span Tracer("RenameOutsideFile");
auto AffectedFiles = findOccurrencesOutsideFile(RenameDecl, MainFilePath,
Index, MaxLimitFiles);
for (auto &FileAndOccurrences : *AffectedFiles) {
llvm::StringRef FilePath = FileAndOccurrences.first();
- auto AffectedFileCode = GetFileContent(FilePath);
- if (!AffectedFileCode) {
- elog("Fail to read file content: {0}", AffectedFileCode.takeError());
+ auto ExpBuffer = FS.getBufferForFile(FilePath);
+ if (!ExpBuffer) {
+ elog("Fail to read file content: Fail to open file {0}: {1}", FilePath,
+ ExpBuffer.getError().message());
continue;
}
+
+ auto AffectedFileCode = (*ExpBuffer)->getBuffer();
auto RenameRanges =
- adjustRenameRanges(*AffectedFileCode, RenameDecl.getNameAsString(),
+ adjustRenameRanges(AffectedFileCode, RenameDecl.getNameAsString(),
std::move(FileAndOccurrences.second),
RenameDecl.getASTContext().getLangOpts());
if (!RenameRanges) {
FilePath);
}
auto RenameEdit =
- buildRenameEdit(FilePath, *AffectedFileCode, *RenameRanges, NewName);
+ buildRenameEdit(FilePath, AffectedFileCode, *RenameRanges, NewName);
if (!RenameEdit)
return error("failed to rename in file {0}: {1}", FilePath,
RenameEdit.takeError());
} // namespace
llvm::Expected<RenameResult> rename(const RenameInputs &RInputs) {
+ assert(!RInputs.Index == !RInputs.FS &&
+ "Index and FS must either both be specified or both null.");
trace::Span Tracer("Rename flow");
const auto &Opts = RInputs.Opts;
ParsedAST &AST = RInputs.AST;
const SourceManager &SM = AST.getSourceManager();
llvm::StringRef MainFileCode = SM.getBufferData(SM.getMainFileID());
- auto GetFileContent = [&RInputs,
- &SM](PathRef AbsPath) -> llvm::Expected<std::string> {
- llvm::Optional<std::string> DirtyBuffer;
- if (RInputs.GetDirtyBuffer &&
- (DirtyBuffer = RInputs.GetDirtyBuffer(AbsPath)))
- return std::move(*DirtyBuffer);
-
- auto Content =
- SM.getFileManager().getVirtualFileSystem().getBufferForFile(AbsPath);
- if (!Content)
- return error("Fail to open file {0}: {1}", AbsPath,
- Content.getError().message());
- if (!*Content)
- return error("Got no buffer for file {0}", AbsPath);
-
- return (*Content)->getBuffer().str();
- };
// Try to find the tokens adjacent to the cursor position.
auto Loc = sourceLocationInMainFile(SM, RInputs.Pos);
if (!Loc)
RenameDecl, RInputs.MainFilePath, RInputs.NewName, *RInputs.Index,
Opts.LimitFiles == 0 ? std::numeric_limits<size_t>::max()
: Opts.LimitFiles,
- GetFileContent);
+ *RInputs.FS);
if (!OtherFilesEdits)
return OtherFilesEdits.takeError();
Result.GlobalChanges = *OtherFilesEdits;
class ParsedAST;
class SymbolIndex;
-/// Gets dirty buffer for a given file \p AbsPath.
-/// Returns None if there is no dirty buffer for the given file.
-using DirtyBufferGetter =
- llvm::function_ref<llvm::Optional<std::string>(PathRef AbsPath)>;
-
struct RenameOptions {
/// The maximum number of affected files (0 means no limit), only meaningful
/// when AllowCrossFile = true.
ParsedAST &AST;
llvm::StringRef MainFilePath;
+ // The filesystem to query when performing cross file renames.
+ // If this is set, Index must also be set, likewise if this is nullptr, Index
+ // must also be nullptr.
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = nullptr;
+
const SymbolIndex *Index = nullptr;
RenameOptions Opts = {};
- // When set, used by the rename to get file content for all rename-related
- // files.
- // If there is no corresponding dirty buffer, we will use the file content
- // from disk.
- DirtyBufferGetter GetDirtyBuffer = nullptr;
};
struct RenameResult {
using testing::UnorderedElementsAre;
using testing::UnorderedElementsAreArray;
+llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>
+createOverlay(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> Base,
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> Overlay) {
+ auto OFS =
+ llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(std::move(Base));
+ OFS->pushOverlay(std::move(Overlay));
+ return OFS;
+}
+
+llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> getVFSFromAST(ParsedAST &AST) {
+ return &AST.getSourceManager().getFileManager().getVirtualFileSystem();
+}
+
// Convert a Range to a Ref.
Ref refWithRange(const clangd::Range &Range, const std::string &URI) {
Ref Result;
auto Index = TU.index();
for (const auto &RenamePos : Code.points()) {
auto RenameResult =
- rename({RenamePos, NewName, AST, testPath(TU.Filename), Index.get()});
+ rename({RenamePos, NewName, AST, testPath(TU.Filename),
+ getVFSFromAST(AST), Index.get()});
ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError();
ASSERT_EQ(1u, RenameResult->GlobalChanges.size());
EXPECT_EQ(
auto AST = TU.build();
auto Main = testPath("main.cc");
+ auto InMemFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
+ InMemFS->addFile(testPath("main.cc"), 0,
+ llvm::MemoryBuffer::getMemBuffer(Code.code()));
+ InMemFS->addFile(testPath("other.cc"), 0,
+ llvm::MemoryBuffer::getMemBuffer(Code.code()));
auto Rename = [&](const SymbolIndex *Idx) {
- auto GetDirtyBuffer = [&](PathRef Path) -> llvm::Optional<std::string> {
- return Code.code().str(); // Every file has the same content.
- };
- RenameInputs Inputs{Code.point(), "xPrime", AST, Main,
- Idx, RenameOptions(), GetDirtyBuffer};
+ RenameInputs Inputs{Code.point(),
+ "xPrime",
+ AST,
+ Main,
+ Idx ? createOverlay(getVFSFromAST(AST), InMemFS)
+ : nullptr,
+ Idx,
+ RenameOptions()};
auto Results = rename(Inputs);
EXPECT_TRUE(bool(Results)) << llvm::toString(Results.takeError());
return std::move(*Results);
Annotations MainCode("class [[Fo^o]] {};");
auto MainFilePath = testPath("main.cc");
- // Dirty buffer for foo.cc.
- auto GetDirtyBuffer = [&](PathRef Path) -> llvm::Optional<std::string> {
- if (Path == FooPath)
- return FooDirtyBuffer.code().str();
- return llvm::None;
- };
+ llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemFS =
+ new llvm::vfs::InMemoryFileSystem;
+ InMemFS->addFile(FooPath, 0,
+ llvm::MemoryBuffer::getMemBuffer(FooDirtyBuffer.code()));
// Run rename on Foo, there is a dirty buffer for foo.cc, rename should
// respect the dirty buffer.
TestTU TU = TestTU::withCode(MainCode.code());
auto AST = TU.build();
llvm::StringRef NewName = "newName";
- auto Results = rename({MainCode.point(),
- NewName,
- AST,
- MainFilePath,
- Index.get(),
- {},
- GetDirtyBuffer});
+ auto Results =
+ rename({MainCode.point(), NewName, AST, MainFilePath,
+ createOverlay(getVFSFromAST(AST), InMemFS), Index.get()});
ASSERT_TRUE(bool(Results)) << Results.takeError();
EXPECT_THAT(
applyEdits(std::move(Results->GlobalChanges)),
// Set a file "bar.cc" on disk.
TU.AdditionalFiles["bar.cc"] = std::string(BarCode.code());
AST = TU.build();
- Results = rename({MainCode.point(),
- NewName,
- AST,
- MainFilePath,
- Index.get(),
- {},
- GetDirtyBuffer});
+ Results = rename({MainCode.point(), NewName, AST, MainFilePath,
+ createOverlay(getVFSFromAST(AST), InMemFS), Index.get()});
ASSERT_TRUE(bool(Results)) << Results.takeError();
EXPECT_THAT(
applyEdits(std::move(Results->GlobalChanges)),
size_t estimateMemoryUsage() const override { return 0; }
} PIndex;
- Results = rename({MainCode.point(),
- NewName,
- AST,
- MainFilePath,
- &PIndex,
- {},
- GetDirtyBuffer});
+ Results = rename({MainCode.point(), NewName, AST, MainFilePath,
+ createOverlay(getVFSFromAST(AST), InMemFS), &PIndex});
EXPECT_FALSE(Results);
EXPECT_THAT(llvm::toString(Results.takeError()),
testing::HasSubstr("too many occurrences"));
Ref ReturnedRef;
} DIndex(XRefInBarCC);
llvm::StringRef NewName = "newName";
- auto Results =
- rename({MainCode.point(), NewName, AST, MainFilePath, &DIndex});
+ auto Results = rename({MainCode.point(), NewName, AST, MainFilePath,
+ getVFSFromAST(AST), &DIndex});
ASSERT_TRUE(bool(Results)) << Results.takeError();
EXPECT_THAT(
applyEdits(std::move(Results->GlobalChanges)),