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