blob: 9b6c1fc4052cd2b29356e7c878c7f2b1c2f053e5 [file] [log] [blame]
Ilya Biryukov38d79772017-05-16 09:38:59 +00001//===--- 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"
Simon Marchi98082622018-03-26 14:41:40 +000011#include "SourceCode.h"
12#include "llvm/Support/Errc.h"
Ilya Biryukov38d79772017-05-16 09:38:59 +000013
Sam McCallc008af62018-10-20 15:30:37 +000014namespace clang {
15namespace clangd {
Ilya Biryukov38d79772017-05-16 09:38:59 +000016
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000017llvm::Optional<std::string> DraftStore::getDraft(PathRef File) const {
Ilya Biryukov38d79772017-05-16 09:38:59 +000018 std::lock_guard<std::mutex> Lock(Mutex);
19
20 auto It = Drafts.find(File);
21 if (It == Drafts.end())
Sam McCallc008af62018-10-20 15:30:37 +000022 return None;
Simon Marchi9569fd52018-03-16 14:30:42 +000023
Ilya Biryukov38d79772017-05-16 09:38:59 +000024 return It->second;
25}
26
Simon Marchi5178f922018-02-22 14:00:39 +000027std::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++)
Simon Marchi9569fd52018-03-16 14:30:42 +000032 ResultVector.push_back(DraftIt->getKey());
Simon Marchi5178f922018-02-22 14:00:39 +000033
34 return ResultVector;
35}
36
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000037void DraftStore::addDraft(PathRef File, llvm::StringRef Contents) {
Ilya Biryukov38d79772017-05-16 09:38:59 +000038 std::lock_guard<std::mutex> Lock(Mutex);
39
Simon Marchi9569fd52018-03-16 14:30:42 +000040 Drafts[File] = Contents;
Ilya Biryukov38d79772017-05-16 09:38:59 +000041}
42
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000043llvm::Expected<std::string> DraftStore::updateDraft(
44 PathRef File, llvm::ArrayRef<TextDocumentContentChangeEvent> Changes) {
Simon Marchi98082622018-03-26 14:41:40 +000045 std::lock_guard<std::mutex> Lock(Mutex);
46
47 auto EntryIt = Drafts.find(File);
48 if (EntryIt == Drafts.end()) {
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000049 return llvm::make_error<llvm::StringError>(
Simon Marchi98082622018-03-26 14:41:40 +000050 "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;
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000063 llvm::Expected<size_t> StartIndex =
64 positionToOffset(Contents, Start, false);
Simon Marchi98082622018-03-26 14:41:40 +000065 if (!StartIndex)
66 return StartIndex.takeError();
67
68 const Position &End = Change.range->end;
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000069 llvm::Expected<size_t> EndIndex = positionToOffset(Contents, End, false);
Simon Marchi98082622018-03-26 14:41:40 +000070 if (!EndIndex)
71 return EndIndex.takeError();
72
73 if (*EndIndex < *StartIndex)
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000074 return llvm::make_error<llvm::StringError>(
75 llvm::formatv(
76 "Range's end position ({0}) is before start position ({1})", End,
77 Start),
Simon Marchi98082622018-03-26 14:41:40 +000078 llvm::errc::invalid_argument);
79
Sam McCall71891122018-10-23 11:51:53 +000080 // 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)
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000091 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),
Simon Marchi98082622018-03-26 14:41:40 +000095 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
Simon Marchi9569fd52018-03-16 14:30:42 +0000112void DraftStore::removeDraft(PathRef File) {
Ilya Biryukov38d79772017-05-16 09:38:59 +0000113 std::lock_guard<std::mutex> Lock(Mutex);
114
Simon Marchi9569fd52018-03-16 14:30:42 +0000115 Drafts.erase(File);
Ilya Biryukov38d79772017-05-16 09:38:59 +0000116}
Sam McCallc008af62018-10-20 15:30:37 +0000117
118} // namespace clangd
119} // namespace clang