FeaturesDocs & SupportCommunityBlogsPartners

扩展 NetBeans IDE 6.0 中的 C/C++ 编辑器以提供标记实例的高亮显示

本页上的内容适用于 NetBeans IDE 6.0在本教程中,您将看到怎样扩展 C/C++ 编辑器的功能。我们用 NetBeans 编辑器的高亮 API 来创建一个高亮显示代码的 NetBeans 模块。
我们还将用 C/C++ 引用 API 来从语言模型获取信息。

要获取更多有关在 NetBeans IDE 中使用 C/C++ 应用程序的信息,请参见 NetBeans 网站上的C/C++ 应用程序学习课程页。

教程需求

在继续前,请确保您重新检查了本节的需求。

先决条件

本教程假设您已经有一些使用 IDE 的基本知识和 Java 编程经验。

本教程所需的软件

在开始前,您需要安装 NetBeans 6.0。您将同时需要 C/C++ 和 Java SE 支持,所以最佳选择是选择“下载全部”选项并在安装时排除 Base IDE、Java SE 和 C/C++ 包之外的所有模块。

准备项目

对本教程,我们需要两个项目。一个是管理我们的插件的源代码的 NetBeans 模块。另一个是用来测试它的 C++ 项目。

创建 NetBeans 插件模块

  1. 选择“文件”>“新建项目”。在“新建项目”向导中,选择“类别”下的“NetBeans 模块”和“项目”下的“模块”。单击“下一步”。
  2. 在“名称和位置”页,在“项目名称”字段中键入 MarkOccurrences,并把“项目位置”设置到磁盘上一个适当的文件夹里。请选中“独立模块”和“设置为主项目”,如果没有选中的话。单击“下一步”。
  3. 在“基本模块配置”页,在“代码名称基”字段中键入 org.netbeans.modules.markoccurrences。单击“完成”。
  4. 在本项目中,我们需要一些依赖关系。在“项目”窗口,右键单击“库”节点并在“添加模块依赖关系”对话框中选择“库”,然后添加屏幕快照里列出的各个库。C/C++ 模块 API 正处于开发中,所以您需要在对话框中选择“显示非-API 模块”,以便在模块列表中看到它们。
    显示所需库的“项目”窗口
  5. 右键单击各个 C/C++ 模块,选择“编辑”,然后选择“实现版本”。

创建测试应用程序

  1. 选择“文件”>“新建项目”。选择“样例”>“C/C++”>“C/C++”类别下的 Args 项目。单击“下一步”。
  2. 在“项目名称和位置”页,把“项目位置”设置到磁盘上一个适当的文件夹里。单击“完成”。
  3. 现已创建了 Args_1 项目。在编辑器中打开 arg.c 源文件。我们将使用这个文件来测试我们的模块。

页首

创建高亮基础结构

现在我们将使用 NetBeans API 来向 C/C++ 编辑器添加高亮显示。

创建高亮提供者

  1. 在标记实例项目的“源包”节点中右键单击包 org.netbeans.modules.markoccurrences,然后选择“新建”>“Java 类”。
  2. 将新类命名为 MarkOccurrencesHighlighter 并单击“完成”。
  3. 用下面的代码替换新类中的代码:
    package org.netbeans.modules.markoccurrences;

    import java.awt.Color;
    import java.lang.ref.WeakReference;
    import javax.swing.JEditorPane;
    import javax.swing.event.CaretEvent;
    import javax.swing.event.CaretListener;
    import javax.swing.text.AttributeSet;
    import javax.swing.text.Document;
    import javax.swing.text.StyleConstants;
    import org.netbeans.api.editor.settings.AttributesUtilities;
    import org.netbeans.modules.cnd.modelutil.CsmUtilities;
    import org.netbeans.modules.editor.NbEditorUtilities;
    import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
    import org.openide.cookies.EditorCookie;
    import org.openide.loaders.DataObject;

    public class MarkOccurrencesHighlighter implements CaretListener {

    private static final AttributeSet defaultColors = AttributesUtilities.createImmutable(StyleConstants.Background, new Color(236, 235, 163));

    public void caretUpdate(CaretEvent e) {
    bag.clear();
    bag.addHighlight(0, 5, defaultColors);
    }

    private final WeakReference<Document> weakDoc;

    public MarkOccurrencesHighlighter(Document doc) {
    bag = new OffsetsBag(doc);
    weakDoc = new WeakReference<Document>((Document) doc);
    DataObject dobj = NbEditorUtilities.getDataObject(weakDoc.get());
    JEditorPane[] panes = CsmUtilities.getOpenedPanesInEQ(dobj.getCookie(EditorCookie.class));
    if (panes != null && panes.length > 0) {
    panes[0].addCaretListener(this);
    }
    }

    private final OffsetsBag bag;

    public OffsetsBag getHighlightsBag() {
    return bag;
    }

    }
    本类尚未提供任何智能功能。它只是向光标事件注册了一个侦听器,并高亮显示文档开头的符号。

创建并注册 HighlightsLayerFactory

现在我们创建 HighlightsLayerFactory,来让 NetBeans 知道我们的高亮显示提供者。

  1. 向项目源文件添加一个新的 Java 类,并将其命名为 MarkOccurrencesHighlightsLayerFactory
  2. 用下面的代码替换新类中的代码:
    package org.netbeans.modules.markoccurrences;

    import javax.swing.text.Document;
    import org.netbeans.spi.editor.highlighting.HighlightsLayer;
    import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory;
    import org.netbeans.spi.editor.highlighting.ZOrder;

    public class MarkOccurrencesHighlightsLayerFactory implements HighlightsLayerFactory {

    public static MarkOccurrencesHighlighter getMarkOccurrencesHighlighter(Document doc) {
    MarkOccurrencesHighlighter highlighter = (MarkOccurrencesHighlighter) doc.getProperty(MarkOccurrencesHighlighter.class);
    if (highlighter == null) {
    doc.putProperty(MarkOccurrencesHighlighter.class, highlighter = new MarkOccurrencesHighlighter(doc));
    }
    return highlighter;
    }

    public HighlightsLayer[] createLayers(Context context) {
    return new HighlightsLayer[] {
    HighlightsLayer.create(
    MarkOccurrencesHighlighter.class.getName(),
    ZOrder.CARET_RACK.forPosition(2000),
    true,
    getMarkOccurrencesHighlighter(context.getDocument()).getHighlightsBag())
    };
    }

    }
  3. 我们已经提供了一个 HighlightsLayerFactory 的实现,它用 MarkOccurrencesHighlighte类提供的数据创建了一个高亮显示层。现在我们需要在 layer.xml 中注册这个类。打开 org.netbeans.modules.markoccurrences 包中的 layer.xml,并将其内容更改为以下内容:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.1//EN" "http://www.netbeans.org/dtds/filesystem-1_1.dtd">
    <filesystem>
    <folder name="Editors">
    <folder name="text">
    <folder name="x-c++">
    <file name="org-netbeans-modules-markoccurrences-MarkOccurrencesHighlightsLayerFactory.instance" />
    </folder>
    <folder name="x-c">
    <file name="org-netbeans-modules-markoccurrences-MarkOccurrencesHighlightsLayerFactory.instance" />
    </folder>
    </folder>
    </folder>
    </filesystem>

现在我们已经准备好第一次运行我们的高亮显示器了。

  1. 生成项目。
  2. 项目成功生成后,运行它。
  3. 打开我们在前一节中创建的 Args 项目。
  4. 在编辑器中打开 args.c 文件,并单击文件中的任一位置。高亮显示看起来就会像下面的例子一样:
    高亮显示的例子

太棒了。我们的高亮显示工作了。现在我们来教它变得更有用。

页首

从 C/C++ 语言模型收集信息

  1. MarkOccurrencesHighlighter.java 类中,删除我们粗略实现的 caretUpdate() ,并添加以下代码:
    private WeakReference<CsmFile> weakFile;

    public void caretUpdate(CaretEvent e) {
    bag.clear();
    CsmFile file = getCsmFile();
    if (file != null) {
    CsmReference ref = CsmReferenceResolver.getDefault().findReference(file, e.getDot());
    if (ref != null && ref.getReferencedObject() != null) {
    Collection<CsmReference> out = CsmReferenceRepository.getDefault().getReferences(ref.getReferencedObject(), file, true);
    for (CsmReference csmReference : out) {
    bag.addHighlight(csmReference.getStartOffset(), csmReference.getEndOffset(), defaultColors);
    }
    }
    }
    }

    private CsmFile getCsmFile() {
    if (weakFile == null || weakFile.get() == null) {
    if (weakDoc == null || weakDoc.get() == null) {
    return null;
    }
    DataObject dobj = NbEditorUtilities.getDataObject(weakDoc.get());
    CsmFile file = CsmUtilities.getCsmFile(dobj, false);
    if (file != null) {
    weakFile = new WeakReference<CsmFile>(file);
    } else {
    return null;
    }
    }
    return weakFile.get();
    }
    caretUpdate() 方法中,我们使用 CsmReferenceResolver 来查找光标下对语言实体的引用。如果存在有效的实体,我们就向 CsmReferenceRepository 询问文件中所有相同实体的出现位置并存储它们的偏移量。getCsmFile() 方法是一段衔接代码,用来确保我们不保留任何语言模型数据。
  2. 按下 Ctrl-Shift-I 以修复导入(或者右键单击,然后选择“修复导入”)。
  3. 生成并运行项目。
  4. 如果把鼠标放到 main()argc 参数上,您就将看到如下的高亮显示:
    带有标记实例的代码的例子
  5. 单击文件的不同位置以查看标记实例是怎样工作的。您也许想尝试更复杂的项目中以查看它怎样与类、宏等等协同工作。

页首

提高性能

对静态文本来说,我们当前的代码足够好了,但将在编辑文件的过程中产生严重的延迟。出现延迟的原因是我们每按下一个键,就立即开始搜索。要解决这个问题,我们将推迟任务以分析代码,如果鼠标位置在任务开始前改变了,我们就取消并重新计划任务。

  1. MarkOccurrencesHighlighter.java 类中,把先前的 caretUpdate() 实现更改为以下代码:
    public void caretUpdate(CaretEvent e) {
    bag.clear();
    lastCaret = e.getDot();
    scheduleUpdate();
    }

    private int lastCaret;
    private RequestProcessor.Task task = null;
    private final static int DELAY = 1000;

    public void scheduleUpdate() {
    if (task==null) {
    task = RequestProcessor.getDefault().create(new Runnable() {
    public void run() {
    CsmFile file = getCsmFile();
    if (file != null) {
    CsmReference ref = CsmReferenceResolver.getDefault().findReference(file, lastCaret);
    if (ref!=null && ref.getReferencedObject()!=null) {
    Collection<CsmReference> out = CsmReferenceRepository.getDefault().getReferences(ref.getReferencedObject(), file, true);
    for (CsmReference csmReference : out) {
    bag.addHighlight(csmReference.getStartOffset(), csmReference.getEndOffset(), defaultColors);
    }
    }
    }
    }
    }, true);
    task.setPriority(Thread.MIN_PRIORITY);
    }
    task.cancel();
    task.schedule(DELAY);
    }
    在这个代码块中,我们使用 org.openide.util.RequestProcessor 来处理代码分析任务。如果得到几个光标更新,我们就取消先前的任务,记住鼠标位置,然后在稍后的时间重新计划任务。
  2. 修复导入,然后生成并运行项目。
  3. 现在您将注意到,在键入大块代码时没有延迟了。

页首

下载

Companion
Projects:
                  Powered by: