实例分析SharpDevelop代码完成功能

介绍

SharpDevelop源代码里自带一个CSharp代码自动完成功能(Code Completion)的例子。如下图所示:

 

 

1. 代码完成

 

看上去似乎好像挺不好做的,理论上要做词法分析、语法分析,还要解析一些如mscorlib之类的DLL。但是事实上SharpDevelop已经为我们做了这些,上面的例子只要写几个类就可以完成。整个Solution如下图所示:

 

2. 代码完成例子的Solution

 

3. Code Metrics Results

 

直接使用SharpDevelop里的分析器,就可以很简单地完成Code Completion的功能。平均每个文件只有100行的代码。也许有人要说这样没有意思,但是我们不应该“重新做个轮子”对吗?而且就算要深入地学习,也要先来把这个例子弄明白的。

 

这个例子里的Code Completion并不象SharpDevelop里的那么完成,没有提供下面的功能。

 

4. 缺失的ToolTip提示

 

在输入左括号时,应该在ToolTip中给出这个函数的所有重载,但是这个例子里并没有给出这个功能的实现。下面在介绍代码完成的同时,会给出这个ToolTip的实现。

 

博客园的Michael Zhang在其SharpDevelop浅析系列文章中也介绍了代码完成功能。如果对整个SharpDevelop有兴趣可以参考一下。下面进入正题……

 

第一部分 Code Completion的实现

下图是这个例子里的类体系

 

5. 整个例子的类体系

 

从类的结构来看,其实与SharpDevelop本身的实现不是一个层次的,这个类结构的耦合性很强,还好要看的是如何实现Code Completion,而不是它的设计。要看设计,还是看SharpDevelop好了。

 

下面逐个介绍一下里面的类。

 

CodeCompletionData类:是用于表示代码完成列表中每个项的Visual Object。看看它的父类会更明显一些。

 

6. DefaultCompletionData类图

 

MainForm类:就是看到的整个窗体。里面有三个控件,一个是SharpDevelopTextEditorControl,一个状态栏,一个ImageList。主窗体加载之后会开启一个Parser线程,每2秒对整个文档做一次Parse。(汗,用Stopwatch测试了一下,对一个很小的文件的一次Parse50ms左右。)Parse之后,用于做Code Completion的数据就有了。在哪儿?没去认真找过。应该是在ProjectContent里。下面是Parse的步骤:

 

Code

 

CodeCompletionKeyHandler类:看名字就知道,就是针对CodeCompletion处理窗体的键盘事件的,如果点了”.”,就实例化一个CodeCompletionProvider并显示CodeCompletion窗口。按VS的分析,这个只有22行代码的文件就不细介绍了。

 

ShowCodeCompletionList

 

HostCallbackImplementation类:这个更少,就13行代码。做的事件就是后台分析出错的时候,把消息显示出来。

CodeCompletionProvider类:虽然最重要的部分,却也只有51行代码。主要完成了两件事情:1. 生成CodeCompletion列表。2. 用户触发一个Item的时候把相应的内容插入(就2行)。

生成CodeCompletion列表的功能也十分简单,大致分成三个步骤:

a.       FindExpression:就是找到当前用户的点”.”之前的那个东西到底是个什么东西。

b.       Resolve:得到找到的那个东西的Memeber列表。

c.       GenerateData:从得到的列表生成用于在UI上显示的CodeCompletionData

ToolTipProvider类:这个可不是那个缺失的功能哦。这个只是在MouseOver一个Member时,显示出一些信息。在图1的上面的那个就是了。(好像ToolTip还错了……)这个过程和CodeCompletionProvider大致相同。就不再赘述了。

 

这样,整个CodeCompletion的功能就完成了。整个例子359行代码。(仔细看看代码,你会有信心把它到缩减到200行。)

 

第二部分 添加Code Insight功能

什么是Code Insight?就是之前提到的缺失的ToolTip啊。在SharpDevelop里给了它一个更专业的名字叫“Code Insight”。

 

一向奉行“Don’t recreate the wheel”,这次也不例外。Sample没有,SharpDevelop有啊。Ctrl+V过来不就行了?(当然商业项目不行哦,连Sample也不行。人家是GPL

 

然后就在SharpDevelop的源代码中找到了一个名为MethodInsightDataProvider的文件(在ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor名字空间下)加了进来,然后在KeyHandler里加入对InsightWindow的支持。

 

ShowInsightWindow

 

再把SharpDevelop里的CodeCompletionData里的GetDocumentation函数也Copy到这个SampleCodeCompletionData类中。

 

这样所有的类的准备完了。

 

然而在MethodInsightDataProvider中使用到的ParserServiceAmbienceServiceLoggingServiceMessageService都没有找到引用。又去看了一下SharpDevelop的运行流程,找到了开启这几个Service的地方,发现差不多也已经同时把整个IDE开启了。这个可不是我想要的。而且很多类都是protected sealed类。从外界是Call不到的。看来必须要”Recreate the wheel”,把那个DataProvider重新写一个了。还好那个Provider只有100多行代码,自己写一个并不复杂。把LoggingServiceMessageService的代码都删除。而ParserServiceAmbienceService的作用不过是ParserAmbience的工厂类,自己实例化一个不就得了?这样,DataProvider中所引用的所有Service都被剥离了。

整个MethodInsightDataProvider的代码如下所示:

 

MethodInsightDataProvider

 

运行一下。

 

 

7. Method Insight

 

大功告成。

 

其实还有一种Insight——IndexerInsight。就是在输入‘[’的时候给出ToolTip提示。这个就留给有兴趣的读者吧。

/Files/nankezhishi/source/CSharpCodeCompletion.zip这里是改版的代码完成的所有代码。

下一篇中,将为这个示例添加Boo语言的支持。

 

posted on 2008-11-22 14:45  南柯之石  阅读(13652)  评论(19编辑  收藏  举报

导航