[转]开发 Eclipse 插件的最佳实践
第 1 部分:创建您自己的标记
标记用于在不更改资源的情况下将信息链接到资源上。被标记信息的常见示例有断点、书签和编译错误。以编译错误为例,每一次执行编译作业,作业会通过创建一个新的标记来浏览源并突出错误。
在本教程的第一部分,我们将创建我们自己的标记。只要通过标记扩展点,并且只要编写一行代码即可完成此项工作。
您可以通过扩展 IMarker
扩展点 org.eclipse.core.resources.markers 来创建您自己的标记。首先,在 plugin.xml 文件中添加 清单 1 中的代码。也就是说,您将以 id 为 com.ibm.mymarkers.mymarker 创建一个名为 My Marker 的标记。它的超类型为 org.eclipse.core.resources.marker。这是最基本的标记类型。您可以扩展其他提供稍微更具体的功能的超类型。其他的超类型有:
- org.eclipse.core.resources.problemmarker
- org.eclipse.core.resources.textmarker
注意:您可以随需使用任意多的这些超类型。
清单 1. 清单 1 来自 plugin.xml 的标记扩展定义
<extension point="org.eclipse.core.resources.markers" id="com.ibm.mymarkers.mymarker" name="My Marker"> <super type="org.eclipse.core.resources.marker"/> <persistent value="false"/> <attribute name="description"/></extension> |
注意清单 1 中的两个要素 persistent
和 attribute
。这些都是分配给 mymarker
标记的属性。 Persistent 指明标记是否应该存储在工作区中。还有另外一个分配给 mymarker
的新属性 description
。
您可以通过调用所需资源上的 createMarker
方法来创建一个标记。同时,您也可以为其设置属性。
清单 2 中所示的方法也可以用来创建标记。只需输入 IResource
,将其链接到标记。创建后,您就可以设置其属性。
public static IMarker createMarker(IResource res)throws CoreException { IMarker marker = null; //note: you use the id that is defined in your plugin.xml marker = res.createMarker("com.ibm.mymarkers.mymarker"); marker.setAttribute("description," "this is one of my markers"); //note: you can also use attributes from your supertype marker.setAttribute(IMarker.MESSAGE, "My Marker"); return marker;} |
要查找与 IResource
相关的标记,您可以查询资源来获取所有具有指定 id 的标记,并指定是否搜索相关资源。要搜索标记,调用资源上的 findMarkers
方法。指定诸如标记类型和搜索深度的参数。
清单 3 中的代码使用 IResource.DEPTH_ZERO
参数查找直接与该资源链接的标记。
public static final String MARKER = "com.ibm.mymarkers.mymarker";public static List<IMarker> findMarkers(IResource resource) { try { return Arrays.asList(resource.findMarkers(MARKER, true, IResource.DEPTH_ZERO)); } catch (CoreException e) { return new ArrayList<IMarker>(); } } |
将资源深度更改为 IResource.DEPTH_INFINITE
会返回与其相关的标记或任何子资源。例如,您可以传入包,并获取与包中资源相链接的所有标记。
第 2 部分:使用注释来显示和更新标记
注释用于标记编辑器内的一个文本区域。它们广泛用于在 Eclipse 中显示信息,比如错误、警告、构建问题、任务或断点。注释在编辑器标尺上和文本上都可看到。图 1 列出显示语法错误的注释。
常见的注释实现是,在解析文件并且挑选出诸如错误和 “todo” 标签时生成标记。这通常在构建时完成。将其他的链接到持久标记,如断点。该示例所使用的是持久标记。
用户查看注释的方法可以使用菜单 General > Editors > Text Editors > Annotations 下的首选项面板进行定制。也就是说用户自定义注释不需要执行额外的工作。
查看 大图。
在本教程的第 1 部分中,标记只扩展了默认的标记类型。在第 2 部分,我们将扩展文本标记类型,以标记文本位置。此类型定义了两个关键属性: charStart
和 charEnd
。我们也希望标记在会话间能持久,所以我们将 persistent 值改为 true
。为此,我们需要更新 清单 4 中所定义的标记。
清单 4. 清单 4 来自 plugin.xml 的标记扩展定义
<extension point="org.eclipse.core.resources.markers" id="com.ibm.mymarkers.mymarker" name="My Marker"> <super type="org.eclipse.core.resources.textmarker"/> <super type="org.eclipse.core.resources.marker"/> <persistent value="true"/></extension> |
要创建您自己的注释,使用扩展点 org.eclipse.ui.editors.markerAnnotationSpecification
(参见 清单 5)。它定义了注释的属性以及其默认显示选项。
清单 5. 清单 5 来自 plugin.xml 的注释扩展定义
<extension point="org.eclipse.ui.editors.markerAnnotationSpecification" id="myannotationspecification" name="MyAnnotation"> <specification annotationType="com.ibm.example.myannotation" label="MyAnnotation" icon="icons/sample.gif" overviewRulerPreferenceKey="clruler" overviewRulerPreferenceValue="true" colorPreferenceKey="clcolor" colorPreferenceValue="255,255,0" textPreferenceKey="cltext" textPreferenceValue="true" verticalRulerPreferenceKey="clvertical" verticalRulerPreferenceValue="true" textStylePreferenceKey="clstyle" textStylePreferenceValue="BOX"> </specification></extension> |
XML 中我们将用来链接到规范的部分是 id
和 annotationType
属性。Eclipse.org 记录了用于自定义的其他属性(参见 参考资料)。
下一步是使用扩展点 org.eclipse.ui.editors.annotationTypes
把现有的标记链接到新的注释规范中。我们使用来自规范的注释类型和来自标记定义的 id 来链接规范。
清单 6. 清单 6 来自 plugin.xml 的注释类型定义
<extension point="org.eclipse.ui.editors.annotationTypes"> <type markerSeverity="0" super="org.eclipse.ui.workbench.texteditor.info" name="com.ibm.example.myannotation" markerType="com.ibm.mymarkers.mymarker"/></extension> |
请参阅 参考资料,在 Eclipse.org 上查找更多关于此扩展点的信息。
清单 7 中的代码用于创建并添加注释到编辑器。
public static void addAnnotation(IMarker marker, ITextSelection selection, ITextEditor editor) { //The DocumentProvider enables to get the document currently loaded in the editor IDocumentProvider idp = editor.getDocumentProvider(); //This is the document we want to connect to. This is taken from //the current editor input. IDocument document = idp.getDocument(editor.getEditorInput()); //The IannotationModel enables to add/remove/change annotation to a Document //loaded in an Editor IAnnotationModel iamf = idp.getAnnotationModel(editor.getEditorInput()); //Note: The annotation type id specify that you want to create one of your //annotations SimpleMarkerAnnotation ma = new SimpleMarkerAnnotation( “com.ibm.example.myannotation”,marker); //Finally add the new annotation to the model iamf.connect(document); iamf.addAnnotation(ma,newPosition(selection.getOffset(),selection.getLength())); iamf.disconnect(document);} |
注释模式负责在文档被编辑时移动注释。然而,在注释移动时,它并不更新标记的属性。在这种情况下,我们要更新标记的 charStart
和 charEnd
属性。最后的扩展点为标记更新器(updater)。它定义了一个类,用于在移动注释时更新标记。
清单 8. 清单 8 来自 plugin.xml 的标记更新器定义
<extension point="org.eclipse.ui.editors.markerUpdaters"> <updater id="com.ibm.example.MarkerUpdater" class="com.ibm.example.mymarker.MarkerUpdater" markerType="com.ibm.mymarkers.mymarker"> </updater></extension>: |
我们使用 IMarkerUpdater 界面来提供我们在移动注释时想要执行的代码。清单 9 中显示的类是我们的标记更新器。我们感兴趣的代码在 updateMarker
方法中。 此处,我们用它来更新标记的 charStart
和 charEnd
属性。
public class MarkerUpdater implements IMarkerUpdater { /* *Returns the attributes for which this updater is responsible. *If the result is null, the updater assumes responsibility for any attributes. */ @Override public String[] getAttribute() { return null; } @Override public String getMarkerType() { //returns the marker type that we are interested in updating return "com.ibm.mymarkers.mymarker"; } @Override public boolean updateMarker(IMarker marker, IDocument doc, Position position) { try { int start = position.getOffset(); int end = position.getOffset() + position.getLength(); marker.setAttribute(IMarker.CHAR_START, start); marker.setAttribute(IMarker.CHAR_END, end); return true; } catch (CoreException e) { return false; } }} |
第 3 部分:使用修饰符来识别已标记的 IResources
在 Eclipse 中,修饰符用于为工作区中的对象添加视觉信息。他们通常显示对象类型以及目前与该对象相关的任何重要属性。图 3 显示了如何在包资源管理器中将修饰符显示给用户。该修饰符显示了哪些项为 Java 源文件,或哪些项为包,并且显示了包含警告或错误的源和包的标记图标。这里,修饰符也用来添加细节,比如文件是否与存储库同步。
添加我们的修饰符的第一步是扩展 org.eclipse.ui.decorators
扩展点。它启用新修饰符的定义,并且选择它将要修饰哪种对象。
此处重要的字段是:
- class,它必须是实现
ILightweightLabelDecorator
的类的完全限定名(将 lightweight 设置为 true)。 - enablement,它包含修饰符所应用的 Eclipse 对象列表。
清单 10. 清单 10 来自 plugin.xml 的修饰符定义
<extension point="org.eclipse.ui.decorators"> <decorator id="com.ibm.example.filedecorator" label="MyMarker Decorator" state="true" class= "com.ibm.example.mymarker.FileDecorator" adaptable="true" lightweight="true"> <enablement> <objectClass name="org.eclipse.core.resources.IResource"/> </enablement> </decorator></extension> |
请参阅 参考资料,在 Eclipse.org 上查找更多关于扩展点的文件。
注意:轻量级与非轻量级:根据 API, 非轻量级修饰符可能会在新版本的 Eclipe 中被弃用。
我们需要实现 FileDecorator
类来决定修饰符的行为。这一类必须实现 ILightweightLabelDecorator
。用它来扩展 LabelProvider
的想法非常好,因为它可以让我们只重写我们感兴趣的方法,即 decorate()
。
decorate()
的基本实现如 清单 11 所示。
public void decorate(Object resource, IDecoration decoration) { decoration.addOverlay(ImageDescriptor.createFromFile(FileDecorator.class, "/icons/sample.gif"), IDecoration.TOP_RIGHT); decoration.addPrefix("My Prefix "); decoration.addSuffix(" My Suffix");} |
IDecoration
对象也可用于自定义字体、文本或背景颜色。
图 4. 图 4 清单 11 中用来修饰 IResources 的代码屏幕截图
decorate()
的第一个参数可以用来筛选我们要修饰的资源。如果我们只想要修饰包含指定标记的资源,就使用如 清单 12 所示的代码。
public void decorate(Object resource, IDecoration decoration) { if(resource instanceof IResource){ List<IMarker> markers = MyMarkerFactory.findMarkers((IResource) resource); if (markers.size() > 0) { decoration.addSuffix(" Marker !!"); } }} |
您已经遵循了本教程的步骤,接下来该做什么?
进一步的改进包括:
- 为标记添加可编辑的属性。允许用户更改标记的状态。
- 自动创建和删除标记。 使用后台处理作业来自动创建、更新和删除标记。
- 自定义标记的 hover。使用高级标记 hover 来支持 HTML 或多媒体内容。
在本教程中,我们使用了 Eclipse 来轻松创建和自定义标记,并执行高级的资源标记工作。 鼓励开发人员使用这个简单而又强大的工具来把他们的插件完美地整合到 Eclipse IDE 中。但是,注意,如果此特性用得太广泛,则对用户来说会显得很突兀。此外,将 Eclipse 用户界面指南考虑在内来维护 Eclipse 的外观与风格是开发人员的职责,参见 参考资料。