我想获取文本中给定位置的最可能标记列表(行号和列号),以确定必须为自动代码完成填充的内容。这可以使用 ANTLR 4 API 轻松实现吗?
我想获取给定位置的可能标记列表,因为用户可能正在文本中间的某处编写/编辑,这仍然保证可能的标记列表。
请给我一些指导,因为我无法找到有关此主题的在线资源。
按行号获取标记的一种方法是ParseTreeListener
为您的语法创建一个,使用它来遍历给定的 ParseTree 并按行号索引 TerminalNodes。我不知道 C#,但这是我在 Java 中的做法。逻辑应该类似。
public class MyLineIndexer extends MyGrammarParserBaseListener {
protected MultiMap<Integer, TerminalNode> filelineTokenIndex;
@Override
public void visitTerminal(@NotNull TerminalNode node) {
// map every token to its file line for searching later...
if ( node.getSymbol() != null ) {
List<TerminalNode> tokens;
Integer line = node.getSymbol().getLine();
if (!filelineTokenIndex.containsKey(line)) {
tokens = new ArrayList<>();
filelineTokenIndex.put(line, tokens);
} else {
tokens = filelineTokenIndex.get(line);
}
tokens.add(node);
}
super.visitTerminal(node);
}
}
然后以通常的方式遍历解析树...
ParseTree parseTree = ... ; // parse it how you want to
MyLineIndexer indexer = new MyLineIndexer();
ParseTreeWalker walker = new ParseTreeWalker();
walker.walk(indexer, parseTree);
假设您在一条线上有合理数量的令牌,那么在一条线和范围内获取令牌现在相当直接且有效。例如,您可以像这样向侦听器添加另一个方法:
public TerminalNode findTerminalNodeAtCaret(int caretPos, int caretLine) {
if (caretPos <= 0) return null;
if (this.filelineTokenIndex.containsKey(caretLine)) {
List<TerminalNode> nodes = filelineTokenIndex.get(caretLine);
if (nodes.size() == 0) return null;
int tokenEndPos, tokenStartPos;
for (TerminalNode n : nodes) {
if (n.getSymbol() != null) {
tokenEndPos = n.getSymbol().getCharPositionInLine() + n.getText().length();
tokenStartPos = n.getSymbol().getCharPositionInLine();
// If the caret is within this token, return this token
if (caretPos >= tokenStartPos && caretPos <= tokenEndPos) {
return n;
}
}
}
}
return null;
}
您还需要确保您的解析器允许“松散”解析。在键入语言构造时,它可能无效。您的解析器规则应该允许这样做。