diff options
Diffstat (limited to 'clang-tools-extra/clangd/FS.cpp')
-rw-r--r-- | clang-tools-extra/clangd/FS.cpp | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/clang-tools-extra/clangd/FS.cpp b/clang-tools-extra/clangd/FS.cpp new file mode 100644 index 00000000000..ce62a59271f --- /dev/null +++ b/clang-tools-extra/clangd/FS.cpp @@ -0,0 +1,92 @@ +//===--- FS.cpp - File system related utils ----------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "FS.h" +#include "clang/Basic/VirtualFileSystem.h" +#include "llvm/ADT/None.h" + +namespace clang { +namespace clangd { + +void PreambleFileStatusCache::update(const vfs::FileSystem &FS, vfs::Status S) { + SmallString<32> PathStore(S.getName()); + if (auto Err = FS.makeAbsolute(PathStore)) + return; + // Stores the latest status in cache as it can change in a preamble build. + StatCache.insert({PathStore, std::move(S)}); +} + +llvm::Optional<vfs::Status> +PreambleFileStatusCache::lookup(llvm::StringRef File) const { + auto I = StatCache.find(File); + if (I != StatCache.end()) + return I->getValue(); + return llvm::None; +} + +IntrusiveRefCntPtr<vfs::FileSystem> PreambleFileStatusCache::getProducingFS( + IntrusiveRefCntPtr<vfs::FileSystem> FS) { + // This invalidates old status in cache if files are re-`open()`ed or + // re-`stat()`ed in case file status has changed during preamble build. + class CollectFS : public vfs::ProxyFileSystem { + public: + CollectFS(IntrusiveRefCntPtr<vfs::FileSystem> FS, + PreambleFileStatusCache &StatCache) + : ProxyFileSystem(std::move(FS)), StatCache(StatCache) {} + + llvm::ErrorOr<std::unique_ptr<vfs::File>> + openFileForRead(const Twine &Path) override { + auto File = getUnderlyingFS().openFileForRead(Path); + if (!File || !*File) + return File; + // Eagerly stat opened file, as the followup `status` call on the file + // doesn't necessarily go through this FS. This puts some extra work on + // preamble build, but it should be worth it as preamble can be reused + // many times (e.g. code completion) and the repeated status call is + // likely to be cached in the underlying file system anyway. + if (auto S = File->get()->status()) + StatCache.update(getUnderlyingFS(), std::move(*S)); + return File; + } + + llvm::ErrorOr<vfs::Status> status(const Twine &Path) override { + auto S = getUnderlyingFS().status(Path); + if (S) + StatCache.update(getUnderlyingFS(), *S); + return S; + } + + private: + PreambleFileStatusCache &StatCache; + }; + return IntrusiveRefCntPtr<CollectFS>(new CollectFS(std::move(FS), *this)); +} + +IntrusiveRefCntPtr<vfs::FileSystem> PreambleFileStatusCache::getConsumingFS( + IntrusiveRefCntPtr<vfs::FileSystem> FS) const { + class CacheVFS : public vfs::ProxyFileSystem { + public: + CacheVFS(IntrusiveRefCntPtr<vfs::FileSystem> FS, + const PreambleFileStatusCache &StatCache) + : ProxyFileSystem(std::move(FS)), StatCache(StatCache) {} + + llvm::ErrorOr<vfs::Status> status(const Twine &Path) override { + if (auto S = StatCache.lookup(Path.str())) + return *S; + return getUnderlyingFS().status(Path); + } + + private: + const PreambleFileStatusCache &StatCache; + }; + return IntrusiveRefCntPtr<CacheVFS>(new CacheVFS(std::move(FS), *this)); +} + +} // namespace clangd +} // namespace clang |