我正在编写一个脚本编辑器,需要根据我定义的一些规则将用户的输入(类似于脚本语法)转换为有效的C#。例如,如果用户放入
using System;
public string hello()
{
return "Hi!" // whoops, semicolon here is missing!
}
我需要将其转换为
using System;
public class ContainerClass
{
public string hello()
{
return "Hi!" // whoops, semicolon here is missing!
}
}
我的转换将插入新节点(例如类声明)并可能移动现有节点,但它永远不会修改或删除现有节点。 (我知道SourceCodeKind = Script做了一些模糊的类似的,但由于各种原因我无法使用它。)
现在我需要提出一种方法来进行这种转换,考虑到以下因素:
由于每次用户更改原始文档时都需要运行转换(即只键入一个字母),因此从性能角度来看,我无法每次都重新解析整个事物。例如,如果用户在“;”之后插入缺少的分号,理想情况下我只需将相同(或克隆)的节点插入到我已经转换的文档中,而不是重新解析所有内容。我想这会排除标准的修改方式,例如DocumentEditor。
我需要有办法将已转换文档中的位置重新映射到原始文档中的位置。由于我永远不会删除节点,我认为理论上这应该是可能的(但是如何?)。 这是必要的,例如因为我最终会得到指向转换后文档中位置的诊断消息(和智能感知信息等),并且需要获取原始文档的位置以便实际向用户显示这些消息。
任何事情都可以或多或少直接地做到这一点吗?对于像这样的用例,甚至可能有一些Roslyn辅助类吗?
我的想法如下。我不太确定他们会工作,我认为他们很难实施,所以我希望有一些更简单的方法可以诚实;
对于#1,我唯一的想法是在源代码更改后获取原始文档的文本更改(Document.GetTextChangesAsync);然后以某种方式尝试找出哪些节点受此影响(可能获取与旧文档和新文档中已编辑区域相交的节点,然后计算哪些节点已被删除,添加或修改) - 然后应用这些更改我转型的文件。这看起来非常复杂。
对于#2,我到目前为止唯一的想法是启用对原始文档节点的跟踪。然后我会找到一个位置在转换后的文档中指向的节点,并在原始文档中找到源自该节点的节点(然后找到该节点的位置)。 但问题是,例如上面的代码会产生一个诊断错误,指向“Hi!”之后的位置,位置跨度的长度为0,因为缺少分号。所以该位置根本没有指向一个节点。也许我可以尝试在那种情况下找到相邻的节点?!