Custom Language Indent

Hey.  I'm developing a custom language plugin for a language with syntax similar to Lisp.  A typical piece of code looks a lot like this:

(main_operation
(sub_op_1 param_1 param_2)
(several (chained (ops
param_3
param_4
)))
)

For this example, let's set the Tab size to 4 and check the "Use tab character" box.  When I tell IntelliJ to format my code, I want the result to look like this:

(main_operation
(sub_op_1 param_1 param_2)
(several (chained (ops
param_3
param_4
)))
)

However, I have only been able to make the formatter spit out code that looks like this:

(main_operation
(sub_op_1 param_1 param_2)
(several (chained (ops
param_3
param_4
)))
)

Here's the same example again, with -> representing tab and . representing leading spaces.  This is what I want:

(main_operation
->(sub_op_1 param_1 param_2)
->(several (chained (ops
->->param_3
->->param_4
->)))
)

This is as close as I've gotten the formatter to give me:

(main_operation
->.(sub_op_1 param_1 param_2)
->.(several (chained (ops
->->->->->->..param_3
->->->->->->..param_4
->->->->->..)))
)

Here are the two main areas that are giving me trouble.  The first is a minor annoyance, but the second has me dead in the water.

  1. The start of line two is indenting from the first non-open-paren character of line one.  This gives me an actual indent size of 5 instead of the 4 I wanted.  I can work around this by using Indent.Type.SPACES and setting the value to one less than the indent size I actually want (3 in this case), but that feels hacky and I hope there's a better way.
  2. The real problem I can't get past is line 4.  That line should be indented one level deeper than the previous line.  However, when IndentAdjuster calculates the block offset in the adjustSpacingByIndentOffset method, tons of extra indentation is added as a result of the multiple blocks on the previous line.

I'd prefer not to reimplement any of the whitespace adjustment classes like IndentAdjuster and AdjustWhiteSpacesState.  Please let me know if there's some quick and easy solution I'm missing, or if you can recommend a good example that accomplishes something similar.  If some potentially helpful information is missing from my question, let me know and I'll add it.

3 comments

You can try to use Indent.getSmartIndent, basically it's a type of indent which tries to collapse multiple indents into single one if proper conditions are met.

It's used in java to format anonymous classes nicely:

foooooooooo(new Runnable() {
@Override
public void run() {
}
},
"fooo");

while when there is no second parameter indent is collapsed

foooooooooo(new Runnable() {
@Override
public void run() {
}
});
0

Thanks for the suggestion.  In this case, SmartIndent results in the same spacing as normal indent, so it unfortunately did not magically solve my problem.  

I ended up with a fairly hacky "solution" that manually parses the file and calculates the amount the indent should be offset to work around the issues I mentioned earlier.  So, in this example, the Indent returned by my Block implementation for the param_3 line ends up being -11 spaces.  

    4 Tab Size
- 14 Number of chars after the first expression. The extra chars are " (chained (ops"
- 1 Offset for opening paren
= -11 Spaces

It's ugly, but it outputs the desired formating.  I'm willing to throw it all in the garbage if something better comes along, but for now it's the best I've got.

 

0

As expected, the hacky solution didn't really work.

It turns out the problem was with Alignment, not Indent, and the fix had to be made in two places.  The first was at the grammar level.  I had to move the expression body to a separate element so it could be indented and aligned independently from the enclosing parentheses.  The second fix was to properly set the parent alignment of child blocks during child block generation.  

// Before
FILE ::= EXPRESSION*
EXPRESSION ::= OPEN_PAREN COMMAND PARAMETER* CLOSE_PAREN
PARAMETER ::= EXPRESSION|LITERAL

// Fixed
FILE ::= EXPRESSION*
EXPRESSION ::= OPEN_PAREN EXPRESSION_BODY CLOSE_PAREN
EXPRESSION_BODY ::= COMMAND PARAMETER*
PARAMETER ::= EXPRESSION|LITERAL


 

0

Please sign in to leave a comment.