基于 JFace Text Framework 构建全功能代码编辑器: 第 1 部分

级别: 中级

马 若劼 ([email protected]), 软件工程师, IBM 中国软件开发中心
(暂时没有维护图片链接)
2008 年 3 月 20 日

    JFace Text Framework(JFace 文本框架,后面直接简称为 JTF)是 Eclipse 中重要的框架,是其它开发工具的基石之一,比如 JDT (Java Development Tool) 中的 Java 源代码编辑器就是基于它开发的。相信用过 JDT 的人都会对它的源代码编辑器有深刻印象,因为它的很多功能可以让我们很方便的编辑 Java 源代码,比如语法高亮和内容提示等等。所有这些功能都是在 JTF 架的基础上实现的,所以学会使用这个框架对于开发某种语言的编辑工具是至关重要的。即使不是为了这个目的,学习这个框架也有助于提高对 Eclipse 整体的认识。但是JTF本身也确实是一个复杂的框架,非几句话能解释清楚。本系列文章以 JTF 的特性为中心,逐个介绍某个特性的相关概念以及实现方式,并且会给出相应的示例程序。因此随着系列的进行,示例程序会发展成一个全功能的编辑器。本文是此系列文章的第一篇,列举了 JTF 的一些特性,并且简略的介绍了 ANTLR 的相关技术,为后续的文章做好了铺垫。本系列的所有代码都是在 Eclipse 3.3 上调试运行的。

JFace Text Framework(JFace 文本框架,后面直接简称为 JTF)是 Eclipse 中重要的框架,是其它 开发工具的基石之一,比如 JDT (Java Development Tool) 中的 Java 源代码编辑器就是基于它开发的。 相信用过 JDT 的人都会对它的源代码编辑器有深刻印象,因为它的很多功能可以让我们很方便的编辑 Java 源代码,比如语法高亮和内容提示等等。所有这些功能都是在 JTF 架的基础上实现的,所以学会 使用这个框架对于开发某种语言的编辑工具是至关重要的。即使不是为了这个目的,学习这个框架也 有助于提高对 Eclipse 整体的认识。但是JTF本身也确实是一个复杂的框架,非几句话能解释清楚。 本系列文章以 JTF 的特性为中心,逐个介绍某个特性的相关概念以及实现方式,并且会给出相应的示 例程序。因此随着系列的进行,示例程序会发展成一个全功能的编辑器。本文是此系列文章的第一篇, 列举了 JTF 的一些特性,并且简略的介绍了 ANTLR 的相关技术,为后续的文章做好了铺垫。本系列的所有 代码都是在 Eclipse 3.3 上调试运行的。

JFace 文本框架特性一览

JFace 本身是一个更高级的 UI 库,文本框架只是其中的一部分。JFace 本身是基于 SWT 的,所以文本框架 的功能也是受制于 SWT 的,具体的说,是受制于 SWT 中 StyledText 的能力。如果要简单的概括一下 JTF 是 什么,可以说 JTF 只不过是把 StyledText 的一些功能包装的很方便让你来用罢了。所以对 JTF的功能不 要感到很神奇,所有的特性都可以在SWT中找到它的根据。目前,JTF支持以下特性:

    * Syntax Highlight(语法高亮)
    * Double Click (鼠标双击)
    * Content Assistant (内容提示)
    * Text Decoration (文本装饰)
    * Text Hover (文本悬浮帮助)
    * Annotation Hover (标注悬浮帮助)
    * Quick Assistant (快速帮助)
    * Hyperlink (超链接)
    * Template (模版)
    * Text Formatting (文本格式化)
    * Text Folding (文本折叠)

本系列将介绍所有这些特性,至于这些特性的具体概念会在以后的文章中一一介绍,而且还会顺带介绍一些 JTF 本身并不支持的特性,比如 Triple Click (鼠标三击) 。由于 JFace 和 SWT 都在不断发展中,所以也有可能提到一些上面没有列出的特性。但是JTF本身是一个复杂的框架,所以我不打算一一讲解每一行示例代码的含义,只会关注基本概念和架构以及核心代码,同时为了尽量保持示例代码的简洁,我也不考虑性能等诸多因素。




回页首


ANTLR

对于编写自定义的源代码编辑器来说,有一个源代码的解析器是很重要的。现在有很多这样的工具,比如 JavaCC, Antlr,bison 等等,我们将使用 Antlr 来构造我们的语言解析器。如果你不熟 Antlr 也没有关系,这并不是本文的重点,我会稍微介绍一下 Antlr 的基本概念,你只要了解我用 Antlr 做了什么事就可以了。

Antlr 是一个解析器生成器,可以根据你书写的文法自动生成解析器代码,并且可以生成语法树。之所以需要一个解析器是因为我们要在本系列中一步步实现一个源代码编辑器,很多情况下我们需要一颗语法树来帮助我们获得一些信息。我们可以通过遍历这棵语法树查询我们想要的信息,然后决定我们的程序逻辑。

我将自定义一种简单的语言,然后实现这种语言的编辑器,可以称这种语言为数学表达式语言,在这种语言里,我们仅支持变量声明和四则运算,并且只支持整数类型。看下面的例子:

清单1. 非常简单的假想语言:数学表达式语言

                a = 3;
b = (1 + 2) * 3;
a / 2 + b * 5;


从例子中可以看出,我定义的这种语言以分号作为行的结尾,变量声明采用等号作为操作符,变量声明了之后,可以参与到四则运算中,或者直接使用数字常量。这就是全部的语法了。下面是对这种简单语言的一个总结:

    * 操作符: +, -, *, /, (, ), =
    * 变量名: 任意长度,仅能由字母组成
    * 每条语句以分号结尾

我为这种语言定义了 Antlr 的文法,由于并非本文主题,这里不列举其代码,下面是一个与之相关的文件清单,大家可以在示例代码中找到它们:

表 1. ANTLR 相关文件说明
文件名 说明
Expr.g 语言的ANTLR语法描述
Expr__.g ANTLR自动从Expr.g生成的词法分析器文法描述
Expr.tokens ANTLR自动生成的所有符号的列表
ExprLexer.java 语言的词法分析器
ExprParser.java 语言的语法分析器
TokenList.java 符号列表,可以通过它得到指定偏移位置的符号以及得到某个符号的下一个符号
TokenManager.java 符号管理器,维护 IDocument 实例到符号列表的映射,每次 IDocument 实例发生变化时,符号列表缓存将被清空,这样下次会得到一个重新生成的符号列表
TreeManager.java 语法树管理器,维护 IDocument 实例到语法树的映射,每次 IDocument 实例发生变化时,语法树的缓存将被清空,这样下次会得到一个重新生成的语法树
TreeHelper.java 这个方法定义了一些和语法树相关的方法,可以从语法树中抽取一些信息
IExprTokens.java 这个接口定义了语言中可能出现的所有符号
SharedParser.java 为了稍微提高一点性能,维护了词法分析器和语法分析器的单一实例

需要强调的是:上述这些类对于实现一个完整的代码编辑器是非常重要的。对于文本来说,我们没有篇幅去仔细了解它们是怎么实现的,只需要明白它们能够做什么就可以了。对于 ANTLR 的具体使用和解析器相关的内容,值得花一本书的篇幅来介绍,对此有兴趣的读者可以阅读最后的参考文献。我们不做详细阐述,但是下面会介绍一些ANTLR的基本概念:

    * 符号(Token): 可以认为符号是一个具有特定意义的词法单元,比如一个英文单词,一个数字都是一个符号,比如语句”a = 3;”,它包含了 6 个符号:a, 空格,等于号,空格,3,分号。当然具体哪些部分能构成一个符号是由你写的文法来规定的。符号可以有很多属性,比如符号的类型、起始位置、结束位置等等
    * 词法分析器(Lexer): 词法分析器把一个字符流解析成为一个符号流。词法分析器一般要和一个语法分析器结合起来使用。词法分析器的输出就是语法分析器的输入
    * 语法分析器(Parser)和语法树(Tree):语法分析器把一个符号流解析成其它的格式,可以是一棵树或者其它什么东西。比如3 + 4可以变为如下图所示的结构:

      图1. 语法树
      语法树

      加号变成了一颗树的根节点,而3和4成为了它的孩子节点。在这样的树结构里,包含了一定的语义信息。我们通过遍历这颗树,得到数学表达式的结果或者进行其它操作。语法树的好处是可以重复的遍历它而不是每次都去把表达式解析一次。语法树实际上是一种中间格式。
    * 文法(Grammar):ANTLR中可以编写很多文法:比如词法分析器的文法,语法解析器的文法,语法树文法等。ANTLR 的文法文件的扩展名是”.g”, 根据你的文法类型,ANTLR会生成不同的源代码文件

一旦我们拥有了处理我们语言源代码的能力,并能够方便的管理源代码的词法语法信息,就相当于大楼有了牢固的地基一样,事情已经成功了一半了。后面的文章将告诉大家如何完成另外一半的工作。




回页首


初始示例代码

为了提供文本编辑和源代码编辑的能力,JTF 提供了 TextViewer 和 SourceViewer。TextViewer 仅仅面向纯文本的编辑,而 SourceViewer 提供了一个代码编辑器所需要的特性。代码编辑相对纯文本编辑来说复杂很多,需要很多附加功能,比如前面提到的 JTF 的所有特性,SourceViewer 都是做了一定的包装的。为了方便自定义,SourceViewer 通过一个 SourceViewerConfiguration 来控制这些特性的行为。缺省的实现基本上就和纯文本编辑器一样,所以我们从扩展 SourceViewer 以及 SourceViewerConfiguration 开始,一步步来完成我们自己的代码编辑器。本文附带的例子是一个起点,虽然有 ExprViewer 和 ExprConfiguation,但是还没有任何自定义代码。

在初始示例代码里,我们为 Eclipse 工具条添加了一个按钮,点击之后会出来一个对话框,我们的自定义编辑器就放在对话框里。之所以没有用 Eclipse 标准的 org.eclipse.ui.editors 扩展点,是因为 editor 本质上也是包装了一个 SourceViewer, 它为我们隐藏了一些东西。所以为了更清楚的说明问题,我们从一个较为原始的状态开始。




回页首


结束语

JTF 是一个比较成熟的文本编辑框架,通过 JTF 我们可以实现自己的代码编辑器。我的目的是对JTF的每个特性进行详细的解说,最终能够构建出一个可以达到和 Java Editor 相同水平的代码编辑器。本系列的每一篇都会附带一个阶段性的代码示例,以便读者对照代码更好的理解 JTF 的架构和细节。




回页首


声明

本文仅代表作者的个人观点,不代表 IBM 的立场。

你可能感兴趣的:(eclipse,框架,UI,IBM)