13 #include "llvm/ADT/SmallString.h" 14 #include "llvm/ADT/StringExtras.h" 15 #include "llvm/Support/Chrono.h" 16 #include "llvm/Support/Errno.h" 17 #include "llvm/Support/FormatVariadic.h" 18 #include "llvm/Support/JSON.h" 19 #include "llvm/Support/SourceMgr.h" 23 using namespace clang;
24 using namespace clangd;
33 RequestSpan(llvm::json::Object *Args) : Args(Args) {}
35 llvm::json::Object *Args;
41 return Context::current().
derive(
42 RSKey, std::unique_ptr<RequestSpan>(
new RequestSpan(Span.
Args)));
47 template <
typename Func>
static void attach(Func &&F) {
48 auto *RequestArgs = Context::current().
get(RSKey);
49 if (!RequestArgs || !*RequestArgs || !(*RequestArgs)->Args)
51 std::lock_guard<std::mutex> Lock((*RequestArgs)->Mu);
52 F(*(*RequestArgs)->Args);
58 void JSONOutput::writeMessage(
const json::Value &
Message) {
60 llvm::raw_string_ostream OS(S);
62 OS << llvm::formatv(
"{0:2}", Message);
68 std::lock_guard<std::mutex> Guard(StreamMutex);
69 Outs <<
"Content-Length: " << S.size() <<
"\r\n\r\n" << S;
76 const llvm::formatv_object_base &
Message) {
79 llvm::sys::TimePoint<> Timestamp = std::chrono::system_clock::now();
81 std::lock_guard<std::mutex> Guard(StreamMutex);
82 Logs << llvm::formatv(
"{0}[{1:%H:%M:%S.%L}] {2}\n", indicator(Level),
87 void JSONOutput::mirrorInput(
const Twine &
Message) {
96 auto ID = Context::current().get(RequestID);
98 elog(
"Attempted to reply to a notification!");
101 RequestSpan::attach([&](json::Object &Args) { Args[
"Reply"] = Result; });
102 log(
"--> reply({0})", *ID);
104 .getExisting(RequestOut)
105 ->writeMessage(json::Object{
108 {
"result", std::move(Result)},
113 elog(
"Error {0}: {1}", static_cast<int>(Code), Message);
114 RequestSpan::attach([&](json::Object &Args) {
115 Args[
"Error"] = json::Object{{
"code",
static_cast<int>(Code)},
116 {
"message", Message.str()}};
119 if (
auto ID = Context::current().get(RequestID)) {
120 log(
"--> reply({0}) error: {1}", *ID, Message);
122 .getExisting(RequestOut)
123 ->writeMessage(json::Object{
126 {
"error", json::Object{{
"code",
static_cast<int>(Code)},
127 {
"message", Message}}},
133 RequestSpan::attach([&](json::Object &Args) {
134 Args[
"Call"] = json::Object{{
"method", Method.str()}, {
"params", Params}};
139 log(
"--> {0}({1})", Method, ID);
141 .getExisting(RequestOut)
142 ->writeMessage(json::Object{
146 {
"params", std::move(Params)},
150 void JSONRPCDispatcher::registerHandler(StringRef Method,
Handler H) {
151 assert(!Handlers.count(Method) &&
"Handler already registered!");
152 Handlers[Method] = std::move(H);
156 llvm::Optional<StringRef> Method,
157 const json::Object *Error) {
160 log(
"<-- {0}({1})", *Method, *ID);
162 log(
"<-- {0}", *Method);
165 log(
"<-- reply({0}) error: {1}", *ID,
166 Error->getString(
"message").getValueOr(
"<no message>"));
168 log(
"<-- reply({0})", *ID);
175 auto *
Object = Message.getAsObject();
176 if (!
Object ||
Object->getString(
"jsonrpc") != Optional<StringRef>(
"2.0"))
179 llvm::Optional<json::Value> ID;
180 if (
auto *I =
Object->get(
"id"))
182 auto Method =
Object->getString(
"method");
187 json::Value Params =
nullptr;
188 if (
auto *P =
Object->get(
"params"))
189 Params = std::move(*P);
191 auto I = Handlers.find(*Method);
192 auto &
Handler = I != Handlers.end() ? I->second : UnknownHandler;
196 llvm::Optional<WithContextValue> WithID;
198 WithID.emplace(RequestID, *ID);
207 WithContext WithRequestSpan(RequestSpan::stash(Tracer));
214 static bool readLine(std::FILE *In, std::string &Out) {
215 static constexpr
int BufSize = 1024;
219 Out.resize(Size + BufSize);
221 if (!llvm::sys::RetryAfterSignal(
nullptr, ::fgets, &Out[Size], BufSize, In))
226 size_t Read = std::strlen(&Out[Size]);
227 if (Read > 0 && Out[Size + Read - 1] ==
'\n') {
228 Out.resize(Size + Read);
242 unsigned long long ContentLength = 0;
245 if (feof(In) || ferror(In) || !
readLine(In, Line))
249 llvm::StringRef LineRef(Line);
253 if (LineRef.startswith(
"#"))
257 if (LineRef.consume_front(
"Content-Length: ")) {
258 if (ContentLength != 0) {
259 elog(
"Warning: Duplicate Content-Length header received. " 260 "The previous value for this message ({0}) was ignored.",
263 llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength);
265 }
else if (!LineRef.trim().empty()) {
276 if (ContentLength > 1 << 30) {
277 elog(
"Refusing to read message with long Content-Length: {0}. " 278 "Expect protocol errors",
282 if (ContentLength == 0) {
283 log(
"Warning: Missing Content-Length header, or zero-length message.");
287 std::string JSON(ContentLength,
'\0');
290 Read = llvm::sys::RetryAfterSignal(0u, ::fread, &JSON[
Pos], 1,
291 ContentLength - Pos, In);
294 elog(
"Input was aborted. Read only {0} bytes of expected {1}.", Pos,
302 return std::move(JSON);
315 auto LineRef = llvm::StringRef(Line).trim();
316 if (LineRef.startswith(
"#"))
320 if (LineRef.rtrim() ==
"---")
327 elog(
"Input error while reading message!");
331 llvm::formatv(
"Content-Length: {0}\r\n\r\n{1}", JSON.size(), JSON));
332 return std::move(JSON);
349 while (!IsDone && !feof(In)) {
351 elog(
"IO error: {0}", llvm::sys::StrError());
354 if (
auto JSON = ReadMessage(In, Out)) {
355 if (
auto Doc = json::parse(*JSON)) {
357 vlog(Out.
Pretty ?
"<<< {0:2}\n" :
"<<< {0}\n", *Doc);
359 if (!Dispatcher.
call(*Doc, Out))
360 elog(
"JSON dispatch failed!");
363 vlog(
"<<< {0}\n", *JSON);
static llvm::Optional< std::string > readStandardMessage(std::FILE *In, JSONOutput &Out)
JSONStreamStyle
Controls the way JSON-RPC messages are encoded (both input and output).
Encapsulates output and logs streams and provides thread-safe access to them.
Some operations such as code completion produce a set of candidates.
static bool readLine(std::FILE *In, std::string &Out)
static void logIncomingMessage(const llvm::Optional< json::Value > &ID, llvm::Optional< StringRef > Method, const json::Object *Error)
void log(Logger::Level, const llvm::formatv_object_base &)
static llvm::Optional< std::string > readDelimitedMessage(std::FILE *In, JSONOutput &Out)
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
Values in a Context are indexed by typed keys.
static const StringRef Message
void vlog(const char *Fmt, Ts &&... Vals)
void elog(const char *Fmt, Ts &&... Vals)
const Type * get(const Key< Type > &Key) const
Get data stored for a typed Key.
void replyError(ErrorCode Code, const llvm::StringRef &Message)
Sends an error response to the client, and logs it.
void mirrorInput(const Twine &Message)
Mirror Message into InputMirror stream.
Messages are delimited by a '—' line. Comment lines start with #.
Main JSONRPC entry point.
A context is an immutable container for per-request data that must be propagated through layers that ...
WithContext replaces Context::current() with a provided scope.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::function< void(const llvm::json::Value &)> Handler
Context derive(const Key< Type > &Key, typename std::decay< Type >::type Value) const &
Derives a child context It is safe to move or destroy a parent context after calling derive()...
bool call(const llvm::json::Value &Message, JSONOutput &Out) const
Parses a JSONRPC message and calls the Handler for it.
void call(llvm::StringRef Method, llvm::json::Value &&Params)
Sends a request to the client.
WithContextValue extends Context::current() with a single value.
llvm::json::Object *const Args
Mutable metadata, if this span is interested.
Records an event whose duration is the lifetime of the Span object.
void reply(llvm::json::Value &&Result)
Sends a successful reply.
#define SPAN_ATTACH(S, Name, Expr)
Attach a key-value pair to a Span event.
void runLanguageServerLoop(std::FILE *In, JSONOutput &Out, JSONStreamStyle InputStyle, JSONRPCDispatcher &Dispatcher, bool &IsDone)
Parses input queries from LSP client (coming from In) and runs call method of Dispatcher for each que...
static llvm::cl::opt< JSONStreamStyle > InputStyle("input-style", llvm::cl::desc("Input JSON stream encoding"), llvm::cl::values(clEnumValN(JSONStreamStyle::Standard, "standard", "usual LSP protocol"), clEnumValN(JSONStreamStyle::Delimited, "delimited", "messages delimited by --- lines, with # comment support")), llvm::cl::init(JSONStreamStyle::Standard))