[mlir:PDLL] Add support for inlay hints
These allow for displaying additional inline information, such as the types of variables, names operands/results, constraint/rewrite arguments, etc. This requires a bump in the vscode extension to a newer version, as inlay hints are a new LSP feature. Differential Revision: https://reviews.llvm.org/D126033
This commit is contained in:
parent
6187178e83
commit
5919eab55c
|
@ -219,6 +219,18 @@ necessarily provided by all IDE clients.
|
|||
|
||||

|
||||
|
||||
#### Inlay hints
|
||||
|
||||
The language server provides additional information inline with the source code.
|
||||
Editors usually render this using read-only virtual text snippets interspersed
|
||||
with code. Hints may be shown for:
|
||||
|
||||
* types of local variables
|
||||
* names of operand and result groups
|
||||
* constraint and rewrite arguments
|
||||
|
||||

|
||||
|
||||
## TableGen LSP Language Server : `tblgen-lsp-server`
|
||||
|
||||
MLIR provides an implementation of an LSP language server for `.td` text files
|
||||
|
|
|
@ -850,3 +850,45 @@ llvm::json::Value mlir::lsp::toJSON(const DocumentLink &value) {
|
|||
{"target", value.target},
|
||||
};
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// InlayHintsParams
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
bool mlir::lsp::fromJSON(const llvm::json::Value &value,
|
||||
InlayHintsParams &result, llvm::json::Path path) {
|
||||
llvm::json::ObjectMapper o(value, path);
|
||||
return o && o.map("textDocument", result.textDocument) &&
|
||||
o.map("range", result.range);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// InlayHint
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
llvm::json::Value mlir::lsp::toJSON(const InlayHint &value) {
|
||||
return llvm::json::Object{{"position", value.position},
|
||||
{"kind", (int)value.kind},
|
||||
{"label", value.label},
|
||||
{"paddingLeft", value.paddingLeft},
|
||||
{"paddingRight", value.paddingRight}};
|
||||
}
|
||||
bool mlir::lsp::operator==(const InlayHint &lhs, const InlayHint &rhs) {
|
||||
return std::tie(lhs.position, lhs.kind, lhs.label) ==
|
||||
std::tie(rhs.position, rhs.kind, rhs.label);
|
||||
}
|
||||
bool mlir::lsp::operator<(const InlayHint &lhs, const InlayHint &rhs) {
|
||||
return std::tie(lhs.position, lhs.kind, lhs.label) <
|
||||
std::tie(rhs.position, rhs.kind, rhs.label);
|
||||
}
|
||||
|
||||
llvm::raw_ostream &mlir::lsp::operator<<(llvm::raw_ostream &os,
|
||||
InlayHintKind value) {
|
||||
switch (value) {
|
||||
case InlayHintKind::Parameter:
|
||||
return os << "parameter";
|
||||
case InlayHintKind::Type:
|
||||
return os << "type";
|
||||
}
|
||||
llvm_unreachable("Unknown InlayHintKind");
|
||||
}
|
||||
|
|
|
@ -1000,6 +1000,86 @@ struct DocumentLink {
|
|||
/// Add support for JSON serialization.
|
||||
llvm::json::Value toJSON(const DocumentLink &value);
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// InlayHintsParams
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// A parameter literal used in inlay hint requests.
|
||||
struct InlayHintsParams {
|
||||
/// The text document.
|
||||
TextDocumentIdentifier textDocument;
|
||||
|
||||
/// The visible document range for which inlay hints should be computed.
|
||||
Range range;
|
||||
};
|
||||
|
||||
/// Add support for JSON serialization.
|
||||
bool fromJSON(const llvm::json::Value &value, InlayHintsParams &result,
|
||||
llvm::json::Path path);
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// InlayHintKind
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Inlay hint kinds.
|
||||
enum class InlayHintKind {
|
||||
/// An inlay hint that for a type annotation.
|
||||
///
|
||||
/// An example of a type hint is a hint in this position:
|
||||
/// auto var ^ = expr;
|
||||
/// which shows the deduced type of the variable.
|
||||
Type = 1,
|
||||
|
||||
/// An inlay hint that is for a parameter.
|
||||
///
|
||||
/// An example of a parameter hint is a hint in this position:
|
||||
/// func(^arg);
|
||||
/// which shows the name of the corresponding parameter.
|
||||
Parameter = 2,
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// InlayHint
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Inlay hint information.
|
||||
struct InlayHint {
|
||||
InlayHint(InlayHintKind kind, Position pos) : position(pos), kind(kind) {}
|
||||
|
||||
/// The position of this hint.
|
||||
Position position;
|
||||
|
||||
/// The label of this hint. A human readable string or an array of
|
||||
/// InlayHintLabelPart label parts.
|
||||
///
|
||||
/// *Note* that neither the string nor the label part can be empty.
|
||||
std::string label;
|
||||
|
||||
/// The kind of this hint. Can be omitted in which case the client should fall
|
||||
/// back to a reasonable default.
|
||||
InlayHintKind kind;
|
||||
|
||||
/// Render padding before the hint.
|
||||
///
|
||||
/// Note: Padding should use the editor's background color, not the
|
||||
/// background color of the hint itself. That means padding can be used
|
||||
/// to visually align/separate an inlay hint.
|
||||
bool paddingLeft = false;
|
||||
|
||||
/// Render padding after the hint.
|
||||
///
|
||||
/// Note: Padding should use the editor's background color, not the
|
||||
/// background color of the hint itself. That means padding can be used
|
||||
/// to visually align/separate an inlay hint.
|
||||
bool paddingRight = false;
|
||||
};
|
||||
|
||||
/// Add support for JSON serialization.
|
||||
llvm::json::Value toJSON(const InlayHint &);
|
||||
bool operator==(const InlayHint &lhs, const InlayHint &rhs);
|
||||
bool operator<(const InlayHint &lhs, const InlayHint &rhs);
|
||||
llvm::raw_ostream &operator<<(llvm::raw_ostream &os, InlayHintKind value);
|
||||
|
||||
} // namespace lsp
|
||||
} // namespace mlir
|
||||
|
||||
|
|
|
@ -82,6 +82,12 @@ struct LSPServer {
|
|||
void onSignatureHelp(const TextDocumentPositionParams ¶ms,
|
||||
Callback<SignatureHelp> reply);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Inlay Hints
|
||||
|
||||
void onInlayHint(const InlayHintsParams ¶ms,
|
||||
Callback<std::vector<InlayHint>> reply);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// PDLL View Output
|
||||
|
||||
|
@ -140,6 +146,7 @@ void LSPServer::onInitialize(const InitializeParams ¶ms,
|
|||
}},
|
||||
{"hoverProvider", true},
|
||||
{"documentSymbolProvider", true},
|
||||
{"inlayHintProvider", true},
|
||||
};
|
||||
|
||||
llvm::json::Object result{
|
||||
|
@ -248,6 +255,16 @@ void LSPServer::onSignatureHelp(const TextDocumentPositionParams ¶ms,
|
|||
reply(server.getSignatureHelp(params.textDocument.uri, params.position));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Inlay Hints
|
||||
|
||||
void LSPServer::onInlayHint(const InlayHintsParams ¶ms,
|
||||
Callback<std::vector<InlayHint>> reply) {
|
||||
std::vector<InlayHint> hints;
|
||||
server.getInlayHints(params.textDocument.uri, params.range, hints);
|
||||
reply(std::move(hints));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// PDLL ViewOutput
|
||||
|
||||
|
@ -305,6 +322,10 @@ LogicalResult mlir::lsp::runPdllLSPServer(PDLLServer &server,
|
|||
messageHandler.method("textDocument/signatureHelp", &lspServer,
|
||||
&LSPServer::onSignatureHelp);
|
||||
|
||||
// Inlay Hints
|
||||
messageHandler.method("textDocument/inlayHint", &lspServer,
|
||||
&LSPServer::onInlayHint);
|
||||
|
||||
// PDLL ViewOutput
|
||||
messageHandler.method("pdll/viewOutput", &lspServer,
|
||||
&LSPServer::onPDLLViewOutput);
|
||||
|
|
|
@ -62,6 +62,14 @@ static lsp::Location getLocationFromLoc(llvm::SourceMgr &mgr, SMRange range,
|
|||
return lsp::Location(getURIFromLoc(mgr, range, uri), lsp::Range(mgr, range));
|
||||
}
|
||||
|
||||
/// Returns true if the given range contains the given source location. Note
|
||||
/// that this has different behavior than SMRange because it is inclusive of the
|
||||
/// end location.
|
||||
static bool contains(SMRange range, SMLoc loc) {
|
||||
return range.Start.getPointer() <= loc.getPointer() &&
|
||||
loc.getPointer() <= range.End.getPointer();
|
||||
}
|
||||
|
||||
/// Convert the given MLIR diagnostic to the LSP form.
|
||||
static Optional<lsp::Diagnostic>
|
||||
getLspDiagnoticFromDiag(llvm::SourceMgr &sourceMgr, const ast::Diagnostic &diag,
|
||||
|
@ -358,6 +366,25 @@ struct PDLDocument {
|
|||
lsp::SignatureHelp getSignatureHelp(const lsp::URIForFile &uri,
|
||||
const lsp::Position &helpPos);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Inlay Hints
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
void getInlayHints(const lsp::URIForFile &uri, const lsp::Range &range,
|
||||
std::vector<lsp::InlayHint> &inlayHints);
|
||||
void getInlayHintsFor(const ast::VariableDecl *decl,
|
||||
const lsp::URIForFile &uri,
|
||||
std::vector<lsp::InlayHint> &inlayHints);
|
||||
void getInlayHintsFor(const ast::CallExpr *expr, const lsp::URIForFile &uri,
|
||||
std::vector<lsp::InlayHint> &inlayHints);
|
||||
void getInlayHintsFor(const ast::OperationExpr *expr,
|
||||
const lsp::URIForFile &uri,
|
||||
std::vector<lsp::InlayHint> &inlayHints);
|
||||
|
||||
/// Add a parameter hint for the given expression using `label`.
|
||||
void addParameterHintFor(std::vector<lsp::InlayHint> &inlayHints,
|
||||
const ast::Expr *expr, StringRef label);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// PDLL ViewOutput
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
@ -1169,6 +1196,153 @@ lsp::SignatureHelp PDLDocument::getSignatureHelp(const lsp::URIForFile &uri,
|
|||
return signatureHelp;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// PDLDocument: Inlay Hints
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Returns true if the given name should be added as a hint for `expr`.
|
||||
static bool shouldAddHintFor(const ast::Expr *expr, StringRef name) {
|
||||
if (name.empty())
|
||||
return false;
|
||||
|
||||
// If the argument is a reference of the same name, don't add it as a hint.
|
||||
if (auto *ref = dyn_cast<ast::DeclRefExpr>(expr)) {
|
||||
const ast::Name *declName = ref->getDecl()->getName();
|
||||
if (declName && declName->getName() == name)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PDLDocument::getInlayHints(const lsp::URIForFile &uri,
|
||||
const lsp::Range &range,
|
||||
std::vector<lsp::InlayHint> &inlayHints) {
|
||||
if (failed(astModule))
|
||||
return;
|
||||
SMRange rangeLoc = range.getAsSMRange(sourceMgr);
|
||||
if (!rangeLoc.isValid())
|
||||
return;
|
||||
(*astModule)->walk([&](const ast::Node *node) {
|
||||
SMRange loc = node->getLoc();
|
||||
|
||||
// Check that the location of this node is within the input range.
|
||||
if (!contains(rangeLoc, loc.Start) && !contains(rangeLoc, loc.End))
|
||||
return;
|
||||
|
||||
// Handle hints for various types of nodes.
|
||||
llvm::TypeSwitch<const ast::Node *>(node)
|
||||
.Case<ast::VariableDecl, ast::CallExpr, ast::OperationExpr>(
|
||||
[&](const auto *node) {
|
||||
this->getInlayHintsFor(node, uri, inlayHints);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void PDLDocument::getInlayHintsFor(const ast::VariableDecl *decl,
|
||||
const lsp::URIForFile &uri,
|
||||
std::vector<lsp::InlayHint> &inlayHints) {
|
||||
// Check to see if the variable has a constraint list, if it does we don't
|
||||
// provide initializer hints.
|
||||
if (!decl->getConstraints().empty())
|
||||
return;
|
||||
|
||||
// Check to see if the variable has an initializer.
|
||||
if (const ast::Expr *expr = decl->getInitExpr()) {
|
||||
// Don't add hints for operation expression initialized variables given that
|
||||
// the type of the variable is easily inferred by the expression operation
|
||||
// name.
|
||||
if (isa<ast::OperationExpr>(expr))
|
||||
return;
|
||||
}
|
||||
|
||||
lsp::InlayHint hint(lsp::InlayHintKind::Type,
|
||||
lsp::Position(sourceMgr, decl->getLoc().End));
|
||||
{
|
||||
llvm::raw_string_ostream labelOS(hint.label);
|
||||
labelOS << ": " << decl->getType();
|
||||
}
|
||||
|
||||
inlayHints.emplace_back(std::move(hint));
|
||||
}
|
||||
|
||||
void PDLDocument::getInlayHintsFor(const ast::CallExpr *expr,
|
||||
const lsp::URIForFile &uri,
|
||||
std::vector<lsp::InlayHint> &inlayHints) {
|
||||
// Try to extract the callable of this call.
|
||||
const auto *callableRef = dyn_cast<ast::DeclRefExpr>(expr->getCallableExpr());
|
||||
const auto *callable =
|
||||
callableRef ? dyn_cast<ast::CallableDecl>(callableRef->getDecl())
|
||||
: nullptr;
|
||||
if (!callable)
|
||||
return;
|
||||
|
||||
// Add hints for the arguments to the call.
|
||||
for (const auto &it : llvm::zip(expr->getArguments(), callable->getInputs()))
|
||||
addParameterHintFor(inlayHints, std::get<0>(it),
|
||||
std::get<1>(it)->getName().getName());
|
||||
}
|
||||
|
||||
void PDLDocument::getInlayHintsFor(const ast::OperationExpr *expr,
|
||||
const lsp::URIForFile &uri,
|
||||
std::vector<lsp::InlayHint> &inlayHints) {
|
||||
// Check for ODS information.
|
||||
ast::OperationType opType = expr->getType().dyn_cast<ast::OperationType>();
|
||||
const auto *odsOp = opType ? opType.getODSOperation() : nullptr;
|
||||
|
||||
auto addOpHint = [&](const ast::Expr *valueExpr, StringRef label) {
|
||||
// If the value expression used the same location as the operation, don't
|
||||
// add a hint. This expression was materialized during parsing.
|
||||
if (expr->getLoc().Start == valueExpr->getLoc().Start)
|
||||
return;
|
||||
addParameterHintFor(inlayHints, valueExpr, label);
|
||||
};
|
||||
|
||||
// Functor used to process hints for the operands and results of the
|
||||
// operation. They effectively have the same format, and thus can be processed
|
||||
// using the same logic.
|
||||
auto addOperandOrResultHints = [&](ArrayRef<ast::Expr *> values,
|
||||
ArrayRef<ods::OperandOrResult> odsValues,
|
||||
StringRef allValuesName) {
|
||||
if (values.empty())
|
||||
return;
|
||||
|
||||
// The values should either map to a single range, or be equivalent to the
|
||||
// ODS values.
|
||||
if (values.size() != odsValues.size()) {
|
||||
// Handle the case of a single element that covers the full range.
|
||||
if (values.size() == 1)
|
||||
return addOpHint(values.front(), allValuesName);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &it : llvm::zip(values, odsValues))
|
||||
addOpHint(std::get<0>(it), std::get<1>(it).getName());
|
||||
};
|
||||
|
||||
// Add hints for the operands and results of the operation.
|
||||
addOperandOrResultHints(expr->getOperands(),
|
||||
odsOp ? odsOp->getOperands()
|
||||
: ArrayRef<ods::OperandOrResult>(),
|
||||
"operands");
|
||||
addOperandOrResultHints(expr->getResultTypes(),
|
||||
odsOp ? odsOp->getResults()
|
||||
: ArrayRef<ods::OperandOrResult>(),
|
||||
"results");
|
||||
}
|
||||
|
||||
void PDLDocument::addParameterHintFor(std::vector<lsp::InlayHint> &inlayHints,
|
||||
const ast::Expr *expr, StringRef label) {
|
||||
if (!shouldAddHintFor(expr, label))
|
||||
return;
|
||||
|
||||
lsp::InlayHint hint(lsp::InlayHintKind::Parameter,
|
||||
lsp::Position(sourceMgr, expr->getLoc().Start));
|
||||
hint.label = (label + ":").str();
|
||||
hint.paddingRight = true;
|
||||
inlayHints.emplace_back(std::move(hint));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// PDLL ViewOutput
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -1271,9 +1445,14 @@ public:
|
|||
lsp::Position completePos);
|
||||
lsp::SignatureHelp getSignatureHelp(const lsp::URIForFile &uri,
|
||||
lsp::Position helpPos);
|
||||
void getInlayHints(const lsp::URIForFile &uri, lsp::Range range,
|
||||
std::vector<lsp::InlayHint> &inlayHints);
|
||||
lsp::PDLLViewOutputResult getPDLLViewOutput(lsp::PDLLViewOutputKind kind);
|
||||
|
||||
private:
|
||||
using ChunkIterator = llvm::pointee_iterator<
|
||||
std::vector<std::unique_ptr<PDLTextFileChunk>>::iterator>;
|
||||
|
||||
/// Initialize the text file from the given file contents.
|
||||
void initialize(const lsp::URIForFile &uri, int64_t newVersion,
|
||||
std::vector<lsp::Diagnostic> &diagnostics);
|
||||
|
@ -1281,7 +1460,10 @@ 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);
|
||||
ChunkIterator getChunkItFor(lsp::Position &pos);
|
||||
PDLTextFileChunk &getChunkFor(lsp::Position &pos) {
|
||||
return *getChunkItFor(pos);
|
||||
}
|
||||
|
||||
/// The full string contents of the file.
|
||||
std::string contents;
|
||||
|
@ -1436,6 +1618,45 @@ lsp::SignatureHelp PDLTextFile::getSignatureHelp(const lsp::URIForFile &uri,
|
|||
return getChunkFor(helpPos).document.getSignatureHelp(uri, helpPos);
|
||||
}
|
||||
|
||||
void PDLTextFile::getInlayHints(const lsp::URIForFile &uri, lsp::Range range,
|
||||
std::vector<lsp::InlayHint> &inlayHints) {
|
||||
auto startIt = getChunkItFor(range.start);
|
||||
auto endIt = getChunkItFor(range.end);
|
||||
|
||||
// Functor used to get the chunks for a given file, and fixup any locations
|
||||
auto getHintsForChunk = [&](ChunkIterator chunkIt, lsp::Range range) {
|
||||
size_t currentNumHints = inlayHints.size();
|
||||
chunkIt->document.getInlayHints(uri, range, inlayHints);
|
||||
|
||||
// If this isn't the first chunk, update any positions to account for line
|
||||
// number differences.
|
||||
if (&*chunkIt != &*chunks.front()) {
|
||||
for (auto &hint : llvm::drop_begin(inlayHints, currentNumHints))
|
||||
chunkIt->adjustLocForChunkOffset(hint.position);
|
||||
}
|
||||
};
|
||||
// Returns the number of lines held by a given chunk.
|
||||
auto getNumLines = [](ChunkIterator chunkIt) {
|
||||
return (chunkIt + 1)->lineOffset - chunkIt->lineOffset;
|
||||
};
|
||||
|
||||
// Check if the range is fully within a single chunk.
|
||||
if (startIt == endIt)
|
||||
return getHintsForChunk(startIt, range);
|
||||
|
||||
// Otherwise, the range is split between multiple chunks. The first chunk
|
||||
// has the correct range start, but covers the total document.
|
||||
getHintsForChunk(startIt, lsp::Range(range.start, getNumLines(startIt)));
|
||||
|
||||
// Every chunk in between uses the full document.
|
||||
for (++startIt; startIt != endIt; ++startIt)
|
||||
getHintsForChunk(startIt, lsp::Range(0, getNumLines(startIt)));
|
||||
|
||||
// The range for the last chunk starts at the beginning of the document, up
|
||||
// through the end of the input range.
|
||||
getHintsForChunk(startIt, lsp::Range(0, range.end));
|
||||
}
|
||||
|
||||
lsp::PDLLViewOutputResult
|
||||
PDLTextFile::getPDLLViewOutput(lsp::PDLLViewOutputKind kind) {
|
||||
lsp::PDLLViewOutputResult result;
|
||||
|
@ -1490,9 +1711,9 @@ void PDLTextFile::initialize(const lsp::URIForFile &uri, int64_t newVersion,
|
|||
totalNumLines = lineOffset;
|
||||
}
|
||||
|
||||
PDLTextFileChunk &PDLTextFile::getChunkFor(lsp::Position &pos) {
|
||||
PDLTextFile::ChunkIterator PDLTextFile::getChunkItFor(lsp::Position &pos) {
|
||||
if (chunks.size() == 1)
|
||||
return *chunks.front();
|
||||
return chunks.begin();
|
||||
|
||||
// Search for the first chunk with a greater line offset, the previous chunk
|
||||
// is the one that contains `pos`.
|
||||
|
@ -1500,9 +1721,9 @@ PDLTextFileChunk &PDLTextFile::getChunkFor(lsp::Position &pos) {
|
|||
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;
|
||||
ChunkIterator chunkIt(it == chunks.end() ? (chunks.end() - 1) : --it);
|
||||
pos.line -= chunkIt->lineOffset;
|
||||
return chunkIt;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -1623,6 +1844,19 @@ lsp::SignatureHelp lsp::PDLLServer::getSignatureHelp(const URIForFile &uri,
|
|||
return SignatureHelp();
|
||||
}
|
||||
|
||||
void lsp::PDLLServer::getInlayHints(const URIForFile &uri, const Range &range,
|
||||
std::vector<InlayHint> &inlayHints) {
|
||||
auto fileIt = impl->files.find(uri.file());
|
||||
if (fileIt == impl->files.end())
|
||||
return;
|
||||
fileIt->second->getInlayHints(uri, range, inlayHints);
|
||||
|
||||
// Drop any duplicated hints that may have cropped up.
|
||||
llvm::sort(inlayHints);
|
||||
inlayHints.erase(std::unique(inlayHints.begin(), inlayHints.end()),
|
||||
inlayHints.end());
|
||||
}
|
||||
|
||||
Optional<lsp::PDLLViewOutputResult>
|
||||
lsp::PDLLServer::getPDLLViewOutput(const URIForFile &uri,
|
||||
PDLLViewOutputKind kind) {
|
||||
|
|
|
@ -24,8 +24,10 @@ struct CompletionList;
|
|||
struct DocumentLink;
|
||||
struct DocumentSymbol;
|
||||
struct Hover;
|
||||
struct InlayHint;
|
||||
struct Location;
|
||||
struct Position;
|
||||
struct Range;
|
||||
struct SignatureHelp;
|
||||
struct TextDocumentContentChangeEvent;
|
||||
class URIForFile;
|
||||
|
@ -95,6 +97,10 @@ public:
|
|||
SignatureHelp getSignatureHelp(const URIForFile &uri,
|
||||
const Position &helpPos);
|
||||
|
||||
/// Get the inlay hints for the range within the given file.
|
||||
void getInlayHints(const URIForFile &uri, const Range &range,
|
||||
std::vector<InlayHint> &inlayHints);
|
||||
|
||||
/// Get the output of the given PDLL file, or None if there is no valid
|
||||
/// output.
|
||||
Optional<PDLLViewOutputResult> getPDLLViewOutput(const URIForFile &uri,
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "documentSymbolProvider": true,
|
||||
// CHECK-NEXT: "hoverProvider": true,
|
||||
// CHECK-NEXT: "inlayHintProvider": true,
|
||||
// CHECK-NEXT: "referencesProvider": true,
|
||||
// CHECK-NEXT: "signatureHelpProvider": {
|
||||
// CHECK-NEXT: "triggerCharacters": [
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
// RUN: mlir-pdll-lsp-server -pdll-extra-dir %S -pdll-extra-dir %S/../../include -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":"#include \"include/included.td\"\nConstraint Cst(attr: Attr);\nPattern {\n op<test.multi>(_: Value, _: Value);\n op<test.multi>(_: ValueRange) -> (_: Type, _: Type);\n let op = op<test.multi>;\n let value = op.0;\n Cst(_: Attr);\n erase op;\n}\n"
|
||||
}}}
|
||||
// -----
|
||||
{"jsonrpc":"2.0","id":2,"method":"textDocument/inlayHint","params":{
|
||||
"textDocument":{"uri":"test:///foo.pdll"},
|
||||
"range": {
|
||||
"start": {"line":3,"character":0},
|
||||
"end": {"line":5,"character":0}
|
||||
}
|
||||
}}
|
||||
// CHECK: "id": 2,
|
||||
// CHECK-NEXT: "jsonrpc": "2.0",
|
||||
// CHECK-NEXT: "result": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "kind": 2,
|
||||
// CHECK-NEXT: "label": "operand:",
|
||||
// CHECK-NEXT: "paddingLeft": false,
|
||||
// CHECK-NEXT: "paddingRight": true,
|
||||
// CHECK-NEXT: "position": {
|
||||
// CHECK-NEXT: "character": 17,
|
||||
// CHECK-NEXT: "line": 3
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "kind": 2,
|
||||
// CHECK-NEXT: "label": "operand2:",
|
||||
// CHECK-NEXT: "paddingLeft": false,
|
||||
// CHECK-NEXT: "paddingRight": true,
|
||||
// CHECK-NEXT: "position": {
|
||||
// CHECK-NEXT: "character": 27,
|
||||
// CHECK-NEXT: "line": 3
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "kind": 2,
|
||||
// CHECK-NEXT: "label": "operands:",
|
||||
// CHECK-NEXT: "paddingLeft": false,
|
||||
// CHECK-NEXT: "paddingRight": true,
|
||||
// CHECK-NEXT: "position": {
|
||||
// CHECK-NEXT: "character": 17,
|
||||
// CHECK-NEXT: "line": 4
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "kind": 2,
|
||||
// CHECK-NEXT: "label": "result:",
|
||||
// CHECK-NEXT: "paddingLeft": false,
|
||||
// CHECK-NEXT: "paddingRight": true,
|
||||
// CHECK-NEXT: "position": {
|
||||
// CHECK-NEXT: "character": 36,
|
||||
// CHECK-NEXT: "line": 4
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "kind": 2,
|
||||
// CHECK-NEXT: "label": "result2:",
|
||||
// CHECK-NEXT: "paddingLeft": false,
|
||||
// CHECK-NEXT: "paddingRight": true,
|
||||
// CHECK-NEXT: "position": {
|
||||
// CHECK-NEXT: "character": 45,
|
||||
// CHECK-NEXT: "line": 4
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// -----
|
||||
{"jsonrpc":"2.0","id":3,"method":"textDocument/inlayHint","params":{
|
||||
"textDocument":{"uri":"test:///foo.pdll"},
|
||||
"range": {
|
||||
"start": {"line":5,"character":0},
|
||||
"end": {"line":8,"character":0}
|
||||
}
|
||||
}}
|
||||
// CHECK: "id": 3,
|
||||
// CHECK-NEXT: "jsonrpc": "2.0",
|
||||
// CHECK-NEXT: "result": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "kind": 1,
|
||||
// CHECK-NEXT: "label": ": Value",
|
||||
// CHECK-NEXT: "paddingLeft": false,
|
||||
// CHECK-NEXT: "paddingRight": false,
|
||||
// CHECK-NEXT: "position": {
|
||||
// CHECK-NEXT: "character": 11,
|
||||
// CHECK-NEXT: "line": 6
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "kind": 2,
|
||||
// CHECK-NEXT: "label": "attr:",
|
||||
// CHECK-NEXT: "paddingLeft": false,
|
||||
// CHECK-NEXT: "paddingRight": true,
|
||||
// CHECK-NEXT: "position": {
|
||||
// CHECK-NEXT: "character": 6,
|
||||
// CHECK-NEXT: "line": 7
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// -----
|
||||
{"jsonrpc":"2.0","id":4,"method":"shutdown"}
|
||||
// -----
|
||||
{"jsonrpc":"2.0","method":"exit"}
|
File diff suppressed because it is too large
Load Diff
|
@ -7,7 +7,7 @@
|
|||
"homepage": "https://mlir.llvm.org/",
|
||||
"icon": "icon.png",
|
||||
"engines": {
|
||||
"vscode": "^1.52.0"
|
||||
"vscode": "^1.67.0"
|
||||
},
|
||||
"categories": [
|
||||
"Programming Languages"
|
||||
|
@ -36,17 +36,15 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"chokidar": "3.5.2",
|
||||
"vscode-languageclient": "^5.2.1",
|
||||
"vscode-languageserver-types": "3.16.0"
|
||||
"vscode-languageclient": "^8.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^5.2.0",
|
||||
"@types/node": "^8.0.0",
|
||||
"@types/vscode": "1.52.*",
|
||||
"clang-format": "1.4.0",
|
||||
"tslint": "^5.16.0",
|
||||
"typescript": "^3.5.1",
|
||||
"vsce": "^1.75.0",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"@types/node": "^14.17.0",
|
||||
"@types/vscode": "~1.67.0",
|
||||
"clang-format": "^1.8.0",
|
||||
"typescript": "^4.6.4",
|
||||
"vsce": "^2.7.0",
|
||||
"vscode-test": "^1.3.0"
|
||||
},
|
||||
"repository": {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import * as vscodelc from 'vscode-languageclient';
|
||||
import * as vscodelc from 'vscode-languageclient/node';
|
||||
|
||||
import * as config from './config';
|
||||
import * as configWatcher from './configWatcher';
|
||||
|
@ -11,7 +11,7 @@ import * as configWatcher from './configWatcher';
|
|||
*/
|
||||
class WorkspaceFolderContext implements vscode.Disposable {
|
||||
dispose() {
|
||||
this.clients.forEach(client => client.stop());
|
||||
this.clients.forEach(async client => await client.stop());
|
||||
this.clients.clear();
|
||||
}
|
||||
|
||||
|
@ -229,16 +229,8 @@ export class MLIRContext implements vscode.Disposable {
|
|||
|
||||
// Configure the server options.
|
||||
const serverOptions: vscodelc.ServerOptions = {
|
||||
run : {
|
||||
command : serverPath,
|
||||
transport : vscodelc.TransportKind.stdio,
|
||||
args : additionalServerArgs
|
||||
},
|
||||
debug : {
|
||||
command : serverPath,
|
||||
transport : vscodelc.TransportKind.stdio,
|
||||
args : additionalServerArgs
|
||||
}
|
||||
command : serverPath,
|
||||
args : additionalServerArgs
|
||||
};
|
||||
|
||||
// Configure file patterns relative to the workspace folder.
|
||||
|
@ -279,7 +271,10 @@ export class MLIRContext implements vscode.Disposable {
|
|||
},
|
||||
outputChannel : outputChannel,
|
||||
workspaceFolder : workspaceFolder,
|
||||
middleware : middleware
|
||||
middleware : middleware,
|
||||
|
||||
// Don't switch to output window when the server returns output.
|
||||
revealOutputChannelOn : vscodelc.RevealOutputChannelOn.Never,
|
||||
};
|
||||
|
||||
// Create the language client and start the client.
|
||||
|
|
Loading…
Reference in New Issue