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 | |
19 | std::optional<DraftStore::Draft> DraftStore::getDraft(PathRef File) const { |
20 | std::lock_guard<std::mutex> Lock(Mutex); |
21 | |
22 | auto It = Drafts.find(Key: File); |
23 | if (It == Drafts.end()) |
24 | return std::nullopt; |
25 | |
26 | return It->second.D; |
27 | } |
28 | |
29 | 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(x: std::string(DraftIt->getKey())); |
35 | |
36 | return ResultVector; |
37 | } |
38 | |
39 | static void increment(std::string &S) { |
40 | // Ensure there is a numeric suffix. |
41 | if (S.empty() || !llvm::isDigit(C: S.back())) { |
42 | S.push_back(c: '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(C: *I)) { |
49 | // Reached start of numeric section, it was all 9s. |
50 | S.insert(p: I.base(), c: '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 | |
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(RHS: D.Version) <= 0) |
67 | log(Fmt: "File version went from {0} to {1}" , Vals&: D.Version, Vals&: 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(S&: D.Version); |
72 | } |
73 | } |
74 | |
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.D, SpecifiedVersion: Version); |
81 | std::time(timer: &D.MTime); |
82 | D.D.Contents = std::make_shared<std::string>(args&: Contents); |
83 | return D.D.Version; |
84 | } |
85 | |
86 | void DraftStore::removeDraft(PathRef File) { |
87 | std::lock_guard<std::mutex> Lock(Mutex); |
88 | |
89 | Drafts.erase(Key: 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: |
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(BufStart: BufferContents->c_str(), |
111 | BufEnd: BufferContents->c_str() + BufferContents->size(), |
112 | /*RequiresNullTerminator=*/true); |
113 | } |
114 | }; |
115 | } // namespace |
116 | |
117 | 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(Path: Draft.getKey(), ModificationTime: Draft.getValue().MTime, |
122 | Buffer: std::make_unique<SharedStringBuffer>( |
123 | args: Draft.getValue().D.Contents, args: Draft.getKey())); |
124 | return MemFS; |
125 | } |
126 | } // namespace clangd |
127 | } // namespace clang |
128 | |