我正在尝试使用Xamarin在Android上的C#中实现语法突出显示。我正在使用ANTLR v4 library来实现此目的。我的代码(当前使用this grammar突出显示Java的语法)不会尝试构建解析树并使用访问者模式。相反,我只是将输入转换为令牌列表:
Unit Delay
然后我遍历荧光笔中的所有标记,并根据它们的种类为它们指定颜色。
private static IList<IToken> Tokenize(string text)
{
var inputStream = new AntlrInputStream(text);
var lexer = new JavaLexer(inputStream);
var tokenStream = new CommonTokenStream(lexer);
tokenStream.Fill();
return tokenStream.GetTokens();
}
最初,我认为这是明智的,因为语法突出显示主要与上下文无关。但是,我已经发现自己需要public void HighlightAll(IList<IToken> tokens)
{
int tokenCount = tokens.Count;
for (int i = 0; i < tokenCount; i++)
{
var token = tokens[i];
var kind = GetSyntaxKind(token);
HighlightNext(token, kind);
if (kind == SyntaxKind.Annotation)
{
var nextToken = tokens[++i];
Debug.Assert(token.Text == "@" && nextToken.Type == Identifier);
HighlightNext(nextToken, SyntaxKind.Annotation);
}
}
}
public void HighlightNext(IToken token, SyntaxKind tokenKind)
{
int count = token.Text.Length;
if (token.Type != -1)
{
_text.SetSpan(_styler.GetSpan(tokenKind), _index, _index + count, SpanTypes.InclusiveExclusive);
_index += count;
}
}
前面的特殊情况标识符,因为我希望这些标识符作为注释突出显示,就像在GitHub(example)上一样。 GitHub还有一些在某些上下文中着色标识符的示例:here,@
和List
是彩色的,而ArrayList
则不是。mItems
。我可能需要添加更多代码来突出显示那些场景中的标识符。
我的问题是,在这里检查令牌而不是解析树是一个好主意吗?一方面,我担心当令牌的邻居改变应该如何突出显示时,我可能不得不做很多特殊的套管。另一方面,解析将为内存受限的移动设备增加额外开销,并且当用户在代码编辑器中编辑文本时,使得实现有效语法突出显示(例如,不重新标记/解析所有内容)变得更加复杂。我还发现处理所有令牌类型而不是解析器规则类型要复杂得多,因为你switch
上的token.Type
而不是覆盖一堆Visit*
方法。
作为参考,语法高亮显示的完整代码可用here.
答案 0 :(得分:2)
这取决于你的语法高亮。
如果您使用天真的解析器,那么文本中的任何语法错误都会导致突出显示失败。这使得它成为一个非常脆弱的解决方案,因为您可能希望语法突出显示的许多文本不能保证是正确的(特别是用户输入,在完全键入之前最多不会是正确的)。由于语法突出显示可以帮助使语法错误可见并且通常用于此目的,因此完全失败的语法错误会适得其反。
有错误的文本不适合语法树。但它确实具有比令牌流更多的结构。可能最准确的表示形式是一个子树片段的森林,但这是一个比树更难处理的数据结构。
无论您选择哪种解决方案,最终都会在相互冲突的目标之间进行协商:复杂性与准确性与速度与可用性之间的关系。解析器可以是解决方案的一部分,但也可以是临时模式匹配。
答案 1 :(得分:1)
你的方法完全没问题,几乎是每个人都在使用的方法。通过环顾四周来调整类型匹配是完全正常的(因为令牌类型被缓存,所以它很便宜)。因此,如果您需要调整实际使用的SyntaxKind
,您可以随时在令牌流中向后或向前看。不要开始解析您的输入。它对你没有帮助。
答案 2 :(得分:1)
我最终选择使用解析器,因为有太多的临时规则。例如,虽然我想将常规标识符着色为白色,但我希望类型声明中的类型(例如C
中的class C
)为绿色。最终总共约有20条特殊规则。此外,与我的应用程序中的其他瓶颈相比,解析的额外开销变得微不足道。
对于有兴趣的人,您可以在此处查看我的代码:https://github.com/jamesqo/Repository/blob/e5d5653093861bc35f4c0ac71ad6e27265e656f3/Repository.EditorServices/Internal/Java/Highlighting/JavaSyntaxHighlighter.VisitMethods.cs#L19-L76。我已经强调了我必须做出的所有约20项特殊规则。