The documentation says that

• with the left rule modifier, the "PSI node for this rule will enclose the one to the left."
• with the inner rule modifier, the "PSI node for this rule will be injected into the one to the left," and

As I understand it, PSI nodes form a tree from ranges that define higher elements and subranges of those that define lower elements. So, like this:

Nodes    (WHILE_LOOP..........................................)
(BOOL_EXPR............)    (LOOP_BODY......)
(EXPR)     (EXPR......)    (STATEMENT......)
Tokens    while _ ( x    _ < _ y  _ + _ 1  ) _ doSomething ( ) EOF
Char Idx  0.........7....8.....11.......15.....18............. 30

Text     "while (x < y + 1) doSomething()"

This raises several questions:

What does "the one to the left" mean in this context?

Let's say I apply the left rule modifier to LOOP_BODY. I see five possible "left" nodes:
• Whitespace token at 17
• Close-parentheses token at 16
• EXPR node at 11–15
• BOOL_EXPR node at 7–15

What does it mean to "enclose"?

Let's assume that the rule is enclosing the last possibility above, BOOL_EXPR. Does that mean the tree will actually end up like this, with the underlined change?

Nodes    (WHILE_LOOP..........................................)
(LOOP_BODY.................................)
(BOOL_EXPR............)    (STATEMENT......)
(EXPR)     (EXPR......)
Tokens    while _ ( x    _ < _ y  _ + _ 1  ) _ doSomething ( ) EOF
Char Idx  0.........7....8.....11.......15.....18............. 30

Would the close-parentheses token then become a child of LOOP_BODY instead of WHILE_LOOP?

What does it mean to "inject"?

Is this a class hierarchy, inheritance, or composition thing, or is inner just the opposite of left, so an inner BOOL_EXPR would result in a tree like this?

Nodes    (WHILE_LOOP..........................................)
(BOOL_EXPR.................................)
(EXPR)     (EXPR......)    (LOOP_BODY......)
Tokens    while _ ( x    _ < _ y  _ + _ 1  ) _ doSomething ( ) EOF
Char Idx  0.........7....8.....11.......15.....18............. 30

And if that is the case, does left inner combine with both the left and the right nodes? And do they chain?

1 comment

The one to the left is just a synonym for PsiBuilder.getLatestDoneMarker().
In terms of BNF this refers (usually) to any non-private-rule-to-the-left in a sequence.

Consider after parsing some my_rule you have curMarker to finish & latestMarker obtained at the beginning as psiBuilder.getLatestDoneMarker();

1. left my_rule completion sequence:
curMarker.drop()
latestMarker.precede().done(my_rule_type);

2. left inner my_rule:
curMarker.done(my_rule_type);
latestMarker.precede().done(latestMarker.type)
latestMarker.drop();

3. private left = private left inner:
curMarker.drop()
latestMarker.precede().done(latestMarker.type)
latestMarker.drop();

Code references:
Store leftMarker on start: https://github.com/JetBrains/Grammar-Kit/blob/master/support/org/intellij/grammar/parser/GeneratedParserUtilBase.java?source=c#L347
Finish leftRule processing: https://github.com/JetBrains/Grammar-Kit/blob/master/support/org/intellij/grammar/parser/GeneratedParserUtilBase.java?source=c#L484