nixd
Loading...
Searching...
No Matches
DraftStore.cpp
Go to the documentation of this file.
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
10#include "lspserver/Logger.h"
11#include "llvm/ADT/StringExtras.h"
12#include "llvm/Support/VirtualFileSystem.h"
13#include <memory>
14#include <optional>
15
16namespace lspserver {
17
18std::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 std::nullopt;
24
25 return It->second.D;
26}
27
28std::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
38static 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
61std::optional<int64_t> DraftStore::decodeVersion(llvm::StringRef Encoded) {
63 if (llvm::to_integer(Encoded, Result, 10))
64 return Result;
65 if (!Encoded.empty()) // Empty can be e.g. diagnostics on close.
66 elog("unexpected non-numeric version {0}", Encoded);
67 return std::nullopt;
68}
69
70static void updateVersion(DraftStore::Draft &D,
71 llvm::StringRef SpecifiedVersion) {
72 if (!SpecifiedVersion.empty()) {
73 // We treat versions as opaque, but the protocol says they increase.
74 if (SpecifiedVersion.compare_numeric(D.Version) <= 0)
75 lspserver::log("File version went from {0} to {1}", D.Version,
77 D.Version = SpecifiedVersion.str();
78 } else {
79 // Note that if D was newly-created, this will bump D.Version from "" to 1.
80 increment(D.Version);
81 }
82}
83
84std::string DraftStore::encodeVersion(std::optional<int64_t> LSPVersion) {
85 return LSPVersion ? std::to_string(*LSPVersion) : "";
86}
87
88std::string DraftStore::addDraft(PathRef File, llvm::StringRef Version,
89 llvm::StringRef Contents) {
90 std::lock_guard<std::mutex> Lock(Mutex);
91
92 auto &D = Drafts[File];
93 updateVersion(D.D, Version);
94 std::time(&D.MTime);
95 D.D.Contents = std::make_shared<std::string>(Contents);
96 return D.D.Version;
97}
98
100 std::lock_guard<std::mutex> Lock(Mutex);
101
102 Drafts.erase(File);
103}
104
105namespace {
106
107/// A read only MemoryBuffer shares ownership of a ref counted string. The
108/// shared string object must not be modified while an owned by this buffer.
109class SharedStringBuffer : public llvm::MemoryBuffer {
110 const std::shared_ptr<const std::string> BufferContents;
111 const std::string Name;
112
113public:
114 BufferKind getBufferKind() const override {
115 return MemoryBuffer::MemoryBuffer_Malloc;
116 }
117
118 llvm::StringRef getBufferIdentifier() const override { return Name; }
119
120 SharedStringBuffer(std::shared_ptr<const std::string> Data,
121 llvm::StringRef Name)
122 : BufferContents(std::move(Data)), Name(Name) {
123 assert(BufferContents && "Can't create from empty shared_ptr");
124 MemoryBuffer::init(BufferContents->c_str(),
125 BufferContents->c_str() + BufferContents->size(),
126 /*RequiresNullTerminator=*/true);
127 }
128};
129} // namespace
130
131llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> DraftStore::asVFS() const {
132 auto MemFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
133 std::lock_guard<std::mutex> Guard(Mutex);
134 for (const auto &Draft : Drafts)
135 MemFS->addFile(Draft.getKey(), Draft.getValue().MTime,
136 std::make_unique<SharedStringBuffer>(
137 Draft.getValue().D.Contents, Draft.getKey()));
138 return MemFS;
139}
140
141} // namespace lspserver
static std::string encodeVersion(std::optional< int64_t > LSPVersion)
std::vector< Path > getActiveFiles() const
std::optional< Draft > getDraft(PathRef File) const
void removeDraft(PathRef File)
Remove the draft from the store.
std::string addDraft(PathRef File, llvm::StringRef Version, llvm::StringRef Contents)
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > asVFS() const
static std::optional< int64_t > decodeVersion(llvm::StringRef Encoded)
Whether current platform treats paths case insensitively.
Definition Connection.h:11
bool fromJSON(const llvm::json::Value &, URIForFile &, llvm::json::Path)
void elog(const char *Fmt, Ts &&...Vals)
Definition Logger.h:52
llvm::StringRef PathRef
Definition Path.h:27
void log(const char *Fmt, Ts &&...Vals)
Definition Logger.h:58
std::shared_ptr< const std::string > Contents
Definition DraftStore.h:28