xref: /llvm-project/clang-tools-extra/clangd/DraftStore.cpp (revision f71ffd3b735b4d6ae3c12be1806cdd6205b3b378)
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 #include <optional>
15 
16 namespace clang {
17 namespace clangd {
18 
getDraft(PathRef File) const19 std::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 
getActiveFiles() const29 std::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 
increment(std::string & S)39 static 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 
updateVersion(DraftStore::Draft & D,llvm::StringRef SpecifiedVersion)62 static 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 
addDraft(PathRef File,llvm::StringRef Version,llvm::StringRef Contents)75 std::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 
removeDraft(PathRef File)86 void DraftStore::removeDraft(PathRef File) {
87   std::lock_guard<std::mutex> Lock(Mutex);
88 
89   Drafts.erase(File);
90 }
91 
92 namespace {
93 
94 /// A read only MemoryBuffer shares ownership of a ref counted string. The
95 /// shared string object must not be modified while an owned by this buffer.
96 class SharedStringBuffer : public llvm::MemoryBuffer {
97   const std::shared_ptr<const std::string> BufferContents;
98   const std::string Name;
99 
100 public:
getBufferKind() const101   BufferKind getBufferKind() const override {
102     return MemoryBuffer::MemoryBuffer_Malloc;
103   }
104 
getBufferIdentifier() const105   StringRef getBufferIdentifier() const override { return Name; }
106 
SharedStringBuffer(std::shared_ptr<const std::string> Data,StringRef Name)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 } // namespace
116 
asVFS() const117 llvm::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 clangd
127 } // namespace clang
128