实例分析SharpDevelop代码完成功能(续)——添加对Boo语言的支持
接着上一篇《实例分析SharpDevelop代码完成功能》里添加的InsightWindow的功能。这回将要为那个CSharpCodeCompletion的Demo添加Boo语言的代码自动完成功能的支持。
Boo语言,可能不少人还没有听说过。好像是语言使用的排行榜上100名之外的一种编程语言。语法接近Python。就连Wiki上对它的介绍也少得可怜。目前支持这种语言的IDE只有SharpDevelop以其衍生的IDE。
SharpDevelop支持Boo的CodeCompletion,但是给的Sample里,只支持C#和VB.NET。下面就来介绍,如何一步步地让Sample支持Boo。
与C#不同,Boo是一种弱类型的语言,所以(也许不充分)不能像C#和VB那样用一个Parser进行分析来得到语法树。SD通过插件的方式,提供了对于Python、Boo等语言的支持。如图1所示。
图1. SharpDevelop中的Backends Bindings
BooBinding里,应该就提供了SD对于支持Boo所需要的全部代码。再来看BooBinding。
图2. BooBinding Project
里面居然专门有一个文件夹叫CodeCompletion,看来我们要找的东西都在里面了。下一步就是支持Boo了,其实说起来还是很容易的,不就是等价类替换一下嘛。在上一篇里介绍的用于CodeCompletion的类不少,但是语言相关的大约有5个类。3种语言的的这5 个类如表1所示。
|
C# |
VB |
Boo |
Parser |
ParserFactory |
ParserFactory |
BooParser |
Ambience |
CSharpAmbience |
VBNetAmbience |
BooAmbience |
Resolver |
NRefactoryResolver |
NRefactoryResolver |
BooResolver |
Expression Finder |
CSharpExpressionFinder |
VBExpressionFinder |
ExpressionFinder |
LanguageProperties |
LanguageProperties |
LanguageProperties |
BooLanugageProperties |
表1. 三种语言对于CodeCompletion功能的等价类
在进行无味的等价类替换工作之前先分别大致介绍一下3个类的用途。
1. Ambience:词典里的是“环境,气氛”。从名字看不出什么用途,图3是其定义。
图3. IAmbience
从这个接口里的方法大致就能猜个八九不离十了。就是将Parse出来的类啊,变量啊转化成用户可读的文本。因为不同的编程语言有不同的语言和专有名词,所以转化出来的文本也应该根据语言有所不同喽。这个接口,就分离了不同语言间这种可读性的不同。
2. ExpressionFinder:感觉图4里IExpressionFinder的定义没有给出比这个名字更多的信息了。
图4. IExpressionFinder
这个就是源代码里找表达式的接口。通过这个接口里定义的方法,就可以知道当前位置上应该是个什么东西,以及当前位置上是个什么东西。是个数字,还是变量等等。它找到的结果是个这么个东西。如图5。
图5. ExpressionResult
有兴趣的话,可以去看看它的内部结构。这里就不展开了。
3. IParser:这个就是最重要的部分。可以叫解析器吧。图6里把函数的参数也显示了出来,更能清楚地表达IParser接口的作用。(C#和VB的Parser接口与此略有不同)
图6. IParser
其中Parse函数的stringfileContent就是要Parse的源文件。Demo的注释里就说明了fileName参数不一定就是源文件在磁盘上的文件名,就当是一个GUID就可以。再来看一下图7所示的这个函数的返回类型的结构就一目了然了。
图7. ICompilationUnit
很强大的样子,从源文件直接得到了里面的Class,Attribute,Usings……,这些数据就可以用来做CodeCompletion了。如果看一下CSharp和Boo的Parser的源代码就可以知道这两个Parser是完全不一样的。CSharp的Parser是完全的对源文件进行分析的结果。而Boo的Parser只是搭了Boo Compiler的便车,在使用BooCompiler编译源文件的过程中让Compiler“顺便”帮它把它想要的语法树给它了。所以CSharp的Parser的效率会高不少。例子里就是用这个Parser在后台每2秒做一次Parser,用结果对ProjectContent进行更新。Resolver就是用Parser进行表达式类型的分析的。SharpDevelop的运行时也与此类似。
4. Resolver:用ExpressionFinder找到当前的Expression之后,就会用Resolver去知道当前的Expression倒底是个什么东西。Resolver的接口如图8所示。非常简单。
图8. IResolver
Resolve出来的Result也很简单。如图9所示。
图9. ResolveResult
有了上面的分析,解理这个图应该不成问题了吧。
5. LanguageProperties:这个在DEMO里的作用就是指示当前语言是什么。
基于上面的分析,我们知道Boo的Parser使用到了Boo的编译器,所以先把要用到的Library找到。如下:
1. BooBinding
2. Boo语言编译支持:Boo.Lang, Boo.Lang.Compiler, Boo.Lange.Parser, Boo.Lang.Useful
3. 还有一些其它的SharpDevelopr的Library:ICSharpCode.SharpDevelop….
为了提供Boo的CodeCompletion的支持居然要多加这么多库,一开始也没有想到。因为C#的CodeCompletion只有3、4个就足够了。(而且这些库都是必需的)
终于到了更改源代码的时候了。先要把Demo里的所有实例化这些Resolver, Ambience, Parser, ExpressionFinder的所有代码进行重构。把实例化过程分离出来,并交给MainForm来做。然后,添加Boo语言支持的工作就变成了——给四个工厂方法添加一个Else if分支——这么简单。其中一个工厂方法的代码如下。
2public static IAmbience GetAmbience()
3{
4 if (CurrentLanguageProperties == LanguageProperties.VBNet)
5 return new VBNetAmbience();
6 else if (CurrentLanguageProperties == LanguageProperties.CSharp)
7 return new CSharpAmbience();
8 else if (CurrentLanguageProperties == BooLanguageProperties.Instance)
9 return new BooAmbience();
10 else
11 throw new NotSupportedException();
12}
13
修改完成之后。运行一下。如图10所示。
图10. Boo代码完成功能
表面上是成功了,可以分析出类和方法。但是事实上运行一会儿就会Crash,而且如果改了方法名,给的Code Completion还是原来的结果。可见后台的Parse线程成功地走了一次,然后就死掉了。还把整个程序搞死了。
后来经过向Output输出消息,找出了出错的地方,是BooParser在调用Boo compiler的时候有一个编译步骤出现了StackOverflowException。这个问题可以通过稍稍修改BooParser的方式实现。有兴趣的读者可以自己尝试一下。修复版的代码将会在下一个续文中给出。这里是添加了Boo语言支持的CodeCompletionDemo的源代码。