是否建议使用标记来突出显示语法?
Is it advisable to use tokens for the purpose of syntax highlighting?
我正在尝试在 Android 上使用 Xamarin 在 C# 中实现语法突出显示。我正在使用 ANTLR v4 library for C# to achieve this. My code, which is currently syntax highlighting Java with this grammar,不会尝试构建解析树并使用访问者模式。相反,我只是将输入转换为标记列表:
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 has further examples of coloring identifiers in certain contexts: here、List
和 ArrayList
是彩色的,而 mItems
不是。在这些情况下,我可能需要添加更多代码来突出显示标识符。
我的问题是,在这里检查标记而不是解析树是个好主意吗?一方面,我担心当令牌的邻居改变它应该如何突出显示时,我可能不得不做很多特殊的外壳。另一方面,解析将为内存受限的移动设备增加额外的开销,并使用户在代码编辑器中编辑文本时实现高效的语法高亮显示(例如,不是 re-tokenizing/parsing 一切)变得更加复杂。我还发现处理所有令牌类型比处理解析器规则类型要简单得多,因为你只是 switch
on token.Type
而不是覆盖一堆 Visit*
方法。
语法高亮器的完整代码可供参考here.
这取决于你的语法高亮。
如果您使用简单的解析器,那么文本中的任何语法错误都会导致突出显示失败。这使得它成为一个非常脆弱的解决方案,因为很多你可能想要语法高亮的文本都不能保证是正确的(特别是用户输入,在完全输入之前最多不会是正确的)。由于语法突出显示有助于使语法错误可见并且通常用于此目的,因此完全失败语法错误会适得其反。
有错误的文本不适合语法树。但它确实比令牌流具有更多的结构。最准确的表示可能是子树片段的森林,但这是一种比树更难处理的数据结构。
无论您选择哪种解决方案,您最终都会在相互冲突的目标之间进行谈判:复杂性与准确性与速度与可用性。解析器可能是解决方案的一部分,但临时模式匹配也可能是解决方案的一部分。
您的方法非常好,几乎每个人都在使用。通过环顾四周来微调类型匹配是完全正常的(而且它很便宜,因为令牌类型被缓存)。因此,如果您需要调整实际使用的 SyntaxKind
,您始终可以在令牌流中回顾或回顾。不要开始解析您的输入。帮不了你。
我最终选择使用解析器,因为临时规则太多了。例如,虽然我想将常规标识符着色为白色,但我希望类型声明中的类型(例如 class C
中的 C
)为绿色。这些特殊规则总共有大约 20 条。此外,与我的应用程序中的其他瓶颈相比,解析的额外开销被证明是微不足道的。
有兴趣的可以在这里查看我的代码:https://github.com/jamesqo/Repository/blob/e5d5653093861bc35f4c0ac71ad6e27265e656f3/Repository.EditorServices/Internal/Java/Highlighting/JavaSyntaxHighlighter.VisitMethods.cs#L19-L76。我已经突出显示了我必须制定的所有 ~20 条特殊规则。
我正在尝试在 Android 上使用 Xamarin 在 C# 中实现语法突出显示。我正在使用 ANTLR v4 library for C# to achieve this. My code, which is currently syntax highlighting Java with this grammar,不会尝试构建解析树并使用访问者模式。相反,我只是将输入转换为标记列表:
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 has further examples of coloring identifiers in certain contexts: here、List
和 ArrayList
是彩色的,而 mItems
不是。在这些情况下,我可能需要添加更多代码来突出显示标识符。
我的问题是,在这里检查标记而不是解析树是个好主意吗?一方面,我担心当令牌的邻居改变它应该如何突出显示时,我可能不得不做很多特殊的外壳。另一方面,解析将为内存受限的移动设备增加额外的开销,并使用户在代码编辑器中编辑文本时实现高效的语法高亮显示(例如,不是 re-tokenizing/parsing 一切)变得更加复杂。我还发现处理所有令牌类型比处理解析器规则类型要简单得多,因为你只是 switch
on token.Type
而不是覆盖一堆 Visit*
方法。
语法高亮器的完整代码可供参考here.
这取决于你的语法高亮。
如果您使用简单的解析器,那么文本中的任何语法错误都会导致突出显示失败。这使得它成为一个非常脆弱的解决方案,因为很多你可能想要语法高亮的文本都不能保证是正确的(特别是用户输入,在完全输入之前最多不会是正确的)。由于语法突出显示有助于使语法错误可见并且通常用于此目的,因此完全失败语法错误会适得其反。
有错误的文本不适合语法树。但它确实比令牌流具有更多的结构。最准确的表示可能是子树片段的森林,但这是一种比树更难处理的数据结构。
无论您选择哪种解决方案,您最终都会在相互冲突的目标之间进行谈判:复杂性与准确性与速度与可用性。解析器可能是解决方案的一部分,但临时模式匹配也可能是解决方案的一部分。
您的方法非常好,几乎每个人都在使用。通过环顾四周来微调类型匹配是完全正常的(而且它很便宜,因为令牌类型被缓存)。因此,如果您需要调整实际使用的 SyntaxKind
,您始终可以在令牌流中回顾或回顾。不要开始解析您的输入。帮不了你。
我最终选择使用解析器,因为临时规则太多了。例如,虽然我想将常规标识符着色为白色,但我希望类型声明中的类型(例如 class C
中的 C
)为绿色。这些特殊规则总共有大约 20 条。此外,与我的应用程序中的其他瓶颈相比,解析的额外开销被证明是微不足道的。
有兴趣的可以在这里查看我的代码:https://github.com/jamesqo/Repository/blob/e5d5653093861bc35f4c0ac71ad6e27265e656f3/Repository.EditorServices/Internal/Java/Highlighting/JavaSyntaxHighlighter.VisitMethods.cs#L19-L76。我已经突出显示了我必须制定的所有 ~20 条特殊规则。