[Mlir-commits] [mlir] b3911cd - [mlir-lsp-server] Add support for sending diagnostics to the client
River Riddle
llvmlistbot at llvm.org
Wed May 12 13:12:31 PDT 2021
Author: River Riddle
Date: 2021-05-12T13:02:25-07:00
New Revision: b3911cdfc89f3b560fc00048d6f99280268fa63c
URL: https://github.com/llvm/llvm-project/commit/b3911cdfc89f3b560fc00048d6f99280268fa63c
DIFF: https://github.com/llvm/llvm-project/commit/b3911cdfc89f3b560fc00048d6f99280268fa63c.diff
LOG: [mlir-lsp-server] Add support for sending diagnostics to the client
This allows for diagnostics emitted during parsing/verification to be surfaced to the user by the language client, as opposed to just being emitted to the logs like they are now.
Differential Revision: https://reviews.llvm.org/D102293
Added:
mlir/test/mlir-lsp-server/diagnostics.test
Modified:
mlir/include/mlir/Parser/AsmParserState.h
mlir/lib/Parser/AsmParserState.cpp
mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp
mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp
mlir/lib/Tools/mlir-lsp-server/MLIRServer.h
mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.cpp
mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.h
mlir/lib/Tools/mlir-lsp-server/lsp/Transport.cpp
mlir/lib/Tools/mlir-lsp-server/lsp/Transport.h
Removed:
################################################################################
diff --git a/mlir/include/mlir/Parser/AsmParserState.h b/mlir/include/mlir/Parser/AsmParserState.h
index b468f582f4964..0b9e2553a8054 100644
--- a/mlir/include/mlir/Parser/AsmParserState.h
+++ b/mlir/include/mlir/Parser/AsmParserState.h
@@ -82,6 +82,7 @@ class AsmParserState {
AsmParserState();
~AsmParserState();
+ AsmParserState &operator=(AsmParserState &&other);
//===--------------------------------------------------------------------===//
// Access State
diff --git a/mlir/lib/Parser/AsmParserState.cpp b/mlir/lib/Parser/AsmParserState.cpp
index 1c629351b73eb..8655cd1f963d2 100644
--- a/mlir/lib/Parser/AsmParserState.cpp
+++ b/mlir/lib/Parser/AsmParserState.cpp
@@ -52,6 +52,10 @@ struct AsmParserState::Impl {
AsmParserState::AsmParserState() : impl(std::make_unique<Impl>()) {}
AsmParserState::~AsmParserState() {}
+AsmParserState &AsmParserState::operator=(AsmParserState &&other) {
+ impl = std::move(other.impl);
+ return *this;
+}
//===----------------------------------------------------------------------===//
// Access State
diff --git a/mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp b/mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp
index 44d6f3da99f19..954482f48b5e1 100644
--- a/mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp
+++ b/mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp
@@ -59,6 +59,10 @@ struct LSPServer::Impl {
MLIRServer &server;
JSONTransport &transport;
+ /// An outgoing notification used to send diagnostics to the client when they
+ /// are ready to be processed.
+ OutgoingNotification<PublishDiagnosticsParams> publishDiagnostics;
+
/// Used to indicate that the 'shutdown' request was received from the
/// Language Server client.
bool shutdownRequestReceived = false;
@@ -99,11 +103,21 @@ void LSPServer::Impl::onShutdown(const NoParams &,
void LSPServer::Impl::onDocumentDidOpen(
const DidOpenTextDocumentParams ¶ms) {
- server.addOrUpdateDocument(params.textDocument.uri, params.textDocument.text);
+ PublishDiagnosticsParams diagParams(params.textDocument.uri);
+ server.addOrUpdateDocument(params.textDocument.uri, params.textDocument.text,
+ diagParams.diagnostics);
+
+ // Publish any recorded diagnostics.
+ publishDiagnostics(diagParams);
}
void LSPServer::Impl::onDocumentDidClose(
const DidCloseTextDocumentParams ¶ms) {
server.removeDocument(params.textDocument.uri);
+
+ // Empty out the diagnostics shown for this document. This will clear out
+ // anything currently displayed by the client for this document (e.g. in the
+ // "Problems" pane of VSCode).
+ publishDiagnostics(PublishDiagnosticsParams(params.textDocument.uri));
}
void LSPServer::Impl::onDocumentDidChange(
const DidChangeTextDocumentParams ¶ms) {
@@ -111,8 +125,13 @@ void LSPServer::Impl::onDocumentDidChange(
// to avoid this.
if (params.contentChanges.size() != 1)
return;
+ PublishDiagnosticsParams diagParams(params.textDocument.uri);
server.addOrUpdateDocument(params.textDocument.uri,
- params.contentChanges.front().text);
+ params.contentChanges.front().text,
+ diagParams.diagnostics);
+
+ // Publish any recorded diagnostics.
+ publishDiagnostics(diagParams);
}
//===----------------------------------------------------------------------===//
@@ -173,6 +192,11 @@ LogicalResult LSPServer::run() {
// Hover
messageHandler.method("textDocument/hover", impl.get(), &Impl::onHover);
+ // Diagnostics
+ impl->publishDiagnostics =
+ messageHandler.outgoingNotification<PublishDiagnosticsParams>(
+ "textDocument/publishDiagnostics");
+
// Run the main loop of the transport.
LogicalResult result = success();
if (llvm::Error error = impl->transport.run(messageHandler)) {
diff --git a/mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp b/mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp
index 807e1864ed3ba..f9b0bfb526353 100644
--- a/mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp
+++ b/mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp
@@ -60,7 +60,28 @@ static Optional<lsp::Location> getLocationFromLoc(FileLineColLoc loc) {
lsp::Position position;
position.line = loc.getLine() - 1;
position.character = loc.getColumn();
- return lsp::Location{*sourceURI, lsp::Range{position, position}};
+ return lsp::Location{*sourceURI, lsp::Range(position)};
+}
+
+/// Returns a language server location from the given MLIR location, or None if
+/// one couldn't be created. `uri` is an optional additional filter that, when
+/// present, is used to filter sub locations that do not share the same uri.
+static Optional<lsp::Location>
+getLocationFromLoc(Location loc, const lsp::URIForFile *uri = nullptr) {
+ Optional<lsp::Location> location;
+ loc->walk([&](Location nestedLoc) {
+ FileLineColLoc fileLoc = nestedLoc.dyn_cast<FileLineColLoc>();
+ if (!fileLoc)
+ return WalkResult::advance();
+
+ Optional<lsp::Location> sourceLoc = getLocationFromLoc(fileLoc);
+ if (sourceLoc && (!uri || sourceLoc->uri == *uri)) {
+ location = *sourceLoc;
+ return WalkResult::interrupt();
+ }
+ return WalkResult::advance();
+ });
+ return location;
}
/// Collect all of the locations from the given MLIR location that are not
@@ -173,6 +194,56 @@ static void printDefBlockName(raw_ostream &os,
printDefBlockName(os, def.block, def.definition.loc);
}
+/// Convert the given MLIR diagnostic to the LSP form.
+static lsp::Diagnostic getLspDiagnoticFromDiag(Diagnostic &diag,
+ const lsp::URIForFile &uri) {
+ lsp::Diagnostic lspDiag;
+ lspDiag.source = "mlir";
+
+ // Note: Right now all of the diagnostics are treated as parser issues, but
+ // some are parser and some are verifier.
+ lspDiag.category = "Parse Error";
+
+ // Try to grab a file location for this diagnostic.
+ // TODO: For simplicity, we just grab the first one. It may be likely that we
+ // will need a more interesting heuristic here.'
+ Optional<lsp::Location> lspLocation =
+ getLocationFromLoc(diag.getLocation(), &uri);
+ if (lspLocation)
+ lspDiag.range = lspLocation->range;
+
+ // Convert the severity for the diagnostic.
+ switch (diag.getSeverity()) {
+ case DiagnosticSeverity::Note:
+ llvm_unreachable("expected notes to be handled separately");
+ case DiagnosticSeverity::Warning:
+ lspDiag.severity = lsp::DiagnosticSeverity::Warning;
+ break;
+ case DiagnosticSeverity::Error:
+ lspDiag.severity = lsp::DiagnosticSeverity::Error;
+ break;
+ case DiagnosticSeverity::Remark:
+ lspDiag.severity = lsp::DiagnosticSeverity::Information;
+ break;
+ }
+ lspDiag.message = diag.str();
+
+ // Attach any notes to the main diagnostic as related information.
+ std::vector<lsp::DiagnosticRelatedInformation> relatedDiags;
+ for (Diagnostic ¬e : diag.getNotes()) {
+ lsp::Location noteLoc;
+ if (Optional<lsp::Location> loc = getLocationFromLoc(note.getLocation()))
+ noteLoc = *loc;
+ else
+ noteLoc.uri = uri;
+ relatedDiags.emplace_back(noteLoc, note.str());
+ }
+ if (!relatedDiags.empty())
+ lspDiag.relatedInformation = std::move(relatedDiags);
+
+ return lspDiag;
+}
+
//===----------------------------------------------------------------------===//
// MLIRDocument
//===----------------------------------------------------------------------===//
@@ -182,7 +253,8 @@ namespace {
/// document.
struct MLIRDocument {
MLIRDocument(const lsp::URIForFile &uri, StringRef contents,
- DialectRegistry ®istry);
+ DialectRegistry ®istry,
+ std::vector<lsp::Diagnostic> &diagnostics);
//===--------------------------------------------------------------------===//
// Definitions and References
@@ -227,15 +299,12 @@ struct MLIRDocument {
} // namespace
MLIRDocument::MLIRDocument(const lsp::URIForFile &uri, StringRef contents,
- DialectRegistry ®istry)
+ DialectRegistry ®istry,
+ std::vector<lsp::Diagnostic> &diagnostics)
: context(registry) {
context.allowUnregisteredDialects();
ScopedDiagnosticHandler handler(&context, [&](Diagnostic &diag) {
- // TODO: What should we do with these diagnostics?
- // * Cache and show to the user?
- // * Ignore?
- lsp::Logger::error("Error when parsing MLIR document `{0}`: `{1}`",
- uri.file(), diag.str());
+ diagnostics.push_back(getLspDiagnoticFromDiag(diag, uri));
});
// Try to parsed the given IR string.
@@ -246,9 +315,13 @@ MLIRDocument::MLIRDocument(const lsp::URIForFile &uri, StringRef contents,
}
sourceMgr.AddNewSourceBuffer(std::move(memBuffer), llvm::SMLoc());
- if (failed(
- parseSourceFile(sourceMgr, &parsedIR, &context, nullptr, &asmState)))
+ if (failed(parseSourceFile(sourceMgr, &parsedIR, &context, nullptr,
+ &asmState))) {
+ // If parsing failed, clear out any of the current state.
+ parsedIR.clear();
+ asmState = AsmParserState();
return;
+ }
}
//===----------------------------------------------------------------------===//
@@ -495,10 +568,11 @@ lsp::MLIRServer::MLIRServer(DialectRegistry ®istry)
: impl(std::make_unique<Impl>(registry)) {}
lsp::MLIRServer::~MLIRServer() {}
-void lsp::MLIRServer::addOrUpdateDocument(const URIForFile &uri,
- StringRef contents) {
- impl->documents[uri.file()] =
- std::make_unique<MLIRDocument>(uri, contents, impl->registry);
+void lsp::MLIRServer::addOrUpdateDocument(
+ const URIForFile &uri, StringRef contents,
+ std::vector<Diagnostic> &diagnostics) {
+ impl->documents[uri.file()] = std::make_unique<MLIRDocument>(
+ uri, contents, impl->registry, diagnostics);
}
void lsp::MLIRServer::removeDocument(const URIForFile &uri) {
diff --git a/mlir/lib/Tools/mlir-lsp-server/MLIRServer.h b/mlir/lib/Tools/mlir-lsp-server/MLIRServer.h
index 6b974a5c95884..f3af2a67453d5 100644
--- a/mlir/lib/Tools/mlir-lsp-server/MLIRServer.h
+++ b/mlir/lib/Tools/mlir-lsp-server/MLIRServer.h
@@ -16,6 +16,7 @@ namespace mlir {
class DialectRegistry;
namespace lsp {
+struct Diagnostic;
struct Hover;
struct Location;
struct Position;
@@ -30,8 +31,10 @@ class MLIRServer {
MLIRServer(DialectRegistry ®istry);
~MLIRServer();
- /// Add or update the document at the given URI.
- void addOrUpdateDocument(const URIForFile &uri, StringRef contents);
+ /// Add or update the document at the given URI. Any diagnostics emitted for
+ /// this document should be added to `diagnostics`
+ void addOrUpdateDocument(const URIForFile &uri, StringRef contents,
+ std::vector<Diagnostic> &diagnostics);
/// Remove the document with the given uri.
void removeDocument(const URIForFile &uri);
diff --git a/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.cpp b/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.cpp
index 9b906243e0fa6..8d4fbe1807915 100644
--- a/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.cpp
+++ b/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.cpp
@@ -473,3 +473,44 @@ llvm::json::Value mlir::lsp::toJSON(const Hover &hover) {
result["range"] = toJSON(*hover.range);
return std::move(result);
}
+
+//===----------------------------------------------------------------------===//
+// DiagnosticRelatedInformation
+//===----------------------------------------------------------------------===//
+
+llvm::json::Value mlir::lsp::toJSON(const DiagnosticRelatedInformation &info) {
+ return llvm::json::Object{
+ {"location", info.location},
+ {"message", info.message},
+ };
+}
+
+//===----------------------------------------------------------------------===//
+// Diagnostic
+//===----------------------------------------------------------------------===//
+
+llvm::json::Value mlir::lsp::toJSON(const Diagnostic &diag) {
+ llvm::json::Object result{
+ {"range", diag.range},
+ {"severity", (int)diag.severity},
+ {"message", diag.message},
+ };
+ if (diag.category)
+ result["category"] = *diag.category;
+ if (!diag.source.empty())
+ result["source"] = diag.source;
+ if (diag.relatedInformation)
+ result["relatedInformation"] = *diag.relatedInformation;
+ return std::move(result);
+}
+
+//===----------------------------------------------------------------------===//
+// PublishDiagnosticsParams
+//===----------------------------------------------------------------------===//
+
+llvm::json::Value mlir::lsp::toJSON(const PublishDiagnosticsParams ¶ms) {
+ return llvm::json::Object{
+ {"uri", params.uri},
+ {"diagnostics", params.diagnostics},
+ };
+}
diff --git a/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.h b/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.h
index 78d313dfa9518..de08fa247bd84 100644
--- a/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.h
+++ b/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.h
@@ -228,6 +228,10 @@ raw_ostream &operator<<(raw_ostream &os, const Position &value);
//===----------------------------------------------------------------------===//
struct Range {
+ Range() = default;
+ Range(Position start, Position end) : start(start), end(end) {}
+ Range(Position loc) : Range(loc, loc) {}
+
/// The range's start position.
Position start;
@@ -393,6 +397,79 @@ struct Hover {
};
llvm::json::Value toJSON(const Hover &hover);
+//===----------------------------------------------------------------------===//
+// DiagnosticRelatedInformation
+//===----------------------------------------------------------------------===//
+
+/// Represents a related message and source code location for a diagnostic.
+/// This should be used to point to code locations that cause or related to a
+/// diagnostics, e.g. when duplicating a symbol in a scope.
+struct DiagnosticRelatedInformation {
+ DiagnosticRelatedInformation(Location location, std::string message)
+ : location(location), message(std::move(message)) {}
+
+ /// The location of this related diagnostic information.
+ Location location;
+ /// The message of this related diagnostic information.
+ std::string message;
+};
+llvm::json::Value toJSON(const DiagnosticRelatedInformation &info);
+
+//===----------------------------------------------------------------------===//
+// Diagnostic
+//===----------------------------------------------------------------------===//
+
+enum class DiagnosticSeverity {
+ /// It is up to the client to interpret diagnostics as error, warning, info or
+ /// hint.
+ Undetermined = 0,
+ Error = 1,
+ Warning = 2,
+ Information = 3,
+ Hint = 4
+};
+
+struct Diagnostic {
+ /// The source range where the message applies.
+ Range range;
+
+ /// The diagnostic's severity. Can be omitted. If omitted it is up to the
+ /// client to interpret diagnostics as error, warning, info or hint.
+ DiagnosticSeverity severity = DiagnosticSeverity::Undetermined;
+
+ /// A human-readable string describing the source of this diagnostic, e.g.
+ /// 'typescript' or 'super lint'.
+ std::string source;
+
+ /// The diagnostic's message.
+ std::string message;
+
+ /// An array of related diagnostic information, e.g. when symbol-names within
+ /// a scope collide all definitions can be marked via this property.
+ Optional<std::vector<DiagnosticRelatedInformation>> relatedInformation;
+
+ /// The diagnostic's category. Can be omitted.
+ /// An LSP extension that's used to send the name of the category over to the
+ /// client. The category typically describes the compilation stage during
+ /// which the issue was produced, e.g. "Semantic Issue" or "Parse Issue".
+ Optional<std::string> category;
+};
+llvm::json::Value toJSON(const Diagnostic &diag);
+
+//===----------------------------------------------------------------------===//
+// PublishDiagnosticsParams
+//===----------------------------------------------------------------------===//
+
+struct PublishDiagnosticsParams {
+ PublishDiagnosticsParams(URIForFile uri) : uri(uri) {}
+
+ /// The URI for which diagnostic information is reported.
+ URIForFile uri;
+ /// The list of reported diagnostics.
+ std::vector<Diagnostic> diagnostics;
+};
+llvm::json::Value toJSON(const PublishDiagnosticsParams ¶ms);
+
} // namespace lsp
} // namespace mlir
diff --git a/mlir/lib/Tools/mlir-lsp-server/lsp/Transport.cpp b/mlir/lib/Tools/mlir-lsp-server/lsp/Transport.cpp
index c13fc85bb1d7d..9468d39f273b7 100644
--- a/mlir/lib/Tools/mlir-lsp-server/lsp/Transport.cpp
+++ b/mlir/lib/Tools/mlir-lsp-server/lsp/Transport.cpp
@@ -21,6 +21,30 @@ using namespace mlir::lsp;
// Reply
//===----------------------------------------------------------------------===//
+namespace {
+/// Function object to reply to an LSP call.
+/// Each instance must be called exactly once, otherwise:
+/// - if there was no reply, an error reply is sent
+/// - if there were multiple replies, only the first is sent
+class Reply {
+public:
+ Reply(const llvm::json::Value &id, StringRef method,
+ JSONTransport &transport);
+ Reply(Reply &&other);
+ Reply &operator=(Reply &&) = delete;
+ Reply(const Reply &) = delete;
+ Reply &operator=(const Reply &) = delete;
+
+ void operator()(llvm::Expected<llvm::json::Value> reply);
+
+private:
+ StringRef method;
+ std::atomic<bool> replied = {false};
+ llvm::json::Value id;
+ JSONTransport *transport;
+};
+} // namespace
+
Reply::Reply(const llvm::json::Value &id, llvm::StringRef method,
JSONTransport &transport)
: id(id), transport(&transport) {}
diff --git a/mlir/lib/Tools/mlir-lsp-server/lsp/Transport.h b/mlir/lib/Tools/mlir-lsp-server/lsp/Transport.h
index 2cbf8f1ddb235..23b565c7dce5a 100644
--- a/mlir/lib/Tools/mlir-lsp-server/lsp/Transport.h
+++ b/mlir/lib/Tools/mlir-lsp-server/lsp/Transport.h
@@ -28,32 +28,61 @@
namespace mlir {
namespace lsp {
-class JSONTransport;
+class MessageHandler;
//===----------------------------------------------------------------------===//
-// Reply
+// JSONTransport
//===----------------------------------------------------------------------===//
-/// Function object to reply to an LSP call.
-/// Each instance must be called exactly once, otherwise:
-/// - if there was no reply, an error reply is sent
-/// - if there were multiple replies, only the first is sent
-class Reply {
+/// The encoding style of the JSON-RPC messages (both input and output).
+enum JSONStreamStyle {
+ /// Encoding per the LSP specification, with mandatory Content-Length header.
+ Standard,
+ /// Messages are delimited by a '// -----' line. Comment lines start with //.
+ Delimited
+};
+
+/// A transport class that performs the JSON-RPC communication with the LSP
+/// client.
+class JSONTransport {
public:
- Reply(const llvm::json::Value &id, StringRef method,
- JSONTransport &transport);
- Reply(Reply &&other);
- Reply &operator=(Reply &&) = delete;
- Reply(const Reply &) = delete;
- Reply &operator=(const Reply &) = delete;
+ JSONTransport(std::FILE *in, raw_ostream &out,
+ JSONStreamStyle style = JSONStreamStyle::Standard,
+ bool prettyOutput = false)
+ : in(in), out(out), style(style), prettyOutput(prettyOutput) {}
- void operator()(llvm::Expected<llvm::json::Value> reply);
+ /// The following methods are used to send a message to the LSP client.
+ void notify(StringRef method, llvm::json::Value params);
+ void call(StringRef method, llvm::json::Value params, llvm::json::Value id);
+ void reply(llvm::json::Value id, llvm::Expected<llvm::json::Value> result);
+
+ /// Start executing the JSON-RPC transport.
+ llvm::Error run(MessageHandler &handler);
private:
- StringRef method;
- std::atomic<bool> replied = {false};
- llvm::json::Value id;
- JSONTransport *transport;
+ /// Dispatches the given incoming json message to the message handler.
+ bool handleMessage(llvm::json::Value msg, MessageHandler &handler);
+ /// Writes the given message to the output stream.
+ void sendMessage(llvm::json::Value msg);
+
+ /// Read in a message from the input stream.
+ LogicalResult readMessage(std::string &json) {
+ return style == JSONStreamStyle::Delimited ? readDelimitedMessage(json)
+ : readStandardMessage(json);
+ }
+ LogicalResult readDelimitedMessage(std::string &json);
+ LogicalResult readStandardMessage(std::string &json);
+
+ /// An output buffer used when building output messages.
+ SmallVector<char, 0> outputBuffer;
+ /// The input file stream.
+ std::FILE *in;
+ /// The output file stream.
+ raw_ostream &out;
+ /// The JSON stream style to use.
+ JSONStreamStyle style;
+ /// If the output JSON should be formatted for easier readability.
+ bool prettyOutput;
};
//===----------------------------------------------------------------------===//
@@ -65,6 +94,11 @@ class Reply {
template <typename T>
using Callback = llvm::unique_function<void(llvm::Expected<T>)>;
+/// An OutgoingNotification<T> is a function used for outgoing notifications
+/// send to the client.
+template <typename T>
+using OutgoingNotification = llvm::unique_function<void(const T &)>;
+
/// A handler used to process the incoming transport messages.
class MessageHandler {
public:
@@ -119,6 +153,14 @@ class MessageHandler {
};
}
+ /// Create an OutgoingNotification object used for the given method.
+ template <typename T>
+ OutgoingNotification<T> outgoingNotification(llvm::StringLiteral method) {
+ return [&, method](const T ¶ms) {
+ transport.notify(method, llvm::json::Value(params));
+ };
+ }
+
private:
template <typename HandlerT>
using HandlerMap = llvm::StringMap<llvm::unique_function<HandlerT>>;
@@ -130,61 +172,6 @@ class MessageHandler {
JSONTransport &transport;
};
-//===----------------------------------------------------------------------===//
-// JSONTransport
-//===----------------------------------------------------------------------===//
-
-/// The encoding style of the JSON-RPC messages (both input and output).
-enum JSONStreamStyle {
- /// Encoding per the LSP specification, with mandatory Content-Length header.
- Standard,
- /// Messages are delimited by a '// -----' line. Comment lines start with //.
- Delimited
-};
-
-/// A transport class that performs the JSON-RPC communication with the LSP
-/// client.
-class JSONTransport {
-public:
- JSONTransport(std::FILE *in, raw_ostream &out,
- JSONStreamStyle style = JSONStreamStyle::Standard,
- bool prettyOutput = false)
- : in(in), out(out), style(style), prettyOutput(prettyOutput) {}
-
- /// The following methods are used to send a message to the LSP client.
- void notify(StringRef method, llvm::json::Value params);
- void call(StringRef method, llvm::json::Value params, llvm::json::Value id);
- void reply(llvm::json::Value id, llvm::Expected<llvm::json::Value> result);
-
- /// Start executing the JSON-RPC transport.
- llvm::Error run(MessageHandler &handler);
-
-private:
- /// Dispatches the given incoming json message to the message handler.
- bool handleMessage(llvm::json::Value msg, MessageHandler &handler);
- /// Writes the given message to the output stream.
- void sendMessage(llvm::json::Value msg);
-
- /// Read in a message from the input stream.
- LogicalResult readMessage(std::string &json) {
- return style == JSONStreamStyle::Delimited ? readDelimitedMessage(json)
- : readStandardMessage(json);
- }
- LogicalResult readDelimitedMessage(std::string &json);
- LogicalResult readStandardMessage(std::string &json);
-
- /// An output buffer used when building output messages.
- SmallVector<char, 0> outputBuffer;
- /// The input file stream.
- std::FILE *in;
- /// The output file stream.
- raw_ostream &out;
- /// The JSON stream style to use.
- JSONStreamStyle style;
- /// If the output JSON should be formatted for easier readability.
- bool prettyOutput;
-};
-
} // namespace lsp
} // namespace mlir
diff --git a/mlir/test/mlir-lsp-server/diagnostics.test b/mlir/test/mlir-lsp-server/diagnostics.test
new file mode 100644
index 0000000000000..d04183e60c942
--- /dev/null
+++ b/mlir/test/mlir-lsp-server/diagnostics.test
@@ -0,0 +1,35 @@
+// RUN: mlir-lsp-server -lit-test < %s | FileCheck -strict-whitespace %s
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"mlir","capabilities":{},"trace":"off"}}
+// -----
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{
+ "uri":"test:///foo.mlir",
+ "languageId":"mlir",
+ "version":1,
+ "text":"func ()"
+}}}
+// CHECK: "method": "textDocument/publishDiagnostics",
+// CHECK-NEXT: "params": {
+// CHECK-NEXT: "diagnostics": [
+// CHECK-NEXT: {
+// CHECK-NEXT: "category": "Parse Error",
+// CHECK-NEXT: "message": "custom op 'func' expected valid '@'-identifier for symbol name",
+// CHECK-NEXT: "range": {
+// CHECK-NEXT: "end": {
+// CHECK-NEXT: "character": 6,
+// CHECK-NEXT: "line": 0
+// CHECK-NEXT: },
+// CHECK-NEXT: "start": {
+// CHECK-NEXT: "character": 6,
+// CHECK-NEXT: "line": 0
+// CHECK-NEXT: }
+// CHECK-NEXT: },
+// CHECK-NEXT: "severity": 1,
+// CHECK-NEXT: "source": "mlir"
+// CHECK-NEXT: }
+// CHECK-NEXT: ],
+// CHECK-NEXT: "uri": "test:///foo.mlir"
+// CHECK-NEXT: }
+// -----
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
+// -----
+{"jsonrpc":"2.0","method":"exit"}
More information about the Mlir-commits
mailing list