Wednesday Jan 02, 2013


When using the CSL API, you should not use org.netbeans.spi.editor.fold.FoldManager directly. It is too heavyweight, when compared to the alternative provided by the CSL API:  org.netbeans.modules.csl.api.StructureScanner.

If you have a class like this, i.e., extending org.netbeans.modules.csl.spi.DefaultLanguageConfig and using the org.netbeans.modules.csl.spi.LanguageRegistration annotation, then you can pass in a StructureScanner, as shown below:

package org.simplejava;

import org.netbeans.api.lexer.Language;
import org.netbeans.modules.csl.api.StructureScanner;
import org.netbeans.modules.csl.spi.DefaultLanguageConfig;
import org.netbeans.modules.csl.spi.LanguageRegistration;
import org.netbeans.modules.parsing.spi.Parser;
import org.simplejava.lexer.SJTokenId;
import org.simplejava.parser.SJParser;
import org.simplejava.parser.SJStructureScanner;

@LanguageRegistration(mimeType = "text/x-sj")
public class SJLanguage extends DefaultLanguageConfig {

    public Language<SJTokenId> getLexerLanguage() {
        return SJTokenId.getLanguage();

    public String getDisplayName() {
        return "SJ";

    public Parser getParser() {
        return new SJParser();

    public StructureScanner getStructureScanner() {
        return new SJStructureScanner();

    public boolean hasStructureScanner() {
        return true;


Then here's a dummy implementation of the above:

package org.simplejava.parser;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.netbeans.modules.csl.api.OffsetRange;
import org.netbeans.modules.csl.api.StructureItem;
import org.netbeans.modules.csl.api.StructureScanner;
import org.netbeans.modules.csl.api.StructureScanner.Configuration;
import org.netbeans.modules.csl.spi.ParserResult;

public class SJStructureScanner implements StructureScanner {

    public List<? extends StructureItem> scan(ParserResult pr) {
        return new ArrayList<>();

    public Map<String, List<OffsetRange>> folds(ParserResult pr) {
        return new HashMap<>();

    public Configuration getConfiguration() {
        return null;

When you look in, which any CSL plugin automatically includes thanks to the @LanguageRegistration annotation above, you'll see that from the DefaultLanguageConfig extension the StructureScanner is located and an internal FoldManager implementation is used to get the folds defined in StructureScanner.folds(), after which all the necessary things are done for you.

Some default folds are created automatically by the internal FoldManager implementation, such as for the initial comment, as shown below:

As the ParserResult from your parser is passed to the SS.folds() method, you should be able to get the folds since the parser result should contain a parse tree where the code block is described by some node in the tree.

Note: For the above to work, your ParserResult class must extend org.netbeans.modules.csl.spi.ParserResult and NOT org.netbeans.modules.parsing.spi.parser.Result. Just return Collections.EMPTY_LIST from getDiagnostics initially.

Useful implementations:

Thanks to Marek Fukala for help with the above. 

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.


« January 2013 »