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 clang; 15 using namespace clang::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 llvm::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, 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 if (Change.rangeLength && 81 (ssize_t)(*EndIndex - *StartIndex) != *Change.rangeLength) 82 return llvm::make_error<llvm::StringError>( 83 llvm::formatv("Change's rangeLength ({0}) doesn't match the " 84 "computed range length ({1}).", 85 *Change.rangeLength, *EndIndex - *StartIndex), 86 llvm::errc::invalid_argument); 87 88 std::string NewContents; 89 NewContents.reserve(*StartIndex + Change.text.length() + 90 (Contents.length() - *EndIndex)); 91 92 NewContents = Contents.substr(0, *StartIndex); 93 NewContents += Change.text; 94 NewContents += Contents.substr(*EndIndex); 95 96 Contents = std::move(NewContents); 97 } 98 99 EntryIt->second = Contents; 100 return Contents; 101 } 102 103 void DraftStore::removeDraft(PathRef File) { 104 std::lock_guard<std::mutex> Lock(Mutex); 105 106 Drafts.erase(File); 107 } 108