blob: 16d7ddeeefc378b7fcbf21ffa16f489900abc2e6 [file] [log] [blame]
Ilya Biryukov38d79772017-05-16 09:38:59 +00001//===--- DraftStore.cpp - File contents container ---------------*- C++ -*-===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Ilya Biryukov38d79772017-05-16 09:38:59 +00006//
7//===----------------------------------------------------------------------===//
8
9#include "DraftStore.h"
Simon Marchi98082622018-03-26 14:41:40 +000010#include "SourceCode.h"
11#include "llvm/Support/Errc.h"
Ilya Biryukov38d79772017-05-16 09:38:59 +000012
Sam McCallc008af62018-10-20 15:30:37 +000013namespace clang {
14namespace clangd {
Ilya Biryukov38d79772017-05-16 09:38:59 +000015
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000016llvm::Optional<std::string> DraftStore::getDraft(PathRef File) const {
Ilya Biryukov38d79772017-05-16 09:38:59 +000017 std::lock_guard<std::mutex> Lock(Mutex);
18
19 auto It = Drafts.find(File);
20 if (It == Drafts.end())
Sam McCallc008af62018-10-20 15:30:37 +000021 return None;
Simon Marchi9569fd52018-03-16 14:30:42 +000022
Ilya Biryukov38d79772017-05-16 09:38:59 +000023 return It->second;
24}
25
Simon Marchi5178f922018-02-22 14:00:39 +000026std::vector<Path> DraftStore::getActiveFiles() const {
27 std::lock_guard<std::mutex> Lock(Mutex);
28 std::vector<Path> ResultVector;
29
30 for (auto DraftIt = Drafts.begin(); DraftIt != Drafts.end(); DraftIt++)
Simon Marchi9569fd52018-03-16 14:30:42 +000031 ResultVector.push_back(DraftIt->getKey());
Simon Marchi5178f922018-02-22 14:00:39 +000032
33 return ResultVector;
34}
35
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000036void DraftStore::addDraft(PathRef File, llvm::StringRef Contents) {
Ilya Biryukov38d79772017-05-16 09:38:59 +000037 std::lock_guard<std::mutex> Lock(Mutex);
38
Simon Marchi9569fd52018-03-16 14:30:42 +000039 Drafts[File] = Contents;
Ilya Biryukov38d79772017-05-16 09:38:59 +000040}
41
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000042llvm::Expected<std::string> DraftStore::updateDraft(
43 PathRef File, llvm::ArrayRef<TextDocumentContentChangeEvent> Changes) {
Simon Marchi98082622018-03-26 14:41:40 +000044 std::lock_guard<std::mutex> Lock(Mutex);
45
46 auto EntryIt = Drafts.find(File);
47 if (EntryIt == Drafts.end()) {
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000048 return llvm::make_error<llvm::StringError>(
Simon Marchi98082622018-03-26 14:41:40 +000049 "Trying to do incremental update on non-added document: " + File,
50 llvm::errc::invalid_argument);
51 }
52
53 std::string Contents = EntryIt->second;
54
55 for (const TextDocumentContentChangeEvent &Change : Changes) {
56 if (!Change.range) {
57 Contents = Change.text;
58 continue;
59 }
60
61 const Position &Start = Change.range->start;
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000062 llvm::Expected<size_t> StartIndex =
63 positionToOffset(Contents, Start, false);
Simon Marchi98082622018-03-26 14:41:40 +000064 if (!StartIndex)
65 return StartIndex.takeError();
66
67 const Position &End = Change.range->end;
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000068 llvm::Expected<size_t> EndIndex = positionToOffset(Contents, End, false);
Simon Marchi98082622018-03-26 14:41:40 +000069 if (!EndIndex)
70 return EndIndex.takeError();
71
72 if (*EndIndex < *StartIndex)
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000073 return llvm::make_error<llvm::StringError>(
74 llvm::formatv(
75 "Range's end position ({0}) is before start position ({1})", End,
76 Start),
Simon Marchi98082622018-03-26 14:41:40 +000077 llvm::errc::invalid_argument);
78
Sam McCall71891122018-10-23 11:51:53 +000079 // Since the range length between two LSP positions is dependent on the
80 // contents of the buffer we compute the range length between the start and
81 // end position ourselves and compare it to the range length of the LSP
82 // message to verify the buffers of the client and server are in sync.
83
84 // EndIndex and StartIndex are in bytes, but Change.rangeLength is in UTF-16
85 // code units.
86 ssize_t ComputedRangeLength =
87 lspLength(Contents.substr(*StartIndex, *EndIndex - *StartIndex));
88
89 if (Change.rangeLength && ComputedRangeLength != *Change.rangeLength)
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000090 return llvm::make_error<llvm::StringError>(
91 llvm::formatv("Change's rangeLength ({0}) doesn't match the "
92 "computed range length ({1}).",
93 *Change.rangeLength, *EndIndex - *StartIndex),
Simon Marchi98082622018-03-26 14:41:40 +000094 llvm::errc::invalid_argument);
95
96 std::string NewContents;
97 NewContents.reserve(*StartIndex + Change.text.length() +
98 (Contents.length() - *EndIndex));
99
100 NewContents = Contents.substr(0, *StartIndex);
101 NewContents += Change.text;
102 NewContents += Contents.substr(*EndIndex);
103
104 Contents = std::move(NewContents);
105 }
106
107 EntryIt->second = Contents;
108 return Contents;
109}
110
Simon Marchi9569fd52018-03-16 14:30:42 +0000111void DraftStore::removeDraft(PathRef File) {
Ilya Biryukov38d79772017-05-16 09:38:59 +0000112 std::lock_guard<std::mutex> Lock(Mutex);
113
Simon Marchi9569fd52018-03-16 14:30:42 +0000114 Drafts.erase(File);
Ilya Biryukov38d79772017-05-16 09:38:59 +0000115}
Sam McCallc008af62018-10-20 15:30:37 +0000116
117} // namespace clangd
118} // namespace clang