128 lines · cpp
1//===--- DraftStore.cpp - File contents container ---------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "DraftStore.h"10#include "support/Logger.h"11#include "llvm/ADT/StringExtras.h"12#include "llvm/Support/VirtualFileSystem.h"13#include <memory>14#include <optional>15 16namespace clang {17namespace clangd {18 19std::optional<DraftStore::Draft> DraftStore::getDraft(PathRef File) const {20 std::lock_guard<std::mutex> Lock(Mutex);21 22 auto It = Drafts.find(File);23 if (It == Drafts.end())24 return std::nullopt;25 26 return It->second.D;27}28 29std::vector<Path> DraftStore::getActiveFiles() const {30 std::lock_guard<std::mutex> Lock(Mutex);31 std::vector<Path> ResultVector;32 33 for (auto DraftIt = Drafts.begin(); DraftIt != Drafts.end(); DraftIt++)34 ResultVector.push_back(std::string(DraftIt->getKey()));35 36 return ResultVector;37}38 39static void increment(std::string &S) {40 // Ensure there is a numeric suffix.41 if (S.empty() || !llvm::isDigit(S.back())) {42 S.push_back('0');43 return;44 }45 // Increment the numeric suffix.46 auto I = S.rbegin(), E = S.rend();47 for (;;) {48 if (I == E || !llvm::isDigit(*I)) {49 // Reached start of numeric section, it was all 9s.50 S.insert(I.base(), '1');51 break;52 }53 if (*I != '9') {54 // Found a digit we can increment, we're done.55 ++*I;56 break;57 }58 *I = '0'; // and keep incrementing to the left.59 }60}61 62static void updateVersion(DraftStore::Draft &D,63 llvm::StringRef SpecifiedVersion) {64 if (!SpecifiedVersion.empty()) {65 // We treat versions as opaque, but the protocol says they increase.66 if (SpecifiedVersion.compare_numeric(D.Version) <= 0)67 log("File version went from {0} to {1}", D.Version, SpecifiedVersion);68 D.Version = SpecifiedVersion.str();69 } else {70 // Note that if D was newly-created, this will bump D.Version from "" to 1.71 increment(D.Version);72 }73}74 75std::string DraftStore::addDraft(PathRef File, llvm::StringRef Version,76 llvm::StringRef Contents) {77 std::lock_guard<std::mutex> Lock(Mutex);78 79 auto &D = Drafts[File];80 updateVersion(D.D, Version);81 std::time(&D.MTime);82 D.D.Contents = std::make_shared<std::string>(Contents);83 return D.D.Version;84}85 86void DraftStore::removeDraft(PathRef File) {87 std::lock_guard<std::mutex> Lock(Mutex);88 89 Drafts.erase(File);90}91 92namespace {93 94/// A read only MemoryBuffer shares ownership of a ref counted string. The95/// shared string object must not be modified while an owned by this buffer.96class SharedStringBuffer : public llvm::MemoryBuffer {97 const std::shared_ptr<const std::string> BufferContents;98 const std::string Name;99 100public:101 BufferKind getBufferKind() const override {102 return MemoryBuffer::MemoryBuffer_Malloc;103 }104 105 StringRef getBufferIdentifier() const override { return Name; }106 107 SharedStringBuffer(std::shared_ptr<const std::string> Data, StringRef Name)108 : BufferContents(std::move(Data)), Name(Name) {109 assert(BufferContents && "Can't create from empty shared_ptr");110 MemoryBuffer::init(BufferContents->c_str(),111 BufferContents->c_str() + BufferContents->size(),112 /*RequiresNullTerminator=*/true);113 }114};115} // namespace116 117llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> DraftStore::asVFS() const {118 auto MemFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();119 std::lock_guard<std::mutex> Guard(Mutex);120 for (const auto &Draft : Drafts)121 MemFS->addFile(Draft.getKey(), Draft.getValue().MTime,122 std::make_unique<SharedStringBuffer>(123 Draft.getValue().D.Contents, Draft.getKey()));124 return MemFS;125}126} // namespace clangd127} // namespace clang128