1 //===--- DraftStore.cpp - File contents container ---------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "DraftStore.h" 11 #include "SourceCode.h" 12 #include "llvm/Support/Errc.h" 13 14 using namespace llvm; 15 namespace clang { 16 namespace clangd { 17 18 Optional<std::string> 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 None; 24 25 return It->second; 26 } 27 28 std::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(DraftIt->getKey()); 34 35 return ResultVector; 36 } 37 38 void DraftStore::addDraft(PathRef File, StringRef Contents) { 39 std::lock_guard<std::mutex> Lock(Mutex); 40 41 Drafts[File] = Contents; 42 } 43 44 Expected<std::string> 45 DraftStore::updateDraft(PathRef File, 46 ArrayRef<TextDocumentContentChangeEvent> Changes) { 47 std::lock_guard<std::mutex> Lock(Mutex); 48 49 auto EntryIt = Drafts.find(File); 50 if (EntryIt == Drafts.end()) { 51 return make_error<StringError>( 52 "Trying to do incremental update on non-added document: " + File, 53 llvm::errc::invalid_argument); 54 } 55 56 std::string Contents = EntryIt->second; 57 58 for (const TextDocumentContentChangeEvent &Change : Changes) { 59 if (!Change.range) { 60 Contents = Change.text; 61 continue; 62 } 63 64 const Position &Start = Change.range->start; 65 Expected<size_t> StartIndex = positionToOffset(Contents, Start, false); 66 if (!StartIndex) 67 return StartIndex.takeError(); 68 69 const Position &End = Change.range->end; 70 Expected<size_t> EndIndex = positionToOffset(Contents, End, false); 71 if (!EndIndex) 72 return EndIndex.takeError(); 73 74 if (*EndIndex < *StartIndex) 75 return make_error<StringError>( 76 formatv("Range's end position ({0}) is before start position ({1})", 77 End, Start), 78 llvm::errc::invalid_argument); 79 80 // Since the range length between two LSP positions is dependent on the 81 // contents of the buffer we compute the range length between the start and 82 // end position ourselves and compare it to the range length of the LSP 83 // message to verify the buffers of the client and server are in sync. 84 85 // EndIndex and StartIndex are in bytes, but Change.rangeLength is in UTF-16 86 // code units. 87 ssize_t ComputedRangeLength = 88 lspLength(Contents.substr(*StartIndex, *EndIndex - *StartIndex)); 89 90 if (Change.rangeLength && ComputedRangeLength != *Change.rangeLength) 91 return make_error<StringError>( 92 formatv("Change's rangeLength ({0}) doesn't match the " 93 "computed range length ({1}).", 94 *Change.rangeLength, *EndIndex - *StartIndex), 95 llvm::errc::invalid_argument); 96 97 std::string NewContents; 98 NewContents.reserve(*StartIndex + Change.text.length() + 99 (Contents.length() - *EndIndex)); 100 101 NewContents = Contents.substr(0, *StartIndex); 102 NewContents += Change.text; 103 NewContents += Contents.substr(*EndIndex); 104 105 Contents = std::move(NewContents); 106 } 107 108 EntryIt->second = Contents; 109 return Contents; 110 } 111 112 void DraftStore::removeDraft(PathRef File) { 113 std::lock_guard<std::mutex> Lock(Mutex); 114 115 Drafts.erase(File); 116 } 117 118 } // namespace clangd 119 } // namespace clang 120