Mercurial > hg > CbC > CbC_llvm
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 |