NetBeans Mark Occurrences Module Tutorial

This tutorial needs a review. You can edit it in GitHub following these contribution guidelines.

This tutorial demonstrates how to create a NetBeans module that highlights all instances of a selected word in an HTML file, as shown below:

mark occs 72 result 3

For troubleshooting purposes, you are welcome to download the completed tutorial source code.

Introduction to Highlight Layers

In this tutorial, you implement several classes provided by the NetBeans Highlighting SPI. The two principle classes in this context are as follows:

The above two classes need to be registered in the module’s layer.xml file, within the editor folder of the relevant MIME-type.

Setting up the Module Project

Before you start writing the module, you have to make sure you that your project is set up correctly. The IDE provides a wizard that sets up all the basic files needed for a module.

  1. Choose File > New Project (Ctrl+Shift+N). Under Categories, select NetBeans Modules. Under Projects, select Module. Click Next.

  1. In the Name and Location panel, type MarkHTMLOccurrences in the Project Name field. Change the Project Location to any directory on your computer. Click Next.

  1. In the Basic Module Configuration panel, type org.netbeans.modules.markhtmloccurrences in Code Name Base. Click Finish.

The IDE creates the MarkHTMLOccurrences project. The project contains all of your sources and project metadata, such as the project’s Ant build script. The project opens in the IDE. You can view its logical structure in the Projects window (Ctrl-1) and its file structure in the Files window (Ctrl-2).

Creating the Highlight Layer and Factory

Next, we will implement the Highlighting SPI. The SPI’s classes are as follows:

Class Description

HighlightsLayerFactory

Defines the factory for creating highlight layers.

HighlightsLayerFactory.Context

Defines the context passed to the factory when the factory is asked to create highlight layers.

HighlightsContainer

Defines the container of highlighted areas and related attributes.

HighlightsLayer

Defines the highlight layer, consisting of attributes.

ZOrder

Defines position of highlight layer in relation to other highlight layers.

OffsetsBag

Defines a set of highlighted areas specified by their offsets in the document.

Specifying the Module’s Dependencies

You will need to subclass several classes that belong to NetBeans APIs. Each has to be declared as a Module dependency. Use the Project Properties dialog box for this purpose.

  1. In the Projects window, right-click the Libraries node in the MarkHTMLOccurrences project and choose Add Module Dependency.

  1. For each of the following APIs, select the name from the Module list, and then click OK to confirm that you want to depend on it:

    • Datasystems API

    • Editor

    • Editor Library 2

    • Editor Settings

    • File System API

    • Lookup API

    • MIME Lookup API

    • Nodes API

    • Text API

    • UI Utilities API

    • Utilities API

You should now see the following:

mark occs 72 project 1
  1. In the Projects window, expand the Important Files node, double-click the Project Metadata node, and note the long list of APIs that you selected have been declared as module dependencies. When the module is built, the dependencies will be registered in the manifest file.

Creating the Highlight Layer Factory

Highlight layer factories are defined by the Highlighting SPI.

To use the Highlighting SPI to create the palette in this tutorial, take the following steps:

  1. Right-click the MarkHTMLOccurrences project node and choose New > Java Class. Create a Java file called MarkHTMLOccurrencesHighlightsLayerFactory .

  1. Replace the default content of the MarkHTMLOccurrencesHighlightsLayerFactory.java file with the following:

import javax.swing.text.Document;
import org.netbeans.api.editor.mimelookup.MimeRegistration;
import org.netbeans.api.editor.mimelookup.MimeRegistrations;
import org.netbeans.spi.editor.highlighting.HighlightsLayer;
import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory;
import org.netbeans.spi.editor.highlighting.ZOrder;

@MimeRegistrations({
    @MimeRegistration(mimeType = "text/html", service = HighlightsLayerFactory.class),
    @MimeRegistration(mimeType = "text/xml", service = HighlightsLayerFactory.class)
})
public class MarkHTMLOccurrencesHighlightsLayerFactory implements HighlightsLayerFactory {

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

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

}

Several statements remain underlined in red because they refer to the "MarkHTMLOccurrencesHighlighter" class, which we will create in the next section.

Creating the Highlight Layer

In this section, we create the highlight layer. Create a new Java class named MarkHTMLOccurrencesHighlighter , with the content below.

import java.awt.Color;
import java.lang.ref.WeakReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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.JTextComponent;
import javax.swing.text.StyleConstants;
import org.netbeans.api.editor.settings.AttributesUtilities;
import org.netbeans.modules.editor.NbEditorUtilities;
import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
import org.openide.cookies.EditorCookie;
import org.openide.loaders.DataObject;
import org.openide.util.RequestProcessor;

public class MarkHTMLOccurrencesHighlighter implements CaretListener {

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

    private final OffsetsBag bag;

    private JTextComponent comp;
    private final WeakReference<Document> weakDoc;

    private final RequestProcessor rp;
    private final static int REFRESH_DELAY = 100;
    private RequestProcessor.Task lastRefreshTask;

    public MarkHTMLOccurrencesHighlighter(Document doc) {
        rp = new RequestProcessor(MarkHTMLOccurrencesHighlighter.class);
        bag = new OffsetsBag(doc);
        weakDoc = new WeakReference<Document>Document) doc);          DataObject dobj = NbEditorUtilities.getDataObject(weakDoc.get(;
        if (dobj != null) {
            EditorCookie pane = dobj.getLookup().lookup(EditorCookie.class);
            JEditorPane[] panes = pane.getOpenedPanes();
            if (panes != null && panes.length > 0) {
                comp = panes[0];
                comp.addCaretListener(this);
            }
        }
    }

    @Override
    public void caretUpdate(CaretEvent e) {
        bag.clear();
        setupAutoRefresh();
    }

    public void setupAutoRefresh() {
        if (lastRefreshTask == null) {
            lastRefreshTask = rp.create(new Runnable() {
                @Override
                public void run() {
                    String selection = comp.getSelectedText();
                    if (selection != null) {
                        Pattern p = Pattern.compile(selection);
                        Matcher m = p.matcher(comp.getText());
                        while (m.find() == true) {
                            int startOffset = m.start();
                            int endOffset = m.end();
                            bag.addHighlight(startOffset, endOffset, defaultColors);
                        }
                    }
                }
            });
        }
        lastRefreshTask.schedule(REFRESH_DELAY);
    }

    public OffsetsBag getHighlightsBag() {
        return bag;
    }

}

Building and Installing the Module

The IDE uses an Ant build script to build and install your module. The build script is created for you when you create the module project.

  1. In the Projects window, right-click the MarkHTMLOccurrences project and choose Run.

  1. The module is built and installed in a new instance of the IDE. Open an HTML file or an XML file, double-click on a word, and notice that all matching words are automatically highlighted: mark occs 72 result 3

  1. Navigate from one matching word to the next via Ctrl-F3. Send Us Your Feedback

Next Steps

For more information about creating and developing NetBeans modules, see the following resources: