diff --git a/mlir/lib/Tools/lsp-server-support/SourceMgrUtils.h b/mlir/lib/Tools/lsp-server-support/SourceMgrUtils.h --- a/mlir/lib/Tools/lsp-server-support/SourceMgrUtils.h +++ b/mlir/lib/Tools/lsp-server-support/SourceMgrUtils.h @@ -19,12 +19,39 @@ namespace mlir { namespace lsp { +//===----------------------------------------------------------------------===// +// Utils +//===----------------------------------------------------------------------===// /// Returns the range of a lexical token given a SMLoc corresponding to the /// start of an token location. The range is computed heuristically, and /// supports identifier-like tokens, strings, etc. SMRange convertTokenLocToRange(SMLoc loc); +//===----------------------------------------------------------------------===// +// SourceMgrInclude +//===----------------------------------------------------------------------===// + +/// This class represents a single include within a root file. +struct SourceMgrInclude { + SourceMgrInclude(const lsp::URIForFile &uri, const lsp::Range &range) + : uri(uri), range(range) {} + + /// Build a hover for the current include file. + Hover buildHover() const; + + /// The URI of the file that is included. + lsp::URIForFile uri; + + /// The range of the include directive. + lsp::Range range; +}; + +/// Given a source manager, gather all of the processed include files. These are +/// assumed to be all of the files other than the main root file. +void gatherIncludeFiles(llvm::SourceMgr &sourceMgr, + SmallVectorImpl &includes); + } // namespace lsp } // namespace mlir diff --git a/mlir/lib/Tools/lsp-server-support/SourceMgrUtils.cpp b/mlir/lib/Tools/lsp-server-support/SourceMgrUtils.cpp --- a/mlir/lib/Tools/lsp-server-support/SourceMgrUtils.cpp +++ b/mlir/lib/Tools/lsp-server-support/SourceMgrUtils.cpp @@ -7,10 +7,15 @@ //===----------------------------------------------------------------------===// #include "SourceMgrUtils.h" +#include "llvm/Support/Path.h" using namespace mlir; using namespace mlir::lsp; +//===----------------------------------------------------------------------===// +// Utils +//===----------------------------------------------------------------------===// + /// Find the end of a string whose contents start at the given `curPtr`. Returns /// the position at the end of the string, after a terminal or invalid character /// (e.g. `"` or `\0`). @@ -59,3 +64,46 @@ return SMRange(loc, SMLoc::getFromPointer(curPtr)); } + +//===----------------------------------------------------------------------===// +// SourceMgrInclude +//===----------------------------------------------------------------------===// + +Hover SourceMgrInclude::buildHover() const { + Hover hover(range); + { + llvm::raw_string_ostream hoverOS(hover.contents.value); + hoverOS << "`" << llvm::sys::path::filename(uri.file()) << "`\n***\n" + << uri.file(); + } + return hover; +} + +void lsp::gatherIncludeFiles(llvm::SourceMgr &sourceMgr, + SmallVectorImpl &includes) { + for (unsigned i = 1, e = sourceMgr.getNumBuffers(); i < e; ++i) { + // Check to see if this file was included by the main file. + SMLoc includeLoc = sourceMgr.getBufferInfo(i + 1).IncludeLoc; + if (!includeLoc.isValid() || sourceMgr.FindBufferContainingLoc( + includeLoc) != sourceMgr.getMainFileID()) + continue; + + // Try to build a URI for this file path. + auto *buffer = sourceMgr.getMemoryBuffer(i + 1); + llvm::SmallString<256> path(buffer->getBufferIdentifier()); + llvm::sys::path::remove_dots(path, /*remove_dot_dot=*/true); + + llvm::Expected includedFileURI = URIForFile::fromFile(path); + if (!includedFileURI) + continue; + + // Find the end of the include token. + const char *includeStart = includeLoc.getPointer() - 2; + while (*(--includeStart) != '\"') + continue; + + // Push this include. + SMRange includeRange(SMLoc::getFromPointer(includeStart), includeLoc); + includes.emplace_back(*includedFileURI, Range(sourceMgr, includeRange)); + } +} 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 @@ -11,6 +11,7 @@ #include "../lsp-server-support/CompilationDatabase.h" #include "../lsp-server-support/Logging.h" #include "../lsp-server-support/Protocol.h" +#include "../lsp-server-support/SourceMgrUtils.h" #include "mlir/Tools/PDLL/AST/Context.h" #include "mlir/Tools/PDLL/AST/Nodes.h" #include "mlir/Tools/PDLL/AST/Types.h" @@ -106,24 +107,6 @@ return lspDiag; } -//===----------------------------------------------------------------------===// -// PDLLInclude -//===----------------------------------------------------------------------===// - -namespace { -/// This class represents a single include within a root file. -struct PDLLInclude { - PDLLInclude(const lsp::URIForFile &uri, const lsp::Range &range) - : uri(uri), range(range) {} - - /// The URI of the file that is included. - lsp::URIForFile uri; - - /// The range of the include directive. - lsp::Range range; -}; -} // namespace - //===----------------------------------------------------------------------===// // PDLIndex //===----------------------------------------------------------------------===// @@ -288,7 +271,6 @@ const lsp::Position &hoverPos); Optional findHover(const ast::Decl *decl, const SMRange &hoverRange); - lsp::Hover buildHoverForInclude(const PDLLInclude &include); lsp::Hover buildHoverForOpName(const ods::Operation *op, const SMRange &hoverRange); lsp::Hover buildHoverForVariable(const ast::VariableDecl *varDecl, @@ -343,7 +325,7 @@ PDLIndex index; /// The set of includes of the parsed module. - std::vector parsedIncludes; + SmallVector parsedIncludes; }; } // namespace @@ -373,33 +355,7 @@ astModule = parsePDLAST(astContext, sourceMgr); // Initialize the set of parsed includes. - for (unsigned i = 1, e = sourceMgr.getNumBuffers(); i < e; ++i) { - // Check to see if this file was included by the main file. - SMLoc includeLoc = sourceMgr.getBufferInfo(i + 1).IncludeLoc; - if (!includeLoc.isValid() || sourceMgr.FindBufferContainingLoc( - includeLoc) != sourceMgr.getMainFileID()) - continue; - - // Try to build a URI for this file path. - auto *buffer = sourceMgr.getMemoryBuffer(i + 1); - llvm::SmallString<256> path(buffer->getBufferIdentifier()); - llvm::sys::path::remove_dots(path, /*remove_dot_dot=*/true); - - llvm::Expected includedFileURI = - lsp::URIForFile::fromFile(path); - if (!includedFileURI) - continue; - - // Find the end of the include token. - const char *includeStart = includeLoc.getPointer() - 2; - while (*(--includeStart) != '\"') - continue; - - // Push this include. - SMRange includeRange(SMLoc::getFromPointer(includeStart), includeLoc); - parsedIncludes.emplace_back(*includedFileURI, - lsp::Range(sourceMgr, includeRange)); - } + lsp::gatherIncludeFiles(sourceMgr, parsedIncludes); // If we failed to parse the module, there is nothing left to initialize. if (failed(astModule)) @@ -443,7 +399,7 @@ void PDLDocument::getDocumentLinks(const lsp::URIForFile &uri, std::vector &links) { - for (const PDLLInclude &include : parsedIncludes) + for (const lsp::SourceMgrInclude &include : parsedIncludes) links.emplace_back(include.range, include.uri); } @@ -456,10 +412,9 @@ SMLoc posLoc = hoverPos.getAsSMLoc(sourceMgr); // Check for a reference to an include. - for (const PDLLInclude &include : parsedIncludes) { + for (const lsp::SourceMgrInclude &include : parsedIncludes) if (include.range.contains(hoverPos)) - return buildHoverForInclude(include); - } + return include.buildHover(); // Find the symbol at the given location. SMRange hoverRange; @@ -499,17 +454,6 @@ return llvm::None; } -lsp::Hover PDLDocument::buildHoverForInclude(const PDLLInclude &include) { - lsp::Hover hover(include.range); - { - llvm::raw_string_ostream hoverOS(hover.contents.value); - hoverOS << "`" << llvm::sys::path::filename(include.uri.file()) - << "`\n***\n" - << include.uri.file(); - } - return hover; -} - lsp::Hover PDLDocument::buildHoverForOpName(const ods::Operation *op, const SMRange &hoverRange) { lsp::Hover hover(lsp::Range(sourceMgr, hoverRange)); diff --git a/mlir/lib/Tools/tblgen-lsp-server/LSPServer.cpp b/mlir/lib/Tools/tblgen-lsp-server/LSPServer.cpp --- a/mlir/lib/Tools/tblgen-lsp-server/LSPServer.cpp +++ b/mlir/lib/Tools/tblgen-lsp-server/LSPServer.cpp @@ -42,6 +42,18 @@ void onDocumentDidClose(const DidCloseTextDocumentParams ¶ms); void onDocumentDidChange(const DidChangeTextDocumentParams ¶ms); + //===----------------------------------------------------------------------===// + // DocumentLink + + void onDocumentLink(const DocumentLinkParams ¶ms, + Callback> reply); + + //===--------------------------------------------------------------------===// + // Hover + + void onHover(const TextDocumentPositionParams ¶ms, + Callback> reply); + //===--------------------------------------------------------------------===// // Fields //===--------------------------------------------------------------------===// @@ -72,6 +84,11 @@ {"change", (int)TextDocumentSyncKind::Full}, {"save", true}, }}, + {"documentLinkProvider", + llvm::json::Object{ + {"resolveProvider", false}, + }}, + {"hoverProvider", true}, }; llvm::json::Object result{ @@ -125,6 +142,24 @@ publishDiagnostics(diagParams); } +//===----------------------------------------------------------------------===// +// DocumentLink + +void LSPServer::onDocumentLink(const DocumentLinkParams ¶ms, + Callback> reply) { + std::vector links; + server.getDocumentLinks(params.textDocument.uri, links); + reply(std::move(links)); +} + +//===----------------------------------------------------------------------===// +// Hover + +void LSPServer::onHover(const TextDocumentPositionParams ¶ms, + Callback> reply) { + reply(server.findHover(params.textDocument.uri, params.position)); +} + //===----------------------------------------------------------------------===// // Entry Point //===----------------------------------------------------------------------===// @@ -148,6 +183,13 @@ messageHandler.notification("textDocument/didChange", &lspServer, &LSPServer::onDocumentDidChange); + // Document Link + messageHandler.method("textDocument/documentLink", &lspServer, + &LSPServer::onDocumentLink); + + // Hover + messageHandler.method("textDocument/hover", &lspServer, &LSPServer::onHover); + // Diagnostics lspServer.publishDiagnostics = messageHandler.outgoingNotification( diff --git a/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.h b/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.h --- a/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.h +++ b/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.h @@ -17,6 +17,9 @@ namespace mlir { namespace lsp { struct Diagnostic; +struct DocumentLink; +struct Hover; +struct Position; class URIForFile; /// This class implements all of the TableGen related functionality necessary @@ -52,6 +55,14 @@ /// the server. Optional removeDocument(const URIForFile &uri); + /// Return the document links referenced by the given file. + void getDocumentLinks(const URIForFile &uri, + std::vector &documentLinks); + + /// 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; std::unique_ptr impl; diff --git a/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.cpp b/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.cpp --- a/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.cpp +++ b/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.cpp @@ -103,6 +103,20 @@ /// Return the current version of this text file. int64_t getVersion() const { return version; } + //===--------------------------------------------------------------------===// + // Document Links + //===--------------------------------------------------------------------===// + + void getDocumentLinks(const lsp::URIForFile &uri, + std::vector &links); + + //===--------------------------------------------------------------------===// + // Hover + //===--------------------------------------------------------------------===// + + Optional findHover(const lsp::URIForFile &uri, + const lsp::Position &hoverPos); + private: /// The full string contents of the file. std::string contents; @@ -118,6 +132,9 @@ /// The record keeper containing the parsed tablegen constructs. llvm::RecordKeeper recordKeeper; + + /// The set of includes of the parsed file. + SmallVector parsedIncludes; }; } // namespace @@ -157,10 +174,38 @@ ctx->diagnostics.push_back(*lspDiag); }, &handlerContext); - if (llvm::TableGenParseFile(sourceMgr, recordKeeper)) + bool failedToParse = llvm::TableGenParseFile(sourceMgr, recordKeeper); + + // Process all of the include files. + lsp::gatherIncludeFiles(sourceMgr, parsedIncludes); + if (failedToParse) return; } +//===--------------------------------------------------------------------===// +// TableGenTextFile: Document Links +//===--------------------------------------------------------------------===// + +void TableGenTextFile::getDocumentLinks(const lsp::URIForFile &uri, + std::vector &links) { + for (const lsp::SourceMgrInclude &include : parsedIncludes) + links.emplace_back(include.range, include.uri); +} + +//===----------------------------------------------------------------------===// +// TableGenTextFile: Hover +//===----------------------------------------------------------------------===// + +Optional +TableGenTextFile::findHover(const lsp::URIForFile &uri, + const lsp::Position &hoverPos) { + // Check for a reference to an include. + for (const lsp::SourceMgrInclude &include : parsedIncludes) + if (include.range.contains(hoverPos)) + return include.buildHover(); + return llvm::None; +} + //===----------------------------------------------------------------------===// // TableGenServer::Impl //===----------------------------------------------------------------------===// @@ -209,3 +254,18 @@ impl->files.erase(it); return version; } + +void lsp::TableGenServer::getDocumentLinks( + const URIForFile &uri, std::vector &documentLinks) { + auto fileIt = impl->files.find(uri.file()); + if (fileIt != impl->files.end()) + return fileIt->second->getDocumentLinks(uri, documentLinks); +} + +Optional lsp::TableGenServer::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/tblgen-lsp-server/document-links.test b/mlir/test/tblgen-lsp-server/document-links.test new file mode 100644 --- /dev/null +++ b/mlir/test/tblgen-lsp-server/document-links.test @@ -0,0 +1,34 @@ +// RUN: tblgen-lsp-server -tablegen-extra-dir %S -tablegen-extra-dir %S/../../include -lit-test < %s | FileCheck %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"tablegen","capabilities":{},"trace":"off"}} +// ----- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{ + "uri":"test:///foo.td", + "languageId":"tablegen", + "version":1, + "text":"include \"include/included.td\"" +}}} +// ----- +{"jsonrpc":"2.0","id":1,"method":"textDocument/documentLink","params":{ + "textDocument":{"uri":"test:///foo.td"} +}} +// CHECK: "id": 1, +// CHECK-NEXT: "jsonrpc": "2.0", +// CHECK-NEXT: "result": [ +// CHECK-NEXT: { +// CHECK-NEXT: "range": { +// CHECK-NEXT: "end": { +// CHECK-NEXT: "character": 29, +// CHECK-NEXT: "line": 0 +// CHECK-NEXT: }, +// CHECK-NEXT: "start": { +// CHECK-NEXT: "character": 8, +// CHECK-NEXT: "line": 0 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "target": "file:{{.*}}included.td" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// ----- +{"jsonrpc":"2.0","id":7,"method":"shutdown"} +// ----- +{"jsonrpc":"2.0","method":"exit"} diff --git a/mlir/test/tblgen-lsp-server/hover.test b/mlir/test/tblgen-lsp-server/hover.test new file mode 100644 --- /dev/null +++ b/mlir/test/tblgen-lsp-server/hover.test @@ -0,0 +1,37 @@ +// RUN: tblgen-lsp-server -tablegen-extra-dir %S -tablegen-extra-dir %S/../../include -lit-test < %s | FileCheck %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"tablegen","capabilities":{},"trace":"off"}} +// ----- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{ + "uri":"test:///foo.td", + "languageId":"tablegen", + "version":1, + "text":"include \"include/included.td\"" +}}} +// ----- +// Hover on an include file. +{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{ + "textDocument":{"uri":"test:///foo.td"}, + "position":{"line":0,"character":15} +}} +// CHECK: "id": 1, +// CHECK-NEXT: "jsonrpc": "2.0", +// CHECK-NEXT: "result": { +// CHECK-NEXT: "contents": { +// CHECK-NEXT: "kind": "markdown", +// CHECK-NEXT: "value": "`included.td`\n***\n{{.*}}included.td" +// CHECK-NEXT: }, +// CHECK-NEXT: "range": { +// CHECK-NEXT: "end": { +// CHECK-NEXT: "character": 29, +// CHECK-NEXT: "line": 0 +// CHECK-NEXT: }, +// CHECK-NEXT: "start": { +// CHECK-NEXT: "character": 8, +// CHECK-NEXT: "line": 0 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// ----- +{"jsonrpc":"2.0","id":7,"method":"shutdown"} +// ----- +{"jsonrpc":"2.0","method":"exit"} diff --git a/mlir/test/tblgen-lsp-server/initialize-params.test b/mlir/test/tblgen-lsp-server/initialize-params.test --- a/mlir/test/tblgen-lsp-server/initialize-params.test +++ b/mlir/test/tblgen-lsp-server/initialize-params.test @@ -5,6 +5,10 @@ // CHECK-NEXT: "jsonrpc": "2.0", // CHECK-NEXT: "result": { // CHECK-NEXT: "capabilities": { +// CHECK-NEXT: "documentLinkProvider": { +// CHECK-NEXT: "resolveProvider": false +// CHECK-NEXT: }, +// CHECK-NEXT: "hoverProvider": true, // CHECK-NEXT: "textDocumentSync": { // CHECK-NEXT: "change": 1, // CHECK-NEXT: "openClose": true,