LutsenkoDanil created this revision.
LutsenkoDanil added reviewers: sammccall, ilya-biryukov.
Herald added subscribers: cfe-commits, kadircet, arphaman, jkorous, MaskRay,
ioeric.
Patch changes behavior of preamble building. If headers was changed in editor,
preamble will be built with draft versions of them.
Repository:
rCTE Clang Tools Extra
https://reviews.llvm.org/D54077
Files:
clangd/ClangdLSPServer.cpp
clangd/ClangdLSPServer.h
clangd/DraftStore.cpp
clangd/DraftStore.h
clangd/FS.h
clangd/FSProvider.h
unittests/clangd/DraftStoreTests.cpp
Index: unittests/clangd/DraftStoreTests.cpp
===================================================================
--- unittests/clangd/DraftStoreTests.cpp
+++ unittests/clangd/DraftStoreTests.cpp
@@ -53,7 +53,7 @@
Expected<std::string> Result = DS.updateDraft(Path, {Event});
ASSERT_TRUE(!!Result);
EXPECT_EQ(*Result, SrcAfter.code());
- EXPECT_EQ(*DS.getDraft(Path), SrcAfter.code());
+ EXPECT_EQ(DS.getDraft(Path)->Content, SrcAfter.code());
}
}
@@ -83,7 +83,7 @@
ASSERT_TRUE(!!Result) << toString(Result.takeError());
EXPECT_EQ(*Result, FinalSrc.code());
- EXPECT_EQ(*DS.getDraft(Path), FinalSrc.code());
+ EXPECT_EQ(DS.getDraft(Path)->Content, FinalSrc.code());
}
TEST(DraftStoreIncrementalUpdateTest, Simple) {
@@ -339,9 +339,9 @@
EXPECT_EQ(toString(Result.takeError()),
"UTF-16 offset 100 is invalid for line 0");
- Optional<std::string> Contents = DS.getDraft(File);
- EXPECT_TRUE(Contents);
- EXPECT_EQ(*Contents, OriginalContents);
+ Optional<Draft> NewDraft = DS.getDraft(File);
+ EXPECT_TRUE(NewDraft);
+ EXPECT_EQ(NewDraft->Content, OriginalContents);
}
} // namespace
Index: clangd/FSProvider.h
===================================================================
--- clangd/FSProvider.h
+++ clangd/FSProvider.h
@@ -10,6 +10,7 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FSPROVIDER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FSPROVIDER_H
+#include "FS.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/Support/VirtualFileSystem.h"
@@ -38,6 +39,20 @@
}
};
+class DraftFileSystemProvider : public FileSystemProvider {
+ DraftStore &DS;
+
+public:
+ DraftFileSystemProvider(DraftStore &DS) : DS(DS) {}
+
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
+ getFileSystem() const override {
+ static llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> Result(
+ new DraftFileSystem(llvm::vfs::getRealFileSystem(), DS));
+ return Result;
+ }
+};
+
} // namespace clangd
} // namespace clang
Index: clangd/FS.h
===================================================================
--- clangd/FS.h
+++ clangd/FS.h
@@ -10,13 +10,84 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FS_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FS_H
+#include "DraftStore.h"
+#include "Logger.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/VirtualFileSystem.h"
namespace clang {
namespace clangd {
+class DraftFile : public llvm::vfs::File {
+ std::unique_ptr<File> F;
+ Draft DraftInfo;
+
+public:
+ DraftFile(std::unique_ptr<File> F, Draft D)
+ : F(std::move(F)), DraftInfo(std::move(D)) {}
+
+ llvm::ErrorOr<std::string> getName() override { return F->getName(); }
+
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+ getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
+ bool IsVolatile) override {
+ (void)IsVolatile;
+ (void)RequiresNullTerminator;
+
+ if (FileSize < 0) {
+ FileSize = DraftInfo.Content.size();
+ }
+
+ return llvm::MemoryBuffer::getMemBufferCopy(
+ llvm::StringRef(DraftInfo.Content).substr(0, FileSize), Name);
+ }
+
+ llvm::ErrorOr<llvm::vfs::Status> status() override {
+ auto RealStatus = F->status();
+
+ if (!RealStatus) {
+ return RealStatus;
+ }
+
+ llvm::vfs::Status Result(
+ RealStatus->getName(), RealStatus->getUniqueID(), DraftInfo.ModifyTime,
+ RealStatus->getUser(), RealStatus->getGroup(), DraftInfo.Content.size(),
+ RealStatus->getType(), RealStatus->getPermissions());
+
+ Result.IsVFSMapped = RealStatus->IsVFSMapped;
+ return Result;
+ }
+
+ std::error_code close() override { return F->close(); }
+};
+
+/// Wrapper around real FS. If file was changed in the DraftStrore, returns
+/// changed content, instead of real
+class DraftFileSystem : public llvm::vfs::ProxyFileSystem {
+ DraftStore &DS;
+
+public:
+ DraftFileSystem(IntrusiveRefCntPtr<FileSystem> FS, DraftStore &DS)
+ : llvm::vfs::ProxyFileSystem(FS), DS(DS) {}
+
+ llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
+ openFileForRead(const Twine &Path) override {
+ auto Result = getUnderlyingFS().openFileForRead(Path);
+
+ if (!Result) {
+ return Result;
+ }
+
+ if (auto D = DS.getDraft(Path.str())) {
+ return std::unique_ptr<llvm::vfs::File>(
+ new DraftFile(std::move(*Result), std::move(*D)));
+ }
+
+ return Result;
+ }
+};
+
/// Records status information for files open()ed or stat()ed during preamble
/// build (except for the main file), so we can avoid stat()s on the underlying
/// FS when reusing the preamble. For example, code completion can re-stat files
Index: clangd/DraftStore.h
===================================================================
--- clangd/DraftStore.h
+++ clangd/DraftStore.h
@@ -14,21 +14,27 @@
#include "Protocol.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Chrono.h"
#include <mutex>
#include <string>
#include <vector>
namespace clang {
namespace clangd {
+struct Draft {
+ std::string Content;
+ llvm::sys::TimePoint<> ModifyTime;
+};
+
/// A thread-safe container for files opened in a workspace, addressed by
/// filenames. The contents are owned by the DraftStore. This class supports
/// both whole and incremental updates of the documents.
class DraftStore {
public:
/// \return Contents of the stored document.
/// For untracked files, a llvm::None is returned.
- llvm::Optional<std::string> getDraft(PathRef File) const;
+ llvm::Optional<Draft> getDraft(PathRef File) const;
/// \return List of names of the drafts in this store.
std::vector<Path> getActiveFiles() const;
@@ -51,7 +57,7 @@
private:
mutable std::mutex Mutex;
- llvm::StringMap<std::string> Drafts;
+ llvm::StringMap<Draft> Drafts;
};
} // namespace clangd
Index: clangd/DraftStore.cpp
===================================================================
--- clangd/DraftStore.cpp
+++ clangd/DraftStore.cpp
@@ -15,7 +15,7 @@
namespace clang {
namespace clangd {
-Optional<std::string> DraftStore::getDraft(PathRef File) const {
+Optional<Draft> DraftStore::getDraft(PathRef File) const {
std::lock_guard<std::mutex> Lock(Mutex);
auto It = Drafts.find(File);
@@ -38,7 +38,7 @@
void DraftStore::addDraft(PathRef File, StringRef Contents) {
std::lock_guard<std::mutex> Lock(Mutex);
- Drafts[File] = Contents;
+ Drafts[File] = { Contents, llvm::sys::TimePoint<>::clock::now() };
}
Expected<std::string>
@@ -53,7 +53,7 @@
llvm::errc::invalid_argument);
}
- std::string Contents = EntryIt->second;
+ std::string Contents = EntryIt->second.Content;
for (const TextDocumentContentChangeEvent &Change : Changes) {
if (!Change.range) {
@@ -105,7 +105,7 @@
Contents = std::move(NewContents);
}
- EntryIt->second = Contents;
+ Drafts[File] = { Contents, llvm::sys::TimePoint<>::clock::now() };
return Contents;
}
Index: clangd/ClangdLSPServer.h
===================================================================
--- clangd/ClangdLSPServer.h
+++ clangd/ClangdLSPServer.h
@@ -117,7 +117,7 @@
void call(StringRef Method, llvm::json::Value Params);
void notify(StringRef Method, llvm::json::Value Params);
- RealFileSystemProvider FSProvider;
+ DraftFileSystemProvider FSProvider;
/// Options used for code completion
clangd::CodeCompleteOptions CCOpts;
/// Options used for diagnostics.
Index: clangd/ClangdLSPServer.cpp
===================================================================
--- clangd/ClangdLSPServer.cpp
+++ clangd/ClangdLSPServer.cpp
@@ -454,15 +454,15 @@
void ClangdLSPServer::onRename(const RenameParams &Params,
Callback<WorkspaceEdit> Reply) {
Path File = Params.textDocument.uri.file();
- Optional<std::string> Code = DraftMgr.getDraft(File);
- if (!Code)
+ auto Draft = DraftMgr.getDraft(File);
+ if (!Draft)
return Reply(make_error<LSPError>("onRename called for non-added file",
ErrorCode::InvalidParams));
Server->rename(
File, Params.position, Params.newName,
Bind(
- [File, Code,
+ [File, Draft,
Params](decltype(Reply) Reply,
Expected<std::vector<tooling::Replacement>> Replacements) {
if (!Replacements)
@@ -472,7 +472,7 @@
// Server Protocol. Fuse them into one big JSON array.
std::vector<TextEdit> Edits;
for (const auto &R : *Replacements)
- Edits.push_back(replacementToEdit(*Code, R));
+ Edits.push_back(replacementToEdit(Draft->Content, R));
WorkspaceEdit WE;
WE.changes = {{Params.textDocument.uri.uri(), Edits}};
Reply(WE);
@@ -491,49 +491,49 @@
const DocumentOnTypeFormattingParams &Params,
Callback<std::vector<TextEdit>> Reply) {
auto File = Params.textDocument.uri.file();
- auto Code = DraftMgr.getDraft(File);
- if (!Code)
+ auto Draft = DraftMgr.getDraft(File);
+ if (!Draft)
return Reply(make_error<LSPError>(
"onDocumentOnTypeFormatting called for non-added file",
ErrorCode::InvalidParams));
- auto ReplacementsOrError = Server->formatOnType(*Code, File, Params.position);
+ auto ReplacementsOrError = Server->formatOnType(Draft->Content, File, Params.position);
if (ReplacementsOrError)
- Reply(replacementsToEdits(*Code, ReplacementsOrError.get()));
+ Reply(replacementsToEdits(Draft->Content, ReplacementsOrError.get()));
else
Reply(ReplacementsOrError.takeError());
}
void ClangdLSPServer::onDocumentRangeFormatting(
const DocumentRangeFormattingParams &Params,
Callback<std::vector<TextEdit>> Reply) {
auto File = Params.textDocument.uri.file();
- auto Code = DraftMgr.getDraft(File);
- if (!Code)
+ auto Draft = DraftMgr.getDraft(File);
+ if (!Draft)
return Reply(make_error<LSPError>(
"onDocumentRangeFormatting called for non-added file",
ErrorCode::InvalidParams));
- auto ReplacementsOrError = Server->formatRange(*Code, File, Params.range);
+ auto ReplacementsOrError = Server->formatRange(Draft->Content, File, Params.range);
if (ReplacementsOrError)
- Reply(replacementsToEdits(*Code, ReplacementsOrError.get()));
+ Reply(replacementsToEdits(Draft->Content, ReplacementsOrError.get()));
else
Reply(ReplacementsOrError.takeError());
}
void ClangdLSPServer::onDocumentFormatting(
const DocumentFormattingParams &Params,
Callback<std::vector<TextEdit>> Reply) {
auto File = Params.textDocument.uri.file();
- auto Code = DraftMgr.getDraft(File);
- if (!Code)
+ auto Draft = DraftMgr.getDraft(File);
+ if (!Draft)
return Reply(
make_error<LSPError>("onDocumentFormatting called for non-added file",
ErrorCode::InvalidParams));
- auto ReplacementsOrError = Server->formatFile(*Code, File);
+ auto ReplacementsOrError = Server->formatFile(Draft->Content, File);
if (ReplacementsOrError)
- Reply(replacementsToEdits(*Code, ReplacementsOrError.get()));
+ Reply(replacementsToEdits(Draft->Content, ReplacementsOrError.get()));
else
Reply(ReplacementsOrError.takeError());
}
@@ -690,7 +690,8 @@
Optional<Path> CompileCommandsDir,
bool UseDirBasedCDB,
const ClangdServer::Options &Opts)
- : Transp(Transp), MsgHandler(new MessageHandler(*this)), CCOpts(CCOpts),
+ : Transp(Transp), MsgHandler(new MessageHandler(*this)),
+ FSProvider(DraftMgr), CCOpts(CCOpts),
SupportedSymbolKinds(defaultSymbolKinds()),
SupportedCompletionItemKinds(defaultCompletionItemKinds()),
UseDirBasedCDB(UseDirBasedCDB),
@@ -783,7 +784,7 @@
void ClangdLSPServer::reparseOpenedFiles() {
for (const Path &FilePath : DraftMgr.getActiveFiles())
- Server->addDocument(FilePath, *DraftMgr.getDraft(FilePath),
+ Server->addDocument(FilePath, DraftMgr.getDraft(FilePath)->Content,
WantDiagnostics::Auto);
}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits