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