The Consulo includes a powerful framework for implementing custom language formatters. In this framework, the plugin specifies the constraints on the spacing between different syntax elements. The formatting engine, provided by the IDE, calculates the smallest number of whitespace modifications that need to be performed on the file to make it match the constraints.
The process of formatting a file or a file fragment consists of the following main steps:
The formatting model is requested to build the structure of the file as applies to formatting, as a tree of blocks (
Block) with an associated indent, wrap, alignment, and spacing settings.
The formatting engine calculates the sequence of whitespace characters (spaces, tabs, and/or line breaks) that needs to be placed at every block boundary, based on the plugin's formatting model.
The formatting model is requested to insert the calculated whitespace characters at necessary positions in the file.
The structure of blocks is usually built so that it mirrors the PSI structure of the file - for example, in Java code, the top-level formatting block covers the entire file. Its children cover individual classes in the file, blocks on the next level cover methods inside classes, etc. The formatter modifies only the characters between blocks, and the tree of blocks must be built so that the bottom-level blocks cover all non-whitespace characters in the file. Otherwise, the characters between blocks may be deleted by the formatter.
If the formatting operation does not affect the entire file (for example, if the formatter is called to format the pasted block of text), a complete tree of blocks is not built. Rather, only blocks for the text range covered by the formatting operation and their parents are built.
For every block, the plugin specifies the following properties:
The spacing (
Spacing) specifies what spaces or line breaks are inserted between the specified children of the block. The spacing object specifies the minimum and maximum number of spaces that must be placed between the specified child blocks, the minimum number of line breaks to put there, and whether the existing line breaks and blank lines should be preserved. The formatting model can also specify that the spacing between the specified blocks may not be modified by the formatter.
The indent specifies how the block is indented relative to its parent block. There are different modes of indenting defined by factory methods in the Indent class. The most commonly used are the none indent (which means the child block is not indented), the regular indent (the child block is indented by the number of spaces specified in the Project Code Style | General | Indent setting), and the continuation indent (based on Project Code Style | General | Continuation Indent setting). If the formatting model does not specify an indent, the "continuation without first" mode is used. This default means that the first block in a sequence of blocks with that type is not indented, and the following blocks are indented with a continuation indent.
The wrap (
Wrap) specifies whether the content of the block is wrapped to the next line. Wrapping is performed by inserting a line break before the block content. The plugin can specify that a particular block is never wrapped, always wrapped, or wrapped only if it exceeds the right margin.
The alignment (
Alignment) specifies which blocks should be aligned with each other. If two blocks with the alignment property set to the same object instance are placed in different lines, and if the second block is the first non-whitespace block in its line, the formatter inserts white spaces before the second block, so that it starts from the same column as the first one.
For each of these properties, several particular use settings exist, described in the JavaDoc comments for the respective classes.
SpacingBuilder, which aids in building rule-based configuration.
An important special case in using the formatter is the smart indent performed when the user presses the
Enter key in a source code file.
To determine the indent for the new line, the formatter engine calls the method
getChildAttributes() on either the block immediately before the caret or the parent of that block, depending on the return value of the
isIncomplete() method for the block before the caret.
If the block before the cursor is incomplete (contains elements that the user will probably type but has not yet typed, like a closing parenthesis of the parameter list or the trailing semicolon of a statement),
getChildAttributes() is called on the block before the caret; otherwise, it's called on the parent block.
New in IntelliJ IDEA 13: Code formatting can be suppressed per region via special comments.
Code Style Settings
To specify the default indent size for the language provided by your plugin, and to allow the user to configure the tab size and indent size, you need to implement the
FileTypeIndentOptionsProvider interface and to register the implementation in the
com.intellij.fileTypeIndentOptionsProvider extension point.
The return value of
createIndentOptions() determines the default indent size.
New in IntelliJ IDEA 12:
Allows custom languages to provide user-configurable arrangement/grouping rules for element types supported by language plugin.
Rules can be refined via modifiers and name, ordering can be applied additionally.
Rearranger and related for JavaDoc.