How to create indents for all children of PsiFile?

Answered

My language consists of independent lines of types FOO and BAR, which both have further structure.  All FOO and BAR elements are children of PsiFile, and their respective blocks are children of the root Block.

Since I want to set the indent for each of those lines, I defined my indentation method as

@Override
public Indent getIndent() {
if (myNode.getElementType() == MyTypes.FOO)
return Indent.getSpaceIndent(1);
else if (myNode.getElementType() == MyTypes.BAR)
return Indent.getNoneIndent();
return null;
}

Unfortunately, Reformat Code now only sets the indent of the first line in the file (i.e., the first child of PsiFile), and sets all other lines (i.e., the remaining children of PsiFile) as no indent.

I don't quite see why this is happening.  I know that the documentation of Indent says that

The indent setting for a formatting model block. Indicates how the block is indented relative to its parent block.

But why does the indent then not apply for all children of PsiFile, instead of only the first?

How would I have to modify my getIndent method to achieve what I want to?

14 comments
Comment actions Permalink

Hi Ralph,

Could you please check how the blocks structure is actually built? You can do it with PSI Viewer (it is mentioned here: https://plugins.jetbrains.com/docs/intellij/code-formatting.html).

0
Comment actions Permalink

Hello Karol,

Thanks for your answer!  I use Psi Viewer all the time; here is my Psi tree:

where LABELDEF is BAR and SLIST is FOO above.  My block structure is identical to the Psi elements.

For unknown reasons, only the first line is indented correctly; all other lines are formatted flush to the beginning of the line, ignoring the indent.

I created a workaround by creating a spacing of 1 between CRLF and SLIST, but I'd prefer it that indenting worked.

0
Comment actions Permalink

Hi Ralph,

Is it confirmed in PSI Viewer that your blocks structure has correct indents? Could you please share a screenshot?

To see the structure go to Tools | View PSI Structure of Current File.... and navigate to the Block Structure tab in the bottom right area.

0
Comment actions Permalink

Yes, you were right, there is something amiss in my block structure:

I didn't know that viewing the Psi tree through the Tools menu could also show the block tree, so thanks a lot!

0
Comment actions Permalink

No, sorry, I still don't get it.  Looking at the Psi and block structures, we see one line consisting of 4 hierarchy steps on each side (ignore last PsiElement and first block):

Now no matter which hierarchy I select, the other side always remains on the lowest hierarchy:

and so on.  I don't really understand this logic, but is it wrong?  My code is (almost) identical to the tutorial:

protected List<Block> buildChildren() {
List<Block> blocks = new ArrayList<>();
if (myNode.getElementType() == Xbas99LTypes.W_DATA || myNode.getElementType() == Xbas99LTypes.W_IMAGE)
return blocks; // do not substructure DATA and IMAGE
ASTNode child = myNode.getFirstChildNode();
while (child != null) {
if (child.getElementType() != TokenType.WHITE_SPACE) {
if (child.getTextLength() > 0) { // mostly required for potentially empty PsiErrorElement nodes
blocks.add(new Xbas99LBlock(child, mySettings));
}
}
child = child.getTreeNext();
}
return blocks;
}

I don't really understand the problem here.

0
Comment actions Permalink

Hi Ralph,

Selecting an item on one side will not jump to counterpart on the other side when text ranges are the same. Please ignore it as this is just UI (I'll check the exact reason for this).

What is important is your overall structure. Ideally, please show a full screenshot of the code text and both structures if it is possible and confirm what is the actual and expected behavior if it changed since you created this post.

0
Comment actions Permalink

First, this is the actual source code I use for indenting:

    @Override
public Indent getIndent() {
if (myNode.getElementType() == Xbas99LTypes.SLIST)
return Indent.getSpaceIndent(1);
else if (myNode.getElementType() == Xbas99LTypes.LABELDEF)
return Indent.getNoneIndent();
return null;
}

And this is the language code I'd like to format, with Psi tree on the right.  I expanded the first 5 or so lines.

Each line is either Xbas99LSlist or Xbas99LLabeldef.  The desired formatting is exactly as shown, i.e., labels flush and statements indented (e.g., by 1 space).

What actually happens is this, though:

I looked at other plugins, but their code didn't really help me here.

0
Comment actions Permalink

I'm sorry, but I don't see the issue cause. Could you please share more code (preferably on a GitHub repo branch)?

0
Comment actions Permalink

I pushed my code to https://github.com/endlos99/idea-temp. The code in question is in src/main/java/net/endlos/xdt99/xbas99l (with an 'L'!).

0
Comment actions Permalink

Ralph, could you please add the building script to it as well? I would like to run it for an easier understanding of what's happening, so I need a repo that I can checkout and run without any additional actions, due to the limited time.

0
Comment actions Permalink

Karol, I pushed the entire project, including the example file shown above in examples/.

0
Comment actions Permalink

BTW, I have several FileIndentOptionsProvider, but not for Xbas99L, and the others all return null for Xbas99L file type.

0
Comment actions Permalink

Hi Ralph,

It seems that your getSpacing() method is an issue cause. It returns 0 spaces for other than known cases, so also for the case between LABELDEF and CRLF tokens.

I suggest returning null for the spacing that you don't want to handle or adjusting it a different way.

0
Comment actions Permalink

Oh yes, of course!  I did that to not list all the cases where spacing is 0, but forgot the spacing between \n and lines!  Just what I added in my workaround ...  OK, I'll fix that.

I'm truly sorry that this problem has such a boring solution, but thanks a million for helping me.

0

Please sign in to leave a comment.