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
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to