comparison clang-tools-extra/clangd/DraftStore.cpp @ 221:79ff65ed7e25

LLVM12 Original
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Tue, 15 Jun 2021 19:15:29 +0900
parents 0572611fdcc8
children c4bab56944e8
comparison
equal deleted inserted replaced
220:42394fc6a535 221:79ff65ed7e25
7 //===----------------------------------------------------------------------===// 7 //===----------------------------------------------------------------------===//
8 8
9 #include "DraftStore.h" 9 #include "DraftStore.h"
10 #include "SourceCode.h" 10 #include "SourceCode.h"
11 #include "support/Logger.h" 11 #include "support/Logger.h"
12 #include "llvm/ADT/StringExtras.h"
12 #include "llvm/Support/Errc.h" 13 #include "llvm/Support/Errc.h"
14 #include "llvm/Support/VirtualFileSystem.h"
15 #include <memory>
13 16
14 namespace clang { 17 namespace clang {
15 namespace clangd { 18 namespace clangd {
16 19
17 llvm::Optional<DraftStore::Draft> DraftStore::getDraft(PathRef File) const { 20 llvm::Optional<DraftStore::Draft> DraftStore::getDraft(PathRef File) const {
19 22
20 auto It = Drafts.find(File); 23 auto It = Drafts.find(File);
21 if (It == Drafts.end()) 24 if (It == Drafts.end())
22 return None; 25 return None;
23 26
24 return It->second; 27 return It->second.D;
25 } 28 }
26 29
27 std::vector<Path> DraftStore::getActiveFiles() const { 30 std::vector<Path> DraftStore::getActiveFiles() const {
28 std::lock_guard<std::mutex> Lock(Mutex); 31 std::lock_guard<std::mutex> Lock(Mutex);
29 std::vector<Path> ResultVector; 32 std::vector<Path> ResultVector;
32 ResultVector.push_back(std::string(DraftIt->getKey())); 35 ResultVector.push_back(std::string(DraftIt->getKey()));
33 36
34 return ResultVector; 37 return ResultVector;
35 } 38 }
36 39
37 static void updateVersion(DraftStore::Draft &D, 40 static void increment(std::string &S) {
38 llvm::Optional<int64_t> Version) { 41 // Ensure there is a numeric suffix.
39 if (Version) { 42 if (S.empty() || !llvm::isDigit(S.back())) {
40 // We treat versions as opaque, but the protocol says they increase. 43 S.push_back('0');
41 if (*Version <= D.Version) 44 return;
42 log("File version went from {0} to {1}", D.Version, Version); 45 }
43 D.Version = *Version; 46 // Increment the numeric suffix.
44 } else { 47 auto I = S.rbegin(), E = S.rend();
45 // Note that if D was newly-created, this will bump D.Version from -1 to 0. 48 for (;;) {
46 ++D.Version; 49 if (I == E || !llvm::isDigit(*I)) {
50 // Reached start of numeric section, it was all 9s.
51 S.insert(I.base(), '1');
52 break;
53 }
54 if (*I != '9') {
55 // Found a digit we can increment, we're done.
56 ++*I;
57 break;
58 }
59 *I = '0'; // and keep incrementing to the left.
47 } 60 }
48 } 61 }
49 62
50 int64_t DraftStore::addDraft(PathRef File, llvm::Optional<int64_t> Version, 63 static void updateVersion(DraftStore::Draft &D,
51 llvm::StringRef Contents) { 64 llvm::StringRef SpecifiedVersion) {
65 if (!SpecifiedVersion.empty()) {
66 // We treat versions as opaque, but the protocol says they increase.
67 if (SpecifiedVersion.compare_numeric(D.Version) <= 0)
68 log("File version went from {0} to {1}", D.Version, SpecifiedVersion);
69 D.Version = SpecifiedVersion.str();
70 } else {
71 // Note that if D was newly-created, this will bump D.Version from "" to 1.
72 increment(D.Version);
73 }
74 }
75
76 std::string DraftStore::addDraft(PathRef File, llvm::StringRef Version,
77 llvm::StringRef Contents) {
52 std::lock_guard<std::mutex> Lock(Mutex); 78 std::lock_guard<std::mutex> Lock(Mutex);
53 79
54 Draft &D = Drafts[File]; 80 auto &D = Drafts[File];
55 updateVersion(D, Version); 81 updateVersion(D.D, Version);
56 D.Contents = Contents.str(); 82 std::time(&D.MTime);
57 return D.Version; 83 D.D.Contents = std::make_shared<std::string>(Contents);
58 } 84 return D.D.Version;
59
60 llvm::Expected<DraftStore::Draft> DraftStore::updateDraft(
61 PathRef File, llvm::Optional<int64_t> Version,
62 llvm::ArrayRef<TextDocumentContentChangeEvent> Changes) {
63 std::lock_guard<std::mutex> Lock(Mutex);
64
65 auto EntryIt = Drafts.find(File);
66 if (EntryIt == Drafts.end()) {
67 return llvm::make_error<llvm::StringError>(
68 "Trying to do incremental update on non-added document: " + File,
69 llvm::errc::invalid_argument);
70 }
71 Draft &D = EntryIt->second;
72 std::string Contents = EntryIt->second.Contents;
73
74 for (const TextDocumentContentChangeEvent &Change : Changes) {
75 if (!Change.range) {
76 Contents = Change.text;
77 continue;
78 }
79
80 const Position &Start = Change.range->start;
81 llvm::Expected<size_t> StartIndex =
82 positionToOffset(Contents, Start, false);
83 if (!StartIndex)
84 return StartIndex.takeError();
85
86 const Position &End = Change.range->end;
87 llvm::Expected<size_t> EndIndex = positionToOffset(Contents, End, false);
88 if (!EndIndex)
89 return EndIndex.takeError();
90
91 if (*EndIndex < *StartIndex)
92 return llvm::make_error<llvm::StringError>(
93 llvm::formatv(
94 "Range's end position ({0}) is before start position ({1})", End,
95 Start),
96 llvm::errc::invalid_argument);
97
98 // Since the range length between two LSP positions is dependent on the
99 // contents of the buffer we compute the range length between the start and
100 // end position ourselves and compare it to the range length of the LSP
101 // message to verify the buffers of the client and server are in sync.
102
103 // EndIndex and StartIndex are in bytes, but Change.rangeLength is in UTF-16
104 // code units.
105 ssize_t ComputedRangeLength =
106 lspLength(Contents.substr(*StartIndex, *EndIndex - *StartIndex));
107
108 if (Change.rangeLength && ComputedRangeLength != *Change.rangeLength)
109 return llvm::make_error<llvm::StringError>(
110 llvm::formatv("Change's rangeLength ({0}) doesn't match the "
111 "computed range length ({1}).",
112 *Change.rangeLength, ComputedRangeLength),
113 llvm::errc::invalid_argument);
114
115 std::string NewContents;
116 NewContents.reserve(*StartIndex + Change.text.length() +
117 (Contents.length() - *EndIndex));
118
119 NewContents = Contents.substr(0, *StartIndex);
120 NewContents += Change.text;
121 NewContents += Contents.substr(*EndIndex);
122
123 Contents = std::move(NewContents);
124 }
125
126 updateVersion(D, Version);
127 D.Contents = std::move(Contents);
128 return D;
129 } 85 }
130 86
131 void DraftStore::removeDraft(PathRef File) { 87 void DraftStore::removeDraft(PathRef File) {
132 std::lock_guard<std::mutex> Lock(Mutex); 88 std::lock_guard<std::mutex> Lock(Mutex);
133 89
134 Drafts.erase(File); 90 Drafts.erase(File);
135 } 91 }
136 92
93 namespace {
94
95 /// A read only MemoryBuffer shares ownership of a ref counted string. The
96 /// shared string object must not be modified while an owned by this buffer.
97 class SharedStringBuffer : public llvm::MemoryBuffer {
98 const std::shared_ptr<const std::string> BufferContents;
99 const std::string Name;
100
101 public:
102 BufferKind getBufferKind() const override {
103 return MemoryBuffer::MemoryBuffer_Malloc;
104 }
105
106 StringRef getBufferIdentifier() const override { return Name; }
107
108 SharedStringBuffer(std::shared_ptr<const std::string> Data, StringRef Name)
109 : BufferContents(std::move(Data)), Name(Name) {
110 assert(BufferContents && "Can't create from empty shared_ptr");
111 MemoryBuffer::init(BufferContents->c_str(),
112 BufferContents->c_str() + BufferContents->size(),
113 /*RequiresNullTerminator=*/true);
114 }
115 };
116 } // namespace
117
118 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> DraftStore::asVFS() const {
119 auto MemFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
120 std::lock_guard<std::mutex> Guard(Mutex);
121 for (const auto &Draft : Drafts)
122 MemFS->addFile(Draft.getKey(), Draft.getValue().MTime,
123 std::make_unique<SharedStringBuffer>(
124 Draft.getValue().D.Contents, Draft.getKey()));
125 return MemFS;
126 }
137 } // namespace clangd 127 } // namespace clangd
138 } // namespace clang 128 } // namespace clang