Skip to content

Commit bc6cf47

Browse files
committed
Improve dentation at EOF
Check trailing blank line at EOF for OUTDENT If EOF is preceded by only spaces on the last line, do not outdent because it will complain about alignment during an edit. Preserve EOF token when probing arrow EOL For REPL, `: x =>` at EOF sees an EOF in order to detect lambda eol.
1 parent b1575f0 commit bc6cf47

File tree

3 files changed

+32
-6
lines changed

3 files changed

+32
-6
lines changed

Diff for: compiler/src/dotty/tools/dotc/parsing/Scanners.scala

+21-5
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,20 @@ object Scanners {
603603
lastWidth = r.knownWidth
604604
newlineIsSeparating = r.isInstanceOf[InBraces]
605605

606+
// can emit OUTDENT if line is not non-empty blank line at EOF
607+
inline def isTrailingBlankLine: Boolean =
608+
token == EOF && {
609+
val end = buf.length - 1 // take terminal NL as empty last line
610+
val prev = buf.lastIndexWhere(!isWhitespace(_), end = end)
611+
prev < 0 || end - prev > 0 && isLineBreakChar(buf(prev))
612+
}
613+
614+
inline def canDedent: Boolean =
615+
lastToken != INDENT
616+
&& !isLeadingInfixOperator(nextWidth)
617+
&& !statCtdTokens.contains(lastToken)
618+
&& !isTrailingBlankLine
619+
606620
if newlineIsSeparating
607621
&& canEndStatTokens.contains(lastToken)
608622
&& canStartStatTokens.contains(token)
@@ -615,9 +629,8 @@ object Scanners {
615629
|| nextWidth == lastWidth && (indentPrefix == MATCH || indentPrefix == CATCH) && token != CASE then
616630
if currentRegion.isOutermost then
617631
if nextWidth < lastWidth then currentRegion = topLevelRegion(nextWidth)
618-
else if !isLeadingInfixOperator(nextWidth) && !statCtdTokens.contains(lastToken) && lastToken != INDENT then
632+
else if canDedent then
619633
currentRegion match
620-
case _ if token == EOF => // no OUTDENT at EOF
621634
case r: Indented =>
622635
insert(OUTDENT, offset)
623636
handleNewIndentWidth(r.enclosing, ir =>
@@ -671,13 +684,16 @@ object Scanners {
671684
reset()
672685
if atEOL then token = COLONeol
673686

674-
// consume => and insert <indent> if applicable
687+
// consume => and insert <indent> if applicable. Used to detect colon arrow: x =>
675688
def observeArrowIndented(): Unit =
676689
if isArrow && indentSyntax then
677690
peekAhead()
678-
val atEOL = isAfterLineEnd || token == EOF
691+
val atEOL = isAfterLineEnd
692+
val atEOF = token == EOF
679693
reset()
680-
if atEOL then
694+
if atEOF then
695+
token = EOF
696+
else if atEOL then
681697
val nextWidth = indentWidth(next.offset)
682698
val lastWidth = currentRegion.indentWidth
683699
if lastWidth < nextWidth then

Diff for: compiler/src/dotty/tools/dotc/util/Chars.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ object Chars:
5050
}
5151

5252
/** Is character a whitespace character (but not a new line)? */
53-
def isWhitespace(c: Char): Boolean =
53+
inline def isWhitespace(c: Char): Boolean =
5454
c == ' ' || c == '\t' || c == CR
5555

5656
/** Can character form part of a doc comment variable $xxx? */

Diff for: compiler/test/dotty/tools/repl/ReplCompilerTests.scala

+10
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,16 @@ class ReplCompilerTests extends ReplTest:
511511
val all = lines()
512512
assertTrue(hints.forall(hint => all.exists(_.contains(hint))))
513513

514+
@Test def `i22844 regression colon eol`: Unit = initially:
515+
run:
516+
"""|println:
517+
| "hello, world"
518+
|""".stripMargin // outdent, but this test does not exercise the bug
519+
assertEquals(List("hello, world"), lines())
520+
521+
@Test def `i22844b regression colon arrow eol`: Unit = contextually:
522+
assertTrue(ParseResult.isIncomplete("List(42).map: x =>"))
523+
514524
object ReplCompilerTests:
515525

516526
private val pattern = Pattern.compile("\\r[\\n]?|\\n");

0 commit comments

Comments
 (0)