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 namespace clang { 15 namespace clangd { 16 17 llvm::Optional<std::string> DraftStore::getDraft(PathRef File) const { 18 std::lock_guard<std::mutex> Lock(Mutex); 19 20 auto It = Drafts.find(File); 21 if (It == Drafts.end()) 22 return None; 23 24 return It->second; 25 } 26 27 std::vector<Path> DraftStore::getActiveFiles() const { 28 std::lock_guard<std::mutex> Lock(Mutex); 29 std::vector<Path> ResultVector; 30 31 for (auto DraftIt = Drafts.begin(); DraftIt != Drafts.end(); DraftIt++) 32 ResultVector.push_back(DraftIt->getKey()); 33 34 return ResultVector; 35 } 36 37 void DraftStore::addDraft(PathRef File, llvm::StringRef Contents) { 38 std::lock_guard<std::mutex> Lock(Mutex); 39 40 Drafts[File] = Contents; 41 } 42 43 llvm::Expected<std::string> DraftStore::updateDraft( 44 PathRef File, llvm::ArrayRef<TextDocumentContentChangeEvent> Changes) { 45 std::lock_guard<std::mutex> Lock(Mutex); 46 47 auto EntryIt = Drafts.find(File); 48 if (EntryIt == Drafts.end()) { 49 return llvm::make_error<llvm::StringError>( 50 "Trying to do incremental update on non-added document: " + File, 51 llvm::errc::invalid_argument); 52 } 53 54 std::string Contents = EntryIt->second; 55 56 for (const TextDocumentContentChangeEvent &Change : Changes) { 57 if (!Change.range) { 58 Contents = Change.text; 59 continue; 60 } 61 62 const Position &Start = Change.range->start; 63 llvm::Expected<size_t> StartIndex = 64 positionToOffset(Contents, Start, false); 65 if (!StartIndex) 66 return StartIndex.takeError(); 67 68 const Position &End = Change.range->end; 69 llvm::Expected<size_t> EndIndex = positionToOffset(Contents, End, false); 70 if (!EndIndex) 71 return EndIndex.takeError(); 72 73 if (*EndIndex < *StartIndex) 74 return llvm::make_error<llvm::StringError>( 75 llvm::formatv( 76 "Range's end position ({0}) is before start position ({1})", End, 77 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 llvm::make_error<llvm::StringError>( 92 llvm::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