@@ -60,15 +60,17 @@ object Scanners {
60
60
/** the base of a number */
61
61
var base : Int = 0
62
62
63
- def copyFrom (td : TokenData ): Unit = {
63
+ def copyFrom (td : TokenData ): this . type =
64
64
this .token = td.token
65
65
this .offset = td.offset
66
66
this .lastOffset = td.lastOffset
67
67
this .lineOffset = td.lineOffset
68
68
this .name = td.name
69
69
this .strVal = td.strVal
70
70
this .base = td.base
71
- }
71
+ this
72
+
73
+ def saveCopy : TokenData = newTokenData.copyFrom(this )
72
74
73
75
def isNewLine = token == NEWLINE || token == NEWLINES
74
76
def isStatSep = isNewLine || token == SEMI
@@ -86,12 +88,14 @@ object Scanners {
86
88
87
89
def isOperator =
88
90
token == BACKQUOTED_IDENT
89
- || token == IDENTIFIER && isOperatorPart(name(name.length - 1 ) )
91
+ || token == IDENTIFIER && isOperatorPart(name.last )
90
92
91
93
def isArrow =
92
94
token == ARROW || token == CTXARROW
93
95
}
94
96
97
+ def newTokenData : TokenData = new TokenData {}
98
+
95
99
abstract class ScannerCommon (source : SourceFile )(using Context ) extends CharArrayReader with TokenData {
96
100
val buf : Array [Char ] = source.content
97
101
def nextToken (): Unit
@@ -264,11 +268,10 @@ object Scanners {
264
268
if (idx >= 0 && idx <= lastKeywordStart) handleMigration(kwArray(idx))
265
269
else IDENTIFIER
266
270
267
- def newTokenData : TokenData = new TokenData {}
268
-
269
271
/** We need one token lookahead and one token history
270
272
*/
271
273
val next = newTokenData
274
+ val last = newTokenData
272
275
private val prev = newTokenData
273
276
274
277
/** The current region. This is initially an Indented region with zero indentation width. */
@@ -385,12 +388,11 @@ object Scanners {
385
388
/** Produce next token, filling TokenData fields of Scanner.
386
389
*/
387
390
def nextToken (): Unit =
388
- val lastToken = token
389
- val lastName = name
390
- adjustSepRegions(lastToken)
391
- getNextToken(lastToken)
392
- if isAfterLineEnd then handleNewLine(lastToken)
393
- postProcessToken(lastToken, lastName)
391
+ last.copyFrom(this )
392
+ adjustSepRegions(last.token)
393
+ getNextToken(last.token)
394
+ if isAfterLineEnd then handleNewLine()
395
+ postProcessToken()
394
396
profile.recordNewToken()
395
397
printState()
396
398
@@ -433,7 +435,7 @@ object Scanners {
433
435
// in backticks and is a binary operator. Hence, `x` is not classified as a
434
436
// leading infix operator.
435
437
def assumeStartsExpr (lexeme : TokenData ) =
436
- (canStartExprTokens.contains(lexeme.token) || lexeme.token == COLONeol )
438
+ (canStartExprTokens.contains(lexeme.token) || lexeme.token == COLONfollow )
437
439
&& (! lexeme.isOperator || nme.raw.isUnary(lexeme.name))
438
440
val lookahead = LookaheadScanner ()
439
441
lookahead.allowLeadingInfixOperators = false
@@ -483,7 +485,7 @@ object Scanners {
483
485
if (nextChar == ch)
484
486
recur(idx - 1 , ch, n + 1 , k)
485
487
else {
486
- val k1 : IndentWidth => IndentWidth = if (n == 0 ) k else Conc (_ , Run (ch, n))
488
+ val k1 : IndentWidth => IndentWidth = if (n == 0 ) k else iw => k( Conc (iw , Run (ch, n) ))
487
489
recur(idx - 1 , nextChar, 1 , k1)
488
490
}
489
491
else recur(idx - 1 , ' ' , 0 , identity)
@@ -523,7 +525,7 @@ object Scanners {
523
525
*
524
526
* The following tokens can start an indentation region:
525
527
*
526
- * : = => <- if then else while do try catch
528
+ * : = => <- if then else while do try catch
527
529
* finally for yield match throw return with
528
530
*
529
531
* Inserting an INDENT starts a new indentation region with the indentation of the current
@@ -547,7 +549,7 @@ object Scanners {
547
549
* I.e. `a <= b` iff `b.startsWith(a)`. If indentation is significant it is considered an error
548
550
* if the current indentation width and the indentation of the current token are incomparable.
549
551
*/
550
- def handleNewLine (lastToken : Token ) =
552
+ def handleNewLine () =
551
553
var indentIsSignificant = false
552
554
var newlineIsSeparating = false
553
555
var lastWidth = IndentWidth .Zero
@@ -576,7 +578,7 @@ object Scanners {
576
578
*/
577
579
inline def isContinuing =
578
580
lastWidth < nextWidth
579
- && (openParensTokens.contains(token) || lastToken == RETURN )
581
+ && (openParensTokens.contains(token) || last.token == RETURN )
580
582
&& ! pastBlankLine
581
583
&& ! migrateTo3
582
584
&& ! noindentSyntax
@@ -590,12 +592,12 @@ object Scanners {
590
592
case _ : InString => ()
591
593
case r =>
592
594
indentIsSignificant = indentSyntax
593
- r.proposeKnownWidth(nextWidth, lastToken )
595
+ r.proposeKnownWidth(nextWidth, last.token )
594
596
lastWidth = r.knownWidth
595
597
newlineIsSeparating = r.isInstanceOf [InBraces ]
596
598
597
599
if newlineIsSeparating
598
- && canEndStatTokens.contains(lastToken )
600
+ && canEndStatTokens.contains(last.token )
599
601
&& canStartStatTokens.contains(token)
600
602
&& ! isLeadingInfixOperator(nextWidth)
601
603
&& ! isContinuing
@@ -606,7 +608,7 @@ object Scanners {
606
608
|| nextWidth == lastWidth && (indentPrefix == MATCH || indentPrefix == CATCH ) && token != CASE then
607
609
if currentRegion.isOutermost then
608
610
if nextWidth < lastWidth then currentRegion = topLevelRegion(nextWidth)
609
- else if ! isLeadingInfixOperator(nextWidth) && ! statCtdTokens.contains(lastToken ) && lastToken != INDENT then
611
+ else if ! isLeadingInfixOperator(nextWidth) && ! statCtdTokens.contains(last.token ) && last.token != INDENT then
610
612
currentRegion match
611
613
case r : Indented =>
612
614
insert(OUTDENT , offset)
@@ -630,15 +632,16 @@ object Scanners {
630
632
report.warning(" Line is indented too far to the left, or a `}` is missing" , sourcePos())
631
633
632
634
else if lastWidth < nextWidth
633
- || lastWidth == nextWidth && (lastToken == MATCH || lastToken == CATCH ) && token == CASE then
634
- if canStartIndentTokens.contains(lastToken ) then
635
- currentRegion = Indented (nextWidth, lastToken , currentRegion)
635
+ || lastWidth == nextWidth && (last.token == MATCH || last.token == CATCH ) && token == CASE then
636
+ if canStartIndentTokens.contains(last.token ) then
637
+ currentRegion = Indented (nextWidth, last.token , currentRegion)
636
638
insert(INDENT , offset)
637
- else if lastToken == SELFARROW then
639
+ else if last.token == SELFARROW then
638
640
currentRegion.knownWidth = nextWidth
639
641
else if (lastWidth != nextWidth)
640
642
val lw = lastWidth
641
- errorButContinue(spaceTabMismatchMsg(lw, nextWidth))
643
+ val msg = spaceTabMismatchMsg(lw, nextWidth)
644
+ if rewriteToIndent then report.warning(msg) else errorButContinue(msg)
642
645
if token != OUTDENT then
643
646
handleNewIndentWidth(currentRegion, _.otherIndentWidths += nextWidth)
644
647
if next.token == EMPTY then
@@ -702,7 +705,7 @@ object Scanners {
702
705
* SEMI + ELSE => ELSE, COLON following id/)/] => COLONfollow
703
706
* - Insert missing OUTDENTs at EOF
704
707
*/
705
- def postProcessToken (lastToken : Token , lastName : SimpleName ): Unit = {
708
+ def postProcessToken (): Unit = {
706
709
def fuse (tok : Int ) = {
707
710
token = tok
708
711
offset = prev.offset
@@ -738,8 +741,8 @@ object Scanners {
738
741
case END =>
739
742
if ! isEndMarker then token = IDENTIFIER
740
743
case COLONop =>
741
- if lastToken == IDENTIFIER && lastName != null && isIdentifierStart(lastName .head)
742
- || colonEOLPredecessors.contains(lastToken )
744
+ if last.token == IDENTIFIER && last.name != null && isIdentifierStart(last.name .head)
745
+ || colonEOLPredecessors.contains(last.token )
743
746
then token = COLONfollow
744
747
case RBRACE | RPAREN | RBRACKET =>
745
748
closeIndented()
@@ -758,6 +761,8 @@ object Scanners {
758
761
if endMarkerTokens.contains(lookahead.token)
759
762
&& source.offsetToLine(lookahead.offset) == endLine
760
763
then
764
+ if rewriteToIndent && lookahead.token == MATCH then
765
+ patch(Span (offset, offset + 3 ), " `end`" )
761
766
lookahead.nextToken()
762
767
if lookahead.token == EOF
763
768
|| source.offsetToLine(lookahead.offset) > endLine
@@ -1266,6 +1271,7 @@ object Scanners {
1266
1271
putChar(ch) ; nextRawChar()
1267
1272
loopRest()
1268
1273
else
1274
+ next.lineOffset = if next.lastOffset < lineStartOffset then lineStartOffset else - 1
1269
1275
finishNamedToken(IDENTIFIER , target = next)
1270
1276
end loopRest
1271
1277
setStrVal()
@@ -1312,10 +1318,10 @@ object Scanners {
1312
1318
}
1313
1319
end getStringPart
1314
1320
1315
- private def fetchStringPart (multiLine : Boolean ) = {
1321
+ private def fetchStringPart (multiLine : Boolean ) =
1316
1322
offset = charOffset - 1
1323
+ lineOffset = if lastOffset < lineStartOffset then lineStartOffset else - 1
1317
1324
getStringPart(multiLine)
1318
- }
1319
1325
1320
1326
private def isTripleQuote (): Boolean =
1321
1327
if (ch == '"' ) {
@@ -1657,21 +1663,31 @@ object Scanners {
1657
1663
case Run (ch : Char , n : Int )
1658
1664
case Conc (l : IndentWidth , r : Run )
1659
1665
1660
- def <= (that : IndentWidth ): Boolean = this match {
1661
- case Run (ch1, n1) =>
1662
- that match {
1663
- case Run (ch2, n2) => n1 <= n2 && (ch1 == ch2 || n1 == 0 )
1664
- case Conc (l, r) => this <= l
1665
- }
1666
- case Conc (l1, r1) =>
1667
- that match {
1668
- case Conc (l2, r2) => l1 == l2 && r1 <= r2
1669
- case _ => false
1670
- }
1671
- }
1666
+ def <= (that : IndentWidth ): Boolean = (this , that) match
1667
+ case (Run (ch1, n1), Run (ch2, n2)) => n1 <= n2 && (ch1 == ch2 || n1 == 0 )
1668
+ case (Conc (l1, r1), Conc (l2, r2)) => (l1 == l2 && r1 <= r2) || this <= l2
1669
+ case (_, Conc (l2, _)) => this <= l2
1670
+ case _ => false
1672
1671
1673
1672
def < (that : IndentWidth ): Boolean = this <= that && ! (that <= this )
1674
1673
1674
+ def >= (that : IndentWidth ): Boolean = that <= this
1675
+
1676
+ def > (that : IndentWidth ): Boolean = that < this
1677
+
1678
+ def size : Int = this match
1679
+ case Run (_, n) => n
1680
+ case Conc (l, r) => l.size + r.n
1681
+
1682
+ /** Add one level of indentation (one tab or two spaces depending on the last char) */
1683
+ def increment : IndentWidth =
1684
+ def incRun (ch : Char , n : Int ): Run = ch match
1685
+ case ' ' => IndentWidth .Run (' ' , n + 2 )
1686
+ case ch => IndentWidth .Run (ch, n + 1 )
1687
+ this match
1688
+ case Run (ch, n) => incRun(ch, n)
1689
+ case Conc (l, Run (ch, n)) => Conc (l, incRun(ch, n))
1690
+
1675
1691
/** Does `this` differ from `that` by not more than a single space? */
1676
1692
def isClose (that : IndentWidth ): Boolean = this match
1677
1693
case Run (ch1, n1) =>
0 commit comments