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