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

xiaoxiao2026-04-14  6

[url]https://www.ibm.com/developerworks/cn/opensource/os-cn-ecljtf6/[/url] 文档选项 将打印机的版面设置成横向打印模式 打印本页 将此页作为电子邮件发送 将此页作为电子邮件发送 样例代码 级别: 中级 马 若劼 (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 的立场。 相关资源:org.eclipse.jface.text_3.12.0.v20170523-1043.jar
转载请注明原文地址: https://www.6miu.com/read-5047363.html

最新回复(0)