JFace Text Editor完全掌握之终极指南(1)

本文详细介绍了JFaceTextEditor的内部工作原理,包括文档分割、内容类型定义、规则配置及Token生成过程,并演示了如何通过扩展实现语法高亮等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JFace Text Editor是JFace里面一个功能强大,结构复杂而且非常重要的组件,要掌握它还需要花不少的功夫,下面我们将通过"Building an Eclipse Text Editor with JFace Text"的翻译来掌握它的用法
提到Text Editor,就不得不提到与之密切相关的SourceViewerConfiguration类,Text Editor的许多功能都是通过该类配置上去的,还有一个是IDocumentProvider接口,Text Editor所要编辑的文件对象就是通过该接口提供的.

Editor编辑的内容封装在IDocument这个类中,对于编辑内容的遍历,定位都是在IDocument的基础上来实现的,而文档的分割信息以及其他的元数据信息(比如高亮区域)则由Editor来保存,Document中的定位信息将使用一个Position类来加以封装.

虽然IDocument有不同的实现,但是他们都一个共同点,那就是这些内容都是可分割的,即能被分割成一块一块的互不重叠的文本块.分割(Partition)在Text Editor中是一个非常非常重要的概念,基本上Text Editor的所有功能都是建立在文档分割的基础上的.

IDocument接口并不关心文本内容的存储和加载,它的工作就是初始化一个文档对象实例,而文档的各种属性的初始化则交给IDocumentProvider来完成.

在打开一个文档的时候编辑器将同时进行文档的分割处理,其结果是得到各种不同类型的互不重叠文本块

下面我们来讲讲分割器,分割器的设置是在IDocumentProvider的createDocument()方法中实现的,如下代码所示:

java 代码
  1. protected IDocument createDocument(Object element) throws CoreException   
  2. {   
  3.     IDocument document = super.createDocument(element);   
  4.     if (document != null)   
  5.     {   
  6.         IDocumentPartitioner partitioner = new XMLPartitioner(   
  7.         new XMLPartitionScanner(), new String[]                               
  8.           {   
  9.                   XMLPartitionScanner.XML_TAG, XMLPartitionScanner.XML_COMMENT   
  10.           });   
  11.         partitioner.connect(document);   
  12.         document.setDocumentPartitioner(partitioner);   
  13.     }   
  14.     return document;   
  15. }  

 

从上面的代码我们可以看到,初始化一个IDocumentPartitioner我们需要两个参数,一个是必须指定一个IPartitionTokenScanner实例,另一个是所支持的内容类型(content type)数组.

与分割相关的还有三个概念:Scanner, Token和Rule, Scanner的任务就是将当前编辑的文档对象进行扫描并得到每一个文本块所对应的Token,对于Token需要解释一下,它是IToken的一个实现,被视为内容类型的唯一标识符.比如一个Token可能表示的是某种语言中的关键字,xml元素名,空格等.

通过下面的代码来说明Scanner是如何来查找各个Token的.

java 代码
  1. public class XMLPartitionScanner extends RuleBasedPartitionScanner   
  2. {   
  3.     public final static String XML_DEFAULT = "__xml_default";   
  4.     public final static String XML_COMMENT = "__xml_comment";   
  5.     public final static String XML_TAG = "__xml_tag";   
  6.   
  7.     public XMLPartitionScanner()   
  8.     {   
  9.   
  10.         IToken xmlComment = new Token(XML_COMMENT);   
  11.         IToken tag = new Token(XML_TAG);   
  12.   
  13.         IPredicateRule[] rules = new IPredicateRule[2];   
  14.   
  15.         rules[0] = new MultiLineRule("", xmlComment);   
  16.         rules[1] = new TagRule(tag);   
  17.   
  18.         setPredicateRules(rules);   
  19.     }   
  20. }   
  21.   

 

在一个编辑器中每一种内容类型(content type)都表示为一个字符串常量.在上面的代码中,定义了三个内容类型:默认,xml标签和xml注释.为了简化,这里我们没有对xml中的节点文本内容以及xml指令定义类型, 在XMLPartitionScanner中,每一个内容类型都和一个IToken实例相关联的,而IToken实例非常简单,仅仅只是一个内容类型标识符,用来在XMLPartitionScanner对文档进行扫描的时候,对各种Token进行识别, 由于我们的Scanner是继承RuleBasedPartitionScanner的,因此它的Token将与一系列规则对应,而分割器通过这些规则来将文档分割成所需要的结构,在XMLPartitionScanner中,我们使用了两条规则来将内容类型进行配置:一个是MultiLineRule,对应注释内容类型,一个是TagRule对应xml标签类型,在根据内容类型进行分割时,当找到一个匹配的分割片段之后将返回一个唯一的Token,这样就在内容类型,Rule 以及Token之间做到了一一对应.

使用一个基于规则的Scanner对文档进行分割的工作原理如下:

1.在RuleBasedPartitionScanner构造器中配置一些规则    2.分割时调用Rule的evaluate()方法    3.在evaluate方法中根据配置的各种规则实现类将对得到的一段字符串片段进行匹配,比如用于查找XML注释的MultiLineRule只去匹配那些以注释开始并以注释结尾 结束的字符片段,这种匹配只有两种结果:成功或失败,还是拿匹配注释的MultiLineRule来说,如果匹配的字符串片段为"一般节点"那么它会马上返回,因为匹配的字符串不是以注释开头开始的,如果匹配失败,扫描器将重新定位到匹配字符串的起始位置,并返回Token.UNDEFINED,接着调用下一个rule的evaluate()方法进行匹配处理,如果找到则返回匹配的Token,而且Scanner将去获取当前字符串在整个文档中的位置以及长度,并使用TypedPosition类将这些信息与其内容类型加以封装保存到IDocument实例中.

IDocumentPartitioner通过computePartitioning()方法将得到文档的分割信息,当然了,在得到分割信息之前,必须要将文档对象与IDocumentPartitioner实例关联起来,这个是通过IDocumentPartitioner.connect(document)来实现的.

当使用规则对文档进行分割,可能主要的工作就是去定义IPredicateRule的各种实现类,该接口的实现包括MultiLineRule, SingleLineRule 和 PatternRule等,理解该接口的难点在于如何配置这些规则,有时候,我们可能需要提供自己的IPredicateRule实现类

除了自定义规则之外,我们也可以提供自己的ITokenScanner实现.比如在JDT中,就实现了自己的ITokenScanner类FastJavaPartitionScanner,显然这个工作是巨大的,一般我们不这样做,除非有必要.

对文档进行分割之后,接下来将要介绍如何显示,Text Editor的底层采用StyledText控件来显示编辑的文本内容, ITextViewer提供了一套API来封装StyledText处理IDocument模型的细节,这样Text Editor的使用者就不用再与底层的StyledText打交道了.

ITextViewer除了对StyledText进行封装之外还提供了其他如Undo Management这样的功能,要了解更多细节可以去看看JavaDoc文档.

为了更容易的创建一个结构化文档编辑器,Eclipse在ITextViewer的基础上又提供了一个ISourceViewer接口,它包括的功能更强大,比如错误标记, 语法高亮等,下面我们将一一进行介绍

JFace Text Editor通过SourceViewerConfiguration类来对Source Viewer进行配置,我们需要做的就是继承SourceViewerConfiguration类来对SourceViewer进行定制,SourceViewer的很多功能比如文本格式化,语法高亮,双击选择,自动提示等等都是通过对SourceViewerConfiguration配置来实现的,足见其重要性不容忽视.

下面将通过在一个增强版的XML编辑器来实战Text Editor的用法

Eclipse插件开发套件提供一个向导来帮助用户创建一个简单的XML Editor,下面我们将在该编辑器的基础上来进行扩展

该XML编辑器将实现下面五个功能:语法高亮(Syntax hightlighting),错误标识(Error marker),格式化(Formating),内容辅助(Content Assistance), 内容大纲(Content Outline)

进行扩展的第一步就是增加新的分割模型,我们增加了下面三个分割的内容类型:XML文本, CData内容, XML处理指令.

为了在我们的XMLPartionScanner中处理指令类型定义, 先:

java 代码
  1. public final static String XML_PI = "__xml_pi";   

 

然后注册到分割器中,这个是在构造IDocumentPartitioner实例时作为参数传递进去的:

java 代码
  1. protected IDocument createDocument(Object element) throws CoreException   
  2. {   
  3.     IDocument document = super.createDocument(element);   
  4.     if (document != null)   
  5.     {   
  6.         IDocumentPartitioner partitioner = new XMLPartitioner(   
  7.          new XMLPartitionScanner(), new String[]   
  8.         {   
  9.                 XMLPartitionScanner.XML_START_TAG,   
  10.                 XMLPartitionScanner.XML_PI,   
  11.                 XMLPartitionScanner.XML_DOCTYPE,   
  12.                 XMLPartitionScanner.XML_END_TAG,   
  13.                 XMLPartitionScanner.XML_TEXT,   
  14.                 XMLPartitionScanner.XML_CDATA,   
  15.                 XMLPartitionScanner.XML_COMMENT   
  16.         });   
  17.         partitioner.connect(document);   
  18.         document.setDocumentPartitioner(partitioner);   
  19.     }   
  20.     return document;   
  21. }   

 

接下来是创建用于标识不同类型内容的Token和Rule,由于我们使用的是基于规则的Scaner(RuleBasedPartitionScanner),因此我们需要添加一对Token和Rule,这样才能让我们的IPartitionTokenScanner实例能感知我们定义的内容类型

根据XML定义规范,XML指令必须以结束,因此我们将这样来定义我们的Token和Rule:

java 代码
  1. public XMLPartitionScanner()   
  2. {   
  3.   
  4.     IToken xmlComment = new Token(XML_COMMENT);   
  5.     IToken xmlPI = new Token(XML_PI);   
  6.     IToken startTag = new Token(XML_START_TAG);   
  7.     IToken endTag = new Token(XML_END_TAG);   
  8.     IToken docType = new Token(XML_DOCTYPE);   
  9.     IToken text = new Token(XML_TEXT);   
  10.   
  11.     IPredicateRule[] rules = new IPredicateRule[7];   
  12.   
  13.     rules[0] = new NonMatchingRule();   
  14.     rules[1] = new MultiLineRule("", xmlComment);   
  15.     rules[2] = new MultiLineRule(", "?>", xmlPI);   
  16.     rules[3] = new MultiLineRule(", ">", endTag);   
  17.     rules[4] = new StartTagRule(startTag);   
  18.     rules[5] = new MultiLineRule(", ">", docType);   
  19.     rules[6] = new XMLTextPredicateRule(text);   
  20.   
  21.     setPredicateRules(rules);   
  22. }   
  23.   

 

为了让SourceViewerConfiguration能知道我们添加的新的内容类型,我们还需要重载getConfiguredContentTypes方法:

java 代码
  1. public String[] getConfiguredContentTypes(ISourceViewer sourceViewer)   
  2. {   
  3.     return new String[]   
  4.     {   
  5.             IDocument.DEFAULT_CONTENT_TYPE,   
  6.             XMLPartitionScanner.XML_COMMENT,   
  7.             XMLPartitionScanner.XML_PI,   
  8.             XMLPartitionScanner.XML_DOCTYPE,   
  9.             XMLPartitionScanner.XML_START_TAG,   
  10.             XMLPartitionScanner.XML_END_TAG,   
  11.             XMLPartitionScanner.XML_TEXT   
  12.     };   
  13. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值