diff --git a/mlir/lib/Tools/mlir-pdll-lsp-server/LSPServer.cpp b/mlir/lib/Tools/mlir-pdll-lsp-server/LSPServer.cpp --- a/mlir/lib/Tools/mlir-pdll-lsp-server/LSPServer.cpp +++ b/mlir/lib/Tools/mlir-pdll-lsp-server/LSPServer.cpp @@ -51,6 +51,12 @@ void onReference(const ReferenceParams ¶ms, Callback> reply); + //===--------------------------------------------------------------------===// + // Hover + + void onHover(const TextDocumentPositionParams ¶ms, + Callback> reply); + //===--------------------------------------------------------------------===// // Fields //===--------------------------------------------------------------------===// @@ -82,6 +88,7 @@ }}, {"definitionProvider", true}, {"referencesProvider", true}, + {"hoverProvider", true}, }; llvm::json::Object result{ @@ -156,6 +163,14 @@ reply(std::move(locations)); } +//===----------------------------------------------------------------------===// +// Hover + +void LSPServer::Impl::onHover(const TextDocumentPositionParams ¶ms, + Callback> reply) { + reply(server.findHover(params.textDocument.uri, params.position)); +} + //===----------------------------------------------------------------------===// // LSPServer //===----------------------------------------------------------------------===// @@ -186,6 +201,9 @@ messageHandler.method("textDocument/references", impl.get(), &Impl::onReference); + // Hover + messageHandler.method("textDocument/hover", impl.get(), &Impl::onHover); + // Diagnostics impl->publishDiagnostics = messageHandler.outgoingNotification( diff --git a/mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.h b/mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.h --- a/mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.h +++ b/mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.h @@ -15,6 +15,7 @@ namespace mlir { namespace lsp { struct Diagnostic; +struct Hover; struct Location; struct Position; class URIForFile; @@ -47,6 +48,10 @@ void findReferencesOf(const URIForFile &uri, const Position &pos, std::vector &references); + /// Find a hover description for the given hover position, or None if one + /// couldn't be found. + Optional findHover(const URIForFile &uri, const Position &hoverPos); + private: struct Impl; diff --git a/mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.cpp b/mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.cpp --- a/mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.cpp +++ b/mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.cpp @@ -267,6 +267,27 @@ void findReferencesOf(const lsp::URIForFile &uri, const lsp::Position &pos, std::vector &references); + //===--------------------------------------------------------------------===// + // Hover + //===--------------------------------------------------------------------===// + + Optional findHover(const lsp::URIForFile &uri, + const lsp::Position &hoverPos); + Optional findHover(const ast::Decl *decl, + const llvm::SMRange &hoverRange); + lsp::Hover buildHoverForOpName(const ods::Operation *op, + const llvm::SMRange &hoverRange); + lsp::Hover buildHoverForVariable(const ast::VariableDecl *varDecl, + const llvm::SMRange &hoverRange); + lsp::Hover buildHoverForPattern(const ast::PatternDecl *patternDecl, + const llvm::SMRange &hoverRange); + lsp::Hover buildHoverForCoreConstraint(const ast::CoreConstraintDecl *decl, + const llvm::SMRange &hoverRange); + template + lsp::Hover + buildHoverForUserConstraintOrRewrite(StringRef typeName, const T *decl, + const llvm::SMRange &hoverRange); + //===--------------------------------------------------------------------===// // Fields //===--------------------------------------------------------------------===// @@ -343,6 +364,157 @@ references.push_back(getLocationFromLoc(sourceMgr, refLoc, uri)); } +//===----------------------------------------------------------------------===// +// PDLDocument: Hover +//===----------------------------------------------------------------------===// + +Optional PDLDocument::findHover(const lsp::URIForFile &uri, + const lsp::Position &hoverPos) { + llvm::SMLoc posLoc = getPosFromLoc(sourceMgr, hoverPos); + llvm::SMRange hoverRange; + const PDLIndexSymbol *symbol = index.lookup(posLoc, &hoverRange); + if (!symbol) + return llvm::None; + + // Add hover for operation names. + if (const auto *op = symbol->definition.dyn_cast()) + return buildHoverForOpName(op, hoverRange); + const auto *decl = symbol->definition.get(); + return findHover(decl, hoverRange); +} + +Optional PDLDocument::findHover(const ast::Decl *decl, + const llvm::SMRange &hoverRange) { + // Add hover for variables. + if (const auto *varDecl = dyn_cast(decl)) + return buildHoverForVariable(varDecl, hoverRange); + + // Add hover for patterns. + if (const auto *patternDecl = dyn_cast(decl)) + return buildHoverForPattern(patternDecl, hoverRange); + + // Add hover for core constraints. + if (const auto *cst = dyn_cast(decl)) + return buildHoverForCoreConstraint(cst, hoverRange); + + // Add hover for user constraints. + if (const auto *cst = dyn_cast(decl)) + return buildHoverForUserConstraintOrRewrite("Constraint", cst, hoverRange); + + // Add hover for user rewrites. + if (const auto *rewrite = dyn_cast(decl)) + return buildHoverForUserConstraintOrRewrite("Rewrite", rewrite, hoverRange); + + return llvm::None; +} + +lsp::Hover PDLDocument::buildHoverForOpName(const ods::Operation *op, + const llvm::SMRange &hoverRange) { + lsp::Hover hover(getRangeFromLoc(sourceMgr, hoverRange)); + { + llvm::raw_string_ostream hoverOS(hover.contents.value); + hoverOS << "**OpName**: `" << op->getName() << "`\n***\n" + << op->getSummary() << "\n***\n" + << op->getDescription(); + } + return hover; +} + +lsp::Hover PDLDocument::buildHoverForVariable(const ast::VariableDecl *varDecl, + const llvm::SMRange &hoverRange) { + lsp::Hover hover(getRangeFromLoc(sourceMgr, hoverRange)); + { + llvm::raw_string_ostream hoverOS(hover.contents.value); + hoverOS << "**variable**: `" << varDecl->getName().getName() << "`\n***\n" + << "Type: `" << varDecl->getType() << "`\n"; + } + return hover; +} + +lsp::Hover +PDLDocument::buildHoverForPattern(const ast::PatternDecl *patternDecl, + const llvm::SMRange &hoverRange) { + lsp::Hover hover(getRangeFromLoc(sourceMgr, hoverRange)); + { + llvm::raw_string_ostream hoverOS(hover.contents.value); + hoverOS << "**Pattern**"; + if (const ast::Name *name = patternDecl->getName()) + hoverOS << ": `" << name->getName() << "`"; + hoverOS << "\n***\n"; + if (Optional benefit = patternDecl->getBenefit()) + hoverOS << "Benefit: " << *benefit << "\n"; + if (patternDecl->hasBoundedRewriteRecursion()) + hoverOS << "HasBoundedRewriteRecursion\n"; + hoverOS << "RootOp: `" + << patternDecl->getRootRewriteStmt()->getRootOpExpr()->getType() + << "`\n"; + } + return hover; +} + +lsp::Hover +PDLDocument::buildHoverForCoreConstraint(const ast::CoreConstraintDecl *decl, + const llvm::SMRange &hoverRange) { + lsp::Hover hover(getRangeFromLoc(sourceMgr, hoverRange)); + { + llvm::raw_string_ostream hoverOS(hover.contents.value); + hoverOS << "**Constraint**: `"; + TypeSwitch(decl) + .Case([&](const ast::AttrConstraintDecl *) { hoverOS << "Attr"; }) + .Case([&](const ast::OpConstraintDecl *opCst) { + hoverOS << "Op"; + if (Optional name = opCst->getName()) + hoverOS << "<" << name << ">"; + }) + .Case([&](const ast::TypeConstraintDecl *) { hoverOS << "Type"; }) + .Case([&](const ast::TypeRangeConstraintDecl *) { + hoverOS << "TypeRange"; + }) + .Case([&](const ast::ValueConstraintDecl *) { hoverOS << "Value"; }) + .Case([&](const ast::ValueRangeConstraintDecl *) { + hoverOS << "ValueRange"; + }); + hoverOS << "`\n"; + } + return hover; +} + +template +lsp::Hover PDLDocument::buildHoverForUserConstraintOrRewrite( + StringRef typeName, const T *decl, const llvm::SMRange &hoverRange) { + lsp::Hover hover(getRangeFromLoc(sourceMgr, hoverRange)); + { + llvm::raw_string_ostream hoverOS(hover.contents.value); + hoverOS << "**" << typeName << "**: `" << decl->getName().getName() + << "`\n***\n"; + ArrayRef inputs = decl->getInputs(); + if (!inputs.empty()) { + hoverOS << "Parameters:\n"; + for (const ast::VariableDecl *input : inputs) + hoverOS << "* " << input->getName().getName() << ": `" + << input->getType() << "`\n"; + hoverOS << "***\n"; + } + ast::Type resultType = decl->getResultType(); + if (auto resultTupleTy = resultType.dyn_cast()) { + if (resultTupleTy.empty()) + return hover; + + hoverOS << "Results:\n"; + for (auto it : llvm::zip(resultTupleTy.getElementNames(), + resultTupleTy.getElementTypes())) { + StringRef name = std::get<0>(it); + hoverOS << "* " << (name.empty() ? "" : (name + ": ")) << "`" + << std::get<1>(it) << "`\n"; + } + } else { + hoverOS << "Results:\n* `" << resultType << "`\n"; + } + hoverOS << "***\n"; + } + return hover; +} + //===----------------------------------------------------------------------===// // PDLTextFileChunk //===----------------------------------------------------------------------===// @@ -394,6 +566,8 @@ std::vector &locations); void findReferencesOf(const lsp::URIForFile &uri, lsp::Position pos, std::vector &references); + Optional findHover(const lsp::URIForFile &uri, + lsp::Position hoverPos); private: /// Find the PDL document that contains the given position, and update the @@ -481,6 +655,17 @@ chunk.adjustLocForChunkOffset(loc.range); } +Optional PDLTextFile::findHover(const lsp::URIForFile &uri, + lsp::Position hoverPos) { + PDLTextFileChunk &chunk = getChunkFor(hoverPos); + Optional hoverInfo = chunk.document.findHover(uri, hoverPos); + + // Adjust any locations within this file for the offset of this chunk. + if (chunk.lineOffset != 0 && hoverInfo && hoverInfo->range) + chunk.adjustLocForChunkOffset(*hoverInfo->range); + return hoverInfo; +} + PDLTextFileChunk &PDLTextFile::getChunkFor(lsp::Position &pos) { if (chunks.size() == 1) return *chunks.front(); @@ -544,3 +729,11 @@ if (fileIt != impl->files.end()) fileIt->second->findReferencesOf(uri, pos, references); } + +Optional lsp::PDLLServer::findHover(const URIForFile &uri, + const Position &hoverPos) { + auto fileIt = impl->files.find(uri.file()); + if (fileIt != impl->files.end()) + return fileIt->second->findHover(uri, hoverPos); + return llvm::None; +} diff --git a/mlir/test/mlir-pdll-lsp-server/hover.test b/mlir/test/mlir-pdll-lsp-server/hover.test new file mode 100644 --- /dev/null +++ b/mlir/test/mlir-pdll-lsp-server/hover.test @@ -0,0 +1,133 @@ +// RUN: mlir-pdll-lsp-server -lit-test < %s | FileCheck -strict-whitespace %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"pdll","capabilities":{},"trace":"off"}} +// ----- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{ + "uri":"test:///foo.pdll", + "languageId":"pdll", + "version":1, + "text":"Constraint FooCst();\nRewrite FooRewrite(op: Op) -> Op;\nPattern Foo {\nlet root: Op;\nerase root;\n}" +}}} +// ----- +// Hover on a variable. +{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{ + "textDocument":{"uri":"test:///foo.pdll"}, + "position":{"line":3,"character":6} +}} +// CHECK: "id": 1, +// CHECK-NEXT: "jsonrpc": "2.0", +// CHECK-NEXT: "result": { +// CHECK-NEXT: "contents": { +// CHECK-NEXT: "kind": "markdown", +// CHECK-NEXT: "value": "**variable**: `root`\n***\nType: `Op`\n" +// CHECK-NEXT: }, +// CHECK-NEXT: "range": { +// CHECK-NEXT: "end": { +// CHECK-NEXT: "character": 8, +// CHECK-NEXT: "line": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "start": { +// CHECK-NEXT: "character": 4, +// CHECK-NEXT: "line": 3 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// ----- +// Hover on a pattern. +{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{ + "textDocument":{"uri":"test:///foo.pdll"}, + "position":{"line":2,"character":9} +}} +// CHECK: "id": 1, +// CHECK-NEXT: "jsonrpc": "2.0", +// CHECK-NEXT: "result": { +// CHECK-NEXT: "contents": { +// CHECK-NEXT: "kind": "markdown", +// CHECK-NEXT: "value": "**Pattern**: `Foo`\n***\nRootOp: `Op`\n" +// CHECK-NEXT: }, +// CHECK-NEXT: "range": { +// CHECK-NEXT: "end": { +// CHECK-NEXT: "character": 11, +// CHECK-NEXT: "line": 2 +// CHECK-NEXT: }, +// CHECK-NEXT: "start": { +// CHECK-NEXT: "character": 8, +// CHECK-NEXT: "line": 2 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// ----- +// Hover on a core constraint. +{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{ + "textDocument":{"uri":"test:///foo.pdll"}, + "position":{"line":3,"character":11} +}} +// CHECK: "id": 1, +// CHECK-NEXT: "jsonrpc": "2.0", +// CHECK-NEXT: "result": { +// CHECK-NEXT: "contents": { +// CHECK-NEXT: "kind": "markdown", +// CHECK-NEXT: "value": "**Constraint**: `Op`\n" +// CHECK-NEXT: }, +// CHECK-NEXT: "range": { +// CHECK-NEXT: "end": { +// CHECK-NEXT: "character": 12, +// CHECK-NEXT: "line": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "start": { +// CHECK-NEXT: "character": 10, +// CHECK-NEXT: "line": 3 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// ----- +// Hover on a user constraint. +{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{ + "textDocument":{"uri":"test:///foo.pdll"}, + "position":{"line":0,"character":14} +}} +// CHECK: "id": 1, +// CHECK-NEXT: "jsonrpc": "2.0", +// CHECK-NEXT: "result": { +// CHECK-NEXT: "contents": { +// CHECK-NEXT: "kind": "markdown", +// CHECK-NEXT: "value": "**Constraint**: `FooCst`\n***\n" +// CHECK-NEXT: }, +// CHECK-NEXT: "range": { +// CHECK-NEXT: "end": { +// CHECK-NEXT: "character": 17, +// CHECK-NEXT: "line": 0 +// CHECK-NEXT: }, +// CHECK-NEXT: "start": { +// CHECK-NEXT: "character": 11, +// CHECK-NEXT: "line": 0 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// ----- +// Hover on a user rewrite. +{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{ + "textDocument":{"uri":"test:///foo.pdll"}, + "position":{"line":1,"character":11} +}} +// CHECK: "id": 1, +// CHECK-NEXT: "jsonrpc": "2.0", +// CHECK-NEXT: "result": { +// CHECK-NEXT: "contents": { +// CHECK-NEXT: "kind": "markdown", +// CHECK-NEXT: "value": "**Rewrite**: `FooRewrite`\n***\nParameters:\n* op: `Op`\n***\nResults:\n* `Op`\n***\n" +// CHECK-NEXT: }, +// CHECK-NEXT: "range": { +// CHECK-NEXT: "end": { +// CHECK-NEXT: "character": 18, +// CHECK-NEXT: "line": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "start": { +// CHECK-NEXT: "character": 8, +// CHECK-NEXT: "line": 1 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// ----- +{"jsonrpc":"2.0","id":7,"method":"shutdown"} +// ----- +{"jsonrpc":"2.0","method":"exit"} diff --git a/mlir/test/mlir-pdll-lsp-server/initialize-params.test b/mlir/test/mlir-pdll-lsp-server/initialize-params.test --- a/mlir/test/mlir-pdll-lsp-server/initialize-params.test +++ b/mlir/test/mlir-pdll-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,