[clangd] Move non-clang base pieces into separate support/ lib. NFCI

Summary:
This enforces layering, reduces a sprawling clangd/ directory, and makes life
easier for embedders.

Reviewers: kbobyrev

Subscribers: mgorny, ilya-biryukov, javed.absar, MaskRay, jkorous, arphaman, jfb, kadircet, usaxena95, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D79014
diff --git a/clang-tools-extra/clangd/support/FSProvider.cpp b/clang-tools-extra/clangd/support/FSProvider.cpp
new file mode 100644
index 0000000..6474a3c
--- /dev/null
+++ b/clang-tools-extra/clangd/support/FSProvider.cpp
@@ -0,0 +1,83 @@
+//===--- FSProvider.cpp - VFS provider for ClangdServer -------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "support/FSProvider.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/VirtualFileSystem.h"
+#include <memory>
+
+namespace clang {
+namespace clangd {
+
+namespace {
+/// Always opens files in the underlying filesystem as "volatile", meaning they
+/// won't be memory-mapped. Memory-mapping isn't desirable for clangd:
+///   - edits to the underlying files change contents MemoryBuffers owned by
+//      SourceManager, breaking its invariants and leading to crashes
+///   - it locks files on windows, preventing edits
+class VolatileFileSystem : public llvm::vfs::ProxyFileSystem {
+public:
+  explicit VolatileFileSystem(llvm::IntrusiveRefCntPtr<FileSystem> FS)
+      : ProxyFileSystem(std::move(FS)) {}
+
+  llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
+  openFileForRead(const llvm::Twine &InPath) override {
+    llvm::SmallString<128> Path;
+    InPath.toVector(Path);
+
+    auto File = getUnderlyingFS().openFileForRead(Path);
+    if (!File)
+      return File;
+    // Try to guess preamble files, they can be memory-mapped even on Windows as
+    // clangd has exclusive access to those and nothing else should touch them.
+    llvm::StringRef FileName = llvm::sys::path::filename(Path);
+    if (FileName.startswith("preamble-") && FileName.endswith(".pch"))
+      return File;
+    return std::unique_ptr<VolatileFile>(new VolatileFile(std::move(*File)));
+  }
+
+private:
+  class VolatileFile : public llvm::vfs::File {
+  public:
+    VolatileFile(std::unique_ptr<llvm::vfs::File> Wrapped)
+        : Wrapped(std::move(Wrapped)) {
+      assert(this->Wrapped);
+    }
+
+    virtual llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+    getBuffer(const llvm::Twine &Name, int64_t FileSize,
+              bool RequiresNullTerminator, bool /*IsVolatile*/) override {
+      return Wrapped->getBuffer(Name, FileSize, RequiresNullTerminator,
+                                /*IsVolatile=*/true);
+    }
+
+    llvm::ErrorOr<llvm::vfs::Status> status() override {
+      return Wrapped->status();
+    }
+    llvm::ErrorOr<std::string> getName() override { return Wrapped->getName(); }
+    std::error_code close() override { return Wrapped->close(); }
+
+  private:
+    std::unique_ptr<File> Wrapped;
+  };
+};
+} // namespace
+
+llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
+clang::clangd::RealFileSystemProvider::getFileSystem() const {
+  // Avoid using memory-mapped files.
+  // FIXME: Try to use a similar approach in Sema instead of relying on
+  //        propagation of the 'isVolatile' flag through all layers.
+  return new VolatileFileSystem(
+      llvm::vfs::createPhysicalFileSystem().release());
+}
+} // namespace clangd
+} // namespace clang