clang-tools  11.0.0
DraftStore.cpp
Go to the documentation of this file.
1 //===--- DraftStore.cpp - File contents container ---------------*- C++ -*-===//
2 //
3 // 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
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "DraftStore.h"
10 #include "SourceCode.h"
11 #include "support/Logger.h"
12 #include "llvm/Support/Errc.h"
13 
14 namespace clang {
15 namespace clangd {
16 
17 llvm::Optional<DraftStore::Draft> 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 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(std::string(DraftIt->getKey()));
33 
34  return ResultVector;
35 }
36 
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  }
48 }
49 
50 int64_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 
60 llvm::Expected<DraftStore::Draft> DraftStore::updateDraft(
61  PathRef File, llvm::Optional<int64_t> Version,
62  llvm::ArrayRef<TextDocumentContentChangeEvent> Changes) {
63  std::lock_guard<std::mutex> Lock(Mutex);
64 
65  auto EntryIt = Drafts.find(File);
66  if (EntryIt == Drafts.end()) {
67  return llvm::make_error<llvm::StringError>(
68  "Trying to do incremental update on non-added document: " + File,
69  llvm::errc::invalid_argument);
70  }
71  Draft &D = EntryIt->second;
72  std::string Contents = EntryIt->second.Contents;
73 
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;
81  llvm::Expected<size_t> StartIndex =
82  positionToOffset(Contents, Start, false);
83  if (!StartIndex)
84  return StartIndex.takeError();
85 
86  const Position &End = Change.range->end;
87  llvm::Expected<size_t> EndIndex = positionToOffset(Contents, End, false);
88  if (!EndIndex)
89  return EndIndex.takeError();
90 
91  if (*EndIndex < *StartIndex)
92  return llvm::make_error<llvm::StringError>(
93  llvm::formatv(
94  "Range's end position ({0}) is before start position ({1})", End,
95  Start),
96  llvm::errc::invalid_argument);
97 
98  // 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)
109  return llvm::make_error<llvm::StringError>(
110  llvm::formatv("Change's rangeLength ({0}) doesn't match the "
111  "computed range length ({1}).",
112  *Change.rangeLength, ComputedRangeLength),
113  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 
126  updateVersion(D, Version);
127  D.Contents = std::move(Contents);
128  return D;
129 }
130 
132  std::lock_guard<std::mutex> Lock(Mutex);
133 
134  Drafts.erase(File);
135 }
136 
137 } // namespace clangd
138 } // namespace clang
clang::clangd::lspLength
size_t lspLength(llvm::StringRef Code)
Definition: SourceCode.cpp:151
Contents
llvm::StringRef Contents
Definition: DraftStoreTests.cpp:22
DraftStore.h
clang::clangd::TextDocumentSyncKind::None
Documents should not be synced at all.
Changes
tooling::Replacements Changes
Definition: Format.cpp:109
clang::clangd::DraftStore::removeDraft
void removeDraft(PathRef File)
Remove the draft from the store.
Definition: DraftStore.cpp:131
clang::clangd::DraftStore::addDraft
int64_t addDraft(PathRef File, llvm::Optional< int64_t > Version, StringRef Contents)
Replace contents of the draft for File with Contents.
Definition: DraftStore.cpp:50
clang::clangd::Position
Definition: Protocol.h:144
Logger.h
clang::clangd::DraftStore::getActiveFiles
std::vector< Path > getActiveFiles() const
Definition: DraftStore.cpp:27
clang::clangd::DraftStore::Draft::Contents
std::string Contents
Definition: DraftStore.h:31
clang::clangd::positionToOffset
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
Definition: SourceCode.cpp:175
clang::clangd::DraftStore::getDraft
llvm::Optional< Draft > getDraft(PathRef File) const
Definition: DraftStore.cpp:17
clang::clangd::DraftStore::Draft::Version
int64_t Version
Definition: DraftStore.h:32
clang::clangd::DraftStore::Draft
Definition: DraftStore.h:30
clang::clangd::log
void log(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:62
clang::clangd::updateVersion
static void updateVersion(DraftStore::Draft &D, llvm::Optional< int64_t > Version)
Definition: DraftStore.cpp:37
SourceCode.h
clang::clangd::CompletionItemKind::File
clang::clangd::PathRef
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:23
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::TextDocumentContentChangeEvent
Definition: Protocol.h:667
clang::clangd::DraftStore::updateDraft
llvm::Expected< Draft > updateDraft(PathRef File, llvm::Optional< int64_t > Version, llvm::ArrayRef< TextDocumentContentChangeEvent > Changes)
Update the contents of the draft for File based on Changes.
Definition: DraftStore.cpp:60