5#include <llvm/ADT/SmallString.h>
6#include <llvm/Support/CommandLine.h>
17#include <system_error>
21llvm::cl::opt<int> ClientProcessID{
24 "Client process ID, if this PID died, the server should exit."),
25 llvm::cl::init(getppid())};
27std::string jsonToString(llvm::json::Value &Message) {
29 llvm::raw_string_ostream OS(Result);
34class ReadEOF :
public llvm::ErrorInfo<ReadEOF> {
40 void log(llvm::raw_ostream &OS)
const override { __builtin_unreachable(); }
42 std::error_code convertToErrorCode()
const override {
43 return std::make_error_code(std::errc::io_error);
53static llvm::json::Object encodeError(llvm::Error
Error) {
56 auto HandlerFn = [&](
const LSPError &E) -> llvm::Error {
59 return llvm::Error::success();
61 if (llvm::Error Unhandled = llvm::handleErrors(std::move(
Error), HandlerFn))
62 Message = llvm::toString(std::move(Unhandled));
64 return llvm::json::Object{
65 {
"message", std::move(Message)},
66 {
"code", int64_t(Code)},
72 llvm::StringRef Message =
73 O.getString(
"message").value_or(
"Unspecified error");
74 if (std::optional<int64_t> Code = O.getInteger(
"code"))
75 return llvm::make_error<LSPError>(Message.str(),
ErrorCode(*Code));
76 return llvm::make_error<llvm::StringError>(llvm::inconvertibleErrorCode(),
84 {
"params", std::move(Params)},
88 llvm::json::Value ID) {
91 {
"id", std::move(ID)},
93 {
"params", std::move(Params)},
97 llvm::Expected<llvm::json::Value> Result) {
101 {
"id", std::move(ID)},
102 {
"result", std::move(*Result)},
107 {
"id", std::move(ID)},
108 {
"error", encodeError(Result.takeError())},
115 vlog(
">>> {0}", Message);
116 std::lock_guard<std::mutex> Guard(Mutex);
117 OutputBuffer.clear();
118 llvm::raw_svector_ostream SVecOS(OutputBuffer);
119 SVecOS << (Pretty ? llvm::formatv(
"{0:2}", Message)
120 : llvm::formatv(
"{0}", Message));
121 Outs <<
"Content-Length: " << OutputBuffer.size() <<
"\r\n\r\n"
128 auto *
Object = Message.getAsObject();
130 Object->getString(
"jsonrpc") != std::optional<llvm::StringRef>(
"2.0")) {
131 elog(
"Not a JSON-RPC 2.0 message: {0:2}", Message);
135 std::optional<llvm::json::Value> ID;
136 if (
auto *I =
Object->get(
"id"))
141 elog(
"No method and no response ID: {0:2}", Message);
144 if (
auto *Err =
Object->getObject(
"error"))
148 llvm::json::Value Result =
nullptr;
149 if (
auto *R =
Object->get(
"result"))
150 Result = std::move(*R);
151 return Handler.
onReply(std::move(*ID), std::move(Result));
154 llvm::json::Value Params =
nullptr;
155 if (
auto *P =
Object->get(
"params"))
156 Params = std::move(*P);
159 return Handler.
onCall(*
Method, std::move(Params), std::move(*ID));
163bool readLine(
int fd,
const std::atomic<bool> &Close,
164 llvm::SmallString<128> &Line) {
167 std::vector<pollfd> FDs;
168 FDs.emplace_back(pollfd{
176 int Poll = poll(FDs.data(), FDs.size(), 1000);
184 if (FDs[0].revents & (POLLHUP | POLLNVAL)) {
188 if (FDs[0].revents & POLLIN) {
189 ssize_t BytesRead = read(fd, &Ch, 1);
190 if (BytesRead == -1) {
193 }
else if (BytesRead == 0)
202 if (kill(ClientProcessID, 0) < 0) {
209llvm::Expected<llvm::json::Value>
211 unsigned long long ContentLength = 0;
212 llvm::SmallString<128> Line;
215 return llvm::make_error<ReadEOF>();
217 llvm::StringRef LineRef = Line;
220 if (LineRef.consume_front(
"Content-Length: ")) {
221 llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength);
226 if (LineRef.trim().empty())
231 Buffer.resize(ContentLength);
232 for (
size_t Pos = 0,
Read; Pos < ContentLength; Pos +=
Read) {
234 Read = read(
In, Buffer.data() + Pos, ContentLength - Pos);
237 elog(
"Input was aborted. Read only {0} bytes of expected {1}.", Pos,
239 return llvm::make_error<ReadEOF>();
242 return llvm::json::parse(Buffer);
245llvm::Expected<llvm::json::Value>
247 enum class State { Prose, JSONBlock, NixBlock };
248 State State = State::Prose;
250 llvm::SmallString<128> Line;
251 std::string NixDocURI;
253 auto LineRef = Line.str().trim();
254 if (State == State::Prose) {
255 if (LineRef.starts_with(
"```json"))
256 State = State::JSONBlock;
257 else if (LineRef.consume_front(
"```nix ")) {
258 State = State::NixBlock;
259 NixDocURI = LineRef.str();
261 }
else if (State == State::JSONBlock) {
263 if (LineRef.starts_with(
"#"))
267 if (LineRef.starts_with(
"```")) {
268 return llvm::json::parse(Buffer);
272 }
else if (State == State::NixBlock) {
277 if (LineRef.starts_with(
"```")) {
278 return llvm::json::Object{
280 {
"method",
"textDocument/didOpen"},
288 {
"languageId",
"nix"},
290 {
"text", llvm::StringRef(Buffer).rtrim().str()},
299 assert(
false &&
"unreachable");
302 return llvm::make_error<ReadEOF>();
305llvm::Expected<llvm::json::Value>
314 assert(
false &&
"Invalid stream style");
315 __builtin_unreachable();
323 vlog(
"<<< {0}", jsonToString(*Message));
329 auto Err = Message.takeError();
330 if (Err.isA<ReadEOF>())
332 else if (Err.isA<llvm::json::ParseError>())
333 elog(
"The received json cannot be parsed, reason: {0}", Err);
335 elog(
"Error reading message: {0}", llvm::toString(std::move(Err)));
llvm::Expected< llvm::json::Value > readMessage(std::string &Buffer)
bool dispatch(llvm::json::Value Message, MessageHandler &Handler)
JSONStreamStyle StreamStyle
llvm::Expected< llvm::json::Value > readStandardMessage(std::string &Buffer)
void loop(MessageHandler &Handler)
llvm::Expected< llvm::json::Value > readLitTestMessage(std::string &Buffer)
Read one message, expecting the input to be one of our Markdown lit-tests.
virtual bool onReply(llvm::json::Value ID, llvm::Expected< llvm::json::Value > Result)=0
virtual bool onNotify(llvm::StringRef Method, llvm::json::Value)=0
virtual bool onCall(llvm::StringRef Method, llvm::json::Value Params, llvm::json::Value ID)=0
void sendMessage(llvm::json::Value Message)
void notify(llvm::StringRef Method, llvm::json::Value Params)
void reply(llvm::json::Value ID, llvm::Expected< llvm::json::Value > Result)
void call(llvm::StringRef Method, llvm::json::Value Params, llvm::json::Value ID)
void log(Logger::Level L, const char *Fmt, Ts &&...Vals)
Whether current platform treats paths case insensitively.
llvm::Error decodeError(const llvm::json::Object &O)
Decode the given JSON object into an error.
void elog(const char *Fmt, Ts &&...Vals)
void vlog(const char *Fmt, Ts &&...Vals)
bool readLine(int fd, const std::atomic< bool > &Close, llvm::SmallString< 128 > &Line)