edit:对于那些有兴趣/想要确切了解我在做什么的人,可以在这里找到我的应用程序的源代码。
我正在使用 C# 构建一个提供语法突出显示的代码编辑器应用程序。我目前正在使用 ANTLR for C# 来解析代码以突出显示它。到目前为止,当用户最初打开文件时,我的应用程序可以非常快速地突出显示代码。但是,我没有编写任何代码来在用户开始编辑文本时重新突出显示文本。
我希望编辑器在大文件上表现良好,所以我不想在用户每次键入字符时重新解析整个文件。我做了一些研究,似乎我正在寻找的是一个增量解析器。不幸的是,ANTLR v4 似乎无法进行增量解析,所以我不确定该怎么做。
我的问题是:我是否可以采取另一种方法,使用 ANTLR,在用户键入时不冻结应用程序?我真的很犹豫要不要放弃 ANTLR,因为有很多免费的语法可供它使用,所以添加对新语言的支持并不费吹灰之力。我研究了TextMate语法,VSCode使用了很多语法,但我不理解它们,也没有C#库可用于操作它们。
感谢您的帮助!
我不会在每次击键后解析,但我确实解析了整个文件。 这适用于我创建的域特定语言的中等大小文件。 我不是尝试仅解析文件的一部分,而是使用混合方法,在存在三个条件中的第一个时进行解析:
- 用户键入n个字符
- 计时器说m毫秒没有变化。
- 对于某些语法,用户键入行终止符/分隔符字符;
底线是,你可能会惊讶于人们在输入任何强加给他们语法的东西时花费了多少时间停下来思考。 可以利用这些暂停在用户思考时执行有用的工作,即使是 400 毫秒。 由于它们的语法,我在为工作创建的 DSL 中使用 #1 和 #2。
"无变化"时钟在每次击键事件后重置,当解析发生在n 个字符之后时,当然会设置n个字符计数器。 我发现这样的组合方法在 IDE 类型的环境中效果很好。
要记住的一件事是,如果这样做,请不要在发现语法错误时弄乱文本控件的插入点,因为错误在键入时是不可避免的。 我只是在标签中显示一条消息:
public override void Recover(Parser recognizer, RecognitionException e)
{
IToken token = recognizer.CurrentToken;
string message = string.Format("parse error at line {0}, position {1} right before {2} ", token.Line, token.Column, GetTokenErrorDisplay(token));
BasicEnvironment.SyntaxError = message;
在我的使用环境中,计时器通常在它关闭时进行控制;如果值为 800 毫秒和 10 个字符,我得到了很好的结果,计时器通常在解析开始时控制。
@JLH基本上也说明了我正在使用的方法,但我想添加一些您可能需要注意的事情:
首先,我将在与编辑器的UI线程不同的线程中进行解析,以防止解析器在用户决定继续编码的同时启动的情况。如果您使用的是其他线程,则用户可以继续编码,甚至不会注意到解析器正在后台运行。当然,您需要使用某种机制,在这种情况下取消解析,或者至少不要在编辑器中对生成的解析树执行任何操作,因为它已经过时了。
接下来是我发现ANTLR在中型文件(~100行(上可能会变得非常慢。而且速度真的很慢,我的意思是解析最多可能需要 20 秒!这可以通过将ANTLR的默认解析算法切换为SLL
并使用救助策略来防止,这样它就不会尝试从语法错误中恢复。如果发生这种情况,您必须使用正常的LL
模式再次解析整个事情,以检查它是否真的是语法错误或只是SLL
无法处理的事情。
通过这种方法,我将某些文件中的解析时间从 ~20 秒减少到 ~1 秒。
您可以在我的源代码中查看其工作(肯定不完美(实现(尽管它是在java中,但原理应该是相同的(。