Skip to content

Commit 3ac5d8d

Browse files
authored
[mlir-lsp] Abstract input and output of the JSONTransport (#129320)
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 b936ef1 commit 3ac5d8d

File tree

2 files changed

+57
-20
lines changed

2 files changed

+57
-20
lines changed

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

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,59 @@ 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 hasError() const = 0;
54+
virtual bool isEndOfInput() const = 0;
55+
56+
/// Read in a message from the input stream.
57+
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+
/// Concrete implementation of the JSONTransportInput that reads from a file.
70+
class JSONTransportInputOverFile : public JSONTransportInput {
71+
public:
72+
explicit JSONTransportInputOverFile(
73+
std::FILE *in, JSONStreamStyle style = JSONStreamStyle::Standard)
74+
: JSONTransportInput(style), in(in) {}
75+
76+
bool hasError() const final { return ferror(in); }
77+
bool isEndOfInput() const final { return feof(in); }
78+
79+
LogicalResult readDelimitedMessage(std::string &json) final;
80+
LogicalResult readStandardMessage(std::string &json) final;
81+
82+
private:
83+
std::FILE *in;
84+
};
85+
4686
/// A transport class that performs the JSON-RPC communication with the LSP
4787
/// client.
4888
class JSONTransport {
4989
public:
90+
JSONTransport(std::unique_ptr<JSONTransportInput> in, raw_ostream &out,
91+
bool prettyOutput = false)
92+
: in(std::move(in)), out(out), prettyOutput(prettyOutput) {}
93+
5094
JSONTransport(std::FILE *in, raw_ostream &out,
5195
JSONStreamStyle style = JSONStreamStyle::Standard,
5296
bool prettyOutput = false)
53-
: in(in), out(out), style(style), prettyOutput(prettyOutput) {}
97+
: in(std::make_unique<JSONTransportInputOverFile>(in, style)), out(out),
98+
prettyOutput(prettyOutput) {}
5499

55100
/// The following methods are used to send a message to the LSP client.
56101
void notify(StringRef method, llvm::json::Value params);
@@ -66,22 +111,12 @@ class JSONTransport {
66111
/// Writes the given message to the output stream.
67112
void sendMessage(llvm::json::Value msg);
68113

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);
76-
77-
/// An output buffer used when building output messages.
114+
private:
115+
/// The input to read a message from.
116+
std::unique_ptr<JSONTransportInput> in;
78117
SmallVector<char, 0> outputBuffer;
79-
/// The input file stream.
80-
std::FILE *in;
81118
/// The output file stream.
82119
raw_ostream &out;
83-
/// The JSON stream style to use.
84-
JSONStreamStyle style;
85120
/// If the output JSON should be formatted for easier readability.
86121
bool prettyOutput;
87122
};

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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->hasError()) {
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();
@@ -303,13 +303,14 @@ 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;
310311
llvm::SmallString<128> line;
311312
while (true) {
312-
if (feof(in) || ferror(in) || failed(readLine(in, line)))
313+
if (feof(in) || hasError() || failed(readLine(in, line)))
313314
return failure();
314315

315316
// Content-Length is a mandatory header, and the only one we handle.
@@ -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)