IndentEngineTutorial

Revision as of 18:28, 6 November 2009 by Admin (Talk | contribs)
(diff) ← Older revision | Current revision (diff) | Newer revision → (diff)

Creating Indentation Engine Tutorial

This tutorial describes how to provide indentation and reformatting capabilities for the NetBeans editor.

The indentation engine is represented by IndentEngine class
It has three abstract methods that need to be implemented:

    public abstract int indentLine(Document doc, int offset);

    public abstract int indentNewLine(Document doc, int offset);

    public abstract Writer createWriter(Document doc, int offset, Writer writer);

They allow to reindent the current line, find an indentation after newline and indent a block of code that is intended to be inserted at a particular offset in the document.

The indentation engine may either be written in a language-independent way or it may be specific for a language such as java or html.

Generic Indentation Engine

Language independent indentation engine requires to implement the three methods described above.

A simplest implementation may look like IndentEngine.Default:

package myindent;

public class MyIndentEngine extends IndentEngine {

    public int indentLine(Document doc, int offset) {
        return offset;
    }

    public int indentNewLine(Document doc, int offset) {
        try {
            doc.insertString(offset, "\n", null); // NOI18N
            offset++;
        } catch (BadLocationException ex) {
            // Invalid offset or document not mutable
        }
        return offset;
    }

    public Writer createWriter(Document doc, int offset, Writer writer) {
        // Return an original writer which means
        // no reindentation of the original text
        return writer;
    }

    protected boolean acceptMimeType(String mime) {
        return true; // Accepts any mime-type
    }

}

Registration of the Indentation Engine in XML Layer

In order to allow the created indentation engine to be used it is necessary to register the indentation engine as a service through the xml layer and create a corresponding beaninfo.

The layer for the module containing the indentation engine needs to be extended in the following way:

<filesystem>
    <folder name="Services">
        <folder name="IndentEngine">
            <file name="myindent-MyIndentEngine.settings" url="MyIndentEngine.settings">
                <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.mymodule.Bundle"/>
                <attr name="SystemFileSystem.icon" urlvalue="nbresloc:/org/netbeans/modules/editor/resources/indentEngine.gif"/>
            </file>
        </folder>
    </folder>
</filesystem>

The MyIndentEngine.settings file is expected to be in the same folder where layer.xml resides. It may look like this:

<?xml version="1.0"?>
<!DOCTYPE settings PUBLIC "-//NetBeans//DTD Session settings 1.0//EN" "http://www.netbeans.org/dtds/sessionsettings-1_0.dtd">
<settings version="1.0">
    <module name="org.netbeans.modules.mymodule/1"/>
    <instanceof class="org.openide.ServiceType"/>
    <instanceof class="org.openide.text.IndentEngine"/>
    <instanceof class="myindent.MyIndentEngine"/>
    <instance class="myindent.MyIndentEngine"/>
</settings>

An example of simple beaninfo:

package myindent;

import java.beans.BeanDescriptor;
import java.beans.SimpleBeanInfo;
import java.util.ResourceBundle;
import org.openide.util.NbBundle;

/**
* Beaninfo for MyIndentEngine.
*
* @author Miloslav Metelka
*/

public class MyIndentEngineBeanInfo extends SimpleBeanInfo {

    private BeanDescriptor beanDescriptor;

    public MyIndentEngineBeanInfo() {
    }

    public BeanDescriptor getBeanDescriptor () {
        if (beanDescriptor <tt></tt> null) {
            beanDescriptor = new BeanDescriptor(MyIndentEngine.class);
            ResourceBundle bundle = NbBundle.getBundle(MyIndentEngine.class);
            beanDescriptor.setDisplayName(bundle.getString("LAB_MyIndentEngine"));
            beanDescriptor.setShortDescription(bundle.getString("HINT_MyIndentEngine"));
            beanDescriptor.setValue("global", Boolean.TRUE); // NOI18N
        }
        return beanDescriptor;
    }

}

A corresponding Bundle.properties:

LAB_MyIndentEngine=My Indent Engine
HINT_MyIndentEngine=My special indentation engine

Retaining indent of previous line

A more complex example of the indentNewLine() is a code snippet showing insertion of indent corresponding to the indent of the first non-empty (and non-whitespace only) line before the cursor:

    public int indentNewLine(Document doc, int offset) {
        try {
            Element rootElem = doc.getDefaultRootElement();
            // Offset should be valid -> no check for lineIndex -1
            int lineIndex = rootElem.getElementIndex(offset);
            String lineText;
            int whitespaceEndIndex;
            do {
                Element lineElem = rootElem.getElement(lineIndex);
                lineText = doc.getText(lineElem.getStartOffset(),
                        lineElem.getEndOffset() - lineElem.getStartOffset() - 1); // strip ending '\n'
                whitespaceEndIndex = 0;
                while (whitespaceEndIndex < lineText.length()) {
                    // Break on non-whitespace char
                    if (!Character.isWhitespace(lineText.charAt(whitespaceEndIndex))) {
                        lineIndex = 0; // stop outer loop
                        break;
                    }
                    whitespaceEndIndex++;
                }
                lineIndex--; // continue to search for previous non-whitespace line
            } while (lineIndex >= 0);

            String nlPlusIndent = "\n" + lineText.substring(0, whitespaceEndIndex);
            doc.insertString(offset, nlPlusIndent, null); // NOI18N
            offset += nlPlusIndent.length();
        } catch (BadLocationException ex) {
            // ignore
        }

        return offset;
    }

Creation of Indentation Writer

The following implementation of createWriter() shows how to write a wrapping indentation writer that adds four extra spaces to each line:

    public Writer createWriter(Document doc, int offset, Writer writer) {
        return new IndentWriter(writer);
    }

    private static final class IndentWriter extends Writer {

        private final Writer writer;

        IndentWriter(Writer writer) {
            this.writer = writer;
        }

        public void write(char[] cbuf, int off, int len) throws IOException {
            int lastOff = off;
            while (off < len) {
                if (cbuf[Off++] <tt></tt> '\n') {
                    writer.write(cbuf, lastOff, off - lastOff);
                    lastOff = off;
                    // Write extra indent
                    writer.write("    ");
                }
            }
        }

        public void flush() throws IOException {
            writer.flush();
        }

        public void close() throws IOException {
            writer.close();
        }

    }

Language-specific Indentation Engine

Indentation engine may be specialized to an indentation of a specific language. %BR% Such indentation language should extend FormatterIndentEngine class which only requires a single method to be implemented:

    protected abstract ExtFormatter createFormatter();

ExtFormatter does the formatting by modifying a chain of TokenItem instances.

Not logged in. Log in, Register

By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo