1 目的
无论是Visual stuio还是Eclipse等众多IDE,映入我们眼帘最直观的就是各种语法着色,关键字如“int”,字符串如"ddd",注释如“com.bbe”。
今天我们学习如何让你的编辑器支持语法着色。
·2 定义
我们首先从思想上定义哪些输入串将被语法着色,以及它们的颜色和字体类型,我们有如下三种模式将被语法着色:
Ø 关键字
关键字包括SELECT, FROM和WHERE
n 颜色:RGB(127, 0, 85)
n style:SWT.BOLD
n 示例:CREATE
Ø 字符串
n 颜色:RGB(42, 0, 255)
n style:默认
n 示例:"String"
Ø 注释
n 颜色:RGB(63, 95, 191)
n style:默认
n 示例:/*Zero Bobo Resource Manager */
是的,正如你所见,我们使用的是Eclipse的默认语法颜色。
·3 分析
·3.1 如何让编辑器着色
想想,怎么才能让编辑器语法着色?
Ø 我们必须有一个语法(词法)扫描器(Scanner),它能够分解出输入中的单词(Token),并且根据某种模式判断出单词的类型,是关键字、字符串还是数字?在此前我们已经定义了各种Token类型对应的语法颜色和字体类型(如·2定义中描述)。
Ø AbstractDecoratedTextEditor上有一个SourceViewer。SourceViewer属于JFace,它封装了StyledText。我们还必须为SourceViewer设置一个Configuration以支持对SourceViewer提供各种增加功能(add-ons),由它支持对SourceViewer进行语法着色。
·3.2 Eclipse的策略
针对我们上面的考虑,Eclipse中有对应的类实现它们:
● org.eclipse.jface.text.rules.RuleBasedScanner类
为了支持语法着色功能,就必须使用某种扫描器对输入进行语法分析,将输入分解为一个个的记号(Token),同时还要确定这些Token属于什么类型,是关键字还是字符串。
定位于org.eclipse.jface.text.rules下的RuleBasedScanner类提供了扫描器的功能,该类可设置多个Rule(规则),对于输入按序执行这些rule,当某个Rule能够成功evaluate一个输入,就返回一个定义好的Token。如果一个rule返回了一个UN_DEFINE的TOKEN,就继续让下一个Rule来解析。如果没有Rule能够返回定义的token,则Scanner返回一个特定的Token,对该token调用isOther()将返回true。如果遇到了文件的结尾,则对Scanner返回的Token调用isEOF返回true。
我们自己定义一个ZSqlScanner类继承RuleBasedScanner,并在构造中设置需要的rule。
关于Rule主要有如下几种(Rule均实现IRule接口):
a) WordRule实现IRule:该规则可以发现一个完整的字,比如Java或SQL的关键字,他们的语法组成是固定的,int就是int,long就是long,通过这个规则可以发现这些关键字。WordRule需要一个IWordDetector接口的实例来辅助分析(实际上WordRule有点小问题,在后面介绍)。
b) WhitespaceRule实现IRule:顾名思义,发现输入中的空白字符。WhitespaceRule需要一个IWhitespaceDetector接口的实例来辅助分析。
c) SingleLineRule继承PatternRule:根据某种模式匹配规则,比如字符串由“””开始和结束。但SingleLineRule要求字符序列必须在单行上匹配模式。
d) MultiLineRule与SingleLineRule类似,不同在于MultiLineRule允许字符序列在多行上匹配模式(比如多行注释/* */可用这种规则)。
e) EndOfLineRule继承自SingleLineRule,它开始于某种指定的模式,结束一定是一个换行符(line delimiter),比如单行注释“//abcd”就可使用这种规则。
● org.eclipse.jface.text.source.SourceViewerConfiguration类
在前言中我们已经了解,SourceViewer通过SourceViewerConfiguration类来控制其各种增件配置,如代码着色,自动提示等。为了支持语法着色,我们必须提供自己的SourceViewerConfiguration类:ZSqlSourceViewerConfiguration,并且重写getPresentationReconciler方法。
IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer)方法,返回一个IPresentationReconciler接口的实例,该实例上注册了了一个毁坏器(damager)和一个修复器(repairer)。
damager负责计算文档被修改的区域
repairer负责重新构建文档被修改区域的表现形式(着色)
·4 源代码
通过分析,我们的思想已经有了,下面就来看源代码。由于篇幅关系,我们不在这里介绍所有的源代码,只介绍增加的和重要的代码。介绍的顺序按照执行的流程。
·4.1 扩展父类的initializeEditor()方法,并如下实现:
super.initializeEditor();
this.setSourceViewerConfiguration(new
ZSqlSourceViewerConfiguration());
initializeEditor()方法会在ZSqlEditor的构造方法前执行,因为在AbstractDecoratedTextEditor类的构造中调用了initializeEditor()方法,此时ZSqlEditor的构造方法还没有执行(请参考我的另一篇文章,Java对象的构造顺序-让代码在构造方法前执行)。
你也许会问,此时我们的SourceViewer还没有创建,怎么能够设置Configuration,实际上AbstractTextEditor会为你保持Configuration对象,并在其createPartControl()方法中将其配置到SourceViewer上。ZSqlEditor.intializeEditor()在ZSqlEditor的构造方法之前执行(因为它在父类AbstractDecoratedTextEditor的构造方法中被调用)因此我们在这里设置Configuration。而在AbstractTextEditor.createPartControl()方法中,将调用createSourceViewer()方法创建SourceViewer的实例,并为SourceViewer配置Configuration(使用我们设置的configuration)。而一旦为SourceViewer设置了Configuration,就不能重新配置,否则会出现IllegalStateException异常(因此,你实际上可以在ZSqlEditor的initializeEditor(), constructor(), init()方法,包括createPartControl()方法(调用super.createPartControl()方法之前)调用setSourceViewerConfiguration()方法)。
·4.2 增加类ZSqlSourceViewerConfiguration
extends SourceViewerConfiguration
重写public IPresentationReconciler getPresentationReconciler(
ISourceViewer sourceViewer)方法
·4.3 增加类ZSqlScanner extends RuleBasedScanner
·4.4 增加类ZSqlWhiteSpaceDetector, ZSqlWordDetector
这两个类均为Rule的辅助类,负责发现空白字符和关键字。
·4.5 增加接口ZISqlSyntax
这个接口用来提供各种字符串数组,当前仅提供关键字字符串数组。ZSqlWordDetector根据这些来判断某个模式是否关键字。
·5 运行
点击下载(http://www.sinadisk.com/pick.aspx?code=com.bbebfe.sql_source_highlight)源代码工程和插件Jar包。
下载完成后你可以选择导入源代码工程或者直接将Jar包拷贝到Eclipse的plugins目录下。通过File -> open file打开任意.sql后缀就可以激活编辑器。运行效果如下:
·6 总结
在本章我们学习了如何让我们的编辑器支持语法着色,虽然我们只定义了最简单的几种模式,但你完全可以以此推导出其他的多种模式。不过目前编辑器在代码着色方面仍然有些小问题,将在下一章介绍并解决。