15#include "llvm/ADT/STLExtras.h"
16#include "llvm/ADT/StringMap.h"
17#include "llvm/ADT/StringRef.h"
18#include "llvm/Support/Error.h"
19#include "llvm/Support/JSON.h"
20#include "llvm/Support/Path.h"
21#include "llvm/Support/raw_ostream.h"
22#include "gtest/gtest.h"
23#include <condition_variable>
40 std::unique_lock<std::mutex> Lock(Mu);
41 static constexpr size_t TimeoutSecs = 60;
43 [
this] {
return Value.has_value(); })) {
44 ADD_FAILURE() <<
"No result from call after " << TimeoutSecs <<
" seconds!";
45 return llvm::json::Value(
nullptr);
47 auto Res = std::move(*
Value);
53 auto ExpValue =
take();
55 ADD_FAILURE() <<
"takeValue(): " << llvm::toString(ExpValue.takeError());
56 return llvm::json::Value(
nullptr);
58 return std::move(*ExpValue);
61void LSPClient::CallResult::set(llvm::Expected<llvm::json::Value> V) {
62 std::lock_guard<std::mutex> Lock(Mu);
64 ADD_FAILURE() <<
"Multiple replies";
65 llvm::consumeError(V.takeError());
74 ADD_FAILURE() << llvm::toString(
Value->takeError());
78static void logBody(llvm::StringRef
Method, llvm::json::Value V,
bool Send) {
80 vlog(
"{0} {1}: {2:2}", Send ?
"<<<" :
">>>",
Method, V);
86 std::lock_guard<std::mutex> Lock(Mu);
87 unsigned ID = CallResults.size();
88 CallResults.emplace_back();
89 return {
ID, &CallResults.back()};
94 std::lock_guard<std::mutex> Lock(Mu);
95 Actions.push(std::move(
Action));
100 std::vector<llvm::json::Value> Result;
102 std::lock_guard<std::mutex> Lock(Mu);
103 std::swap(Result, Notifications[
Method]);
109 std::lock_guard<std::mutex> Lock(Mu);
114 std::vector<llvm::json::Value> Result;
116 std::lock_guard<std::mutex> Lock(Mu);
117 std::swap(Result, Calls[
Method]);
123 void reply(llvm::json::Value
ID,
124 llvm::Expected<llvm::json::Value> V)
override {
127 std::lock_guard<std::mutex> Lock(Mu);
128 if (
auto I =
ID.getAsInteger()) {
129 if (*I >= 0 && *I <
static_cast<int64_t
>(CallResults.size())) {
130 CallResults[*I].set(std::move(V));
134 ADD_FAILURE() <<
"Invalid reply to ID " <<
ID;
135 llvm::consumeError(std::move(V).takeError());
138 void notify(llvm::StringRef Method, llvm::json::Value V)
override {
139 logBody(Method, V,
false);
140 std::lock_guard<std::mutex> Lock(Mu);
141 Notifications[Method].push_back(std::move(V));
144 void call(llvm::StringRef Method, llvm::json::Value Params,
145 llvm::json::Value
ID)
override {
146 logBody(Method, Params,
false);
147 std::lock_guard<std::mutex> Lock(Mu);
148 if (Calls.contains(Method)) {
149 Calls[
Method].push_back(std::move(Params));
151 ADD_FAILURE() <<
"Unexpected server->client call " <<
Method;
155 llvm::Error loop(MessageHandler &H)
override {
156 std::unique_lock<std::mutex> Lock(Mu);
158 CV.wait(Lock, [&] {
return !Actions.empty(); });
159 if (!Actions.front())
160 return llvm::Error::success();
161 auto Action = std::move(Actions.front());
170 std::deque<CallResult> CallResults;
171 std::queue<std::function<void(Transport::MessageHandler &)>> Actions;
172 std::condition_variable CV;
173 llvm::StringMap<std::vector<llvm::json::Value>> Notifications;
174 llvm::StringMap<std::vector<llvm::json::Value>> Calls;
181 llvm::json::Value Params) {
182 auto Slot = T->addCallSlot();
186 H.onCall(
Method, std::move(Params),
ID);
199 H.onNotify(
Method, std::move(Params));
203std::vector<llvm::json::Value>
205 return T->takeNotifications(
Method);
208std::vector<llvm::json::Value>
210 return T->takeCallParams(
Method);
217using Obj = llvm::json::Object;
221 if (!llvm::sys::path::is_absolute(
Path))
231 "textDocument/didOpen",
233 Obj{{
"uri",
uri(
Path)}, {
"text", Content}, {
"languageId",
"cpp"}}}});
236 notify(
"textDocument/didChange",
238 {
"contentChanges", llvm::json::Array{
Obj{{
"text", Content}}}}});
246std::optional<std::vector<llvm::json::Value>>
250 for (
const auto &
Notification : llvm::reverse(Notifications)) {
251 if (
const auto *PubDiagsParams =
Notification.getAsObject()) {
252 auto U = PubDiagsParams->getString(
"uri");
253 auto *D = PubDiagsParams->getArray(
"diagnostics");
255 ADD_FAILURE() <<
"Bad PublishDiagnosticsParams: " << PubDiagsParams;
259 return std::vector<llvm::json::Value>(D->begin(), D->end());
llvm::Expected< llvm::json::Value > take()
llvm::json::Value takeValue()
std::vector< llvm::json::Value > takeNotifications(llvm::StringRef Method)
void enqueue(std::function< void(MessageHandler &)> Action)
void expectCall(llvm::StringRef Method)
std::vector< llvm::json::Value > takeCallParams(llvm::StringRef Method)
std::pair< llvm::json::Value, CallResult * > addCallSlot()
CallResult & call(llvm::StringRef Method, llvm::json::Value Params)
std::vector< llvm::json::Value > takeNotifications(llvm::StringRef Method)
static llvm::json::Value uri(llvm::StringRef Path)
std::vector< llvm::json::Value > takeCallParams(llvm::StringRef Method)
static llvm::json::Value documentID(llvm::StringRef Path)
void didClose(llvm::StringRef Path)
void notify(llvm::StringRef Method, llvm::json::Value Params)
void expectServerCall(llvm::StringRef Method)
void didOpen(llvm::StringRef Path, llvm::StringRef Content)
void didChange(llvm::StringRef Path, llvm::StringRef Content)
std::optional< std::vector< llvm::json::Value > > diagnostics(llvm::StringRef Path)
A threadsafe flag that is initially clear.
std::string Path
A typedef to represent a file path.
void vlog(const char *Fmt, Ts &&... Vals)
static void logBody(llvm::StringRef Method, llvm::json::Value V, bool Send)
std::string testPath(PathRef File, llvm::sys::path::Style Style)
llvm::json::Value toJSON(const FuzzyFindRequest &Request)
void wait(std::unique_lock< std::mutex > &Lock, std::condition_variable &CV, Deadline D)
Wait once on CV for the specified duration.
Deadline timeoutSeconds(std::optional< double > Seconds)
Makes a deadline from a timeout in seconds. std::nullopt means wait forever.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Canonicalizes AbsPath via URI.