blob: 03867dcd286e3115bb6a64cf93a7571c5d4491e5 [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"
Sam McCallcaf5a4d2020-03-03 15:57:39 +010010#include "Logger.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
Sam McCallcaf5a4d2020-03-03 15:57:39 +010017llvm::Optional<DraftStore::Draft> 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++)
Benjamin Krameradcd0262020-01-28 20:23:46 +010032 ResultVector.push_back(std::string(DraftIt->getKey()));
Simon Marchi5178f922018-02-22 14:00:39 +000033
34 return ResultVector;
35}
36
Sam McCallcaf5a4d2020-03-03 15:57:39 +010037static void updateVersion(DraftStore::Draft &D,
38 llvm::Optional<int64_t> Version) {
39 if (Version) {
40 // We treat versions as opaque, but the protocol says they increase.
41 if (*Version <= D.Version)
42 log("File version went from {0} to {1}", D.Version, Version);
43 D.Version = *Version;
44 } else {
45 // Note that if D was newly-created, this will bump D.Version from -1 to 0.
46 ++D.Version;
47 }
Ilya Biryukov38d79772017-05-16 09:38:59 +000048}
49
Sam McCallcaf5a4d2020-03-03 15:57:39 +010050int64_t DraftStore::addDraft(PathRef File, llvm::Optional<int64_t> Version,
51 llvm::StringRef Contents) {
52 std::lock_guard<std::mutex> Lock(Mutex);
53
54 Draft &D = Drafts[File];
55 updateVersion(D, Version);
56 D.Contents = Contents.str();
57 return D.Version;
58}
59
60llvm::Expected<DraftStore::Draft> DraftStore::updateDraft(
61 PathRef File, llvm::Optional<int64_t> Version,
62 llvm::ArrayRef<TextDocumentContentChangeEvent> Changes) {
Simon Marchi98082622018-03-26 14:41:40 +000063 std::lock_guard<std::mutex> Lock(Mutex);
64
65 auto EntryIt = Drafts.find(File);
66 if (EntryIt == Drafts.end()) {
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000067 return llvm::make_error<llvm::StringError>(
Simon Marchi98082622018-03-26 14:41:40 +000068 "Trying to do incremental update on non-added document: " + File,
69 llvm::errc::invalid_argument);
70 }
Sam McCallcaf5a4d2020-03-03 15:57:39 +010071 Draft &D = EntryIt->second;
72 std::string Contents = EntryIt->second.Contents;
Simon Marchi98082622018-03-26 14:41:40 +000073
74 for (const TextDocumentContentChangeEvent &Change : Changes) {
75 if (!Change.range) {
76 Contents = Change.text;
77 continue;
78 }
79
80 const Position &Start = Change.range->start;
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000081 llvm::Expected<size_t> StartIndex =
82 positionToOffset(Contents, Start, false);
Simon Marchi98082622018-03-26 14:41:40 +000083 if (!StartIndex)
84 return StartIndex.takeError();
85
86 const Position &End = Change.range->end;
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000087 llvm::Expected<size_t> EndIndex = positionToOffset(Contents, End, false);
Simon Marchi98082622018-03-26 14:41:40 +000088 if (!EndIndex)
89 return EndIndex.takeError();
90
91 if (*EndIndex < *StartIndex)
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000092 return llvm::make_error<llvm::StringError>(
93 llvm::formatv(
94 "Range's end position ({0}) is before start position ({1})", End,
95 Start),
Simon Marchi98082622018-03-26 14:41:40 +000096 llvm::errc::invalid_argument);
97
Sam McCall71891122018-10-23 11:51:53 +000098 // Since the range length between two LSP positions is dependent on the
99 // contents of the buffer we compute the range length between the start and
100 // end position ourselves and compare it to the range length of the LSP
101 // message to verify the buffers of the client and server are in sync.
102
103 // EndIndex and StartIndex are in bytes, but Change.rangeLength is in UTF-16
104 // code units.
105 ssize_t ComputedRangeLength =
106 lspLength(Contents.substr(*StartIndex, *EndIndex - *StartIndex));
107
108 if (Change.rangeLength && ComputedRangeLength != *Change.rangeLength)
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000109 return llvm::make_error<llvm::StringError>(
110 llvm::formatv("Change's rangeLength ({0}) doesn't match the "
111 "computed range length ({1}).",
Sam McCall6b09e9c2019-08-05 08:14:13 +0000112 *Change.rangeLength, ComputedRangeLength),
Simon Marchi98082622018-03-26 14:41:40 +0000113 llvm::errc::invalid_argument);
114
115 std::string NewContents;
116 NewContents.reserve(*StartIndex + Change.text.length() +
117 (Contents.length() - *EndIndex));
118
119 NewContents = Contents.substr(0, *StartIndex);
120 NewContents += Change.text;
121 NewContents += Contents.substr(*EndIndex);
122
123 Contents = std::move(NewContents);
124 }
125
Sam McCallcaf5a4d2020-03-03 15:57:39 +0100126 updateVersion(D, Version);
127 D.Contents = std::move(Contents);
128 return D;
Simon Marchi98082622018-03-26 14:41:40 +0000129}
130
Simon Marchi9569fd52018-03-16 14:30:42 +0000131void DraftStore::removeDraft(PathRef File) {
Ilya Biryukov38d79772017-05-16 09:38:59 +0000132 std::lock_guard<std::mutex> Lock(Mutex);
133
Simon Marchi9569fd52018-03-16 14:30:42 +0000134 Drafts.erase(File);
Ilya Biryukov38d79772017-05-16 09:38:59 +0000135}
Sam McCallc008af62018-10-20 15:30:37 +0000136
137} // namespace clangd
138} // namespace clang