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
getDraft(PathRef File) const18 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
getActiveFiles() const28 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
increment(std::string & S)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
updateVersion(DraftStore::Draft & D,llvm::StringRef SpecifiedVersion)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
addDraft(PathRef File,llvm::StringRef Version,llvm::StringRef Contents)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
removeDraft(PathRef File)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:
getBufferKind() const100 BufferKind getBufferKind() const override {
101 return MemoryBuffer::MemoryBuffer_Malloc;
102 }
103
getBufferIdentifier() const104 StringRef getBufferIdentifier() const override { return Name; }
105
SharedStringBuffer(std::shared_ptr<const std::string> Data,StringRef Name)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
asVFS() const116 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