基于 JFace Text Framework 构建全功能代码编辑器

转载自https://www.ibm.com/developerworks/cn/opensource/os-cn-ecljtf4/


Content Assistant

Content Assistant(内容提示)可以帮助程序员快速的完成代码,并且还有代码自动补全的附加功能。这对于一个代码编辑器来说是至关重要的,也是不少人喜欢用 IDE 编写代码的原因之一。但是这个功能背后却不是那么简单的,我们先来了解一下 JTF 中和 Content Assistant 相关的概念,下面是 Eclipse 中 Java 编辑器的内容提示的样子:

图1. Java 编辑器的内容提示
Java 编辑器的内容提示

先来介绍一下图 1 中出现的三个概念:

  • Proposal(提议):Proposal 代表了一个可能的自动完成选项,程序员选择之后,代码会自动填入到编辑器里。
  • Proposal Popup(提议弹出列表):Proposal Popup 是用来显示自动完成列表的窗口
  • Additional Info(附加信息):每个提议都可以附带一些帮助信息,叫做 Additional Info,它会显示在弹出列表的旁边,并且当你选择某个 Proposal 的时候自动刷新。

这三个部分都是可以定制的,只不过有的简单有点麻烦一点。比如我们看到弹出列表的下面有一行提示“Press ‘Alt+/’ to show Template Proposals”,这在标准的弹出列表里面是没有的,JDT 定制了这一部分。

为示例代码添加内容提示支持

我打算为本文的示例代码添加以下的内容提示支持:自动提示已经声明的变量名。比如下面的语句:

清单 1. 示例语句
1
2
a = 3;
b = 4;

那么当用户在激活内容提示时,我们将显示出 a 和 b 供它选择,也就是显示之前声明过的变量。所有的声明过的变量可以通过遍历语法树来得到,我们在 TreeHelper 里面有一个 getVariables,它会完成这样的功能,如果你生成的语法树不一样,调整这个方法就可以了。注意输入的时候语法必须是正确的,不然语法解析器识别不出这是一个声明语句,也就得不到变量了。

IContentAssistProcessor

第一步,我们要实现 IContentAssistProcessor 接口,它就是所有 Proposal 的来源。不过这个接口的方法比我们想象的要多一些:

清单 2. IContentAssistProcessor 接口
1
2
3
4
5
6
7
8
public interface IContentAssistProcessor {
     ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset);
     IContextInformation[] computeContextInformation(ITextViewer viewer, int offset);
     char[] getCompletionProposalAutoActivationCharacters();
     char[] getContextInformationAutoActivationCharacters();
     String getErrorMessage();
     IContextInformationValidator getContextInformationValidator();
}

这些方法牵涉到了一些概念,我们来一一的解释它们:

  • computeCompletionProposals:这个就是所有 Proposal 的来源了,返回的类型是 ICompletionProposal 数组,ICompletionProposal 代表的就是单个的自动完成选项。
  • computeContextInformation;Context Information(上下文信息)是个新概念,它在这里表示你选择了某个 Proposal 之后,会有一个提示信息弹出来,那个就叫上下文信息。要注意它和上面提到过的 Additional Info 是不同的东西。
  • getCompletionProposalAutoActivationCharacters:这个方法引入了一个 Auto Activation(自动激活)的概念,所谓自动激活就是在某种条件下 Proposal Popup 自动弹出。这个“某种条件”指的是一些字符,比如最常用的应该是“.”号。
  • getContextInformationAutoActivationCharacters:上下文信息也有自动激活的功能
  • getErrorMessage:如果内容提示无法找到任何 Proposal,它可以返回一个错误信息给用户
  • getContextInformationValidator:上下文信息是可以进行校验的,如果失败,上下文信息不会被显示

computeCompletionProposals 方法显然是必须实现的,我添加了一个 ExprContentAssistProcessor 类,下面是它的实现方式:

清单 3. ExprContentAssistProcessor 实现了 IContentAssistProcessor 接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
     // get document
IDocument doc = viewer.getDocument();
     
     // get tree
Tree tree = TreeManager.getTree(doc);
     if(tree == null)
         return null;
     
     // get current selected range
Point range = viewer.getSelectedRange();
     
     // get all declared variables
List< String > variables = TreeHelper.getVariables(tree);
     
     // create proposals
List< ICompletionProposal > proposals = new ArrayList< ICompletionProposal >();
   for(String var : variables) {
     proposals.add(new CompletionProposal(
       var, range.x, range.y, var.length(), null, var, null, "Add your info here"));
     }
     return proposals.toArray(new ICompletionProposal[proposals.size()]);
}

我们遍历语法树得到了所有的变量,你可以看到整个实现代码在 ANTLR 以及一些工具类的帮助下显得非常简洁。注意我们为每个变量创建了一个 CompletionProposal,它的构造函数参数非常多,最后一个就是 Additional Info,我这里只是填了一些无用的信息作为演示之用。其它的参数涉及自动完成需要的所有信息,比如插入的字符串,在哪里插入,图标等等。

配置

又到了将我们的实现和 JTF 连接起来的时间,还是修改 ExprConfiguration, 要覆盖的方法变成了 getContentAssistant:

清单 4. 让 JTF 知道我们的内容提示实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public IContentAssistant getContentAssistant(ISourceViewer sourceViewer)
{
       ContentAssistant assistant = new ContentAssistant();
       assistant.setInformationControlCreator(new IInformationControlCreator() {
         public IInformationControl createInformationControl(Shell parent)
         {
             DefaultInformationControl control = new DefaultInformationControl(parent);
             return control;
         }
       });
 
       // add assist processor
       IContentAssistProcessor processor = new ExprContentAssistProcessor();
       assistant.setContentAssistProcessor(processor, IDocument.DEFAULT_CONTENT_TYPE);
       
       return assistant;
}

注意我们第一步实现的只是一个 Processor,还不是真正的内容提示管理器,幸运的是 JTF 为我们提供了 ContentAssistant,我们只要新建一个就可以了。第二行看上去有些不解,稍后我会解释。请注意最后一段,大家可以发现内容提示也是和文本类型绑定到一起的。

快捷键

用过 Java 编辑器的应该知道,内容提示可以用热键进行呼出,这个热键可以在 Eclipse 的设置里找到,以 Eclipse 3.3 为例,我们在设置中找到 General->Keys,然后在 filter 中输入 Content Assist 即可找到。为了能够让快捷键对我们的编辑器也有效果,需要安装一个 Handle 来处理它。这部分内容超出了本文的范围,所以我就不详细解释了。大家可以发现 ExprViewer 中多了一些成员和方法,比如 createHandlers 方法,它们都是为了处理快捷键而准备的。

效果

到这里为止,一个很基本的内容提示就完成了,下图是它的效果:

图 2. 内容提示效果图
内容提示效果图

Information Control

回过头来看看上一节中我卖的关子:ContentAssistant 设置了一个 IInformationControlCreator。从字面上很好理解,Information Control(信息控件)就是用来显示信息的一个控件,而 IInformationControlCreator 就是创建控件的工厂了。信息控件可以用来显示任何信息,在内容提示的情况下,显示的就是 Additional Info。这个控件可以使用任何形式,那么里面的内容也就根据控件的能力可以有不同的变化。比如,你可以用一个浏览器控件来显示信息,这样的话,你的信息可以用HTML来写。在例子中,我们用的是 JTF 的缺省实现:DefaultInformationControl,它内部使用的是 StyledText 控件。它虽然用的不是浏览器,但是它内部提供了一个信息渲染接口:IInformationPresenter。如果你使用 HTMLTextPresenter,它可以支持你在信息中嵌入 HTML 标签。

由于信息控件是一个通用的部件,它被广泛的用在其它需要显示信息的地方,比如我们以后会提到的Text Hover(文本悬浮帮助)。同时由于JTF使用了一系列的接口来抽象信息控件的功能,因此可以很方便的实现自己的信息控件。

结束语

正如我所说,本文的例子是很基本的,有很多可以提高的地方,这些高级的功能留给有兴趣的读者完成。这里给出一些我能想到的问题以供参考:

  • 内容提示只是显示所有的变量,它不会根据用户已经输入的内容来提示。比如有两个变量 test 和 haha,如果用户输入了“te”再激活内容提示,那么我们应该只提示 test。这个并非难事,我们有 TokenList 来帮助我们得到符号信息。
  • 列出的 Proposal 没有图标,只有文字,这是一个小问题。学习了本文之后,你能立刻想起来要加个图标应该修改哪里吗?
  • 对于 Proposal Popup:我们没有定制,可以尝试像 Java 编辑器那样给它底部加上些提示
  • 对于信息控件,用的是缺省实现。可以尝试使用浏览器,然后使用 HTML 显示帮助信息,看上去效果会更好。
  • 对于 IContentAssistProcessor,我们没有实现其它方法,比如上下文信息,自动激活。

要使内容提示功能达到和 Java 编辑器一样的高度,还是要花一些精力的。我一向提倡先了解基本概念,再深入具体细节。希望本文可以作为大家的起点,最终构造出一个专业的内容提示模块。

声明

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


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于计算机专业的学生而言,参加各类比赛能够带来多方面的益处,具体包括但不限于以下几点: 技能提升: 参与比赛促使学生深入学习和掌握计算机领域的专业知识与技能,如编程语言、算法设计、软件工程、网络安全等。 比赛通常涉及实际问题的解决,有助于将理论知识应用于实践中,增强问题解决能力。 实践经验: 大多数比赛都要求参赛者设计并实现解决方案,这提供了宝贵的动手操作机会,有助于积累项目经验。 实践经验对于计算机专业的学生尤为重要,因为雇主往往更青睐有实际项目背景的候选人。 团队合作: 许多比赛鼓励团队协作,这有助于培养学生的团队精神、沟通技巧和领导能力。 团队合作还能促进学生之间的知识共享和思维碰撞,有助于形成更全面的解决方案。 职业发展: 获奖经历可以显著增强简历的吸引力,为求职或继续深造提供有力支持。 某些比赛可能直接与企业合作,提供实习、工作机会或奖学金,为学生的职业生涯打开更多门路。 网络拓展: 比赛是结识同行业人才的好机会,可以帮助学生建立行业联系,这对于未来的职业发展非常重要。 奖金与荣誉: 许多比赛提供奖金或奖品,这不仅能给予学生经济上的奖励,还能增强其成就感和自信心。 荣誉证书或奖状可以证明学生的成就,对个人品牌建设有积极作用。 创新与研究: 参加比赛可以激发学生的创新思维,推动科研项目的开展,有时甚至能促成学术论文的发表。 个人成长: 在准备和参加比赛的过程中,学生将面临压力与挑战,这有助于培养良好的心理素质和抗压能力。 自我挑战和克服困难的经历对个人成长有着深远的影响。 综上所述,参加计算机领域的比赛对于学生来说是一个全面发展的平台,不仅可以提升专业技能,还能增强团队协作、沟通、解决问题的能力,并为未来的职业生涯奠定坚实的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值