DraftStore.cpp
4.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//===--- DraftStore.cpp - File contents container ---------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "DraftStore.h"
#include "SourceCode.h"
#include "support/Logger.h"
#include "llvm/Support/Errc.h"
namespace clang {
namespace clangd {
llvm::Optional<DraftStore::Draft> DraftStore::getDraft(PathRef File) const {
std::lock_guard<std::mutex> Lock(Mutex);
auto It = Drafts.find(File);
if (It == Drafts.end())
return None;
return It->second;
}
std::vector<Path> DraftStore::getActiveFiles() const {
std::lock_guard<std::mutex> Lock(Mutex);
std::vector<Path> ResultVector;
for (auto DraftIt = Drafts.begin(); DraftIt != Drafts.end(); DraftIt++)
ResultVector.push_back(std::string(DraftIt->getKey()));
return ResultVector;
}
static void updateVersion(DraftStore::Draft &D,
llvm::Optional<int64_t> Version) {
if (Version) {
// We treat versions as opaque, but the protocol says they increase.
if (*Version <= D.Version)
log("File version went from {0} to {1}", D.Version, Version);
D.Version = *Version;
} else {
// Note that if D was newly-created, this will bump D.Version from -1 to 0.
++D.Version;
}
}
int64_t DraftStore::addDraft(PathRef File, llvm::Optional<int64_t> Version,
llvm::StringRef Contents) {
std::lock_guard<std::mutex> Lock(Mutex);
Draft &D = Drafts[File];
updateVersion(D, Version);
D.Contents = Contents.str();
return D.Version;
}
llvm::Expected<DraftStore::Draft> DraftStore::updateDraft(
PathRef File, llvm::Optional<int64_t> Version,
llvm::ArrayRef<TextDocumentContentChangeEvent> Changes) {
std::lock_guard<std::mutex> Lock(Mutex);
auto EntryIt = Drafts.find(File);
if (EntryIt == Drafts.end()) {
return llvm::make_error<llvm::StringError>(
"Trying to do incremental update on non-added document: " + File,
llvm::errc::invalid_argument);
}
Draft &D = EntryIt->second;
std::string Contents = EntryIt->second.Contents;
for (const TextDocumentContentChangeEvent &Change : Changes) {
if (!Change.range) {
Contents = Change.text;
continue;
}
const Position &Start = Change.range->start;
llvm::Expected<size_t> StartIndex =
positionToOffset(Contents, Start, false);
if (!StartIndex)
return StartIndex.takeError();
const Position &End = Change.range->end;
llvm::Expected<size_t> EndIndex = positionToOffset(Contents, End, false);
if (!EndIndex)
return EndIndex.takeError();
if (*EndIndex < *StartIndex)
return llvm::make_error<llvm::StringError>(
llvm::formatv(
"Range's end position ({0}) is before start position ({1})", End,
Start),
llvm::errc::invalid_argument);
// Since the range length between two LSP positions is dependent on the
// contents of the buffer we compute the range length between the start and
// end position ourselves and compare it to the range length of the LSP
// message to verify the buffers of the client and server are in sync.
// EndIndex and StartIndex are in bytes, but Change.rangeLength is in UTF-16
// code units.
ssize_t ComputedRangeLength =
lspLength(Contents.substr(*StartIndex, *EndIndex - *StartIndex));
if (Change.rangeLength && ComputedRangeLength != *Change.rangeLength)
return llvm::make_error<llvm::StringError>(
llvm::formatv("Change's rangeLength ({0}) doesn't match the "
"computed range length ({1}).",
*Change.rangeLength, ComputedRangeLength),
llvm::errc::invalid_argument);
std::string NewContents;
NewContents.reserve(*StartIndex + Change.text.length() +
(Contents.length() - *EndIndex));
NewContents = Contents.substr(0, *StartIndex);
NewContents += Change.text;
NewContents += Contents.substr(*EndIndex);
Contents = std::move(NewContents);
}
updateVersion(D, Version);
D.Contents = std::move(Contents);
return D;
}
void DraftStore::removeDraft(PathRef File) {
std::lock_guard<std::mutex> Lock(Mutex);
Drafts.erase(File);
}
} // namespace clangd
} // namespace clang