Skip to content

Commit dcb202d

Browse files
committed
[mlir-lsp] Abstract input and output of the JSONTransport
The patch abstracts sending and receiving json messages of `JSONTransport` to allow custom implementation of them. For example, one concrete implementation can use pipes without a need to convert file descriptor to a `FILE` object.
1 parent b8337bc commit dcb202d

File tree

2 files changed

+90
-31
lines changed

2 files changed

+90
-31
lines changed

mlir/include/mlir/Tools/lsp-server-support/Transport.h

Lines changed: 78 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,86 @@ enum JSONStreamStyle {
4343
Delimited
4444
};
4545

46+
/// An abstract class used by the JSONTransport to read JSON message.
47+
class JSONTransportInput {
48+
public:
49+
explicit JSONTransportInput(JSONStreamStyle style = JSONStreamStyle::Standard)
50+
: style(style) {}
51+
virtual ~JSONTransportInput() = default;
52+
53+
virtual bool getError() const = 0;
54+
virtual bool isEndOfInput() const = 0;
55+
56+
/// Read in a message from the input stream.
57+
virtual LogicalResult readMessage(std::string &json) {
58+
return style == JSONStreamStyle::Delimited ? readDelimitedMessage(json)
59+
: readStandardMessage(json);
60+
}
61+
virtual LogicalResult readDelimitedMessage(std::string &json) = 0;
62+
virtual LogicalResult readStandardMessage(std::string &json) = 0;
63+
64+
private:
65+
/// The JSON stream style to use.
66+
JSONStreamStyle style;
67+
};
68+
69+
/// An abstract class used by the JSONTransport to write JSON messages.
70+
class JSONTransportOutput {
71+
public:
72+
explicit JSONTransportOutput() = default;
73+
virtual ~JSONTransportOutput() = default;
74+
75+
virtual void sendMessage(const llvm::json::Value &msg) = 0;
76+
};
77+
78+
/// Concrete implementation of the JSONTransportInput that reads from a file.
79+
class JSONTransportInputOverFile : public JSONTransportInput {
80+
public:
81+
explicit JSONTransportInputOverFile(
82+
std::FILE *in, JSONStreamStyle style = JSONStreamStyle::Standard)
83+
: JSONTransportInput(style), in(in) {}
84+
85+
bool getError() const final { return ferror(in); }
86+
bool isEndOfInput() const final { return feof(in); }
87+
88+
LogicalResult readDelimitedMessage(std::string &json) final;
89+
LogicalResult readStandardMessage(std::string &json) final;
90+
91+
private:
92+
std::FILE *in;
93+
};
94+
95+
/// Concrete implementation of the JSONTransportOutput that writes to a stream.
96+
class JSONTransportOutputOverStream : public JSONTransportOutput {
97+
public:
98+
explicit JSONTransportOutputOverStream(raw_ostream &out,
99+
bool prettyOutput = false)
100+
: JSONTransportOutput(), out(out), prettyOutput(prettyOutput) {}
101+
102+
/// Writes the given message to the output stream.
103+
void sendMessage(const llvm::json::Value &msg) final;
104+
105+
private:
106+
SmallVector<char, 0> outputBuffer;
107+
raw_ostream &out;
108+
/// If the output JSON should be formatted for easier readability.
109+
bool prettyOutput;
110+
};
111+
46112
/// A transport class that performs the JSON-RPC communication with the LSP
47113
/// client.
48114
class JSONTransport {
49115
public:
116+
JSONTransport(std::unique_ptr<JSONTransportInput> in,
117+
std::unique_ptr<JSONTransportOutput> out)
118+
: in(std::move(in)), out(std::move(out)) {}
119+
50120
JSONTransport(std::FILE *in, raw_ostream &out,
51121
JSONStreamStyle style = JSONStreamStyle::Standard,
52122
bool prettyOutput = false)
53-
: in(in), out(out), style(style), prettyOutput(prettyOutput) {}
123+
: in(std::make_unique<JSONTransportInputOverFile>(in, style)),
124+
out(std::make_unique<JSONTransportOutputOverStream>(out,
125+
prettyOutput)) {}
54126

55127
/// The following methods are used to send a message to the LSP client.
56128
void notify(StringRef method, llvm::json::Value params);
@@ -60,30 +132,15 @@ class JSONTransport {
60132
/// Start executing the JSON-RPC transport.
61133
llvm::Error run(MessageHandler &handler);
62134

63-
private:
64135
/// Dispatches the given incoming json message to the message handler.
65136
bool handleMessage(llvm::json::Value msg, MessageHandler &handler);
66-
/// Writes the given message to the output stream.
67-
void sendMessage(llvm::json::Value msg);
68137

69-
/// Read in a message from the input stream.
70-
LogicalResult readMessage(std::string &json) {
71-
return style == JSONStreamStyle::Delimited ? readDelimitedMessage(json)
72-
: readStandardMessage(json);
73-
}
74-
LogicalResult readDelimitedMessage(std::string &json);
75-
LogicalResult readStandardMessage(std::string &json);
138+
private:
139+
/// The input to read a message from.
140+
std::unique_ptr<JSONTransportInput> in;
76141

77-
/// An output buffer used when building output messages.
78-
SmallVector<char, 0> outputBuffer;
79-
/// The input file stream.
80-
std::FILE *in;
81-
/// The output file stream.
82-
raw_ostream &out;
83-
/// The JSON stream style to use.
84-
JSONStreamStyle style;
85-
/// If the output JSON should be formatted for easier readability.
86-
bool prettyOutput;
142+
/// The output to send a messages to.
143+
std::unique_ptr<JSONTransportOutput> out;
87144
};
88145

89146
//===----------------------------------------------------------------------===//

mlir/lib/Tools/lsp-server-support/Transport.cpp

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -175,15 +175,15 @@ llvm::Error decodeError(const llvm::json::Object &o) {
175175
}
176176

177177
void JSONTransport::notify(StringRef method, llvm::json::Value params) {
178-
sendMessage(llvm::json::Object{
178+
out->sendMessage(llvm::json::Object{
179179
{"jsonrpc", "2.0"},
180180
{"method", method},
181181
{"params", std::move(params)},
182182
});
183183
}
184184
void JSONTransport::call(StringRef method, llvm::json::Value params,
185185
llvm::json::Value id) {
186-
sendMessage(llvm::json::Object{
186+
out->sendMessage(llvm::json::Object{
187187
{"jsonrpc", "2.0"},
188188
{"id", std::move(id)},
189189
{"method", method},
@@ -193,14 +193,14 @@ void JSONTransport::call(StringRef method, llvm::json::Value params,
193193
void JSONTransport::reply(llvm::json::Value id,
194194
llvm::Expected<llvm::json::Value> result) {
195195
if (result) {
196-
return sendMessage(llvm::json::Object{
196+
return out->sendMessage(llvm::json::Object{
197197
{"jsonrpc", "2.0"},
198198
{"id", std::move(id)},
199199
{"result", std::move(*result)},
200200
});
201201
}
202202

203-
sendMessage(llvm::json::Object{
203+
out->sendMessage(llvm::json::Object{
204204
{"jsonrpc", "2.0"},
205205
{"id", std::move(id)},
206206
{"error", encodeError(result.takeError())},
@@ -209,13 +209,13 @@ void JSONTransport::reply(llvm::json::Value id,
209209

210210
llvm::Error JSONTransport::run(MessageHandler &handler) {
211211
std::string json;
212-
while (!feof(in)) {
213-
if (ferror(in)) {
212+
while (!in->isEndOfInput()) {
213+
if (in->getError()) {
214214
return llvm::errorCodeToError(
215215
std::error_code(errno, std::system_category()));
216216
}
217217

218-
if (succeeded(readMessage(json))) {
218+
if (succeeded(in->readMessage(json))) {
219219
if (llvm::Expected<llvm::json::Value> doc = llvm::json::parse(json)) {
220220
if (!handleMessage(std::move(*doc), handler))
221221
return llvm::Error::success();
@@ -227,7 +227,7 @@ llvm::Error JSONTransport::run(MessageHandler &handler) {
227227
return llvm::errorCodeToError(std::make_error_code(std::errc::io_error));
228228
}
229229

230-
void JSONTransport::sendMessage(llvm::json::Value msg) {
230+
void JSONTransportOutputOverStream::sendMessage(const llvm::json::Value &msg) {
231231
outputBuffer.clear();
232232
llvm::raw_svector_ostream os(outputBuffer);
233233
os << llvm::formatv(prettyOutput ? "{0:2}\n" : "{0}", msg);
@@ -303,7 +303,8 @@ LogicalResult readLine(std::FILE *in, SmallVectorImpl<char> &out) {
303303
// Returns std::nullopt when:
304304
// - ferror(), feof(), or shutdownRequested() are set.
305305
// - Content-Length is missing or empty (protocol error)
306-
LogicalResult JSONTransport::readStandardMessage(std::string &json) {
306+
LogicalResult
307+
JSONTransportInputOverFile::readStandardMessage(std::string &json) {
307308
// A Language Server Protocol message starts with a set of HTTP headers,
308309
// delimited by \r\n, and terminated by an empty line (\r\n).
309310
unsigned long long contentLength = 0;
@@ -349,7 +350,8 @@ LogicalResult JSONTransport::readStandardMessage(std::string &json) {
349350
/// This is a testing path, so favor simplicity over performance here.
350351
/// When returning failure: feof(), ferror(), or shutdownRequested() will be
351352
/// set.
352-
LogicalResult JSONTransport::readDelimitedMessage(std::string &json) {
353+
LogicalResult
354+
JSONTransportInputOverFile::readDelimitedMessage(std::string &json) {
353355
json.clear();
354356
llvm::SmallString<128> line;
355357
while (succeeded(readLine(in, line))) {

0 commit comments

Comments
 (0)