[Mlir-commits] [mlir] 5c84195 - [mlir] Add hover support to mlir-lsp-server
River Riddle
llvmlistbot at llvm.org
Fri May 7 18:09:12 PDT 2021
Author: River Riddle
Date: 2021-05-07T18:09:01-07:00
New Revision: 5c84195b8ccb0c1352cd040a01b7b56374dd7ba6
URL: https://github.com/llvm/llvm-project/commit/5c84195b8ccb0c1352cd040a01b7b56374dd7ba6
DIFF: https://github.com/llvm/llvm-project/commit/5c84195b8ccb0c1352cd040a01b7b56374dd7ba6.diff
LOG: [mlir] Add hover support to mlir-lsp-server
This provides information when the user hovers over a part of the source .mlir file. This revision adds the following hover behavior:
* Operation:
- Shows the generic form.
* Operation Result:
- Shows the parent operation name, result number(s), and type(s).
* Block:
- Shows the parent operation name, block number, predecessors, and successors.
* Block Argument:
- Shows the parent operation name, parent block, argument number, and type.
Differential Revision: https://reviews.llvm.org/D101113
Added:
mlir/test/mlir-lsp-server/hover.test
Modified:
mlir/docs/Tools/MLIRLSP.md
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/test/mlir-lsp-server/initialize-params.test
Removed:
################################################################################
diff --git a/mlir/docs/Tools/MLIRLSP.md b/mlir/docs/Tools/MLIRLSP.md
index 1c241d5f0aff..de6c94e84943 100644
--- a/mlir/docs/Tools/MLIRLSP.md
+++ b/mlir/docs/Tools/MLIRLSP.md
@@ -109,3 +109,6 @@ monorepo, a few extra steps for setup are required:
* Syntax highlighting for .mlir files and `mlir` markdown blocks
* go-to-definition and cross references
* Definitions include the source file locations of operations in the .mlir
+* Hover over IR entities to see more information about them
+ * e.g. for a Block, you can see its block number as well as any
+ predecessors or successors.
diff --git a/mlir/include/mlir/Parser/AsmParserState.h b/mlir/include/mlir/Parser/AsmParserState.h
index 318ef174fec6..b468f582f496 100644
--- a/mlir/include/mlir/Parser/AsmParserState.h
+++ b/mlir/include/mlir/Parser/AsmParserState.h
@@ -95,6 +95,10 @@ class AsmParserState {
/// Return a range of the BlockDefinitions held by the current parser state.
iterator_range<BlockDefIterator> getBlockDefs() const;
+ /// Return the definition for the given block, or nullptr if the given
+ /// block does not have a definition.
+ const BlockDefinition *getBlockDef(Block *block) const;
+
/// Return a range of the OperationDefinitions held by the current parser
/// state.
iterator_range<OperationDefIterator> getOpDefs() const;
diff --git a/mlir/lib/Parser/AsmParserState.cpp b/mlir/lib/Parser/AsmParserState.cpp
index 3fdb1c2df81d..1c629351b73e 100644
--- a/mlir/lib/Parser/AsmParserState.cpp
+++ b/mlir/lib/Parser/AsmParserState.cpp
@@ -60,6 +60,12 @@ auto AsmParserState::getBlockDefs() const -> iterator_range<BlockDefIterator> {
return llvm::make_pointee_range(llvm::makeArrayRef(impl->blocks));
}
+auto AsmParserState::getBlockDef(Block *block) const
+ -> const BlockDefinition * {
+ auto it = impl->blocksToIdx.find(block);
+ return it == impl->blocksToIdx.end() ? nullptr : &*impl->blocks[it->second];
+}
+
auto AsmParserState::getOpDefs() const -> iterator_range<OperationDefIterator> {
return llvm::make_pointee_range(llvm::makeArrayRef(impl->operations));
}
diff --git a/mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp b/mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp
index b3cbd59eec3c..44d6f3da99f1 100644
--- a/mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp
+++ b/mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp
@@ -50,6 +50,12 @@ struct LSPServer::Impl {
void onReference(const ReferenceParams ¶ms,
Callback<std::vector<Location>> reply);
+ //===--------------------------------------------------------------------===//
+ // Hover
+
+ void onHover(const TextDocumentPositionParams ¶ms,
+ Callback<Optional<Hover>> reply);
+
MLIRServer &server;
JSONTransport &transport;
@@ -72,6 +78,7 @@ void LSPServer::Impl::onInitialize(const InitializeParams ¶ms,
}},
{"definitionProvider", true},
{"referencesProvider", true},
+ {"hoverProvider", true},
};
llvm::json::Object result{
@@ -125,6 +132,14 @@ void LSPServer::Impl::onReference(const ReferenceParams ¶ms,
reply(std::move(locations));
}
+//===----------------------------------------------------------------------===//
+// Hover
+
+void LSPServer::Impl::onHover(const TextDocumentPositionParams ¶ms,
+ Callback<Optional<Hover>> reply) {
+ reply(server.findHover(params.textDocument.uri, params.position));
+}
+
//===----------------------------------------------------------------------===//
// LSPServer
//===----------------------------------------------------------------------===//
@@ -155,6 +170,10 @@ LogicalResult LSPServer::run() {
messageHandler.method("textDocument/references", impl.get(),
&Impl::onReference);
+ // Hover
+ messageHandler.method("textDocument/hover", impl.get(), &Impl::onHover);
+
+ // Run the main loop of the transport.
LogicalResult result = success();
if (llvm::Error error = impl->transport.run(messageHandler)) {
Logger::error("Transport error: {0}", error);
diff --git a/mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp b/mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp
index 0c49c74dde21..807e1864ed3b 100644
--- a/mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp
+++ b/mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp
@@ -90,13 +90,87 @@ static bool contains(llvm::SMRange range, llvm::SMLoc loc) {
}
/// Returns true if the given location is contained by the definition or one of
-/// the uses of the given SMDefinition.
-static bool isDefOrUse(const AsmParserState::SMDefinition &def,
- llvm::SMLoc loc) {
- auto isUseFn = [&](const llvm::SMRange &range) {
+/// the uses of the given SMDefinition. If provided, `overlappedRange` is set to
+/// the range within `def` that the provided `loc` overlapped with.
+static bool isDefOrUse(const AsmParserState::SMDefinition &def, llvm::SMLoc loc,
+ llvm::SMRange *overlappedRange = nullptr) {
+ // Check the main definition.
+ if (contains(def.loc, loc)) {
+ if (overlappedRange)
+ *overlappedRange = def.loc;
+ return true;
+ }
+
+ // Check the uses.
+ auto useIt = llvm::find_if(def.uses, [&](const llvm::SMRange &range) {
return contains(range, loc);
+ });
+ if (useIt != def.uses.end()) {
+ if (overlappedRange)
+ *overlappedRange = *useIt;
+ return true;
+ }
+ return false;
+}
+
+/// Given a location pointing to a result, return the result number it refers
+/// to or None if it refers to all of the results.
+static Optional<unsigned> getResultNumberFromLoc(llvm::SMLoc loc) {
+ // Skip all of the identifier characters.
+ auto isIdentifierChar = [](char c) {
+ return isalnum(c) || c == '%' || c == '$' || c == '.' || c == '_' ||
+ c == '-';
};
- return contains(def.loc, loc) || llvm::any_of(def.uses, isUseFn);
+ const char *curPtr = loc.getPointer();
+ while (isIdentifierChar(*curPtr))
+ ++curPtr;
+
+ // Check to see if this location indexes into the result group, via `#`. If it
+ // doesn't, we can't extract a sub result number.
+ if (*curPtr != '#')
+ return llvm::None;
+
+ // Compute the sub result number from the remaining portion of the string.
+ const char *numberStart = ++curPtr;
+ while (llvm::isDigit(*curPtr))
+ ++curPtr;
+ StringRef numberStr(numberStart, curPtr - numberStart);
+ unsigned resultNumber = 0;
+ return numberStr.consumeInteger(10, resultNumber) ? Optional<unsigned>()
+ : resultNumber;
+}
+
+/// Given a source location range, return the text covered by the given range.
+/// If the range is invalid, returns None.
+static Optional<StringRef> getTextFromRange(llvm::SMRange range) {
+ if (!range.isValid())
+ return None;
+ const char *startPtr = range.Start.getPointer();
+ return StringRef(startPtr, range.End.getPointer() - startPtr);
+}
+
+/// Given a block, return its position in its parent region.
+static unsigned getBlockNumber(Block *block) {
+ return std::distance(block->getParent()->begin(), block->getIterator());
+}
+
+/// Given a block and source location, print the source name of the block to the
+/// given output stream.
+static void printDefBlockName(raw_ostream &os, Block *block,
+ llvm::SMRange loc = {}) {
+ // Try to extract a name from the source location.
+ Optional<StringRef> text = getTextFromRange(loc);
+ if (text && text->startswith("^")) {
+ os << *text;
+ return;
+ }
+
+ // Otherwise, we don't have a name so print the block number.
+ os << "<Block #" << getBlockNumber(block) << ">";
+}
+static void printDefBlockName(raw_ostream &os,
+ const AsmParserState::BlockDefinition &def) {
+ printDefBlockName(os, def.block, def.definition.loc);
}
//===----------------------------------------------------------------------===//
@@ -110,11 +184,33 @@ struct MLIRDocument {
MLIRDocument(const lsp::URIForFile &uri, StringRef contents,
DialectRegistry ®istry);
+ //===--------------------------------------------------------------------===//
+ // Definitions and References
+ //===--------------------------------------------------------------------===//
+
void getLocationsOf(const lsp::URIForFile &uri, const lsp::Position &defPos,
std::vector<lsp::Location> &locations);
void findReferencesOf(const lsp::URIForFile &uri, const lsp::Position &pos,
std::vector<lsp::Location> &references);
+ //===--------------------------------------------------------------------===//
+ // Hover
+ //===--------------------------------------------------------------------===//
+
+ Optional<lsp::Hover> findHover(const lsp::URIForFile &uri,
+ const lsp::Position &hoverPos);
+ Optional<lsp::Hover>
+ buildHoverForOperation(const AsmParserState::OperationDefinition &op);
+ lsp::Hover buildHoverForOperationResult(llvm::SMRange hoverRange,
+ Operation *op, unsigned resultStart,
+ unsigned resultEnd,
+ llvm::SMLoc posLoc);
+ lsp::Hover buildHoverForBlock(llvm::SMRange hoverRange,
+ const AsmParserState::BlockDefinition &block);
+ lsp::Hover
+ buildHoverForBlockArgument(llvm::SMRange hoverRange, BlockArgument arg,
+ const AsmParserState::BlockDefinition &block);
+
/// The context used to hold the state contained by the parsed document.
MLIRContext context;
@@ -155,6 +251,10 @@ MLIRDocument::MLIRDocument(const lsp::URIForFile &uri, StringRef contents,
return;
}
+//===----------------------------------------------------------------------===//
+// MLIRDocument: Definitions and References
+//===----------------------------------------------------------------------===//
+
void MLIRDocument::getLocationsOf(const lsp::URIForFile &uri,
const lsp::Position &defPos,
std::vector<lsp::Location> &locations) {
@@ -223,6 +323,155 @@ void MLIRDocument::findReferencesOf(const lsp::URIForFile &uri,
}
}
+//===----------------------------------------------------------------------===//
+// MLIRDocument: Hover
+//===----------------------------------------------------------------------===//
+
+Optional<lsp::Hover> MLIRDocument::findHover(const lsp::URIForFile &uri,
+ const lsp::Position &hoverPos) {
+ llvm::SMLoc posLoc = getPosFromLoc(sourceMgr, hoverPos);
+ llvm::SMRange hoverRange;
+
+ // Check for Hovers on operations and results.
+ for (const AsmParserState::OperationDefinition &op : asmState.getOpDefs()) {
+ // Check if the position points at this operation.
+ if (contains(op.loc, posLoc))
+ return buildHoverForOperation(op);
+
+ // Check if the position points at a result group.
+ for (unsigned i = 0, e = op.resultGroups.size(); i < e; ++i) {
+ const auto &result = op.resultGroups[i];
+ if (!isDefOrUse(result.second, posLoc, &hoverRange))
+ continue;
+
+ // Get the range of results covered by the over position.
+ unsigned resultStart = result.first;
+ unsigned resultEnd =
+ (i == e - 1) ? op.op->getNumResults() : op.resultGroups[i + 1].first;
+ return buildHoverForOperationResult(hoverRange, op.op, resultStart,
+ resultEnd, posLoc);
+ }
+ }
+
+ // Check to see if the hover is over a block argument.
+ for (const AsmParserState::BlockDefinition &block : asmState.getBlockDefs()) {
+ if (isDefOrUse(block.definition, posLoc, &hoverRange))
+ return buildHoverForBlock(hoverRange, block);
+
+ for (const auto &arg : llvm::enumerate(block.arguments)) {
+ if (!isDefOrUse(arg.value(), posLoc, &hoverRange))
+ continue;
+
+ return buildHoverForBlockArgument(
+ hoverRange, block.block->getArgument(arg.index()), block);
+ }
+ }
+ return llvm::None;
+}
+
+Optional<lsp::Hover> MLIRDocument::buildHoverForOperation(
+ const AsmParserState::OperationDefinition &op) {
+ // Don't show hovers for operations with regions to avoid huge hover blocks.
+ // TODO: Should we add support for printing an op without its regions?
+ if (llvm::any_of(op.op->getRegions(),
+ [](Region ®ion) { return !region.empty(); }))
+ return llvm::None;
+
+ lsp::Hover hover(getRangeFromLoc(sourceMgr, op.loc));
+ llvm::raw_string_ostream os(hover.contents.value);
+
+ // For hovers on an operation, show the generic form.
+ os << "```mlir\n";
+ op.op->print(
+ os, OpPrintingFlags().printGenericOpForm().elideLargeElementsAttrs());
+ os << "\n```\n";
+
+ return hover;
+}
+
+lsp::Hover MLIRDocument::buildHoverForOperationResult(llvm::SMRange hoverRange,
+ Operation *op,
+ unsigned resultStart,
+ unsigned resultEnd,
+ llvm::SMLoc posLoc) {
+ lsp::Hover hover(getRangeFromLoc(sourceMgr, hoverRange));
+ llvm::raw_string_ostream os(hover.contents.value);
+
+ // Add the parent operation name to the hover.
+ os << "Operation: \"" << op->getName() << "\"\n\n";
+
+ // Check to see if the location points to a specific result within the
+ // group.
+ if (Optional<unsigned> resultNumber = getResultNumberFromLoc(posLoc)) {
+ if ((resultStart + *resultNumber) < resultEnd) {
+ resultStart += *resultNumber;
+ resultEnd = resultStart + 1;
+ }
+ }
+
+ // Add the range of results and their types to the hover info.
+ if ((resultStart + 1) == resultEnd) {
+ os << "Result #" << resultStart << "\n\n"
+ << "Type: `" << op->getResult(resultStart).getType() << "`\n\n";
+ } else {
+ os << "Result #[" << resultStart << ", " << (resultEnd - 1) << "]\n\n"
+ << "Types: ";
+ llvm::interleaveComma(
+ op->getResults().slice(resultStart, resultEnd), os,
+ [&](Value result) { os << "`" << result.getType() << "`"; });
+ }
+
+ return hover;
+}
+
+lsp::Hover
+MLIRDocument::buildHoverForBlock(llvm::SMRange hoverRange,
+ const AsmParserState::BlockDefinition &block) {
+ lsp::Hover hover(getRangeFromLoc(sourceMgr, hoverRange));
+ llvm::raw_string_ostream os(hover.contents.value);
+
+ // Print the given block to the hover output stream.
+ auto printBlockToHover = [&](Block *newBlock) {
+ if (const auto *def = asmState.getBlockDef(newBlock))
+ printDefBlockName(os, *def);
+ else
+ printDefBlockName(os, newBlock);
+ };
+
+ // Display the parent operation, block number, predecessors, and successors.
+ os << "Operation: \"" << block.block->getParentOp()->getName() << "\"\n\n"
+ << "Block #" << getBlockNumber(block.block) << "\n\n";
+ if (!block.block->hasNoPredecessors()) {
+ os << "Predecessors: ";
+ llvm::interleaveComma(block.block->getPredecessors(), os,
+ printBlockToHover);
+ os << "\n\n";
+ }
+ if (!block.block->hasNoSuccessors()) {
+ os << "Successors: ";
+ llvm::interleaveComma(block.block->getSuccessors(), os, printBlockToHover);
+ os << "\n\n";
+ }
+
+ return hover;
+}
+
+lsp::Hover MLIRDocument::buildHoverForBlockArgument(
+ llvm::SMRange hoverRange, BlockArgument arg,
+ const AsmParserState::BlockDefinition &block) {
+ lsp::Hover hover(getRangeFromLoc(sourceMgr, hoverRange));
+ llvm::raw_string_ostream os(hover.contents.value);
+
+ // Display the parent operation, block, the argument number, and the type.
+ os << "Operation: \"" << block.block->getParentOp()->getName() << "\"\n\n"
+ << "Block: ";
+ printDefBlockName(os, block);
+ os << "\n\nArgument #" << arg.getArgNumber() << "\n\n"
+ << "Type: `" << arg.getType() << "`\n\n";
+
+ return hover;
+}
+
//===----------------------------------------------------------------------===//
// MLIRServer::Impl
//===----------------------------------------------------------------------===//
@@ -271,3 +520,11 @@ void lsp::MLIRServer::findReferencesOf(const URIForFile &uri,
if (fileIt != impl->documents.end())
fileIt->second->findReferencesOf(uri, pos, references);
}
+
+Optional<lsp::Hover> lsp::MLIRServer::findHover(const URIForFile &uri,
+ const Position &hoverPos) {
+ auto fileIt = impl->documents.find(uri.file());
+ if (fileIt != impl->documents.end())
+ return fileIt->second->findHover(uri, hoverPos);
+ return llvm::None;
+}
diff --git a/mlir/lib/Tools/mlir-lsp-server/MLIRServer.h b/mlir/lib/Tools/mlir-lsp-server/MLIRServer.h
index c00a696bed70..6b974a5c9588 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 Hover;
struct Location;
struct Position;
class URIForFile;
@@ -43,6 +44,10 @@ class MLIRServer {
void findReferencesOf(const URIForFile &uri, const Position &pos,
std::vector<Location> &references);
+ /// Find a hover description for the given hover position, or None if one
+ /// couldn't be found.
+ Optional<Hover> findHover(const URIForFile &uri, const Position &hoverPos);
+
private:
struct Impl;
diff --git a/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.cpp b/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.cpp
index 6ca9e439952e..9b906243e0fa 100644
--- a/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.cpp
+++ b/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.cpp
@@ -434,3 +434,42 @@ bool mlir::lsp::fromJSON(const llvm::json::Value &value,
return o && o.map("textDocument", result.textDocument) &&
o.map("contentChanges", result.contentChanges);
}
+
+//===----------------------------------------------------------------------===//
+// MarkupContent
+//===----------------------------------------------------------------------===//
+
+static llvm::StringRef toTextKind(MarkupKind kind) {
+ switch (kind) {
+ case MarkupKind::PlainText:
+ return "plaintext";
+ case MarkupKind::Markdown:
+ return "markdown";
+ }
+ llvm_unreachable("Invalid MarkupKind");
+}
+
+raw_ostream &mlir::lsp::operator<<(raw_ostream &os, MarkupKind kind) {
+ return os << toTextKind(kind);
+}
+
+llvm::json::Value mlir::lsp::toJSON(const MarkupContent &mc) {
+ if (mc.value.empty())
+ return nullptr;
+
+ return llvm::json::Object{
+ {"kind", toTextKind(mc.kind)},
+ {"value", mc.value},
+ };
+}
+
+//===----------------------------------------------------------------------===//
+// Hover
+//===----------------------------------------------------------------------===//
+
+llvm::json::Value mlir::lsp::toJSON(const Hover &hover) {
+ llvm::json::Object result{{"contents", toJSON(hover.contents)}};
+ if (hover.range.hasValue())
+ result["range"] = toJSON(*hover.range);
+ return std::move(result);
+}
diff --git a/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.h b/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.h
index 1ebe8767ace9..78d313dfa951 100644
--- a/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.h
+++ b/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.h
@@ -358,6 +358,41 @@ struct DidChangeTextDocumentParams {
bool fromJSON(const llvm::json::Value &value,
DidChangeTextDocumentParams &result, llvm::json::Path path);
+//===----------------------------------------------------------------------===//
+// MarkupContent
+//===----------------------------------------------------------------------===//
+
+/// Describes the content type that a client supports in various result literals
+/// like `Hover`.
+enum class MarkupKind {
+ PlainText,
+ Markdown,
+};
+raw_ostream &operator<<(raw_ostream &os, MarkupKind kind);
+
+struct MarkupContent {
+ MarkupKind kind = MarkupKind::PlainText;
+ std::string value;
+};
+llvm::json::Value toJSON(const MarkupContent &mc);
+
+//===----------------------------------------------------------------------===//
+// Hover
+//===----------------------------------------------------------------------===//
+
+struct Hover {
+ /// Construct a default hover with the given range that uses Markdown content.
+ Hover(Range range) : contents{MarkupKind::Markdown, ""}, range(range) {}
+
+ /// The hover's content.
+ MarkupContent contents;
+
+ /// An optional range is a range inside a text document that is used to
+ /// visualize a hover, e.g. by changing the background color.
+ Optional<Range> range;
+};
+llvm::json::Value toJSON(const Hover &hover);
+
} // namespace lsp
} // namespace mlir
diff --git a/mlir/test/mlir-lsp-server/hover.test b/mlir/test/mlir-lsp-server/hover.test
new file mode 100644
index 000000000000..77fff2a79f89
--- /dev/null
+++ b/mlir/test/mlir-lsp-server/hover.test
@@ -0,0 +1,109 @@
+// 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 @foo(%arg: i1) {\n%value = constant true\nbr ^bb2\n^bb2:\nreturn\n}"
+}}}
+// -----
+// Hover on an operation.
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{
+ "textDocument":{"uri":"test:///foo.mlir"},
+ "position":{"line":1,"character":12}
+}}
+// CHECK: "id": 1,
+// CHECK-NEXT: "jsonrpc": "2.0",
+// CHECK-NEXT: "result": {
+// CHECK-NEXT: "contents": {
+// CHECK-NEXT: "kind": "markdown",
+// CHECK-NEXT: "value": "```mlir\n%true = \"std.constant\"() {value = true} : () -> i1\n```\n"
+// CHECK-NEXT: },
+// CHECK-NEXT: "range": {
+// CHECK-NEXT: "end": {
+// CHECK-NEXT: "character": 17,
+// CHECK-NEXT: "line": 1
+// CHECK-NEXT: },
+// CHECK-NEXT: "start": {
+// CHECK-NEXT: "character": 10,
+// CHECK-NEXT: "line": 1
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+// -----
+// Hover on an operation result.
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{
+ "textDocument":{"uri":"test:///foo.mlir"},
+ "position":{"line":1,"character":2}
+}}
+// CHECK: "id": 1,
+// CHECK-NEXT: "jsonrpc": "2.0",
+// CHECK-NEXT: "result": {
+// CHECK-NEXT: "contents": {
+// CHECK-NEXT: "kind": "markdown",
+// CHECK-NEXT: "value": "Operation: \"std.constant\"\n\nResult #0\n\nType: `i1`\n\n"
+// CHECK-NEXT: },
+// CHECK-NEXT: "range": {
+// CHECK-NEXT: "end": {
+// CHECK-NEXT: "character": 6,
+// CHECK-NEXT: "line": 1
+// CHECK-NEXT: },
+// CHECK-NEXT: "start": {
+// CHECK-NEXT: "character": 1,
+// CHECK-NEXT: "line": 1
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+// -----
+// Hover on a Block.
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{
+ "textDocument":{"uri":"test:///foo.mlir"},
+ "position":{"line":3,"character":2}
+}}
+// CHECK: "id": 1,
+// CHECK-NEXT: "jsonrpc": "2.0",
+// CHECK-NEXT: "result": {
+// CHECK-NEXT: "contents": {
+// CHECK-NEXT: "kind": "markdown",
+// CHECK-NEXT: "value": "Operation: \"func\"\n\nBlock #1\n\nPredecessors: <Block #0>\n\n"
+// CHECK-NEXT: },
+// CHECK-NEXT: "range": {
+// CHECK-NEXT: "end": {
+// CHECK-NEXT: "character": 4,
+// CHECK-NEXT: "line": 3
+// CHECK-NEXT: },
+// CHECK-NEXT: "start": {
+// CHECK-NEXT: "character": 1,
+// CHECK-NEXT: "line": 3
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+// -----
+// Hover on a Block argument.
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{
+ "textDocument":{"uri":"test:///foo.mlir"},
+ "position":{"line":0,"character":12}
+}}
+// CHECK: "id": 1,
+// CHECK-NEXT: "jsonrpc": "2.0",
+// CHECK-NEXT: "result": {
+// CHECK-NEXT: "contents": {
+// CHECK-NEXT: "kind": "markdown",
+// CHECK-NEXT: "value": "Operation: \"func\"\n\nBlock: <Block #0>\n\nArgument #0\n\nType: `i1`\n\n"
+// CHECK-NEXT: },
+// CHECK-NEXT: "range": {
+// CHECK-NEXT: "end": {
+// CHECK-NEXT: "character": 14,
+// CHECK-NEXT: "line": 0
+// CHECK-NEXT: },
+// CHECK-NEXT: "start": {
+// CHECK-NEXT: "character": 11,
+// CHECK-NEXT: "line": 0
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+// -----
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
+// -----
+{"jsonrpc":"2.0","method":"exit"}
diff --git a/mlir/test/mlir-lsp-server/initialize-params.test b/mlir/test/mlir-lsp-server/initialize-params.test
index 93a52e5e9394..261d024efdf5 100644
--- a/mlir/test/mlir-lsp-server/initialize-params.test
+++ b/mlir/test/mlir-lsp-server/initialize-params.test
@@ -6,6 +6,7 @@
// CHECK-NEXT: "result": {
// CHECK-NEXT: "capabilities": {
// CHECK-NEXT: "definitionProvider": true,
+// CHECK-NEXT: "hoverProvider": true,
// CHECK-NEXT: "referencesProvider": true,
// CHECK-NEXT: "textDocumentSync": {
// CHECK-NEXT: "change": 1,
More information about the Mlir-commits
mailing list