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
|
//===--- DraftStore.cpp - File contents container ---------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "DraftStore.h"
#include "SourceCode.h"
#include "llvm/Support/Errc.h"
namespace clang {
namespace clangd {
llvm::Optional<std::string> 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(DraftIt->getKey());
return ResultVector;
}
void DraftStore::addDraft(PathRef File, llvm::StringRef Contents) {
std::lock_guard<std::mutex> Lock(Mutex);
Drafts[File] = Contents;
}
llvm::Expected<std::string> DraftStore::updateDraft(
PathRef File, 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);
}
std::string Contents = EntryIt->second;
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, *EndIndex - *StartIndex),
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);
}
EntryIt->second = Contents;
return Contents;
}
void DraftStore::removeDraft(PathRef File) {
std::lock_guard<std::mutex> Lock(Mutex);
Drafts.erase(File);
}
} // namespace clangd
} // namespace clang
|