FeaturesPluginsDocs & SupportCommunityPartners

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

Content on this page applies to 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. 现在您将注意到,在键入大块代码时没有延迟了。

页首

下载

Bookmark this page

del.icio.us furl simpy slashdot technorati digg
Companion
Projects:
MySQL Database Server   Open JDK: an Open SourceJDK   GlassFish Community: an Open Source Application Server    Mobile & Embedded Community    Open Solaris   java.net - The Source for Java Technology Collaboration   Open ESB - The Open Enterprise Service Bus Powered by