problems understanding FormattingModelBuilder and AbstractBlock

I first formulate the question and then try to illustrate the problem:

Must every element that is processed by a spacing builder be included in a block?

Let's assume the following simple language:

definition::= OPENING_BRACKET innerDefinition CLOSING_BRACKET
innerDefinition::= TERM*

A sentence of the language should formatted look like this:


     something else


i.e. the definition should have alignmentX, the definitionBody should have alignmentY and between every TERM should be a blank line.

My very simple implementation of AbstractBlock looks like this:

public class SimpleBlock extends AbstractBlock {     private SpacingBuilder spacingBuilder;     private Alignment alignmentX = Alignment.createAlignment();     private Alignment alignmentY = Alignment.createAlignment();     protected SimpleBlock(@NotNull 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<Block>();                 ASTNode child = myNode.getFirstChildNode();         while (child != null) {             IElementType type = child.getElementType();             if (type.equals(DEFINITION)) {                 blocks.add(createBlock(child, alignmentX));             } else if (type.equals(DEFINITION_BODY)) {                 blocks.add(createBlock(child, alignmentX));             }             child = child.getTreeNext();         }         return blocks;     }     private Block createBlock(ASTNode child, Alignment alignment) {         return new SOLBlock(child, null, alignment, spacingBuilder);     }     @Nullable     @Override     public Spacing getSpacing(@Nullable Block child1, @NotNull Block child2) {         return spacingBuilder.getSpacing(this, child1, child2);     }     @Override     public boolean isLeaf() {         return false;     } }

So far it works with the alignment of OPENING_BRACKET, CLOSING_BRACKET and TERM. Now I want to insert the blank lines between the terms. And I thought it should work with the following FormattingModelBuilder:

public class SimpleFormattingModelBuilder implements FormattingModelBuilder {     @NotNull     @Override     public FormattingModel createModel(PsiElement element, CodeStyleSettings settings) {         return FormattingModelProvider.createFormattingModelForPsiFile(element.getContainingFile(), new SimpleBlock(                 element.getNode(), Wrap.createWrap(WrapType.NONE, false), Alignment.createAlignment(),                 createSpaceBuilder(settings)), settings);     }     @Nullable     @Override     public TextRange getRangeAffectingIndent(PsiFile file, int offset, ASTNode elementAtOffset) {         return null;     }     private static SpacingBuilder createSpaceBuilder(CodeStyleSettings settings) {         SpacingBuilder builder = new SpacingBuilder(settings);         builder.betweenInside(TERM, TERM, DEFINITION_BODY).blankLines(1);         return builder;     } }

However, this doesn't work and the blank lines are not created. It only works if I create a Block with every single TERM-Element, like this:

  protected List<Block> buildChildren() {         List<Block> blocks = new ArrayList<Block>();         ASTNode child = myNode.getFirstChildNode();         while (child != null) {             IElementType type = child.getElementType();             if (type.equals(DEFINITION)) {                 blocks.add(createBlock(child, alignmentX));             } else if (type.equals(DEFINITION_BODY)) {                 blocks.add(createBlock(child, alignmentX));             } else if(type.equals(TERM)) {                 blocks.add(createBlock(child, alignmentX));             }             child = child.getTreeNext();         }         return blocks;     }   

So my question again: Is it really necessary that every element processed by SpacingBuilder is included in its own block?
Thanks for any help,

1 comment
Comment actions Permalink

Absolutely. Formatter knows nothing about elements, it operates at different abstraction model which consists of blocks. If you want to control spacing between any of two elements, each of them must have its own block.
There are also other things you may need to know: in your case alignments may not be necessary, you just need to set proper indentations (Indent.createIndent) for inner blocks. Furthermore, any two blocks will be aligned only if they have the same alignment instance (the same alignment object shared between them). So perhaps there should be a definition body block containing right brace (no indent), 3 blocks for terms (normal indentation), left brace (no indent) or something like that.


Please sign in to leave a comment.