Dedent the closing token of a block statement

Hello,


In our language, we have an end keyword instead of braces:
if (condition)
  body;
end;

When I indent after pressing enter (smart-indent), I can get the body to indent just fine. But how can I get the `end` to result in a de-indentation to be back out at the same column as the if?

The psi tree is as follows:

      DylanOperandExprImpl(OPERAND_EXPR)(139,160)
        DylanOperandImpl(OPERAND)(139,160)
          DylanIfStatementImpl(IF_STATEMENT)(139,160)
            PsiElement(DylanTokenType.IF)('if')(139,141)
            PsiWhiteSpace(' ')(141,142)
            PsiElement(DylanTokenType.LPAREN)('(')(142,143)
            DylanOperandExprImpl(OPERAND_EXPR)(143,147)
              DylanOperandImpl(OPERAND)(143,147)
                DylanVariableNameImpl(VARIABLE_NAME)(143,147)
                  PsiElement(DylanTokenType.NONDEFINING_NONEXPRESSION_WORD)('cond')(143,147)
            PsiElement(DylanTokenType.RPAREN)(')')(147,148)
            PsiWhiteSpace('\n')(148,149)
            PsiWhiteSpace('  ')(149,151)
            DylanBodyImpl(BODY)(151,156)
              DylanOperandExprImpl(OPERAND_EXPR)(151,155)
                DylanOperandImpl(OPERAND)(151,155)
                  DylanVariableNameImpl(VARIABLE_NAME)(151,155)
                    PsiElement(DylanTokenType.NONDEFINING_NONEXPRESSION_WORD)('body')(151,155)
              PsiElement(DylanTokenType.semicolon)(';')(155,156)
            PsiWhiteSpace('\n')(156,157)
            DylanIfTailImpl(IF_TAIL)(157,160)
              PsiElement(DylanTokenType.END)('end')(157,160)
      PsiElement(DylanTokenType.semicolon)(';')(160,161)


When pressing enter after the `)` the current node is `IF_STATEMENT`, after the first statement inside the body it's `BODY`. I also noticed that if I type `e` on a new line, `getChildAttributes` gets called, why is that? Can I change it so that `getChildAttributes` gets called after I type `end`?



Our FormattingBlock class extends the AbstractBlock class, so we don't implement the `isIncomplete` method.



In our language we have many forms, not just 'if', that are structured in this way, like 'for', 'until', 'while', 'unless' and so on but they've essentially the same structure.

Thanks for your help.
9 comments

In the platform, indentation is always calculated when Enter is pressed, not in any other case. If you want to change the indentation as the user completes typing the "end" keyword, you'll need to do so manually, for example, by using the TypedHandlerDelegate interface to intercept typing.

0

If you press Enter, it calculates the indentation for the new line; it will not change the indentation on any existing lines (including the line on which the cursor was placed before Enter was pressed).

0

Dmitry, I have kind of the same issue. In Java, when I type (| position of cursor)

class Blub {|}


and press Enter, in which method of the IDEA source the indentation is inserted and the closing bracket is shifted one line below, so that the code becomes

class Blub {
    |
}

I assume it happens in one of the EnterHandlerDelegate's or in the EnterHandler itself, but it is hard to find the exact position.

Thanks
Patrick

0

So, if we have this:

if (cond)
  body; (1)
end; (2)


Then, when pressing enter at (1), it will be aligned with body.

When pressing enter at (2), we want it to de-dent that line, but from what you're saying that isn't possible using this mechanism?

Do you have any idea how is this handled in Ruby or other languages?

Thanks for your help,

  - Bruce

0

In Ruby, when you type an 'if' statement (or a similar one), the "end" keyword is automatically added at the same indentation as the statement keyword. Because of that, the "end" is not actually typed by the user, and there's no need to change the indentation of any existing lines.

0

Thanks!

Is that done via live templating or another mechanism?

0

This is done by simple text modification from within an EnterHandler.

0

One possibility is to implement the com.intellij.codeInsight.editorActions.smartEnter.SmartEnterProcessor extension. With this you can create the following behavior: you only have to type if and after pressing Ctrl+Shift+Enter you get the correctly indented if-block with end keyword. It should work in the same way as it works for java when you type if and press smart enter. If you want to have some examples you should look at the com.intellij.codeInsight.editorActions.smartEnter.JavaSmartEnterProcessor and from there maybe inspect the com.intellij.codeInsight.editorActions.smartEnter.IfConditionFixer.

Basically it works like the following: When you press Ctrl+Shift+Enter at a certain position in code, the process method is called. In there the PsiElement at the caret is collected. Additionally, you have to chance to collect further elements which might be interesting. Then you registered "Fixers" are called with each element and you have to chance to "fix" the code which means in your situation to insert a missing "end".

0

Please sign in to leave a comment.