Appearance
The Consulo includes a powerful framework for implementing formatting for custom languages. A formatter enables reformatting code automatically based on code style settings. The formatter controls spaces, indents, wrap, and alignment.
Reference: Code Formatter
15.1. Define a Block
The formatting model represents the formatting structure of a file as a tree of Block objects, with associated indent, wrap, alignment and spacing settings. The goal is to cover each PSI element with such a block. Since each block builds its children's blocks, it can generate extra blocks or skip any PSI elements. Define SimpleBlock based on AbstractBlock.
java
package org.consulo.sdk.language;
import consulo.language.ast.ASTNode;
import consulo.language.ast.TokenType;
import consulo.language.codeStyle.*;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
public class SimpleBlock extends AbstractBlock {
private final SpacingBuilder spacingBuilder;
protected SimpleBlock(@Nonnull ASTNode node, @Nullable Wrap wrap, @Nullable Alignment alignment,
SpacingBuilder spacingBuilder) {
super(node, wrap, alignment);
this.spacingBuilder = spacingBuilder;
}
@Override
protected List<Block> buildChildren() {
List<Block> blocks = new ArrayList<>();
ASTNode child = myNode.getFirstChildNode();
while (child != null) {
if (child.getElementType() != TokenType.WHITE_SPACE) {
Block block = new SimpleBlock(child, Wrap.createWrap(WrapType.NONE, false), Alignment.createAlignment(),
spacingBuilder);
blocks.add(block);
}
child = child.getTreeNext();
}
return blocks;
}
@Override
public Indent getIndent() {
return Indent.getNoneIndent();
}
@Nullable
@Override
public Spacing getSpacing(@Nullable Block child1, @Nonnull Block child2) {
return spacingBuilder.getSpacing(this, child1, child2);
}
@Override
public boolean isLeaf() {
return myNode.getFirstChildNode() == null;
}
}15.2. Define a Formatting Model Builder
Define a formatter that removes extra spaces except for the single spaces around the property separator. For example, reformat "foo = bar" to "foo = bar".
Create SimpleFormattingModelBuilder by subclassing FormattingModelBuilder.
java
package org.consulo.sdk.language;
import consulo.annotation.component.ExtensionImpl;
import consulo.language.Language;
import consulo.language.codeStyle.*;
import org.consulo.sdk.language.psi.SimpleTypes;
import jakarta.annotation.Nonnull;
@ExtensionImpl
final class SimpleFormattingModelBuilder implements FormattingModelBuilder {
@Nonnull
@Override
public Language getLanguage() {
return SimpleLanguage.INSTANCE;
}
private static SpacingBuilder createSpaceBuilder(CodeStyleSettings settings) {
return new SpacingBuilder(settings, SimpleLanguage.INSTANCE)
.around(SimpleTypes.SEPARATOR)
.spaceIf(settings.getCommonSettings(SimpleLanguage.INSTANCE.getID()).SPACE_AROUND_ASSIGNMENT_OPERATORS)
.before(SimpleTypes.PROPERTY)
.none();
}
@Nonnull
@Override
public FormattingModel createModel(@Nonnull FormattingContext formattingContext) {
final CodeStyleSettings codeStyleSettings = formattingContext.getCodeStyleSettings();
return FormattingModelProvider
.createFormattingModelForPsiFile(formattingContext.getContainingFile(),
new SimpleBlock(formattingContext.getNode(),
Wrap.createWrap(WrapType.NONE, false),
Alignment.createAlignment(),
createSpaceBuilder(codeStyleSettings)),
codeStyleSettings);
}
}15.3. Register the Formatter
The SimpleFormattingModelBuilder implementation is registered with the Consulo by annotating the class with @ExtensionImpl. The base interface FormattingModelBuilder is annotated with @ExtensionAPI, so the Consulo discovers the implementation automatically.
15.4. Run the Project
Add some extra spaces around the = separator between language and English. Reformat the code by selecting Code | Show Reformat File Dialog and choose Run.
