Skip to content

Commit 775d881

Browse files
authored
Merge pull request #11959 from dotty-staging/add-indent-after-operator
Better handling of leading infix operators in indented code
2 parents a288432 + 6609022 commit 775d881

File tree

3 files changed

+52
-5
lines changed

3 files changed

+52
-5
lines changed

compiler/src/dotty/tools/dotc/parsing/Scanners.scala

+18-4
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ object Scanners {
369369
* token that can start an expression.
370370
* If a leading infix operator is found and the source version is `3.0-migration`, emit a change warning.
371371
*/
372-
def isLeadingInfixOperator(inConditional: Boolean = true) =
372+
def isLeadingInfixOperator(nextWidth: IndentWidth = indentWidth(offset), inConditional: Boolean = true) =
373373
allowLeadingInfixOperators
374374
&& isOperator
375375
&& (isWhitespace(ch) || ch == LF)
@@ -397,6 +397,20 @@ object Scanners {
397397
assumeStartsExpr(lookahead)
398398
|| lookahead.token == NEWLINE && assumeStartsExpr(lookahead.next)
399399
}
400+
&& {
401+
currentRegion match
402+
case r: Indented =>
403+
r.width <= nextWidth
404+
|| {
405+
r.outer match
406+
case null => true
407+
case Indented(outerWidth, others, _, _) =>
408+
outerWidth < nextWidth && !others.contains(nextWidth)
409+
case outer =>
410+
outer.indentWidth < nextWidth
411+
}
412+
case _ => true
413+
}
400414
&& {
401415
if migrateTo3 then
402416
val (what, previous) =
@@ -512,7 +526,7 @@ object Scanners {
512526
if newlineIsSeparating
513527
&& canEndStatTokens.contains(lastToken)
514528
&& canStartStatTokens.contains(token)
515-
&& !isLeadingInfixOperator()
529+
&& !isLeadingInfixOperator(nextWidth)
516530
&& !(lastWidth < nextWidth && isContinuing(lastToken))
517531
then
518532
insert(if (pastBlankLine) NEWLINES else NEWLINE, lineOffset)
@@ -521,7 +535,7 @@ object Scanners {
521535
|| nextWidth == lastWidth && (indentPrefix == MATCH || indentPrefix == CATCH) && token != CASE then
522536
if currentRegion.isOutermost then
523537
if nextWidth < lastWidth then currentRegion = topLevelRegion(nextWidth)
524-
else if !isLeadingInfixOperator() && !statCtdTokens.contains(lastToken) then
538+
else if !isLeadingInfixOperator(nextWidth) && !statCtdTokens.contains(lastToken) then
525539
currentRegion match
526540
case r: Indented =>
527541
currentRegion = r.enclosing
@@ -1105,7 +1119,7 @@ object Scanners {
11051119
putChar(ch)
11061120
nextRawChar()
11071121
getStringPart(multiLine)
1108-
}
1122+
}
11091123
else if (ch == '$') {
11101124
nextRawChar()
11111125
if (ch == '$' || ch == '"') {

docs/docs/reference/other-new-features/indentation.md

+22-1
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,11 @@ There are two rules:
8080
```
8181
then else do catch finally yield match
8282
```
83-
- the first token on the next line is not a
83+
- if the first token on the next line is a
8484
[leading infix operator](../changed-features/operators.md).
85+
then its indentation width is less then the current indentation width,
86+
and it either matches a previous indentation width or is also less
87+
than the enclosing indentation width.
8588

8689
If an `<outdent>` is inserted, the top element is popped from `IW`.
8790
If the indentation width of the token on the next line is still less than the new current indentation width, step (2) repeats. Therefore, several `<outdent>` tokens
@@ -105,6 +108,24 @@ if x < 0 then
105108
Indentation tokens are only inserted in regions where newline statement separators are also inferred:
106109
at the top-level, inside braces `{...}`, but not inside parentheses `(...)`, patterns or types.
107110

111+
**Note:** The rules for leading infix operators above are there to make sure that
112+
```scala
113+
one
114+
+ two.match
115+
case 1 => b
116+
case 2 => c
117+
+ three
118+
```
119+
is parsed as `one + (two.match ...) + three`. Also, that
120+
```scala
121+
if x then
122+
a
123+
+ b
124+
+ c
125+
else d
126+
```
127+
is parsed as `if x then a + b + c else d`.
128+
108129
### Optional Braces Around Template Bodies
109130

110131
The Scala grammar uses the term _template body_ for the definitions of a class, trait, or object that are normally enclosed in braces. The braces around a template body can also be omitted by means of the following rule.

tests/run/indent.scala

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@main def Test =
2+
val x = false
3+
val y = 1
4+
val result =
5+
x
6+
|| y.match
7+
case 1 => false
8+
case 3 => false
9+
case _ => true
10+
|| !x
11+
assert(result)
12+

0 commit comments

Comments
 (0)