`
hqs7636
  • 浏览: 216312 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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

阅读更多
https://www.ibm.com/developerworks/cn/opensource/os-cn-ecljtf6/

文档选项
将打印机的版面设置成横向打印模式

打印本页
将此页作为电子邮件发送

将此页作为电子邮件发送


样例代码

级别: 中级

马 若劼 (maruojie@cn.ibm.com), 软件工程师, IBM 中国软件开发中心

2008 年 4 月 10 日

    Text Hover(文本悬浮)和 Annotation Hover(标注悬浮)是两种提供快速帮助的功能。本文介绍两种悬浮的基本概念和在 JTF 中的实现方式。

Text Hover

Text Hover(文本悬浮)可以让用户快速的得到某种信息,而不用打开相对缓慢的 Eclipse 帮助系统。Eclipse 的 Java 编辑器使用了文本悬浮来显示 Javadoc 帮助,极大的方便了程序员。从功能上看,文本悬浮可以认为是一种增强型的 Tooltip,因为它支持显示更多的内容,并且可以显示成各种样子。

文本悬浮看起来很酷,但是原理却很简单。基本上就是三个步骤:

   1. 根据鼠标位置得到字符偏移
   2. 根据字符偏移得到字符所在单词和上下文信息
   3. 根据这个单词以及上下文信息显示相关帮助

最关键的事情在于得到字符所在的单词并得到上下文信息,这样才能决定该显示什么样的帮助信息。所以这就又回到了词法解析器和语法解析器的领域了,只好略过不提。不过经过了这么多次的强调之后,希望你对词法解析器和语法解析器的重要性有了深切的体会。它们是代码编辑器里最重要的基石。

第一步不用我们操心,StyledText 已经提供了这样的能力。我们要处理的是第二和第三步。对于文本悬浮,相关的接口是 ITextHover;对于标注悬浮,相关的接口是 IAnnotationHover。




回页首


ITextHover

接下来我打算实现用文本悬浮显示变量值的功能,比如对下面的代码:

清单1. 示例语言

                pa = 4;
b = 4;
a = b + 4;


如果你把鼠标移到 a 上,则会显示“8”,如果移到b上,则会显示“4”,是不是很酷呢?

修改解析器

这样的功能没有解析器的支持可不好做,所以我又修改了 Expr.g 文件,使解析器能够保存所有已经声明的变量和它们的值。SharedParser 也做了一定的修改,提供了一个 getVariableValue 方法来根据变量名得到值。用 ANTLR 完成这些事情确实很简单,我就不一一列出代码了。

实现 ITextHover

好消息是 JTF 已经提供了一个缺省的实现,叫做 DefaultTextHover,只要继承一下就可以了。下面是具体的代码:

清单2. ExprTextHover 继承 DefaultTextHover

               
     
      public class ExprTextHover extends DefaultTextHover {
public ExprTextHover(ISourceViewer sourceViewer) {
super(sourceViewer);
}

@Override
public IRegion getHoverRegion(ITextViewer textViewer, int offset) {
return new Region(offset, 1);
}

@Override
public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) {
  // query super first
  String info = super.getHoverInfo(textViewer, hoverRegion);

  // if null, use our logic
  if(info == null) {
// get document
IDocument doc = textViewer.getDocument();

// get token list
TokenList list = TokenManager.getTokenList(doc);

// get token
Token token = list.getToken(hoverRegion.getOffset());
if(token == null)
return null;

// if token is variable, get variable value
if(token.getType() == IExprTokens.ID) {
// parse
TreeManager.getTree(doc);
return String.valueOf(SharedParser.getVariableValue(token.getText()));
} else
return null;
} else
return info;
}
}


主要的工作在 getHoverInfo 这个方法里面,我先调用了父类的 getHoverInfo,这一般来说是推荐的,因为父类的 getHoverInfo 会去首先查询相应位置处有没有一个标注,如果有,它会返回标注的信息。所以我在后面检查了父类的返回值,如果不是 null 且鼠标下面是一个变量名的话,就从解析器得到变量的值。

配置

最后一步是修改 ExprConfiguration,覆盖 getTextHover 方法, 只是简单的返回我定义的 ExprTextHover 即可。

清单3. 让 JTF 知道我们的 Text Hover 实现

               
public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) {
return new ExprTextHover(sourceViewer);
}


效果

运行后,看看是不是得到了我想要的效果:

图1. 文本悬浮效果图
文本悬浮效果图

如我希望的那样,鼠标移动到 a 上面时,显示出了“8”。如果源代码中有错误,那么效果如下所示:

图2. 当源代码有错误时
当源代码有错误时

这正是由于我首先调用了父类的 getHoverInfo 的效果,父类为我们取出了标注信息。




回页首


IAnnotationHover

Annotation Hover(标注悬浮)只能给标尺上的标注提供悬浮帮助。在实现上,它比文本悬浮更简单一些,因为你不需要判断鼠标所在单词,也不需要想办法获取一些上下文信息。JTF 也提供了缺省实现:DefaultAnnotationHover。我在例子中继承了它,但是什么都没有覆盖。然后在 ExprConfiguration 中覆盖 getAnnotationHover 即可,和文本悬浮非常类似。

下面是标注悬浮的效果:

图3. 标注悬浮效果图
标注悬浮效果图

显示的信息和图2是一样的,因为是同一个标注。如果想返回不同的信息,覆盖 DefaultAnnotationHover 的 getHoverInfo 方法就可以了。




回页首


关于信息显示控件

我曾经在本系列的第四部分提过:JTF使用了一些接口来抽象信息显示方面的功能,比如 IInformationControl 代表了一个信息显示控件,IInformationControlCreator 代表了一个信息显示控件工厂。既然文本悬浮和标注悬浮也是显示信息,那么它们可以不可以定制信息显示控件呢?

答案是绝对没问题,但是你在 ITextHover 和 IAnnotationHover 找不到与之有关系的方法。是不是想起了什么?那些带有“Extension”字样的接口?Bingo! 看看 ITextHoverExtension 和 IAnnotationHoverExtension 吧,具体怎么做我就不演示了。




回页首


Accessibility

一个要达到产品级的软件,必须要考虑 Accessibility 的问题,所谓 Accessibility,简单的说,就是让残疾人也能够使用你的软件。Accessibility 有一个很基本的要求就是关键的文字都要能被读屏软件读出来。文本悬浮和标注悬浮是很酷,但是它带来了 Accessibility 方面的问题,因为悬浮窗口没有焦点,读屏软件是不会读它们的。我们看看在 Java 编辑器中,它如何解决这个问题:

图4. Java 编辑器中的文本悬浮
Java 编辑器中的文本悬浮

可以看到 Java 编辑器的文本悬浮窗口下面有一个提示:按 F2 之后可以得到焦点。这个功能即方便了查看一些比较长的帮助,又解决了 Accessibility 的问题。

我这里不演示具体步骤,但是给有兴趣的读者一些提示:

   1. 悬浮窗口下面的提示可以通过 DefaultInformationControl 的构造函数传进去,如果你用的其它的信息控件,则看具体情况。
   2. 为了处理相应的快捷键,需要定义一个快捷键绑定,快捷键发生的时候调用相应的方法把信息控件变成可设置焦点的。Eclipse中有一些代码可以参考,比如 InformationDispatchAction,它是 TextEditorAction 的内部类。





回页首


AbstractInfor...

如果你是一个喜欢刨根问底的人,你可能会问:我虽然实现了 TextHover 接口,但是文本编辑器是怎么知道什么时候该调用我这个接口的呢?文本编辑器不是上帝,它当然是不知道的,需要外力帮助它知道。可以看看 AbstractInformationControlManager 这个类以及它的子类,会发现它有一个叫做 TextViewerHoverManager 的子类,是不是有点明白了呢?原来 TextViewerHoverManager 会安装在 TextViewer 上,它会监听悬浮事件,然后负责调用我们的实现。

从 AbstractInformationControlManager 可以看出一点:JFace 把信息显示功能包装成了通用的模块,并不是一定要在文本编辑器这样的场合才可以用悬浮信息窗口。如果你需要在其它地方添加类似的功能,可以继承 AbstractInformationControlManager,完成你自己的信息显示功能。




回页首


结束语

又给大家留下了可以发挥的地方,我小小的总结一下:

   1. 没有提供自定义的信息显示控件
   2. 没有实现悬浮窗口的可焦点化
   3. 尝试为其它东西实现一个悬浮提示功能,比如工具条?

如果上面这些部分你都能完成的话,相信你就可以做出来非常专业的悬浮帮助系统了。




回页首


声明

本文仅代表作者的个人观点,不代表 IBM 的立场。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics