[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 &params,
                    Callback<std::vector<Location>> reply);
 
+  //===--------------------------------------------------------------------===//
+  // Hover
+
+  void onHover(const TextDocumentPositionParams &params,
+               Callback<Optional<Hover>> reply);
+
   MLIRServer &server;
   JSONTransport &transport;
 
@@ -72,6 +78,7 @@ void LSPServer::Impl::onInitialize(const InitializeParams &params,
        }},
       {"definitionProvider", true},
       {"referencesProvider", true},
+      {"hoverProvider", true},
   };
 
   llvm::json::Object result{
@@ -125,6 +132,14 @@ void LSPServer::Impl::onReference(const ReferenceParams &params,
   reply(std::move(locations));
 }
 
+//===----------------------------------------------------------------------===//
+// Hover
+
+void LSPServer::Impl::onHover(const TextDocumentPositionParams &params,
+                              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 &registry);
 
+  //===--------------------------------------------------------------------===//
+  // 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 &region) { 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