| 基于 JFace Text Framework 构建全功能代码编辑器: 第 2 部分 |
|
| 作者是 Administrator | ||||||||||||||||||||||||||||||||||||||||||||||
| 2008-03-20 08:18:13 | ||||||||||||||||||||||||||||||||||||||||||||||
|
Syntax Highlight(语法高亮)是指把文本的不同内容用不同的颜色,字体等渲染,通过这种方式,用户可以快速发现某种内容,可以在短时间内对全文的结构有一个大概的了解。本文探讨如何在 JTF 中实现语法高亮。 语法高亮是把一段文本中的不同内容使用不同的样式来渲染的功能,如下图所示: 图1. Java编辑器的语法高亮
图 1 中的 Java 源代码的不同部分被 Java 编辑器渲染成了不同颜色。比如关键字是偏红的,并且是粗体;变量是蓝色,注释是绿色等等。不同的颜色可以让用户迅速的找到想要的内容,可以说是一个很方便的辅助功能。 在本系列的一部分中,我提到了 ANTLR 以及使用 ANTLR 产生词法和语法分析器。语法高亮是一个很依赖词法分析器的功能,因为词法分析器会把字符流解析成符号流,符号包含了各种属性,比如类型,起始位置和结束位置等等,语法高亮恰恰需要这些信息来渲染文本。我们已经有了一些基础类可以帮助我们管理符号列表,比如 TokenList,它可以根据字符偏移得到对应的符号。这为实现语法高亮提供了良好的基础。
JTF 是通过一个 ITokenScanner 接口来了解符号信息的,我们先来看一看这个接口的定义: 清单 1. ITokenScanner 接口
可见通过这个接口,JTF 可以遍历所有的符号并且得到符号的偏移和长度信息。所以实现这个接口是一个实质性的工作。但是 JTF 怎么知道如何去渲染不同的文本呢?为了解决这个问题,JTF 也提供了一个 IToken 接口,并且有一个基本的实现类 Token。注意 ANTLR 里面也有 Token 这个类,两者的概念类似,只不过用法不同。JTF 的 Token 可以附带一些自定义的对象,我们可以把文本的属性,比如前景背景字体样式等等,都放到 Token 里面,这样 JTF 就知道该如何渲染文本了。这些文本属性也被 JTF 包装成了一个 TextAttribute 类,我们在构造 Token 之后的传入 TextAttribute 对象就可以了。 既然我们有了 TokenList 这样的基础类,实现 ITokenScanner 就是很简单的事了,我们看看 ExprTokenScanner 的代码: 清单 2. ExprTokenScanner 实现了 ITokenScanner
getTokenLength 和 getTokenOffset 的实现是相当直接的,因为长度和偏移信息都保存在了 ANTLR 的 Token 实现中。nextToken 稍微有点长,因为我们需要把 ANTLR 的 Token 映射到 JTF 的 Token,所有用了一个 swtich 语句来检查了一下 Token 的类型,从而返回相应的 JTF Token。返回的 Token 已经预先定义好了,并加上了文本属性信息。setRange 可能是个不太直观的方法,这个方法有两个目的:第一是将 ITokenScanner 和 IDocument 分离开,这样一个 ITokenScanner 可以为多个 IDocument 服务;第二是提高性能,因为每次都从头把文本渲染一遍是个不太经济的做法,所以 setRange 有 offset 和 length 两个参数来控制需要渲染的范围,提高了渲染速度。
有了 TokenScanner 之后,剩下的事情就是让JTF知道它。上一部分提到了 SourceViewerConfiguration,它是 JTF 和外界交互的枢纽。我已经实现了 ExprConfiguation,但是它还是空的,现在我们就要覆盖父类的 getPresentationReconciler 方法: 清单 3. 覆盖父类的 getPresentationReconciler 方法
PresentationReconciler,顾名思义,是一个负责 Presentation 的类,这里 Presentation 指的就是文本的颜色,字体等等。文本渲染是一个不断进行的过程,因为用户可能在不断的修改文本内容。JTF 必须知道用户输入了什么,然后重新检查一下输入的内容,再刷新。所以 JTF 引入了 Damager 和 Repairer 的概念,Damager 负责计算用户的输入对哪些区域造成了破坏,Repairer 负责重新渲染被破坏的文本区域。Damager 和 Repairer 是按照文本类型来组织的,但是在我们的例子中,只有一个缺省文本类型,所以我们只为缺省类型设置了 Damager 和 Repairer。稍后我们会简单介绍一下文本类型的概念。
幸运的是 JTF 已经为我们提供了 PresentationReconciler 以及 Damager,Repairer 的缺省实现,很少有需要扩展这些类的情况。这里我就不详细解释了。
你可以把 Content Type 翻译成文本类型后者内容类型,总之它是用来描述具有某种特征的文字的。比如在 XML 语言中,标签肯定是由大于号和小于号括起来的,这样你就可以给 XML 的标签定义一种文本类型,更细一点,XML 的结束标签是由 "</" 和 ">" 括起来的,所以也可以为结束标签定义一种单独的文本类型。上一小节提到了不同的文本类型可以有不同的 Damager 和 Repairer,实际上 JTF 中很多特性都可以针对某个文本类型来做,因此适当的使用文本类型可以做一些更精细的控制。不过,这也得根据你要编辑的文本来具体考虑,像 XML 这样的非常结构化的语言,划分文本类型是很容易的。但是对于我这个例子中的数学表达式语言来说,就不太方便划分出很多类型来,你非要划分一下,可能会使得简单问题复杂化了。所以,要具体问题具体分析。 另外一个问题是 JTF 如何知道某段文本是什么样的类型呢?这里需要引入一个 Partition(分区)的概念,在 JTF 中,每个文档都有一个与之联系的 Document Partitioner(文档分区器),它会根据你给定的规则来扫描整个文本从而辨别哪块是哪个类型。所以它有一个相同的问题:对于非结构化的语言,划分分区不是特别容易的事情。
所以当你要处理一种文法规则不那么严谨的语言的时候,也可以不定义文本类型,就用缺省的就够了。我的例子就都采用了这样的方式。同时由于 ANTLR 生成的词法分析器非常好用,所以也没有必要那么做。
语法高亮的部分到这里就完成了,下图是例子的效果: 图 1. 语法高亮效果图
变量的颜色是咖啡色,数字是蓝色,其它都缺省是黑色。有了语法高亮的帮助,整个文档结构都一目了然了。
|
||||||||||||||||||||||||||||||||||||||||||||||
| 最近更新 ( 2008-03-20 08:18:13 ) | ||||||||||||||||||||||||||||||||||||||||||||||

