Skip to content

Commit 869c8ee

Browse files
committed
Allow indentation to work inside parens
Actually indentation was always enabled inside parens, but the rule which indentation width to use was too simplistic and often required an extra line. This is now fixed, so both of these work: ```scala def test1 = g(1, x => val y = x * x y * y ) def test2 = g(1, x => val y = x * x y * y ) ```
1 parent c149eeb commit 869c8ee

File tree

4 files changed

+56
-5
lines changed

4 files changed

+56
-5
lines changed

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ object Scanners {
462462
indentPrefix = r.prefix
463463
case r =>
464464
indentIsSignificant = indentSyntax
465-
if (r.knownWidth == null) r.knownWidth = nextWidth
465+
r.proposeKnownWidth(nextWidth, lastToken)
466466
lastWidth = r.knownWidth
467467
newlineIsSeparating = r.isInstanceOf[InBraces]
468468

@@ -1349,6 +1349,18 @@ object Scanners {
13491349
/** The indentation width, Zero if not known */
13501350
final def indentWidth: IndentWidth =
13511351
if knownWidth == null then IndentWidth.Zero else knownWidth
1352+
1353+
def proposeKnownWidth(width: IndentWidth, lastToken: Token) =
1354+
if knownWidth == null then
1355+
this match
1356+
case InParens(_, _) if lastToken != LPAREN =>
1357+
useOuterWidth()
1358+
case _ =>
1359+
knownWidth = width
1360+
1361+
private def useOuterWidth(): Unit =
1362+
if enclosing.knownWidth == null then enclosing.useOuterWidth()
1363+
knownWidth = enclosing.knownWidth
13521364
end Region
13531365

13541366
case class InString(multiLine: Boolean, outer: Region) extends Region

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

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,39 @@ Indentation prefixes can consist of spaces and/or tabs. Indentation widths are t
159159

160160
### Indentation and Braces
161161

162-
Indentation can be mixed freely with braces. For interpreting indentation inside braces, the following rules apply.
162+
Indentation can be mixed freely with braces and parentheses. For interpreting indentation inside braces and parentheses, the following rules apply.
163163

164164
1. The assumed indentation width of a multiline region enclosed in braces is the
165165
indentation width of the first token that starts a new line after the opening brace.
166166

167-
2. On encountering a closing brace `}`, as many `<outdent>` tokens as necessary are
168-
inserted to close all open indentation regions inside the pair of braces.
167+
2. The assumed indentation width of a multiline region inside parentheses is:
168+
169+
- if the opening parenthesis is at the end of a line, the indentation width of token following it,
170+
- otherwise, the indentation width of the enclosing region.
171+
172+
3. On encountering a closing brace `}` or parenthesis `)`, as many `<outdent>` tokens as necessary are
173+
inserted to close all open indentation regions inside the pair of braces or parentheses.
174+
175+
For instance, consider:
176+
```scala
177+
{
178+
val x = f(x: Int, y =>
179+
x * (
180+
y + 1
181+
) +
182+
(x +
183+
x)
184+
)
185+
}
186+
```
187+
- Here, the indentation width of the region enclosed by the braces is 3 (i.e. the indentation width of the
188+
statement starting with `val`).
189+
- The indentation width of the region in parentheses that follows `f` is also 3, since the opening
190+
parenthesis is not at the end of a line.
191+
- The indentation width of the region in parentheses around `y + 1` is 9
192+
(i.e. the indentation width of `y + 1`).
193+
- Finally, the indentation width of the last region in parentheses starting with `(x` is 6 (i.e. the indentation
194+
width of the indented region following the `=>`.
169195

170196
### Special Treatment of Case Clauses
171197

tests/pos/indent-in-parens.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
def g(x: Int, op: Int => Int) = op(x)
2+
3+
def test1 = g(1, x =>
4+
val y = x * x
5+
y * y
6+
)
7+
8+
def test2 = g(1,
9+
x =>
10+
val y = x * x
11+
y * y
12+
)
13+
114
def f(x: Int) =
215
assert(
316
if x > 0 then

tests/run/Course-2002-13.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ class Parser(s: String) {
179179
val a = token;
180180
token = it.next;
181181
Con(a,
182-
if (token equals "(") {
182+
if (token equals "(") {
183183
token = it.next;
184184
val ts: List[Term] = if (token equals ")") List() else rep(term);
185185
if (token equals ")") token = it.next else syntaxError("`)` expected");

0 commit comments

Comments
 (0)