Geertjan's Blog

  • May 18, 2008

How Does "Mark Occurrences" Work?

Geertjan Wielenga
Product Manager
Yesterday's code generator was made possible by Editor Library 2: "The NetBeans editor infrastructure provides an implementation of the Swing Text package APIs as well as some additional features such as syntax coloring, code folding, braces matching, etc."

The Code Generator SPI is in that module, but so is the Highlighting SPI. There's several usecases that relate to this SPI, as you can read in that link. Yesterday I came across a very cool tutorial that describes one of them: Extending the C/C++ Editor in NetBeans IDE 6.0 to Provide Mark Occurrences Highlighting. That's an excellent tutorial by Sergey Grinev from the NetBeans C++ team in St. Petersburg. Let's get to know this SPI while doing something pretty useful: we'll create a mark occurrences plugin for HTML files on the NetBeans Platform (i.e., typically, in NetBeans IDE, but also applicable to any other HTML file open in an application on the NetBeans Platform):

Above, I selected one instance of "you" and then all matching instances were highlighted. Try it. Doesn't work, does it? That's because this functionality is not part of the IDE, but part of my plugin. If you're interested in this functionality as a user, get it here from the Plugin Portal:


To create the above, I simply copied Sergey's code, except that I read the content of HTML files rather than C++ (or C) files and then changed the layer.xml accordingly. My MarkOccurrencesHighlightsFactory is identical to Sergey's:

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(

Here's the layer.xml registration:

<folder name="Editors">
<folder name="text">
<folder name="html">
<file name="org-netbeans-modules-markoccurrences-MarkOccurrencesHighlightsLayerFactory.instance"/>

And, finally, the CaretListener, which picks up whatever is selected and then finds matching occurrences:

package org.netbeans.modules.markoccurrences;
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 MarkOccurrencesHighlighter implements CaretListener {
private static final AttributeSet defaultColors =
new Color(236, 235, 163));
private final OffsetsBag bag;
private JTextComponent comp;
private final WeakReference weakDoc;
public MarkOccurrencesHighlighter(Document doc) {
bag = new OffsetsBag(doc);
weakDoc = new WeakReference((Document) doc);
DataObject dobj = NbEditorUtilities.getDataObject(weakDoc.get());
EditorCookie pane = dobj.getCookie(EditorCookie.class);
JEditorPane[] panes = pane.getOpenedPanes();
if (panes != null && panes.length > 0) {
comp = panes[0];
public void caretUpdate(CaretEvent e) {
private RequestProcessor.Task task = null;
private final static int DELAY = 100;
public void scheduleUpdate() {
if (task == null) {
task = RequestProcessor.getDefault().create(new Runnable() {
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);

}, true);
public OffsetsBag getHighlightsBag() {
return bag;

The only difference from Sergey's tutorial is the bit in bold above. Nothing magical, not using any NetBeans APIs, except that whenever a matching pattern is found, it is added to the "bag" (hello, lookup, I knew you well), and highlighted. The only difference between my implementation of "mark occurrences" and the typical implementation is that in my case one actually needs to select something, rather than simply putting the caret in the middle of some word. But that's fine, I think this does the job pretty well. Not only is this a nice introduction to this SPI, but now there is something I've been missing for a while—mark occurrences for HTML files. I've written to Vladimir Voskresensky to ask him what he did to make navigation between marked occurrences possible (as described here) because that would be very cool to add to this plugin. It works pretty well together with Ctrl-F (i.e., search), so that you can search for a word while using mark occurrences for another word (so, you have a double-edged search, as in Java files), as you can see here (orange is from Ctrl-F, while yellow is from mark occurrences):

I'm looking forward to exploring the Highlighting SPI in other contexts too, now that I have this example as an entry point.

Join the discussion

Comments ( 4 )
  • Casper Sunday, May 18, 2008

    Cool. I am curious Geertjan, would this be the API to use, if one wanted to implement a feature which could filter away the noise of non-essential code.

    Say I wanted to be able to toggle a button up in the editor toolbar, which would cause all System.out.print/Logger statements as well as catch blocks to be rendered almost invisible (until my caret is on that line).

    I often find myself using too few logging statements and uniting exception handling into one catch, just to avoid this "noise" which obscures what my code is actually trying to do.

  • Geertjan Sunday, May 18, 2008

    Casper, definitely. This API is specifically for rendering text in editors. So your usecase makes perfect sense. When will you make it? :-)

  • Casper Sunday, May 18, 2008

    Oh great, thanks for the info. I think will look into it this week if off-work time (and girlfriend) permits. ;)

  • Billie-Jo Martin-Conlon Monday, May 26, 2008


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.