clang-tools  14.0.0git
LSPClient.cpp
Go to the documentation of this file.
1 #include "LSPClient.h"
2 #include "gtest/gtest.h"
3 #include <condition_variable>
4 
5 #include "Protocol.h"
6 #include "TestFS.h"
7 #include "Transport.h"
8 #include "support/Threading.h"
9 #include "llvm/Support/Path.h"
10 #include "llvm/Support/raw_ostream.h"
11 #include <queue>
12 
13 namespace clang {
14 namespace clangd {
15 
16 llvm::Expected<llvm::json::Value> clang::clangd::LSPClient::CallResult::take() {
17  std::unique_lock<std::mutex> Lock(Mu);
18  if (!clangd::wait(Lock, CV, timeoutSeconds(10),
19  [this] { return Value.hasValue(); })) {
20  ADD_FAILURE() << "No result from call after 10 seconds!";
21  return llvm::json::Value(nullptr);
22  }
23  auto Res = std::move(*Value);
24  Value.reset();
25  return Res;
26 }
27 
29  auto ExpValue = take();
30  if (!ExpValue) {
31  ADD_FAILURE() << "takeValue(): " << llvm::toString(ExpValue.takeError());
32  return llvm::json::Value(nullptr);
33  }
34  return std::move(*ExpValue);
35 }
36 
37 void LSPClient::CallResult::set(llvm::Expected<llvm::json::Value> V) {
38  std::lock_guard<std::mutex> Lock(Mu);
39  if (Value) {
40  ADD_FAILURE() << "Multiple replies";
41  llvm::consumeError(V.takeError());
42  return;
43  }
44  Value = std::move(V);
45  CV.notify_all();
46 }
47 
49  if (Value && !*Value) {
50  ADD_FAILURE() << llvm::toString(Value->takeError());
51  }
52 }
53 
54 static void logBody(llvm::StringRef Method, llvm::json::Value V, bool Send) {
55  // We invert <<< and >>> as the combined log is from the server's viewpoint.
56  vlog("{0} {1}: {2:2}", Send ? "<<<" : ">>>", Method, V);
57 }
58 
60 public:
61  std::pair<llvm::json::Value, CallResult *> addCallSlot() {
62  std::lock_guard<std::mutex> Lock(Mu);
63  unsigned ID = CallResults.size();
64  CallResults.emplace_back();
65  return {ID, &CallResults.back()};
66  }
67 
68  // A null action causes the transport to shut down.
69  void enqueue(std::function<void(MessageHandler &)> Action) {
70  std::lock_guard<std::mutex> Lock(Mu);
71  Actions.push(std::move(Action));
72  CV.notify_all();
73  }
74 
75  std::vector<llvm::json::Value> takeNotifications(llvm::StringRef Method) {
76  std::vector<llvm::json::Value> Result;
77  {
78  std::lock_guard<std::mutex> Lock(Mu);
79  std::swap(Result, Notifications[Method]);
80  }
81  return Result;
82  }
83 
84 private:
85  void reply(llvm::json::Value ID,
86  llvm::Expected<llvm::json::Value> V) override {
87  if (V) // Nothing additional to log for error.
88  logBody("reply", *V, /*Send=*/false);
89  std::lock_guard<std::mutex> Lock(Mu);
90  if (auto I = ID.getAsInteger()) {
91  if (*I >= 0 && *I < static_cast<int64_t>(CallResults.size())) {
92  CallResults[*I].set(std::move(V));
93  return;
94  }
95  }
96  ADD_FAILURE() << "Invalid reply to ID " << ID;
97  llvm::consumeError(std::move(V).takeError());
98  }
99 
100  void notify(llvm::StringRef Method, llvm::json::Value V) override {
101  logBody(Method, V, /*Send=*/false);
102  std::lock_guard<std::mutex> Lock(Mu);
103  Notifications[Method].push_back(std::move(V));
104  }
105 
106  void call(llvm::StringRef Method, llvm::json::Value Params,
107  llvm::json::Value ID) override {
108  logBody(Method, Params, /*Send=*/false);
109  ADD_FAILURE() << "Unexpected server->client call " << Method;
110  }
111 
112  llvm::Error loop(MessageHandler &H) override {
113  std::unique_lock<std::mutex> Lock(Mu);
114  while (true) {
115  CV.wait(Lock, [&] { return !Actions.empty(); });
116  if (!Actions.front()) // Stop!
117  return llvm::Error::success();
118  auto Action = std::move(Actions.front());
119  Actions.pop();
120  Lock.unlock();
121  Action(H);
122  Lock.lock();
123  }
124  }
125 
126  std::mutex Mu;
127  std::deque<CallResult> CallResults;
128  std::queue<std::function<void(Transport::MessageHandler &)>> Actions;
129  std::condition_variable CV;
130  llvm::StringMap<std::vector<llvm::json::Value>> Notifications;
131 };
132 
133 LSPClient::LSPClient() : T(std::make_unique<TransportImpl>()) {}
134 LSPClient::~LSPClient() = default;
135 
136 LSPClient::CallResult &LSPClient::call(llvm::StringRef Method,
137  llvm::json::Value Params) {
138  auto Slot = T->addCallSlot();
139  T->enqueue([ID(Slot.first), Method(Method.str()),
140  Params(std::move(Params))](Transport::MessageHandler &H) {
141  logBody(Method, Params, /*Send=*/true);
142  H.onCall(Method, std::move(Params), ID);
143  });
144  return *Slot.second;
145 }
146 
147 void LSPClient::notify(llvm::StringRef Method, llvm::json::Value Params) {
148  T->enqueue([Method(Method.str()),
149  Params(std::move(Params))](Transport::MessageHandler &H) {
150  logBody(Method, Params, /*Send=*/true);
151  H.onNotify(Method, std::move(Params));
152  });
153 }
154 
155 std::vector<llvm::json::Value>
156 LSPClient::takeNotifications(llvm::StringRef Method) {
157  return T->takeNotifications(Method);
158 }
159 
160 void LSPClient::stop() { T->enqueue(nullptr); }
161 
163 
164 using Obj = llvm::json::Object;
165 
167  std::string Storage;
168  if (!llvm::sys::path::is_absolute(Path))
169  Path = Storage = testPath(Path);
171 }
173  return Obj{{"uri", uri(Path)}};
174 }
175 
176 void LSPClient::didOpen(llvm::StringRef Path, llvm::StringRef Content) {
177  notify(
178  "textDocument/didOpen",
179  Obj{{"textDocument",
180  Obj{{"uri", uri(Path)}, {"text", Content}, {"languageId", "cpp"}}}});
181 }
182 void LSPClient::didChange(llvm::StringRef Path, llvm::StringRef Content) {
183  notify("textDocument/didChange",
184  Obj{{"textDocument", documentID(Path)},
185  {"contentChanges", llvm::json::Array{Obj{{"text", Content}}}}});
186 }
187 void LSPClient::didClose(llvm::StringRef Path) {
188  notify("textDocument/didClose", Obj{{"textDocument", documentID(Path)}});
189 }
190 
191 void LSPClient::sync() { call("sync", nullptr).takeValue(); }
192 
193 llvm::Optional<std::vector<llvm::json::Value>>
194 LSPClient::diagnostics(llvm::StringRef Path) {
195  sync();
196  auto Notifications = takeNotifications("textDocument/publishDiagnostics");
197  for (const auto &Notification : llvm::reverse(Notifications)) {
198  if (const auto *PubDiagsParams = Notification.getAsObject()) {
199  auto U = PubDiagsParams->getString("uri");
200  auto *D = PubDiagsParams->getArray("diagnostics");
201  if (!U || !D) {
202  ADD_FAILURE() << "Bad PublishDiagnosticsParams: " << PubDiagsParams;
203  continue;
204  }
205  if (*U == uri(Path))
206  return std::vector<llvm::json::Value>(D->begin(), D->end());
207  }
208  }
209  return {};
210 }
211 
212 } // namespace clangd
213 } // namespace clang
clang::clangd::LSPClient::didOpen
void didOpen(llvm::StringRef Path, llvm::StringRef Content)
Definition: LSPClient.cpp:176
clang::clangd::LSPClient::diagnostics
llvm::Optional< std::vector< llvm::json::Value > > diagnostics(llvm::StringRef Path)
Definition: LSPClient.cpp:194
clang::clangd::LSPClient::CallResult::takeValue
llvm::json::Value takeValue()
Definition: LSPClient.cpp:28
LSPClient.h
clang::clangd::timeoutSeconds
Deadline timeoutSeconds(llvm::Optional< double > Seconds)
Makes a deadline from a timeout in seconds. None means wait forever.
Definition: Threading.cpp:105
clang::clangd::LSPClient::TransportImpl::enqueue
void enqueue(std::function< void(MessageHandler &)> Action)
Definition: LSPClient.cpp:69
clang::clangd::LSPClient::CallResult::~CallResult
~CallResult()
Definition: LSPClient.cpp:48
clang::clangd::Obj
llvm::json::Object Obj
Definition: LSPClient.cpp:164
clang::clangd::testPath
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:82
clang::clangd::Path
std::string Path
A typedef to represent a file path.
Definition: Path.h:26
clang::clangd::LSPClient::TransportImpl
Definition: LSPClient.cpp:59
clang::clangd::LSPClient::TransportImpl::addCallSlot
std::pair< llvm::json::Value, CallResult * > addCallSlot()
Definition: LSPClient.cpp:61
clang::clangd::LSPClient::takeNotifications
std::vector< llvm::json::Value > takeNotifications(llvm::StringRef Method)
Definition: LSPClient.cpp:156
clang::clangd::LSPClient::LSPClient
LSPClient()
Definition: LSPClient.cpp:133
Action
llvm::unique_function< void()> Action
Definition: TUScheduler.cpp:596
clang::clangd::LSPClient::CallResult
Definition: LSPClient.h:29
Protocol.h
clang::clangd::LSPClient::~LSPClient
~LSPClient()
clang::clangd::wait
void wait(std::unique_lock< std::mutex > &Lock, std::condition_variable &CV, Deadline D)
Wait once on CV for the specified duration.
Definition: Threading.cpp:113
clang::clangd::Notification
A threadsafe flag that is initially clear.
Definition: Threading.h:28
ns1::ns2::D
@ D
Definition: CategoricalFeature.h:3
clang::clangd::toJSON
llvm::json::Value toJSON(const FuzzyFindRequest &Request)
Definition: Index.cpp:49
TestFS.h
Threading.h
clang::clangd::LSPClient::notify
void notify(llvm::StringRef Method, llvm::json::Value Params)
Definition: LSPClient.cpp:147
clang::clangd::LSPClient::call
CallResult & call(llvm::StringRef Method, llvm::json::Value Params)
Definition: LSPClient.cpp:136
clang::clangd::LSPClient::didChange
void didChange(llvm::StringRef Path, llvm::StringRef Content)
Definition: LSPClient.cpp:182
clang::clangd::vlog
void vlog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:73
clang::clangd::CompletionItemKind::Method
@ Method
clang::clangd::Transport::MessageHandler
Definition: Transport.h:47
clang::clangd::Transport
Definition: Transport.h:35
clang::clangd::LSPClient::TransportImpl::takeNotifications
std::vector< llvm::json::Value > takeNotifications(llvm::StringRef Method)
Definition: LSPClient.cpp:75
Transport.h
clang::clangd::LSPClient::didClose
void didClose(llvm::StringRef Path)
Definition: LSPClient.cpp:187
clang::clangd::logBody
static void logBody(llvm::StringRef Method, llvm::json::Value V, bool Send)
Definition: LSPClient.cpp:54
clang::clangd::LSPClient::transport
Transport & transport()
Definition: LSPClient.cpp:162
ID
static char ID
Definition: Logger.cpp:74
clang::clangd::LSPClient::documentID
static llvm::json::Value documentID(llvm::StringRef Path)
Definition: LSPClient.cpp:172
clang::clangd::LSPClient::sync
void sync()
Definition: LSPClient.cpp:191
clang::clangd::URIForFile::canonicalize
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Canonicalizes AbsPath via URI.
Definition: Protocol.cpp:48
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::cppcoreguidelines::toString
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
Definition: SpecialMemberFunctionsCheck.cpp:55
clang::clangd::LSPClient::CallResult::take
llvm::Expected< llvm::json::Value > take()
Definition: LSPClient.cpp:16
clang::clangd::LSPClient::stop
void stop()
Definition: LSPClient.cpp:160
clang::clangd::LSPClient::uri
static llvm::json::Value uri(llvm::StringRef Path)
Definition: LSPClient.cpp:166
Value
static constexpr bool Value
Definition: SuspiciousCallArgumentCheck.cpp:72