mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-18 16:58:23 +00:00
[mlir][PDLL] Add an initial language server for PDLL
This commits adds a basic language server for PDLL to enable providing language features in IDEs such as VSCode. This initial commit only adds support for tracking definitions, references, and diagnostics, but followup commits will build upon this to provide more significant behavior. In addition to the server, this commit also updates mlir-vscode to support the PDLL language and invoke the server. Differential Revision: https://reviews.llvm.org/D121541
This commit is contained in:
parent
1a820ff039
commit
52b34df9d6
@ -131,6 +131,18 @@ public:
|
||||
/// Print this node to the given stream.
|
||||
void print(raw_ostream &os) const;
|
||||
|
||||
/// Walk all of the nodes including, and nested under, this node in pre-order.
|
||||
void walk(function_ref<void(const Node *)> walkFn) const;
|
||||
template <typename WalkFnT, typename ArgT = typename llvm::function_traits<
|
||||
WalkFnT>::template arg_t<0>>
|
||||
std::enable_if_t<!std::is_convertible<const Node *, ArgT>::value>
|
||||
walk(WalkFnT &&walkFn) const {
|
||||
walk([&](const Node *node) {
|
||||
if (const ArgT *derivedNode = dyn_cast<ArgT>(node))
|
||||
walkFn(derivedNode);
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
Node(TypeID typeID, SMRange loc) : typeID(typeID), loc(loc) {}
|
||||
|
||||
|
@ -0,0 +1,25 @@
|
||||
//===- MlirPdllLspServerMain.h - MLIR PDLL Language Server main -*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Main entry function for mlir-pdll-lsp-server for when built as standalone
|
||||
// binary.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef MLIR_TOOLS_MLIR_PDLL_LSP_SERVER_MLIRPDLLLSPSERVERMAIN_H
|
||||
#define MLIR_TOOLS_MLIR_PDLL_LSP_SERVER_MLIRPDLLLSPSERVERMAIN_H
|
||||
|
||||
namespace mlir {
|
||||
struct LogicalResult;
|
||||
|
||||
/// Implementation for tools like `mlir-pdll-lsp-server`.
|
||||
LogicalResult MlirPdllLspServerMain(int argc, char **argv);
|
||||
|
||||
} // namespace mlir
|
||||
|
||||
#endif // MLIR_TOOLS_MLIR_PDLL_LSP_SERVER_MLIRPDLLLSPSERVERMAIN_H
|
@ -1,6 +1,7 @@
|
||||
add_subdirectory(lsp-server-support)
|
||||
add_subdirectory(mlir-lsp-server)
|
||||
add_subdirectory(mlir-opt)
|
||||
add_subdirectory(mlir-pdll-lsp-server)
|
||||
add_subdirectory(mlir-reduce)
|
||||
add_subdirectory(mlir-translate)
|
||||
add_subdirectory(PDLL)
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "mlir/Tools/PDLL/AST/Nodes.h"
|
||||
#include "mlir/Tools/PDLL/AST/Context.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/TypeSwitch.h"
|
||||
|
||||
using namespace mlir;
|
||||
using namespace mlir::pdll::ast;
|
||||
@ -33,6 +34,135 @@ const Name &Name::create(Context &ctx, StringRef name, SMRange location) {
|
||||
Name(copyStringWithNull(ctx, name), location);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Node
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
class NodeVisitor {
|
||||
public:
|
||||
explicit NodeVisitor(function_ref<void(const Node *)> visitFn)
|
||||
: visitFn(visitFn) {}
|
||||
|
||||
void visit(const Node *node) {
|
||||
if (!node || !alreadyVisited.insert(node).second)
|
||||
return;
|
||||
|
||||
visitFn(node);
|
||||
TypeSwitch<const Node *>(node)
|
||||
.Case<
|
||||
// Statements.
|
||||
const CompoundStmt, const EraseStmt, const LetStmt,
|
||||
const ReplaceStmt, const ReturnStmt, const RewriteStmt,
|
||||
|
||||
// Expressions.
|
||||
const AttributeExpr, const CallExpr, const DeclRefExpr,
|
||||
const MemberAccessExpr, const OperationExpr, const TupleExpr,
|
||||
const TypeExpr,
|
||||
|
||||
// Core Constraint Decls.
|
||||
const AttrConstraintDecl, const OpConstraintDecl,
|
||||
const TypeConstraintDecl, const TypeRangeConstraintDecl,
|
||||
const ValueConstraintDecl, const ValueRangeConstraintDecl,
|
||||
|
||||
// Decls.
|
||||
const NamedAttributeDecl, const OpNameDecl, const PatternDecl,
|
||||
const UserConstraintDecl, const UserRewriteDecl, const VariableDecl,
|
||||
|
||||
const Module>(
|
||||
[&](auto derivedNode) { this->visitImpl(derivedNode); })
|
||||
.Default([](const Node *) { llvm_unreachable("unknown AST node"); });
|
||||
}
|
||||
|
||||
private:
|
||||
void visitImpl(const CompoundStmt *stmt) {
|
||||
for (const Node *child : stmt->getChildren())
|
||||
visit(child);
|
||||
}
|
||||
void visitImpl(const EraseStmt *stmt) { visit(stmt->getRootOpExpr()); }
|
||||
void visitImpl(const LetStmt *stmt) { visit(stmt->getVarDecl()); }
|
||||
void visitImpl(const ReplaceStmt *stmt) {
|
||||
visit(stmt->getRootOpExpr());
|
||||
for (const Node *child : stmt->getReplExprs())
|
||||
visit(child);
|
||||
}
|
||||
void visitImpl(const ReturnStmt *stmt) { visit(stmt->getResultExpr()); }
|
||||
void visitImpl(const RewriteStmt *stmt) {
|
||||
visit(stmt->getRootOpExpr());
|
||||
visit(stmt->getRewriteBody());
|
||||
}
|
||||
|
||||
void visitImpl(const AttributeExpr *expr) {}
|
||||
void visitImpl(const CallExpr *expr) {
|
||||
visit(expr->getCallableExpr());
|
||||
for (const Node *child : expr->getArguments())
|
||||
visit(child);
|
||||
}
|
||||
void visitImpl(const DeclRefExpr *expr) { visit(expr->getDecl()); }
|
||||
void visitImpl(const MemberAccessExpr *expr) { visit(expr->getParentExpr()); }
|
||||
void visitImpl(const OperationExpr *expr) {
|
||||
visit(expr->getNameDecl());
|
||||
for (const Node *child : expr->getOperands())
|
||||
visit(child);
|
||||
for (const Node *child : expr->getResultTypes())
|
||||
visit(child);
|
||||
for (const Node *child : expr->getAttributes())
|
||||
visit(child);
|
||||
}
|
||||
void visitImpl(const TupleExpr *expr) {
|
||||
for (const Node *child : expr->getElements())
|
||||
visit(child);
|
||||
}
|
||||
void visitImpl(const TypeExpr *expr) {}
|
||||
|
||||
void visitImpl(const AttrConstraintDecl *decl) { visit(decl->getTypeExpr()); }
|
||||
void visitImpl(const OpConstraintDecl *decl) { visit(decl->getNameDecl()); }
|
||||
void visitImpl(const TypeConstraintDecl *decl) {}
|
||||
void visitImpl(const TypeRangeConstraintDecl *decl) {}
|
||||
void visitImpl(const ValueConstraintDecl *decl) {
|
||||
visit(decl->getTypeExpr());
|
||||
}
|
||||
void visitImpl(const ValueRangeConstraintDecl *decl) {
|
||||
visit(decl->getTypeExpr());
|
||||
}
|
||||
|
||||
void visitImpl(const NamedAttributeDecl *decl) { visit(decl->getValue()); }
|
||||
void visitImpl(const OpNameDecl *decl) {}
|
||||
void visitImpl(const PatternDecl *decl) { visit(decl->getBody()); }
|
||||
void visitImpl(const UserConstraintDecl *decl) {
|
||||
for (const Node *child : decl->getInputs())
|
||||
visit(child);
|
||||
for (const Node *child : decl->getResults())
|
||||
visit(child);
|
||||
visit(decl->getBody());
|
||||
}
|
||||
void visitImpl(const UserRewriteDecl *decl) {
|
||||
for (const Node *child : decl->getInputs())
|
||||
visit(child);
|
||||
for (const Node *child : decl->getResults())
|
||||
visit(child);
|
||||
visit(decl->getBody());
|
||||
}
|
||||
void visitImpl(const VariableDecl *decl) {
|
||||
visit(decl->getInitExpr());
|
||||
for (const ConstraintRef &child : decl->getConstraints())
|
||||
visit(child.constraint);
|
||||
}
|
||||
|
||||
void visitImpl(const Module *module) {
|
||||
for (const Node *child : module->getChildren())
|
||||
visit(child);
|
||||
}
|
||||
|
||||
function_ref<void(const Node *)> visitFn;
|
||||
SmallPtrSet<const Node *, 16> alreadyVisited;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void Node::walk(function_ref<void(const Node *)> walkFn) const {
|
||||
return NodeVisitor(walkFn).visit(this);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// DeclScope
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "mlir/Support/LLVM.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/Support/JSON.h"
|
||||
#include "llvm/Support/SourceMgr.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <bitset>
|
||||
#include <memory>
|
||||
@ -245,6 +246,13 @@ struct Position {
|
||||
Position(int line = 0, int character = 0)
|
||||
: line(line), character(character) {}
|
||||
|
||||
/// Construct a position from the given source location.
|
||||
Position(llvm::SourceMgr &mgr, SMLoc loc) {
|
||||
std::pair<unsigned, unsigned> lineAndCol = mgr.getLineAndColumn(loc);
|
||||
line = lineAndCol.first - 1;
|
||||
character = lineAndCol.second - 1;
|
||||
}
|
||||
|
||||
/// Line position in a document (zero-based).
|
||||
int line = 0;
|
||||
|
||||
@ -266,6 +274,13 @@ struct Position {
|
||||
return std::tie(lhs.line, lhs.character) <=
|
||||
std::tie(rhs.line, rhs.character);
|
||||
}
|
||||
|
||||
/// Convert this position into a source location in the main file of the given
|
||||
/// source manager.
|
||||
SMLoc getAsSMLoc(llvm::SourceMgr &mgr) const {
|
||||
return mgr.FindLocForLineAndColumn(mgr.getMainFileID(), line + 1,
|
||||
character);
|
||||
}
|
||||
};
|
||||
|
||||
/// Add support for JSON serialization.
|
||||
@ -283,6 +298,10 @@ struct Range {
|
||||
Range(Position start, Position end) : start(start), end(end) {}
|
||||
Range(Position loc) : Range(loc, loc) {}
|
||||
|
||||
/// Construct a range from the given source range.
|
||||
Range(llvm::SourceMgr &mgr, SMRange range)
|
||||
: Range(Position(mgr, range.Start), Position(mgr, range.End)) {}
|
||||
|
||||
/// The range's start position.
|
||||
Position start;
|
||||
|
||||
@ -316,6 +335,13 @@ raw_ostream &operator<<(raw_ostream &os, const Range &value);
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
struct Location {
|
||||
Location() = default;
|
||||
Location(const URIForFile &uri, Range range) : uri(uri), range(range) {}
|
||||
|
||||
/// Construct a Location from the given source range.
|
||||
Location(const URIForFile &uri, llvm::SourceMgr &mgr, SMRange range)
|
||||
: Location(uri, Range(mgr, range)) {}
|
||||
|
||||
/// The text document's URI.
|
||||
URIForFile uri;
|
||||
Range range;
|
||||
|
@ -17,32 +17,6 @@
|
||||
|
||||
using namespace mlir;
|
||||
|
||||
/// Returns a language server position for the given source location.
|
||||
static lsp::Position getPosFromLoc(llvm::SourceMgr &mgr, SMLoc loc) {
|
||||
std::pair<unsigned, unsigned> lineAndCol = mgr.getLineAndColumn(loc);
|
||||
lsp::Position pos;
|
||||
pos.line = lineAndCol.first - 1;
|
||||
pos.character = lineAndCol.second - 1;
|
||||
return pos;
|
||||
}
|
||||
|
||||
/// Returns a source location from the given language server position.
|
||||
static SMLoc getPosFromLoc(llvm::SourceMgr &mgr, lsp::Position pos) {
|
||||
return mgr.FindLocForLineAndColumn(mgr.getMainFileID(), pos.line + 1,
|
||||
pos.character);
|
||||
}
|
||||
|
||||
/// Returns a language server range for the given source range.
|
||||
static lsp::Range getRangeFromLoc(llvm::SourceMgr &mgr, SMRange range) {
|
||||
return {getPosFromLoc(mgr, range.Start), getPosFromLoc(mgr, range.End)};
|
||||
}
|
||||
|
||||
/// Returns a language server location from the given source range.
|
||||
static lsp::Location getLocationFromLoc(llvm::SourceMgr &mgr, SMRange range,
|
||||
const lsp::URIForFile &uri) {
|
||||
return lsp::Location{uri, getRangeFromLoc(mgr, range)};
|
||||
}
|
||||
|
||||
/// Returns a language server location from the given MLIR file location.
|
||||
static Optional<lsp::Location> getLocationFromLoc(FileLineColLoc loc) {
|
||||
llvm::Expected<lsp::URIForFile> sourceURI =
|
||||
@ -348,13 +322,13 @@ MLIRDocument::MLIRDocument(MLIRContext &context, const lsp::URIForFile &uri,
|
||||
void MLIRDocument::getLocationsOf(const lsp::URIForFile &uri,
|
||||
const lsp::Position &defPos,
|
||||
std::vector<lsp::Location> &locations) {
|
||||
SMLoc posLoc = getPosFromLoc(sourceMgr, defPos);
|
||||
SMLoc posLoc = defPos.getAsSMLoc(sourceMgr);
|
||||
|
||||
// Functor used to check if an SM definition contains the position.
|
||||
auto containsPosition = [&](const AsmParserState::SMDefinition &def) {
|
||||
if (!isDefOrUse(def, posLoc))
|
||||
return false;
|
||||
locations.push_back(getLocationFromLoc(sourceMgr, def.loc, uri));
|
||||
locations.emplace_back(uri, sourceMgr, def.loc);
|
||||
return true;
|
||||
};
|
||||
|
||||
@ -367,7 +341,7 @@ void MLIRDocument::getLocationsOf(const lsp::URIForFile &uri,
|
||||
return collectLocationsFromLoc(op.op->getLoc(), locations, uri);
|
||||
for (const auto &symUse : op.symbolUses) {
|
||||
if (contains(symUse, posLoc)) {
|
||||
locations.push_back(getLocationFromLoc(sourceMgr, op.loc, uri));
|
||||
locations.emplace_back(uri, sourceMgr, op.loc);
|
||||
return collectLocationsFromLoc(op.op->getLoc(), locations, uri);
|
||||
}
|
||||
}
|
||||
@ -389,12 +363,12 @@ void MLIRDocument::findReferencesOf(const lsp::URIForFile &uri,
|
||||
// Functor used to append all of the definitions/uses of the given SM
|
||||
// definition to the reference list.
|
||||
auto appendSMDef = [&](const AsmParserState::SMDefinition &def) {
|
||||
references.push_back(getLocationFromLoc(sourceMgr, def.loc, uri));
|
||||
references.emplace_back(uri, sourceMgr, def.loc);
|
||||
for (const SMRange &use : def.uses)
|
||||
references.push_back(getLocationFromLoc(sourceMgr, use, uri));
|
||||
references.emplace_back(uri, sourceMgr, use);
|
||||
};
|
||||
|
||||
SMLoc posLoc = getPosFromLoc(sourceMgr, pos);
|
||||
SMLoc posLoc = pos.getAsSMLoc(sourceMgr);
|
||||
|
||||
// Check all definitions related to operations.
|
||||
for (const AsmParserState::OperationDefinition &op : asmState.getOpDefs()) {
|
||||
@ -403,7 +377,7 @@ void MLIRDocument::findReferencesOf(const lsp::URIForFile &uri,
|
||||
appendSMDef(result.definition);
|
||||
for (const auto &symUse : op.symbolUses)
|
||||
if (contains(symUse, posLoc))
|
||||
references.push_back(getLocationFromLoc(sourceMgr, symUse, uri));
|
||||
references.emplace_back(uri, sourceMgr, symUse);
|
||||
return;
|
||||
}
|
||||
for (const auto &result : op.resultGroups)
|
||||
@ -413,7 +387,7 @@ void MLIRDocument::findReferencesOf(const lsp::URIForFile &uri,
|
||||
if (!contains(symUse, posLoc))
|
||||
continue;
|
||||
for (const auto &symUse : op.symbolUses)
|
||||
references.push_back(getLocationFromLoc(sourceMgr, symUse, uri));
|
||||
references.emplace_back(uri, sourceMgr, symUse);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -435,7 +409,7 @@ void MLIRDocument::findReferencesOf(const lsp::URIForFile &uri,
|
||||
|
||||
Optional<lsp::Hover> MLIRDocument::findHover(const lsp::URIForFile &uri,
|
||||
const lsp::Position &hoverPos) {
|
||||
SMLoc posLoc = getPosFromLoc(sourceMgr, hoverPos);
|
||||
SMLoc posLoc = hoverPos.getAsSMLoc(sourceMgr);
|
||||
SMRange hoverRange;
|
||||
|
||||
// Check for Hovers on operations and results.
|
||||
@ -482,7 +456,7 @@ Optional<lsp::Hover> MLIRDocument::findHover(const lsp::URIForFile &uri,
|
||||
|
||||
Optional<lsp::Hover> MLIRDocument::buildHoverForOperation(
|
||||
SMRange hoverRange, const AsmParserState::OperationDefinition &op) {
|
||||
lsp::Hover hover(getRangeFromLoc(sourceMgr, hoverRange));
|
||||
lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
|
||||
llvm::raw_string_ostream os(hover.contents.value);
|
||||
|
||||
// Add the operation name to the hover.
|
||||
@ -518,7 +492,7 @@ lsp::Hover MLIRDocument::buildHoverForOperationResult(SMRange hoverRange,
|
||||
unsigned resultStart,
|
||||
unsigned resultEnd,
|
||||
SMLoc posLoc) {
|
||||
lsp::Hover hover(getRangeFromLoc(sourceMgr, hoverRange));
|
||||
lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
|
||||
llvm::raw_string_ostream os(hover.contents.value);
|
||||
|
||||
// Add the parent operation name to the hover.
|
||||
@ -551,7 +525,7 @@ lsp::Hover MLIRDocument::buildHoverForOperationResult(SMRange hoverRange,
|
||||
lsp::Hover
|
||||
MLIRDocument::buildHoverForBlock(SMRange hoverRange,
|
||||
const AsmParserState::BlockDefinition &block) {
|
||||
lsp::Hover hover(getRangeFromLoc(sourceMgr, hoverRange));
|
||||
lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
|
||||
llvm::raw_string_ostream os(hover.contents.value);
|
||||
|
||||
// Print the given block to the hover output stream.
|
||||
@ -583,7 +557,7 @@ MLIRDocument::buildHoverForBlock(SMRange hoverRange,
|
||||
lsp::Hover MLIRDocument::buildHoverForBlockArgument(
|
||||
SMRange hoverRange, BlockArgument arg,
|
||||
const AsmParserState::BlockDefinition &block) {
|
||||
lsp::Hover hover(getRangeFromLoc(sourceMgr, hoverRange));
|
||||
lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
|
||||
llvm::raw_string_ostream os(hover.contents.value);
|
||||
|
||||
// Display the parent operation, block, the argument number, and the type.
|
||||
@ -618,16 +592,16 @@ void MLIRDocument::findDocumentSymbols(
|
||||
isa<FunctionOpInterface>(op)
|
||||
? lsp::SymbolKind::Function
|
||||
: lsp::SymbolKind::Class,
|
||||
getRangeFromLoc(sourceMgr, def->scopeLoc),
|
||||
getRangeFromLoc(sourceMgr, def->loc));
|
||||
lsp::Range(sourceMgr, def->scopeLoc),
|
||||
lsp::Range(sourceMgr, def->loc));
|
||||
childSymbols = &symbols.back().children;
|
||||
|
||||
} else if (op->hasTrait<OpTrait::SymbolTable>()) {
|
||||
// Otherwise, if this is a symbol table push an anonymous document symbol.
|
||||
symbols.emplace_back("<" + op->getName().getStringRef() + ">",
|
||||
lsp::SymbolKind::Namespace,
|
||||
getRangeFromLoc(sourceMgr, def->scopeLoc),
|
||||
getRangeFromLoc(sourceMgr, def->loc));
|
||||
lsp::Range(sourceMgr, def->scopeLoc),
|
||||
lsp::Range(sourceMgr, def->loc));
|
||||
childSymbols = &symbols.back().children;
|
||||
}
|
||||
}
|
||||
|
12
mlir/lib/Tools/mlir-pdll-lsp-server/CMakeLists.txt
Normal file
12
mlir/lib/Tools/mlir-pdll-lsp-server/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
||||
llvm_add_library(MLIRPdllLspServerLib
|
||||
LSPServer.cpp
|
||||
PDLLServer.cpp
|
||||
MlirPdllLspServerMain.cpp
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
${MLIR_MAIN_INCLUDE_DIR}/mlir/Tools/mlir-pdll-lsp-server
|
||||
|
||||
LINK_LIBS PUBLIC
|
||||
MLIRPDLLParser
|
||||
MLIRLspServerSupportLib
|
||||
)
|
198
mlir/lib/Tools/mlir-pdll-lsp-server/LSPServer.cpp
Normal file
198
mlir/lib/Tools/mlir-pdll-lsp-server/LSPServer.cpp
Normal file
@ -0,0 +1,198 @@
|
||||
//===- LSPServer.cpp - PDLL Language Server -------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "LSPServer.h"
|
||||
|
||||
#include "../lsp-server-support/Logging.h"
|
||||
#include "../lsp-server-support/Protocol.h"
|
||||
#include "../lsp-server-support/Transport.h"
|
||||
#include "PDLLServer.h"
|
||||
#include "llvm/ADT/FunctionExtras.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
|
||||
#define DEBUG_TYPE "pdll-lsp-server"
|
||||
|
||||
using namespace mlir;
|
||||
using namespace mlir::lsp;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// LSPServer
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
struct LSPServer {
|
||||
LSPServer(PDLLServer &server, JSONTransport &transport)
|
||||
: server(server), transport(transport) {}
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Initialization
|
||||
|
||||
void onInitialize(const InitializeParams ¶ms,
|
||||
Callback<llvm::json::Value> reply);
|
||||
void onInitialized(const InitializedParams ¶ms);
|
||||
void onShutdown(const NoParams ¶ms, Callback<std::nullptr_t> reply);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Document Change
|
||||
|
||||
void onDocumentDidOpen(const DidOpenTextDocumentParams ¶ms);
|
||||
void onDocumentDidClose(const DidCloseTextDocumentParams ¶ms);
|
||||
void onDocumentDidChange(const DidChangeTextDocumentParams ¶ms);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Definitions and References
|
||||
|
||||
void onGoToDefinition(const TextDocumentPositionParams ¶ms,
|
||||
Callback<std::vector<Location>> reply);
|
||||
void onReference(const ReferenceParams ¶ms,
|
||||
Callback<std::vector<Location>> reply);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Fields
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
PDLLServer &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;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Initialization
|
||||
|
||||
void LSPServer::onInitialize(const InitializeParams ¶ms,
|
||||
Callback<llvm::json::Value> reply) {
|
||||
// Send a response with the capabilities of this server.
|
||||
llvm::json::Object serverCaps{
|
||||
{"textDocumentSync",
|
||||
llvm::json::Object{
|
||||
{"openClose", true},
|
||||
{"change", (int)TextDocumentSyncKind::Full},
|
||||
{"save", true},
|
||||
}},
|
||||
{"definitionProvider", true},
|
||||
{"referencesProvider", true},
|
||||
};
|
||||
|
||||
llvm::json::Object result{
|
||||
{{"serverInfo", llvm::json::Object{{"name", "mlir-pdll-lsp-server"},
|
||||
{"version", "0.0.1"}}},
|
||||
{"capabilities", std::move(serverCaps)}}};
|
||||
reply(std::move(result));
|
||||
}
|
||||
void LSPServer::onInitialized(const InitializedParams &) {}
|
||||
void LSPServer::onShutdown(const NoParams &, Callback<std::nullptr_t> reply) {
|
||||
shutdownRequestReceived = true;
|
||||
reply(nullptr);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Document Change
|
||||
|
||||
void LSPServer::onDocumentDidOpen(const DidOpenTextDocumentParams ¶ms) {
|
||||
PublishDiagnosticsParams diagParams(params.textDocument.uri,
|
||||
params.textDocument.version);
|
||||
server.addOrUpdateDocument(params.textDocument.uri, params.textDocument.text,
|
||||
params.textDocument.version,
|
||||
diagParams.diagnostics);
|
||||
|
||||
// Publish any recorded diagnostics.
|
||||
publishDiagnostics(diagParams);
|
||||
}
|
||||
void LSPServer::onDocumentDidClose(const DidCloseTextDocumentParams ¶ms) {
|
||||
Optional<int64_t> version = server.removeDocument(params.textDocument.uri);
|
||||
if (!version)
|
||||
return;
|
||||
|
||||
// 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, *version));
|
||||
}
|
||||
void LSPServer::onDocumentDidChange(const DidChangeTextDocumentParams ¶ms) {
|
||||
// TODO: We currently only support full document updates, we should refactor
|
||||
// to avoid this.
|
||||
if (params.contentChanges.size() != 1)
|
||||
return;
|
||||
PublishDiagnosticsParams diagParams(params.textDocument.uri,
|
||||
params.textDocument.version);
|
||||
server.addOrUpdateDocument(
|
||||
params.textDocument.uri, params.contentChanges.front().text,
|
||||
params.textDocument.version, diagParams.diagnostics);
|
||||
|
||||
// Publish any recorded diagnostics.
|
||||
publishDiagnostics(diagParams);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Definitions and References
|
||||
|
||||
void LSPServer::onGoToDefinition(const TextDocumentPositionParams ¶ms,
|
||||
Callback<std::vector<Location>> reply) {
|
||||
std::vector<Location> locations;
|
||||
server.getLocationsOf(params.textDocument.uri, params.position, locations);
|
||||
reply(std::move(locations));
|
||||
}
|
||||
|
||||
void LSPServer::onReference(const ReferenceParams ¶ms,
|
||||
Callback<std::vector<Location>> reply) {
|
||||
std::vector<Location> locations;
|
||||
server.findReferencesOf(params.textDocument.uri, params.position, locations);
|
||||
reply(std::move(locations));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Entry Point
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
LogicalResult mlir::lsp::runPdllLSPServer(PDLLServer &server,
|
||||
JSONTransport &transport) {
|
||||
LSPServer lspServer(server, transport);
|
||||
MessageHandler messageHandler(transport);
|
||||
|
||||
// Initialization
|
||||
messageHandler.method("initialize", &lspServer, &LSPServer::onInitialize);
|
||||
messageHandler.notification("initialized", &lspServer,
|
||||
&LSPServer::onInitialized);
|
||||
messageHandler.method("shutdown", &lspServer, &LSPServer::onShutdown);
|
||||
|
||||
// Document Changes
|
||||
messageHandler.notification("textDocument/didOpen", &lspServer,
|
||||
&LSPServer::onDocumentDidOpen);
|
||||
messageHandler.notification("textDocument/didClose", &lspServer,
|
||||
&LSPServer::onDocumentDidClose);
|
||||
messageHandler.notification("textDocument/didChange", &lspServer,
|
||||
&LSPServer::onDocumentDidChange);
|
||||
|
||||
// Definitions and References
|
||||
messageHandler.method("textDocument/definition", &lspServer,
|
||||
&LSPServer::onGoToDefinition);
|
||||
messageHandler.method("textDocument/references", &lspServer,
|
||||
&LSPServer::onReference);
|
||||
|
||||
// Diagnostics
|
||||
lspServer.publishDiagnostics =
|
||||
messageHandler.outgoingNotification<PublishDiagnosticsParams>(
|
||||
"textDocument/publishDiagnostics");
|
||||
|
||||
// Run the main loop of the transport.
|
||||
if (llvm::Error error = transport.run(messageHandler)) {
|
||||
Logger::error("Transport error: {0}", error);
|
||||
llvm::consumeError(std::move(error));
|
||||
return failure();
|
||||
}
|
||||
return success(lspServer.shutdownRequestReceived);
|
||||
}
|
28
mlir/lib/Tools/mlir-pdll-lsp-server/LSPServer.h
Normal file
28
mlir/lib/Tools/mlir-pdll-lsp-server/LSPServer.h
Normal file
@ -0,0 +1,28 @@
|
||||
//===- LSPServer.h - PDLL LSP Server ----------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LIB_MLIR_TOOLS_MLIRPDLLLSPSERVER_LSPSERVER_H
|
||||
#define LIB_MLIR_TOOLS_MLIRPDLLLSPSERVER_LSPSERVER_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace mlir {
|
||||
struct LogicalResult;
|
||||
|
||||
namespace lsp {
|
||||
class JSONTransport;
|
||||
class PDLLServer;
|
||||
|
||||
/// Run the main loop of the LSP server using the given PDLL server and
|
||||
/// transport.
|
||||
LogicalResult runPdllLSPServer(PDLLServer &server, JSONTransport &transport);
|
||||
|
||||
} // namespace lsp
|
||||
} // namespace mlir
|
||||
|
||||
#endif // LIB_MLIR_TOOLS_MLIRPDLLLSPSERVER_LSPSERVER_H
|
@ -0,0 +1,72 @@
|
||||
//===- MlirPdllLspServerMain.cpp - MLIR PDLL Language Server main ---------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "mlir/Tools/mlir-pdll-lsp-server/MlirPdllLspServerMain.h"
|
||||
#include "../lsp-server-support/Logging.h"
|
||||
#include "../lsp-server-support/Transport.h"
|
||||
#include "LSPServer.h"
|
||||
#include "PDLLServer.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Program.h"
|
||||
|
||||
using namespace mlir;
|
||||
using namespace mlir::lsp;
|
||||
|
||||
LogicalResult mlir::MlirPdllLspServerMain(int argc, char **argv) {
|
||||
llvm::cl::opt<JSONStreamStyle> inputStyle{
|
||||
"input-style",
|
||||
llvm::cl::desc("Input JSON stream encoding"),
|
||||
llvm::cl::values(clEnumValN(JSONStreamStyle::Standard, "standard",
|
||||
"usual LSP protocol"),
|
||||
clEnumValN(JSONStreamStyle::Delimited, "delimited",
|
||||
"messages delimited by `// -----` lines, "
|
||||
"with // comment support")),
|
||||
llvm::cl::init(JSONStreamStyle::Standard),
|
||||
llvm::cl::Hidden,
|
||||
};
|
||||
llvm::cl::opt<bool> litTest{
|
||||
"lit-test",
|
||||
llvm::cl::desc(
|
||||
"Abbreviation for -input-style=delimited -pretty -log=verbose. "
|
||||
"Intended to simplify lit tests"),
|
||||
llvm::cl::init(false),
|
||||
};
|
||||
llvm::cl::opt<Logger::Level> logLevel{
|
||||
"log",
|
||||
llvm::cl::desc("Verbosity of log messages written to stderr"),
|
||||
llvm::cl::values(
|
||||
clEnumValN(Logger::Level::Error, "error", "Error messages only"),
|
||||
clEnumValN(Logger::Level::Info, "info",
|
||||
"High level execution tracing"),
|
||||
clEnumValN(Logger::Level::Debug, "verbose", "Low level details")),
|
||||
llvm::cl::init(Logger::Level::Info),
|
||||
};
|
||||
llvm::cl::opt<bool> prettyPrint{
|
||||
"pretty",
|
||||
llvm::cl::desc("Pretty-print JSON output"),
|
||||
llvm::cl::init(false),
|
||||
};
|
||||
llvm::cl::ParseCommandLineOptions(argc, argv, "MLIR LSP Language Server");
|
||||
|
||||
if (litTest) {
|
||||
inputStyle = JSONStreamStyle::Delimited;
|
||||
logLevel = Logger::Level::Debug;
|
||||
prettyPrint = true;
|
||||
}
|
||||
|
||||
// Configure the logger.
|
||||
Logger::setLogLevel(logLevel);
|
||||
|
||||
// Configure the transport used for communication.
|
||||
llvm::sys::ChangeStdinToBinary();
|
||||
JSONTransport transport(stdin, llvm::outs(), inputStyle, prettyPrint);
|
||||
|
||||
// Configure the servers and start the main language server.
|
||||
PDLLServer server;
|
||||
return runPdllLSPServer(server, transport);
|
||||
}
|
523
mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.cpp
Normal file
523
mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.cpp
Normal file
@ -0,0 +1,523 @@
|
||||
//===- PDLLServer.cpp - PDLL Language Server ------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "PDLLServer.h"
|
||||
|
||||
#include "../lsp-server-support/Logging.h"
|
||||
#include "../lsp-server-support/Protocol.h"
|
||||
#include "mlir/Tools/PDLL/AST/Context.h"
|
||||
#include "mlir/Tools/PDLL/AST/Nodes.h"
|
||||
#include "mlir/Tools/PDLL/AST/Types.h"
|
||||
#include "mlir/Tools/PDLL/ODS/Constraint.h"
|
||||
#include "mlir/Tools/PDLL/ODS/Context.h"
|
||||
#include "mlir/Tools/PDLL/ODS/Dialect.h"
|
||||
#include "mlir/Tools/PDLL/ODS/Operation.h"
|
||||
#include "mlir/Tools/PDLL/Parser/Parser.h"
|
||||
#include "llvm/ADT/IntervalMap.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/TypeSwitch.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
|
||||
using namespace mlir;
|
||||
using namespace mlir::pdll;
|
||||
|
||||
/// Returns a language server uri for the given source location. `mainFileURI`
|
||||
/// corresponds to the uri for the main file of the source manager.
|
||||
static lsp::URIForFile getURIFromLoc(llvm::SourceMgr &mgr, SMRange loc,
|
||||
const lsp::URIForFile &mainFileURI) {
|
||||
int bufferId = mgr.FindBufferContainingLoc(loc.Start);
|
||||
if (bufferId == 0 || bufferId == static_cast<int>(mgr.getMainFileID()))
|
||||
return mainFileURI;
|
||||
llvm::Expected<lsp::URIForFile> fileForLoc = lsp::URIForFile::fromFile(
|
||||
mgr.getBufferInfo(bufferId).Buffer->getBufferIdentifier());
|
||||
if (fileForLoc)
|
||||
return *fileForLoc;
|
||||
lsp::Logger::error("Failed to create URI for include file: {0}",
|
||||
llvm::toString(fileForLoc.takeError()));
|
||||
return mainFileURI;
|
||||
}
|
||||
|
||||
/// Returns a language server location from the given source range.
|
||||
static lsp::Location getLocationFromLoc(llvm::SourceMgr &mgr, SMRange range,
|
||||
const lsp::URIForFile &uri) {
|
||||
return lsp::Location(getURIFromLoc(mgr, range, uri), lsp::Range(mgr, range));
|
||||
}
|
||||
|
||||
/// Convert the given MLIR diagnostic to the LSP form.
|
||||
static Optional<lsp::Diagnostic>
|
||||
getLspDiagnoticFromDiag(llvm::SourceMgr &sourceMgr, const ast::Diagnostic &diag,
|
||||
const lsp::URIForFile &uri) {
|
||||
lsp::Diagnostic lspDiag;
|
||||
lspDiag.source = "pdll";
|
||||
|
||||
// FIXME: 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.
|
||||
lsp::Location loc = getLocationFromLoc(sourceMgr, diag.getLocation(), uri);
|
||||
lspDiag.range = loc.range;
|
||||
|
||||
// Skip diagnostics that weren't emitted within the main file.
|
||||
if (loc.uri != uri)
|
||||
return llvm::None;
|
||||
|
||||
// Convert the severity for the diagnostic.
|
||||
switch (diag.getSeverity()) {
|
||||
case ast::Diagnostic::Severity::DK_Note:
|
||||
llvm_unreachable("expected notes to be handled separately");
|
||||
case ast::Diagnostic::Severity::DK_Warning:
|
||||
lspDiag.severity = lsp::DiagnosticSeverity::Warning;
|
||||
break;
|
||||
case ast::Diagnostic::Severity::DK_Error:
|
||||
lspDiag.severity = lsp::DiagnosticSeverity::Error;
|
||||
break;
|
||||
case ast::Diagnostic::Severity::DK_Remark:
|
||||
lspDiag.severity = lsp::DiagnosticSeverity::Information;
|
||||
break;
|
||||
}
|
||||
lspDiag.message = diag.getMessage().str();
|
||||
|
||||
// Attach any notes to the main diagnostic as related information.
|
||||
std::vector<lsp::DiagnosticRelatedInformation> relatedDiags;
|
||||
for (const ast::Diagnostic ¬e : diag.getNotes()) {
|
||||
relatedDiags.emplace_back(
|
||||
getLocationFromLoc(sourceMgr, note.getLocation(), uri),
|
||||
note.getMessage().str());
|
||||
}
|
||||
if (!relatedDiags.empty())
|
||||
lspDiag.relatedInformation = std::move(relatedDiags);
|
||||
|
||||
return lspDiag;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// PDLIndex
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
struct PDLIndexSymbol {
|
||||
explicit PDLIndexSymbol(const ast::Decl *definition)
|
||||
: definition(definition) {}
|
||||
explicit PDLIndexSymbol(const ods::Operation *definition)
|
||||
: definition(definition) {}
|
||||
|
||||
/// Return the location of the definition of this symbol.
|
||||
SMRange getDefLoc() const {
|
||||
if (const ast::Decl *decl = definition.dyn_cast<const ast::Decl *>()) {
|
||||
const ast::Name *declName = decl->getName();
|
||||
return declName ? declName->getLoc() : decl->getLoc();
|
||||
}
|
||||
return definition.get<const ods::Operation *>()->getLoc();
|
||||
}
|
||||
|
||||
/// The main definition of the symbol.
|
||||
PointerUnion<const ast::Decl *, const ods::Operation *> definition;
|
||||
/// The set of references to the symbol.
|
||||
std::vector<SMRange> references;
|
||||
};
|
||||
|
||||
/// This class provides an index for definitions/uses within a PDL document.
|
||||
/// It provides efficient lookup of a definition given an input source range.
|
||||
class PDLIndex {
|
||||
public:
|
||||
PDLIndex() : intervalMap(allocator) {}
|
||||
|
||||
/// Initialize the index with the given ast::Module.
|
||||
void initialize(const ast::Module &module, const ods::Context &odsContext);
|
||||
|
||||
/// Lookup a symbol for the given location. Returns nullptr if no symbol could
|
||||
/// be found. If provided, `overlappedRange` is set to the range that the
|
||||
/// provided `loc` overlapped with.
|
||||
const PDLIndexSymbol *lookup(SMLoc loc,
|
||||
SMRange *overlappedRange = nullptr) const;
|
||||
|
||||
private:
|
||||
/// The type of interval map used to store source references. SMRange is
|
||||
/// half-open, so we also need to use a half-open interval map.
|
||||
using MapT =
|
||||
llvm::IntervalMap<const char *, const PDLIndexSymbol *,
|
||||
llvm::IntervalMapImpl::NodeSizer<
|
||||
const char *, const PDLIndexSymbol *>::LeafSize,
|
||||
llvm::IntervalMapHalfOpenInfo<const char *>>;
|
||||
|
||||
/// An allocator for the interval map.
|
||||
MapT::Allocator allocator;
|
||||
|
||||
/// An interval map containing a corresponding definition mapped to a source
|
||||
/// interval.
|
||||
MapT intervalMap;
|
||||
|
||||
/// A mapping between definitions and their corresponding symbol.
|
||||
DenseMap<const void *, std::unique_ptr<PDLIndexSymbol>> defToSymbol;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void PDLIndex::initialize(const ast::Module &module,
|
||||
const ods::Context &odsContext) {
|
||||
auto getOrInsertDef = [&](const auto *def) -> PDLIndexSymbol * {
|
||||
auto it = defToSymbol.try_emplace(def, nullptr);
|
||||
if (it.second)
|
||||
it.first->second = std::make_unique<PDLIndexSymbol>(def);
|
||||
return &*it.first->second;
|
||||
};
|
||||
auto insertDeclRef = [&](PDLIndexSymbol *sym, SMRange refLoc,
|
||||
bool isDef = false) {
|
||||
const char *startLoc = refLoc.Start.getPointer();
|
||||
const char *endLoc = refLoc.End.getPointer();
|
||||
if (!intervalMap.overlaps(startLoc, endLoc)) {
|
||||
intervalMap.insert(startLoc, endLoc, sym);
|
||||
if (!isDef)
|
||||
sym->references.push_back(refLoc);
|
||||
}
|
||||
};
|
||||
auto insertODSOpRef = [&](StringRef opName, SMRange refLoc) {
|
||||
const ods::Operation *odsOp = odsContext.lookupOperation(opName);
|
||||
if (!odsOp)
|
||||
return;
|
||||
|
||||
PDLIndexSymbol *symbol = getOrInsertDef(odsOp);
|
||||
insertDeclRef(symbol, odsOp->getLoc(), /*isDef=*/true);
|
||||
insertDeclRef(symbol, refLoc);
|
||||
};
|
||||
|
||||
module.walk([&](const ast::Node *node) {
|
||||
// Handle references to PDL decls.
|
||||
if (const auto *decl = dyn_cast<ast::OpNameDecl>(node)) {
|
||||
if (Optional<StringRef> name = decl->getName())
|
||||
insertODSOpRef(*name, decl->getLoc());
|
||||
} else if (const ast::Decl *decl = dyn_cast<ast::Decl>(node)) {
|
||||
const ast::Name *name = decl->getName();
|
||||
if (!name)
|
||||
return;
|
||||
PDLIndexSymbol *declSym = getOrInsertDef(decl);
|
||||
insertDeclRef(declSym, name->getLoc(), /*isDef=*/true);
|
||||
|
||||
if (const auto *varDecl = dyn_cast<ast::VariableDecl>(decl)) {
|
||||
// Record references to any constraints.
|
||||
for (const auto &it : varDecl->getConstraints())
|
||||
insertDeclRef(getOrInsertDef(it.constraint), it.referenceLoc);
|
||||
}
|
||||
} else if (const auto *expr = dyn_cast<ast::DeclRefExpr>(node)) {
|
||||
insertDeclRef(getOrInsertDef(expr->getDecl()), expr->getLoc());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const PDLIndexSymbol *PDLIndex::lookup(SMLoc loc,
|
||||
SMRange *overlappedRange) const {
|
||||
auto it = intervalMap.find(loc.getPointer());
|
||||
if (!it.valid() || loc.getPointer() < it.start())
|
||||
return nullptr;
|
||||
|
||||
if (overlappedRange) {
|
||||
*overlappedRange = SMRange(SMLoc::getFromPointer(it.start()),
|
||||
SMLoc::getFromPointer(it.stop()));
|
||||
}
|
||||
return it.value();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// PDLDocument
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
/// This class represents all of the information pertaining to a specific PDL
|
||||
/// document.
|
||||
struct PDLDocument {
|
||||
PDLDocument(const lsp::URIForFile &uri, StringRef contents,
|
||||
std::vector<lsp::Diagnostic> &diagnostics);
|
||||
PDLDocument(const PDLDocument &) = delete;
|
||||
PDLDocument &operator=(const PDLDocument &) = delete;
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// 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);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Fields
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// The include directories for this file.
|
||||
std::vector<std::string> includeDirs;
|
||||
|
||||
/// The source manager containing the contents of the input file.
|
||||
llvm::SourceMgr sourceMgr;
|
||||
|
||||
/// The ODS and AST contexts.
|
||||
ods::Context odsContext;
|
||||
ast::Context astContext;
|
||||
|
||||
/// The parsed AST module, or failure if the file wasn't valid.
|
||||
FailureOr<ast::Module *> astModule;
|
||||
|
||||
/// The index of the parsed module.
|
||||
PDLIndex index;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
PDLDocument::PDLDocument(const lsp::URIForFile &uri, StringRef contents,
|
||||
std::vector<lsp::Diagnostic> &diagnostics)
|
||||
: astContext(odsContext) {
|
||||
auto memBuffer = llvm::MemoryBuffer::getMemBufferCopy(contents, uri.file());
|
||||
if (!memBuffer) {
|
||||
lsp::Logger::error("Failed to create memory buffer for file", uri.file());
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Properly provide include directories from the client.
|
||||
llvm::SmallString<32> uriDirectory(uri.file());
|
||||
llvm::sys::path::remove_filename(uriDirectory);
|
||||
includeDirs.push_back(uriDirectory.str().str());
|
||||
|
||||
sourceMgr.setIncludeDirs(includeDirs);
|
||||
sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
|
||||
|
||||
astContext.getDiagEngine().setHandlerFn([&](const ast::Diagnostic &diag) {
|
||||
if (auto lspDiag = getLspDiagnoticFromDiag(sourceMgr, diag, uri))
|
||||
diagnostics.push_back(std::move(*lspDiag));
|
||||
});
|
||||
astModule = parsePDLAST(astContext, sourceMgr);
|
||||
if (succeeded(astModule))
|
||||
index.initialize(**astModule, odsContext);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// PDLDocument: Definitions and References
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
void PDLDocument::getLocationsOf(const lsp::URIForFile &uri,
|
||||
const lsp::Position &defPos,
|
||||
std::vector<lsp::Location> &locations) {
|
||||
SMLoc posLoc = defPos.getAsSMLoc(sourceMgr);
|
||||
const PDLIndexSymbol *symbol = index.lookup(posLoc);
|
||||
if (!symbol)
|
||||
return;
|
||||
|
||||
locations.push_back(getLocationFromLoc(sourceMgr, symbol->getDefLoc(), uri));
|
||||
}
|
||||
|
||||
void PDLDocument::findReferencesOf(const lsp::URIForFile &uri,
|
||||
const lsp::Position &pos,
|
||||
std::vector<lsp::Location> &references) {
|
||||
SMLoc posLoc = pos.getAsSMLoc(sourceMgr);
|
||||
const PDLIndexSymbol *symbol = index.lookup(posLoc);
|
||||
if (!symbol)
|
||||
return;
|
||||
|
||||
references.push_back(getLocationFromLoc(sourceMgr, symbol->getDefLoc(), uri));
|
||||
for (SMRange refLoc : symbol->references)
|
||||
references.push_back(getLocationFromLoc(sourceMgr, refLoc, uri));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// PDLTextFileChunk
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
/// This class represents a single chunk of an PDL text file.
|
||||
struct PDLTextFileChunk {
|
||||
PDLTextFileChunk(uint64_t lineOffset, const lsp::URIForFile &uri,
|
||||
StringRef contents,
|
||||
std::vector<lsp::Diagnostic> &diagnostics)
|
||||
: lineOffset(lineOffset), document(uri, contents, diagnostics) {}
|
||||
|
||||
/// Adjust the line number of the given range to anchor at the beginning of
|
||||
/// the file, instead of the beginning of this chunk.
|
||||
void adjustLocForChunkOffset(lsp::Range &range) {
|
||||
adjustLocForChunkOffset(range.start);
|
||||
adjustLocForChunkOffset(range.end);
|
||||
}
|
||||
/// Adjust the line number of the given position to anchor at the beginning of
|
||||
/// the file, instead of the beginning of this chunk.
|
||||
void adjustLocForChunkOffset(lsp::Position &pos) { pos.line += lineOffset; }
|
||||
|
||||
/// The line offset of this chunk from the beginning of the file.
|
||||
uint64_t lineOffset;
|
||||
/// The document referred to by this chunk.
|
||||
PDLDocument document;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// PDLTextFile
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
/// This class represents a text file containing one or more PDL documents.
|
||||
class PDLTextFile {
|
||||
public:
|
||||
PDLTextFile(const lsp::URIForFile &uri, StringRef fileContents,
|
||||
int64_t version, std::vector<lsp::Diagnostic> &diagnostics);
|
||||
|
||||
/// Return the current version of this text file.
|
||||
int64_t getVersion() const { return version; }
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// LSP Queries
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
void getLocationsOf(const lsp::URIForFile &uri, lsp::Position defPos,
|
||||
std::vector<lsp::Location> &locations);
|
||||
void findReferencesOf(const lsp::URIForFile &uri, lsp::Position pos,
|
||||
std::vector<lsp::Location> &references);
|
||||
|
||||
private:
|
||||
/// Find the PDL document that contains the given position, and update the
|
||||
/// position to be anchored at the start of the found chunk instead of the
|
||||
/// beginning of the file.
|
||||
PDLTextFileChunk &getChunkFor(lsp::Position &pos);
|
||||
|
||||
/// The full string contents of the file.
|
||||
std::string contents;
|
||||
|
||||
/// The version of this file.
|
||||
int64_t version;
|
||||
|
||||
/// The number of lines in the file.
|
||||
int64_t totalNumLines;
|
||||
|
||||
/// The chunks of this file. The order of these chunks is the order in which
|
||||
/// they appear in the text file.
|
||||
std::vector<std::unique_ptr<PDLTextFileChunk>> chunks;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
PDLTextFile::PDLTextFile(const lsp::URIForFile &uri, StringRef fileContents,
|
||||
int64_t version,
|
||||
std::vector<lsp::Diagnostic> &diagnostics)
|
||||
: contents(fileContents.str()), version(version), totalNumLines(0) {
|
||||
// Split the file into separate PDL documents.
|
||||
// TODO: Find a way to share the split file marker with other tools. We don't
|
||||
// want to use `splitAndProcessBuffer` here, but we do want to make sure this
|
||||
// marker doesn't go out of sync.
|
||||
SmallVector<StringRef, 8> subContents;
|
||||
StringRef(contents).split(subContents, "// -----");
|
||||
chunks.emplace_back(std::make_unique<PDLTextFileChunk>(
|
||||
/*lineOffset=*/0, uri, subContents.front(), diagnostics));
|
||||
|
||||
uint64_t lineOffset = subContents.front().count('\n');
|
||||
for (StringRef docContents : llvm::drop_begin(subContents)) {
|
||||
unsigned currentNumDiags = diagnostics.size();
|
||||
auto chunk = std::make_unique<PDLTextFileChunk>(lineOffset, uri,
|
||||
docContents, diagnostics);
|
||||
lineOffset += docContents.count('\n');
|
||||
|
||||
// Adjust locations used in diagnostics to account for the offset from the
|
||||
// beginning of the file.
|
||||
for (lsp::Diagnostic &diag :
|
||||
llvm::drop_begin(diagnostics, currentNumDiags)) {
|
||||
chunk->adjustLocForChunkOffset(diag.range);
|
||||
|
||||
if (!diag.relatedInformation)
|
||||
continue;
|
||||
for (auto &it : *diag.relatedInformation)
|
||||
if (it.location.uri == uri)
|
||||
chunk->adjustLocForChunkOffset(it.location.range);
|
||||
}
|
||||
chunks.emplace_back(std::move(chunk));
|
||||
}
|
||||
totalNumLines = lineOffset;
|
||||
}
|
||||
|
||||
void PDLTextFile::getLocationsOf(const lsp::URIForFile &uri,
|
||||
lsp::Position defPos,
|
||||
std::vector<lsp::Location> &locations) {
|
||||
PDLTextFileChunk &chunk = getChunkFor(defPos);
|
||||
chunk.document.getLocationsOf(uri, defPos, locations);
|
||||
|
||||
// Adjust any locations within this file for the offset of this chunk.
|
||||
if (chunk.lineOffset == 0)
|
||||
return;
|
||||
for (lsp::Location &loc : locations)
|
||||
if (loc.uri == uri)
|
||||
chunk.adjustLocForChunkOffset(loc.range);
|
||||
}
|
||||
|
||||
void PDLTextFile::findReferencesOf(const lsp::URIForFile &uri,
|
||||
lsp::Position pos,
|
||||
std::vector<lsp::Location> &references) {
|
||||
PDLTextFileChunk &chunk = getChunkFor(pos);
|
||||
chunk.document.findReferencesOf(uri, pos, references);
|
||||
|
||||
// Adjust any locations within this file for the offset of this chunk.
|
||||
if (chunk.lineOffset == 0)
|
||||
return;
|
||||
for (lsp::Location &loc : references)
|
||||
if (loc.uri == uri)
|
||||
chunk.adjustLocForChunkOffset(loc.range);
|
||||
}
|
||||
|
||||
PDLTextFileChunk &PDLTextFile::getChunkFor(lsp::Position &pos) {
|
||||
if (chunks.size() == 1)
|
||||
return *chunks.front();
|
||||
|
||||
// Search for the first chunk with a greater line offset, the previous chunk
|
||||
// is the one that contains `pos`.
|
||||
auto it = llvm::upper_bound(
|
||||
chunks, pos, [](const lsp::Position &pos, const auto &chunk) {
|
||||
return static_cast<uint64_t>(pos.line) < chunk->lineOffset;
|
||||
});
|
||||
PDLTextFileChunk &chunk = it == chunks.end() ? *chunks.back() : **(--it);
|
||||
pos.line -= chunk.lineOffset;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// PDLLServer::Impl
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
struct lsp::PDLLServer::Impl {
|
||||
/// The files held by the server, mapped by their URI file name.
|
||||
llvm::StringMap<std::unique_ptr<PDLTextFile>> files;
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// PDLLServer
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
lsp::PDLLServer::PDLLServer() : impl(std::make_unique<Impl>()) {}
|
||||
lsp::PDLLServer::~PDLLServer() = default;
|
||||
|
||||
void lsp::PDLLServer::addOrUpdateDocument(
|
||||
const URIForFile &uri, StringRef contents, int64_t version,
|
||||
std::vector<Diagnostic> &diagnostics) {
|
||||
impl->files[uri.file()] =
|
||||
std::make_unique<PDLTextFile>(uri, contents, version, diagnostics);
|
||||
}
|
||||
|
||||
Optional<int64_t> lsp::PDLLServer::removeDocument(const URIForFile &uri) {
|
||||
auto it = impl->files.find(uri.file());
|
||||
if (it == impl->files.end())
|
||||
return llvm::None;
|
||||
|
||||
int64_t version = it->second->getVersion();
|
||||
impl->files.erase(it);
|
||||
return version;
|
||||
}
|
||||
|
||||
void lsp::PDLLServer::getLocationsOf(const URIForFile &uri,
|
||||
const Position &defPos,
|
||||
std::vector<Location> &locations) {
|
||||
auto fileIt = impl->files.find(uri.file());
|
||||
if (fileIt != impl->files.end())
|
||||
fileIt->second->getLocationsOf(uri, defPos, locations);
|
||||
}
|
||||
|
||||
void lsp::PDLLServer::findReferencesOf(const URIForFile &uri,
|
||||
const Position &pos,
|
||||
std::vector<Location> &references) {
|
||||
auto fileIt = impl->files.find(uri.file());
|
||||
if (fileIt != impl->files.end())
|
||||
fileIt->second->findReferencesOf(uri, pos, references);
|
||||
}
|
59
mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.h
Normal file
59
mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.h
Normal file
@ -0,0 +1,59 @@
|
||||
//===- PDLLServer.h - PDL General Language Server ---------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LIB_MLIR_TOOLS_MLIRPDLLSPSERVER_SERVER_H_
|
||||
#define LIB_MLIR_TOOLS_MLIRPDLLSPSERVER_SERVER_H_
|
||||
|
||||
#include "mlir/Support/LLVM.h"
|
||||
#include <memory>
|
||||
|
||||
namespace mlir {
|
||||
namespace lsp {
|
||||
struct Diagnostic;
|
||||
struct Location;
|
||||
struct Position;
|
||||
class URIForFile;
|
||||
|
||||
/// This class implements all of the PDLL related functionality necessary for a
|
||||
/// language server. This class allows for keeping the PDLL specific logic
|
||||
/// separate from the logic that involves LSP server/client communication.
|
||||
class PDLLServer {
|
||||
public:
|
||||
PDLLServer();
|
||||
~PDLLServer();
|
||||
|
||||
/// Add or update the document, with the provided `version`, at the given URI.
|
||||
/// Any diagnostics emitted for this document should be added to
|
||||
/// `diagnostics`.
|
||||
void addOrUpdateDocument(const URIForFile &uri, StringRef contents,
|
||||
int64_t version,
|
||||
std::vector<Diagnostic> &diagnostics);
|
||||
|
||||
/// Remove the document with the given uri. Returns the version of the removed
|
||||
/// document, or None if the uri did not have a corresponding document within
|
||||
/// the server.
|
||||
Optional<int64_t> removeDocument(const URIForFile &uri);
|
||||
|
||||
/// Return the locations of the object pointed at by the given position.
|
||||
void getLocationsOf(const URIForFile &uri, const Position &defPos,
|
||||
std::vector<Location> &locations);
|
||||
|
||||
/// Find all references of the object pointed at by the given position.
|
||||
void findReferencesOf(const URIForFile &uri, const Position &pos,
|
||||
std::vector<Location> &references);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
} // namespace lsp
|
||||
} // namespace mlir
|
||||
|
||||
#endif // LIB_MLIR_TOOLS_MLIRPDLLSPSERVER_SERVER_H_
|
@ -82,6 +82,7 @@ set(MLIR_TEST_DEPENDS
|
||||
mlir-capi-pdl-test
|
||||
mlir-linalg-ods-yaml-gen
|
||||
mlir-lsp-server
|
||||
mlir-pdll-lsp-server
|
||||
mlir-opt
|
||||
mlir-pdll
|
||||
mlir-reduce
|
||||
|
37
mlir/test/mlir-pdll-lsp-server/definition-split-file.test
Normal file
37
mlir/test/mlir-pdll-lsp-server/definition-split-file.test
Normal file
@ -0,0 +1,37 @@
|
||||
// RUN: mlir-pdll-lsp-server -lit-test < %s | FileCheck %s
|
||||
// This test checks support for split files by attempting to find the definition
|
||||
// of a symbol in a split file. The interesting part of this test is that the
|
||||
// file chunk before the one we are looking for the definition in has an error.
|
||||
{"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":"Pattern Foo {\n// -----\nPattern {\n erase root: Op<toy.test>;\n }"
|
||||
}}}
|
||||
// -----
|
||||
{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{
|
||||
"textDocument":{"uri":"test:///foo.pdll"},
|
||||
"position":{"line":3,"character":12}
|
||||
}}
|
||||
// CHECK: "id": 1
|
||||
// CHECK-NEXT: "jsonrpc": "2.0",
|
||||
// CHECK-NEXT: "result": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "character": 12,
|
||||
// CHECK-NEXT: "line": 3
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "start": {
|
||||
// CHECK-NEXT: "character": 8,
|
||||
// CHECK-NEXT: "line": 3
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "uri": "{{.*}}/foo.pdll"
|
||||
// CHECK-NEXT: }
|
||||
// -----
|
||||
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
|
||||
// -----
|
||||
{"jsonrpc":"2.0","method":"exit"}
|
55
mlir/test/mlir-pdll-lsp-server/definition.test
Normal file
55
mlir/test/mlir-pdll-lsp-server/definition.test
Normal file
@ -0,0 +1,55 @@
|
||||
// RUN: mlir-pdll-lsp-server -lit-test < %s | FileCheck %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":"Pattern FooPattern {\nlet root: Op<toy.test>;\nerase root;\n}"
|
||||
}}}
|
||||
// -----
|
||||
{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{
|
||||
"textDocument":{"uri":"test:///foo.pdll"},
|
||||
"position":{"line":0,"character":12}
|
||||
}}
|
||||
// CHECK: "id": 1
|
||||
// CHECK-NEXT: "jsonrpc": "2.0",
|
||||
// CHECK-NEXT: "result": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "character": 18,
|
||||
// CHECK-NEXT: "line": 0
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "start": {
|
||||
// CHECK-NEXT: "character": 8,
|
||||
// CHECK-NEXT: "line": 0
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "uri": "{{.*}}/foo.pdll"
|
||||
// CHECK-NEXT: }
|
||||
// -----
|
||||
{"jsonrpc":"2.0","id":2,"method":"textDocument/definition","params":{
|
||||
"textDocument":{"uri":"test:///foo.pdll"},
|
||||
"position":{"line":2,"character":8}
|
||||
}}
|
||||
// CHECK: "id": 2
|
||||
// CHECK-NEXT: "jsonrpc": "2.0",
|
||||
// CHECK-NEXT: "result": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "character": 8,
|
||||
// CHECK-NEXT: "line": 1
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "start": {
|
||||
// CHECK-NEXT: "character": 4,
|
||||
// CHECK-NEXT: "line": 1
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "uri": "{{.*}}/foo.pdll"
|
||||
// CHECK-NEXT: }
|
||||
// -----
|
||||
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
|
||||
// -----
|
||||
{"jsonrpc":"2.0","method":"exit"}
|
7
mlir/test/mlir-pdll-lsp-server/exit-eof.test
Normal file
7
mlir/test/mlir-pdll-lsp-server/exit-eof.test
Normal file
@ -0,0 +1,7 @@
|
||||
// RUN: not mlir-pdll-lsp-server < %s 2> %t.err
|
||||
// RUN: FileCheck %s < %t.err
|
||||
//
|
||||
// No LSP messages here, just let mlir-pdll-lsp-server see the end-of-file
|
||||
// CHECK: Transport error:
|
||||
// (Typically "Transport error: Input/output error" but platform-dependent).
|
||||
|
6
mlir/test/mlir-pdll-lsp-server/exit-with-shutdown.test
Normal file
6
mlir/test/mlir-pdll-lsp-server/exit-with-shutdown.test
Normal file
@ -0,0 +1,6 @@
|
||||
// RUN: mlir-pdll-lsp-server -lit-test < %s
|
||||
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"pdll","capabilities":{},"trace":"off"}}
|
||||
// -----
|
||||
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
|
||||
// -----
|
||||
{"jsonrpc":"2.0","method":"exit"}
|
@ -0,0 +1,4 @@
|
||||
// RUN: not mlir-pdll-lsp-server -lit-test < %s
|
||||
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"pdll","capabilities":{},"trace":"off"}}
|
||||
// -----
|
||||
{"jsonrpc":"2.0","method":"exit"}
|
@ -0,0 +1,12 @@
|
||||
// RUN: mlir-pdll-lsp-server -lit-test < %s | FileCheck %s
|
||||
// Test with invalid initialize request parameters
|
||||
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":"","rootUri":"test:///workspace","capabilities":{},"trace":"verbose"}}
|
||||
// CHECK: "id": 0,
|
||||
// CHECK-NEXT: "jsonrpc": "2.0",
|
||||
// CHECK-NEXT: "result": {
|
||||
// CHECK-NEXT: "capabilities": {
|
||||
// ...
|
||||
// -----
|
||||
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
|
||||
// -----
|
||||
{"jsonrpc":"2.0","method":"exit"}
|
27
mlir/test/mlir-pdll-lsp-server/initialize-params.test
Normal file
27
mlir/test/mlir-pdll-lsp-server/initialize-params.test
Normal file
@ -0,0 +1,27 @@
|
||||
// RUN: mlir-pdll-lsp-server -lit-test < %s | FileCheck %s
|
||||
// Test initialize request parameters with rootUri
|
||||
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootUri":"test:///workspace","capabilities":{},"trace":"off"}}
|
||||
// CHECK: "id": 0,
|
||||
// CHECK-NEXT: "jsonrpc": "2.0",
|
||||
// CHECK-NEXT: "result": {
|
||||
// CHECK-NEXT: "capabilities": {
|
||||
// CHECK-NEXT: "definitionProvider": true,
|
||||
// CHECK-NEXT: "referencesProvider": true,
|
||||
// CHECK-NEXT: "textDocumentSync": {
|
||||
// CHECK-NEXT: "change": 1,
|
||||
// CHECK-NEXT: "openClose": true,
|
||||
// CHECK-NEXT: "save": true
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "serverInfo": {
|
||||
// CHECK-NEXT: "name": "mlir-pdll-lsp-server",
|
||||
// CHECK-NEXT: "version": "{{.*}}"
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// -----
|
||||
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
|
||||
// CHECK: "id": 3,
|
||||
// CHECK-NEXT: "jsonrpc": "2.0",
|
||||
// CHECK-NEXT: "result": null
|
||||
// -----
|
||||
{"jsonrpc":"2.0","method":"exit"}
|
98
mlir/test/mlir-pdll-lsp-server/references.test
Normal file
98
mlir/test/mlir-pdll-lsp-server/references.test
Normal file
@ -0,0 +1,98 @@
|
||||
// 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 ValueCst(value: Value);\nPattern {\nlet arg: ValueCst;\nlet root = op<test.op>(arg);\nreplace root with arg;\n}"
|
||||
}}}
|
||||
// -----
|
||||
{"jsonrpc":"2.0","id":1,"method":"textDocument/references","params":{
|
||||
"textDocument":{"uri":"test:///foo.pdll"},
|
||||
"position":{"line":0,"character":15},
|
||||
"context":{"includeDeclaration": false}
|
||||
}}
|
||||
// CHECK: "id": 1
|
||||
// CHECK-NEXT: "jsonrpc": "2.0",
|
||||
// CHECK-NEXT: "result": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "character": 19,
|
||||
// CHECK-NEXT: "line": 0
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "start": {
|
||||
// CHECK-NEXT: "character": 11,
|
||||
// CHECK-NEXT: "line": 0
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "uri": "{{.*}}/foo.pdll"
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "character": 17,
|
||||
// CHECK-NEXT: "line": 2
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "start": {
|
||||
// CHECK-NEXT: "character": 9,
|
||||
// CHECK-NEXT: "line": 2
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "uri": "{{.*}}/foo.pdll"
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// -----
|
||||
{"jsonrpc":"2.0","id":2,"method":"textDocument/references","params":{
|
||||
"textDocument":{"uri":"test:///foo.pdll"},
|
||||
"position":{"line":2,"character":6},
|
||||
"context":{"includeDeclaration": false}
|
||||
}}
|
||||
// CHECK: "id": 2
|
||||
// CHECK-NEXT: "jsonrpc": "2.0",
|
||||
// CHECK-NEXT: "result": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "character": 7,
|
||||
// CHECK-NEXT: "line": 2
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "start": {
|
||||
// CHECK-NEXT: "character": 4,
|
||||
// CHECK-NEXT: "line": 2
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "uri": "{{.*}}/foo.pdll"
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "character": 26,
|
||||
// CHECK-NEXT: "line": 3
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "start": {
|
||||
// CHECK-NEXT: "character": 23,
|
||||
// CHECK-NEXT: "line": 3
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "uri": "{{.*}}/foo.pdll"
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "character": 21,
|
||||
// CHECK-NEXT: "line": 4
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "start": {
|
||||
// CHECK-NEXT: "character": 18,
|
||||
// CHECK-NEXT: "line": 4
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "uri": "{{.*}}/foo.pdll"
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// -----
|
||||
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
|
||||
// -----
|
||||
{"jsonrpc":"2.0","method":"exit"}
|
@ -1,6 +1,7 @@
|
||||
add_subdirectory(mlir-lsp-server)
|
||||
add_subdirectory(mlir-opt)
|
||||
add_subdirectory(mlir-pdll)
|
||||
add_subdirectory(mlir-pdll-lsp-server)
|
||||
add_subdirectory(mlir-reduce)
|
||||
add_subdirectory(mlir-shlib)
|
||||
add_subdirectory(mlir-spirv-cpu-runner)
|
||||
|
15
mlir/tools/mlir-pdll-lsp-server/CMakeLists.txt
Normal file
15
mlir/tools/mlir-pdll-lsp-server/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
set(LIBS
|
||||
MLIRPdllLspServerLib
|
||||
)
|
||||
|
||||
add_llvm_tool(mlir-pdll-lsp-server
|
||||
mlir-pdll-lsp-server.cpp
|
||||
|
||||
DEPENDS
|
||||
${LIBS}
|
||||
)
|
||||
|
||||
target_link_libraries(mlir-pdll-lsp-server PRIVATE ${LIBS})
|
||||
llvm_update_compile_flags(mlir-pdll-lsp-server)
|
||||
|
||||
mlir_check_all_link_libraries(mlir-pdll-lsp-server)
|
16
mlir/tools/mlir-pdll-lsp-server/mlir-pdll-lsp-server.cpp
Normal file
16
mlir/tools/mlir-pdll-lsp-server/mlir-pdll-lsp-server.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
//===- mlir-pdll-lsp-server.cpp - PDLL Language Server main ---------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "mlir/Support/LogicalResult.h"
|
||||
#include "mlir/Tools/mlir-pdll-lsp-server/MlirPdllLspServerMain.h"
|
||||
|
||||
using namespace mlir;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return failed(MlirPdllLspServerMain(argc, argv));
|
||||
}
|
5394
mlir/utils/vscode/package-lock.json
generated
5394
mlir/utils/vscode/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,10 +14,12 @@
|
||||
],
|
||||
"keywords": [
|
||||
"LLVM",
|
||||
"MLIR"
|
||||
"MLIR",
|
||||
"PDLL"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onLanguage:mlir"
|
||||
"onLanguage:mlir",
|
||||
"onLanguage:pdll"
|
||||
],
|
||||
"main": "./out/extension",
|
||||
"scripts": {
|
||||
@ -62,6 +64,17 @@
|
||||
},
|
||||
{
|
||||
"id": "mlir-injection"
|
||||
},
|
||||
{
|
||||
"id": "pdll",
|
||||
"aliases": [
|
||||
"PDLL",
|
||||
"pdll"
|
||||
],
|
||||
"extensions": [
|
||||
".pdll"
|
||||
],
|
||||
"configuration": "./pdll-language-configuration.json"
|
||||
}
|
||||
],
|
||||
"grammars": [
|
||||
@ -90,6 +103,11 @@
|
||||
"embeddedLanguages": {
|
||||
"source.mlir": "mlir"
|
||||
}
|
||||
},
|
||||
{
|
||||
"language": "pdll",
|
||||
"scopeName": "source.pdll",
|
||||
"path": "./pdll-grammar.json"
|
||||
}
|
||||
],
|
||||
"configuration": {
|
||||
@ -101,6 +119,11 @@
|
||||
"type": "string",
|
||||
"description": "The file path of the mlir-lsp-server executable."
|
||||
},
|
||||
"mlir.pdll_server_path": {
|
||||
"scope": "resource",
|
||||
"type": "string",
|
||||
"description": "The file path of the mlir-pdll-lsp-server executable."
|
||||
},
|
||||
"mlir.onSettingsChanged": {
|
||||
"type": "string",
|
||||
"default": "prompt",
|
||||
|
521
mlir/utils/vscode/pdll-grammar.json
Normal file
521
mlir/utils/vscode/pdll-grammar.json
Normal file
@ -0,0 +1,521 @@
|
||||
{
|
||||
"name": "PDLL",
|
||||
"fileTypes": [
|
||||
"pdll"
|
||||
],
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#comment"
|
||||
},
|
||||
{
|
||||
"include": "#string"
|
||||
},
|
||||
{
|
||||
"include": "#string_block"
|
||||
},
|
||||
{
|
||||
"include": "#integer"
|
||||
},
|
||||
{
|
||||
"include": "#include"
|
||||
},
|
||||
{
|
||||
"include": "#user_constraint"
|
||||
},
|
||||
{
|
||||
"include": "#user_rewrite"
|
||||
},
|
||||
{
|
||||
"include": "#pattern"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"comment": {
|
||||
"match": "\/\/.*$",
|
||||
"name": "comment.line.double-slash.pdll"
|
||||
},
|
||||
"string": {
|
||||
"name": "string.quoted.double.pdll",
|
||||
"begin": "\"",
|
||||
"end": "\"",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.string.begin.pdll"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"match": "\\\\[nt\"]",
|
||||
"name": "constant.character.escape.pdll"
|
||||
},
|
||||
{
|
||||
"match": "\\\\.",
|
||||
"name": "invalid.illegal.pdll"
|
||||
}
|
||||
],
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.string.end.pdll"
|
||||
}
|
||||
}
|
||||
},
|
||||
"string_block": {
|
||||
"name": "string.quoted.triple.pdll",
|
||||
"begin": "\\[{",
|
||||
"end": "}]",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.string.begin.pdll"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"match": "\\\\[nt\"]",
|
||||
"name": "constant.character.escape.pdll"
|
||||
},
|
||||
{
|
||||
"match": "\\\\.",
|
||||
"name": "invalid.illegal.pdll"
|
||||
}
|
||||
],
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.string.end.pdll"
|
||||
}
|
||||
}
|
||||
},
|
||||
"integer": {
|
||||
"match": "[0-9]+",
|
||||
"name": "constant.numeric.pdll"
|
||||
},
|
||||
"include": {
|
||||
"patterns": [
|
||||
{
|
||||
"match": "(#include)",
|
||||
"name": "keyword.control.pdll"
|
||||
}
|
||||
]
|
||||
},
|
||||
"argument_or_result_list": {
|
||||
"patterns": [
|
||||
{
|
||||
"match": "\\b([aA-zZ_0-9]*)\\b\\s*:\\s*([aA-zZ_0-9]+)\\b(\\<([^\\>]+)\\>)?",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "variable.parameter.pdll"
|
||||
},
|
||||
"2": {
|
||||
"name": "entity.name.type.pdll"
|
||||
},
|
||||
"4": {
|
||||
"name": "variable.other.enummember.pdll"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"match": "(\\(|\\>|,)\\s*([aA-zZ_0-9]+)\\b(\\<([^\\>]+)\\>)?\\s*(?=[^:])",
|
||||
"captures": {
|
||||
"2": {
|
||||
"name": "entity.name.type.pdll"
|
||||
},
|
||||
"4": {
|
||||
"name": "variable.other.enummember.pdll"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"user_constraint": {
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "\\b(Constraint)\\b\\s*(\\b[aA-zZ_0-9]*\\b)?",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "keyword.other.pdll"
|
||||
},
|
||||
"2": {
|
||||
"name": "entity.name.type.pdll"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(\\[{)",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "source.cpp"
|
||||
}
|
||||
],
|
||||
"end": "(}])"
|
||||
},
|
||||
{
|
||||
"begin": "(?=\\()",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#argument_or_result_list"
|
||||
}
|
||||
],
|
||||
"end": "\\)"
|
||||
},
|
||||
{
|
||||
"include": "#argument_or_result_list"
|
||||
},
|
||||
{
|
||||
"begin": "{",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#inside_pattern"
|
||||
}
|
||||
],
|
||||
"end": "(?=})"
|
||||
},
|
||||
{
|
||||
"begin": "=>",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#inside_pattern"
|
||||
}
|
||||
],
|
||||
"end": "(?=;|,|\\))"
|
||||
}
|
||||
],
|
||||
"end": "(}|;|,)|(?=\\))"
|
||||
}
|
||||
]
|
||||
},
|
||||
"user_rewrite": {
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "\\b(Rewrite)\\b\\s*(\\b[aA-zZ_0-9]*\\b)?",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "keyword.other.pdll"
|
||||
},
|
||||
"2": {
|
||||
"name": "entity.name.function.pdll"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(\\[{)",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "source.cpp"
|
||||
}
|
||||
],
|
||||
"end": "(}])"
|
||||
},
|
||||
{
|
||||
"begin": "(?=\\()",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#argument_or_result_list"
|
||||
}
|
||||
],
|
||||
"end": "\\)"
|
||||
},
|
||||
{
|
||||
"include": "#argument_or_result_list"
|
||||
},
|
||||
{
|
||||
"begin": "{",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#inside_pattern"
|
||||
}
|
||||
],
|
||||
"end": "(?=})"
|
||||
},
|
||||
{
|
||||
"begin": "=>",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#inside_pattern"
|
||||
}
|
||||
],
|
||||
"end": "(?=;|,|\\))"
|
||||
}
|
||||
],
|
||||
"end": "(}|;|,)|(?=\\))"
|
||||
}
|
||||
]
|
||||
},
|
||||
"pattern_metadata": {
|
||||
"patterns": [
|
||||
{
|
||||
"match": "\\b(with)\\b",
|
||||
"name": "keyword.other.pdll"
|
||||
},
|
||||
{
|
||||
"match": "\\b(benefit)\\b\\(([0-9]+)\\)",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "entity.name.variable.pdll"
|
||||
},
|
||||
"2": {
|
||||
"name": "constant.numeric.pdll"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"match": "\\b(recursion)\\b",
|
||||
"name": "entity.name.variable.pdll"
|
||||
}
|
||||
]
|
||||
},
|
||||
"pattern": {
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "\\b(Pattern)\\b\\s*(\\b[aA-zZ_0-9]*\\b)?\\s*(\\bwith\\b\\s*[^\\{]*)?\\s*({)",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "keyword.other.pdll"
|
||||
},
|
||||
"2": {
|
||||
"name": "entity.name.function.pdll"
|
||||
},
|
||||
"3": {
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#pattern_metadata"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#inside_pattern"
|
||||
}
|
||||
],
|
||||
"end": "(})"
|
||||
},
|
||||
{
|
||||
"begin": "\\b(Pattern)\\b\\s*(\\b[aA-zZ_0-9]*\\b)?\\s*(\\bwith\\b\\s*[^\\=]*)?\\s*(=>)",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "keyword.other.pdll"
|
||||
},
|
||||
"2": {
|
||||
"name": "entity.name.function.pdll"
|
||||
},
|
||||
"3": {
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#pattern_metadata"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#inside_pattern"
|
||||
}
|
||||
],
|
||||
"end": ";"
|
||||
}
|
||||
]
|
||||
},
|
||||
"inside_pattern": {
|
||||
"patterns": [
|
||||
{
|
||||
"match": "\\b(erase|let|replace|return|rewrite|with)\\b",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "keyword.control.pdll"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"include": "#expressions"
|
||||
}
|
||||
]
|
||||
},
|
||||
"variable_constraint": {
|
||||
"patterns": [
|
||||
{
|
||||
"match": "\\b(Op)<([a-zA-Z0-9_\\.$-]*)>",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "entity.name.type.pdll"
|
||||
},
|
||||
"2": {
|
||||
"name": "variable.other.enummember.pdll"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"begin": "<",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#expressions"
|
||||
}
|
||||
],
|
||||
"end": ">"
|
||||
},
|
||||
{
|
||||
"match": "[a-zA-Z_0-9]+",
|
||||
"name": "entity.name.type.pdll"
|
||||
}
|
||||
]
|
||||
},
|
||||
"variable_definition": {
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "\\b([aA-zZ_0-9]*)\\b\\s*:\\s*\\[",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "entity.name.variable.pdll"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#variable_constraint"
|
||||
}
|
||||
],
|
||||
"end": "\\]"
|
||||
},
|
||||
{
|
||||
"match": "\\b([aA-zZ_0-9]*)\\b\\s*:\\s*([aA-zZ_0-9]+(\\<([^\\>]+)\\>)?)",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "entity.name.variable.pdll"
|
||||
},
|
||||
"2": {
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#variable_constraint"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"expressions": {
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#user_constraint"
|
||||
},
|
||||
{
|
||||
"include": "#user_rewrite"
|
||||
},
|
||||
{
|
||||
"include": "#attr_expr"
|
||||
},
|
||||
{
|
||||
"include": "#op_expr"
|
||||
},
|
||||
{
|
||||
"include": "#type_expr"
|
||||
},
|
||||
{
|
||||
"include": "#call_expr"
|
||||
},
|
||||
{
|
||||
"include": "#variable_definition"
|
||||
},
|
||||
{
|
||||
"include": "#identifier_expr"
|
||||
},
|
||||
{
|
||||
"include": "#string"
|
||||
},
|
||||
{
|
||||
"include": "#string_block"
|
||||
},
|
||||
{
|
||||
"include": "#comment"
|
||||
},
|
||||
{
|
||||
"begin": "{",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#inside_pattern"
|
||||
}
|
||||
],
|
||||
"end": "}"
|
||||
},
|
||||
{
|
||||
"begin": "\\(",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#expressions"
|
||||
}
|
||||
],
|
||||
"end": "\\)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"attr_expr": {
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(attr)<",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "keyword.other.pdll"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#string"
|
||||
},
|
||||
{
|
||||
"include": "#string_block"
|
||||
}
|
||||
],
|
||||
"end": ">"
|
||||
}
|
||||
]
|
||||
},
|
||||
"call_expr": {
|
||||
"begin": "\\b([a-zA-Z0-9_]+)\\(",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "entity.name.variable.pdll"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#expressions"
|
||||
}
|
||||
],
|
||||
"end": "\\)"
|
||||
},
|
||||
"identifier_expr": {
|
||||
"match": "\\b([a-zA-Z0-9_]+)\\b",
|
||||
"name": "entity.name.variable.pdll"
|
||||
},
|
||||
"op_expr": {
|
||||
"match": "\\b(op)<([0-9a-zA-Z_\\.]*)>",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "keyword.other.pdll"
|
||||
},
|
||||
"2": {
|
||||
"name": "variable.other.enummember.pdll"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type_expr": {
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "\\b(type)<",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "keyword.other.pdll"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#string"
|
||||
},
|
||||
{
|
||||
"include": "#string_block"
|
||||
}
|
||||
],
|
||||
"end": ">"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"scopeName": "source.pdll"
|
||||
}
|
67
mlir/utils/vscode/pdll-language-configuration.json
Normal file
67
mlir/utils/vscode/pdll-language-configuration.json
Normal file
@ -0,0 +1,67 @@
|
||||
{
|
||||
"comments": {
|
||||
"lineComment": "//"
|
||||
},
|
||||
"brackets": [
|
||||
[
|
||||
"{",
|
||||
"}"
|
||||
],
|
||||
[
|
||||
"[",
|
||||
"]"
|
||||
],
|
||||
[
|
||||
"(",
|
||||
")"
|
||||
],
|
||||
[
|
||||
"<",
|
||||
">"
|
||||
]
|
||||
],
|
||||
"autoClosingPairs": [
|
||||
[
|
||||
"{",
|
||||
"}"
|
||||
],
|
||||
[
|
||||
"[",
|
||||
"]"
|
||||
],
|
||||
[
|
||||
"(",
|
||||
")"
|
||||
],
|
||||
[
|
||||
"<",
|
||||
">"
|
||||
],
|
||||
[
|
||||
"\"",
|
||||
"\""
|
||||
]
|
||||
],
|
||||
"surroundingPairs": [
|
||||
[
|
||||
"{",
|
||||
"}"
|
||||
],
|
||||
[
|
||||
"[",
|
||||
"]"
|
||||
],
|
||||
[
|
||||
"(",
|
||||
")"
|
||||
],
|
||||
[
|
||||
"<",
|
||||
">"
|
||||
],
|
||||
[
|
||||
"\"",
|
||||
"\""
|
||||
]
|
||||
]
|
||||
}
|
@ -46,7 +46,7 @@ export function activate(mlirContext: MLIRContext) {
|
||||
// When a configuration change happens, check to see if we should restart the
|
||||
// server.
|
||||
mlirContext.subscriptions.push(vscode.workspace.onDidChangeConfiguration(event => {
|
||||
const settings: string[] = [ 'server_path' ];
|
||||
const settings: string[] = [ 'server_path', 'pdll_server_path' ];
|
||||
for (const setting of settings) {
|
||||
const expandedSetting = `mlir.${setting}`;
|
||||
if (event.affectsConfiguration(expandedSetting)) {
|
||||
@ -61,21 +61,30 @@ export function activate(mlirContext: MLIRContext) {
|
||||
|
||||
// Track the server file in case it changes. We use `fs` here because the
|
||||
// server may not be in a workspace directory.
|
||||
const userDefinedServerPath = config.get<string>('server_path');
|
||||
const serverPath =
|
||||
path.resolve((userDefinedServerPath === '') ? 'mlir-lsp-server'
|
||||
: userDefinedServerPath);
|
||||
const fileWatcherConfig = {
|
||||
disableGlobbing : true,
|
||||
followSymlinks : true,
|
||||
ignoreInitial : true,
|
||||
};
|
||||
const fileWatcher = chokidar.watch(serverPath, fileWatcherConfig);
|
||||
fileWatcher.on('all', (_event, _filename, _details) => {
|
||||
promptRestart(
|
||||
'onSettingsChanged',
|
||||
'MLIR language server binary has changed. Do you want to reload the server?');
|
||||
});
|
||||
mlirContext.subscriptions.push(
|
||||
new vscode.Disposable(() => { fileWatcher.close(); }));
|
||||
const settings: string[] = [ 'server_path', 'pdll_server_path' ];
|
||||
for (const setting of settings) {
|
||||
const serverPath = config.get<string>(setting);
|
||||
|
||||
// Check that the path actually exists.
|
||||
if (serverPath === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fileWatcherConfig = {
|
||||
disableGlobbing : true,
|
||||
followSymlinks : true,
|
||||
ignoreInitial : true,
|
||||
awaitWriteFinish : true,
|
||||
};
|
||||
const fileWatcher = chokidar.watch(serverPath, fileWatcherConfig);
|
||||
fileWatcher.on('all', (event, _filename, _details) => {
|
||||
if (event != 'unlink') {
|
||||
promptRestart(
|
||||
'onSettingsChanged',
|
||||
'MLIR language server binary has changed. Do you want to reload the server?');
|
||||
}
|
||||
});
|
||||
mlirContext.subscriptions.push(
|
||||
new vscode.Disposable(() => { fileWatcher.close(); }));
|
||||
}
|
||||
}
|
||||
|
@ -16,9 +16,13 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
// Initialize the commands of the extension.
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('mlir.restart', async () => {
|
||||
// Dispose and reactivate the context. This is essentially the same as
|
||||
// the start of the extension, but we don't re-emit warnings for
|
||||
// uninitialized settings.
|
||||
mlirContext.dispose();
|
||||
await mlirContext.activate(outputChannel);
|
||||
await mlirContext.activate(outputChannel,
|
||||
/*warnOnEmptyServerPath=*/ false);
|
||||
}));
|
||||
|
||||
mlirContext.activate(outputChannel);
|
||||
mlirContext.activate(outputChannel, /*warnOnEmptyServerPath=*/ true);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import * as fs from 'fs';
|
||||
import * as vscode from 'vscode';
|
||||
import * as vscodelc from 'vscode-languageclient';
|
||||
|
||||
@ -11,16 +12,57 @@ import * as configWatcher from './configWatcher';
|
||||
export class MLIRContext implements vscode.Disposable {
|
||||
subscriptions: vscode.Disposable[] = [];
|
||||
client!: vscodelc.LanguageClient;
|
||||
pdllClient!: vscodelc.LanguageClient;
|
||||
|
||||
/**
|
||||
* Activate the MLIR context, and start the language client.
|
||||
* Activate the MLIR context, and start the language clients.
|
||||
*/
|
||||
async activate(outputChannel: vscode.OutputChannel) {
|
||||
// Get the path of the mlir-lsp-server that is used to provide language
|
||||
async activate(outputChannel: vscode.OutputChannel,
|
||||
warnOnEmptyServerPath: boolean) {
|
||||
// Create the language clients for mlir and pdll.
|
||||
this.pdllClient = this.startLanguageClient(
|
||||
outputChannel, warnOnEmptyServerPath, 'pdll_server_path', 'pdll');
|
||||
this.client = this.startLanguageClient(outputChannel, warnOnEmptyServerPath,
|
||||
'server_path', 'mlir');
|
||||
|
||||
// Watch for configuration changes.
|
||||
configWatcher.activate(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new language client for the given language.
|
||||
*/
|
||||
startLanguageClient(outputChannel: vscode.OutputChannel,
|
||||
warnOnEmptyServerPath: boolean, serverSettingName: string,
|
||||
languageName: string): vscodelc.LanguageClient {
|
||||
const clientTitle = languageName.toUpperCase() + ' Language Client';
|
||||
|
||||
// Get the path of the lsp-server that is used to provide language
|
||||
// functionality.
|
||||
const userDefinedServerPath = config.get<string>('server_path');
|
||||
const serverPath = (userDefinedServerPath === '') ? 'mlir-lsp-server'
|
||||
: userDefinedServerPath;
|
||||
const serverPath = config.get<string>(serverSettingName);
|
||||
|
||||
// If we aren't emitting warnings on an empty server path, and the server
|
||||
// path is empty, bail.
|
||||
if (!warnOnEmptyServerPath && serverPath === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check that the file actually exists.
|
||||
if (serverPath === '' || !fs.existsSync(serverPath)) {
|
||||
vscode.window
|
||||
.showErrorMessage(
|
||||
`${clientTitle}: Unable to resolve path for '${
|
||||
serverSettingName}', please ensure the path is correct`,
|
||||
"Open Setting")
|
||||
.then((value) => {
|
||||
if (value === "Open Setting") {
|
||||
vscode.commands.executeCommand(
|
||||
'workbench.action.openWorkspaceSettings',
|
||||
{openToSide : false, query : `mlir.${serverSettingName}`});
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
// Configure the server options.
|
||||
const serverOptions: vscodelc.ServerOptions = {
|
||||
@ -38,22 +80,21 @@ export class MLIRContext implements vscode.Disposable {
|
||||
|
||||
// Configure the client options.
|
||||
const clientOptions: vscodelc.LanguageClientOptions = {
|
||||
documentSelector : [ {scheme : 'file', language : 'mlir'} ],
|
||||
documentSelector : [ {scheme : 'file', language : languageName} ],
|
||||
synchronize : {
|
||||
// Notify the server about file changes to *.mlir files contained in the
|
||||
// workspace.
|
||||
fileEvents : vscode.workspace.createFileSystemWatcher('**/*.mlir')
|
||||
// Notify the server about file changes to language files contained in
|
||||
// the workspace.
|
||||
fileEvents :
|
||||
vscode.workspace.createFileSystemWatcher('**/*.' + languageName)
|
||||
},
|
||||
outputChannel : outputChannel,
|
||||
};
|
||||
|
||||
// Create the language client and start the client.
|
||||
this.client = new vscodelc.LanguageClient(
|
||||
'mlir-lsp', 'MLIR Language Client', serverOptions, clientOptions);
|
||||
this.subscriptions.push(this.client.start());
|
||||
|
||||
// Watch for configuration changes.
|
||||
configWatcher.activate(this);
|
||||
let languageClient = new vscodelc.LanguageClient(
|
||||
languageName + '-lsp', clientTitle, serverOptions, clientOptions);
|
||||
this.subscriptions.push(languageClient.start());
|
||||
return languageClient;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user