@@ -60,16 +60,19 @@ 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
+ this
71
72
}
72
73
74
+ def saveCopy : TokenData = newTokenData.copyFrom(this )
75
+
73
76
def isNewLine = token == NEWLINE || token == NEWLINES
74
77
def isStatSep = isNewLine || token == SEMI
75
78
def isIdent = token == IDENTIFIER || token == BACKQUOTED_IDENT
@@ -86,12 +89,14 @@ object Scanners {
86
89
87
90
def isOperator =
88
91
token == BACKQUOTED_IDENT
89
- || token == IDENTIFIER && isOperatorPart(name(name.length - 1 ) )
92
+ || token == IDENTIFIER && isOperatorPart(name.last )
90
93
91
94
def isArrow =
92
95
token == ARROW || token == CTXARROW
93
96
}
94
97
98
+ def newTokenData : TokenData = new TokenData {}
99
+
95
100
abstract class ScannerCommon (source : SourceFile )(using Context ) extends CharArrayReader with TokenData {
96
101
val buf : Array [Char ] = source.content
97
102
def nextToken (): Unit
@@ -264,11 +269,10 @@ object Scanners {
264
269
if (idx >= 0 && idx <= lastKeywordStart) handleMigration(kwArray(idx))
265
270
else IDENTIFIER
266
271
267
- def newTokenData : TokenData = new TokenData {}
268
-
269
272
/** We need one token lookahead and one token history
270
273
*/
271
274
val next = newTokenData
275
+ val last = newTokenData
272
276
private val prev = newTokenData
273
277
274
278
/** The current region. This is initially an Indented region with zero indentation width. */
@@ -385,6 +389,7 @@ object Scanners {
385
389
/** Produce next token, filling TokenData fields of Scanner.
386
390
*/
387
391
def nextToken (): Unit =
392
+ last.copyFrom(this )
388
393
val lastToken = token
389
394
val lastName = name
390
395
adjustSepRegions(lastToken)
@@ -433,7 +438,7 @@ object Scanners {
433
438
// in backticks and is a binary operator. Hence, `x` is not classified as a
434
439
// leading infix operator.
435
440
def assumeStartsExpr (lexeme : TokenData ) =
436
- (canStartExprTokens.contains(lexeme.token) || lexeme.token == COLONeol )
441
+ (canStartExprTokens.contains(lexeme.token) || lexeme.token == COLONfollow )
437
442
&& (! lexeme.isOperator || nme.raw.isUnary(lexeme.name))
438
443
val lookahead = LookaheadScanner ()
439
444
lookahead.allowLeadingInfixOperators = false
@@ -483,7 +488,7 @@ object Scanners {
483
488
if (nextChar == ch)
484
489
recur(idx - 1 , ch, n + 1 , k)
485
490
else {
486
- val k1 : IndentWidth => IndentWidth = if (n == 0 ) k else Conc (_ , Run (ch, n))
491
+ val k1 : IndentWidth => IndentWidth = if (n == 0 ) k else iw => k( Conc (iw , Run (ch, n) ))
487
492
recur(idx - 1 , nextChar, 1 , k1)
488
493
}
489
494
else recur(idx - 1 , ' ' , 0 , identity)
@@ -523,7 +528,7 @@ object Scanners {
523
528
*
524
529
* The following tokens can start an indentation region:
525
530
*
526
- * : = => <- if then else while do try catch
531
+ * : = => <- if then else while do try catch
527
532
* finally for yield match throw return with
528
533
*
529
534
* Inserting an INDENT starts a new indentation region with the indentation of the current
@@ -638,7 +643,8 @@ object Scanners {
638
643
currentRegion.knownWidth = nextWidth
639
644
else if (lastWidth != nextWidth)
640
645
val lw = lastWidth
641
- errorButContinue(spaceTabMismatchMsg(lw, nextWidth))
646
+ val msg = spaceTabMismatchMsg(lw, nextWidth)
647
+ if rewriteToIndent then report.warning(msg) else errorButContinue(msg)
642
648
if token != OUTDENT then
643
649
handleNewIndentWidth(currentRegion, _.otherIndentWidths += nextWidth)
644
650
if next.token == EMPTY then
@@ -758,6 +764,8 @@ object Scanners {
758
764
if endMarkerTokens.contains(lookahead.token)
759
765
&& source.offsetToLine(lookahead.offset) == endLine
760
766
then
767
+ if rewriteToIndent && lookahead.token == MATCH then
768
+ patch(Span (offset, offset + 3 ), " `end`" )
761
769
lookahead.nextToken()
762
770
if lookahead.token == EOF
763
771
|| source.offsetToLine(lookahead.offset) > endLine
@@ -1266,6 +1274,7 @@ object Scanners {
1266
1274
putChar(ch) ; nextRawChar()
1267
1275
loopRest()
1268
1276
else
1277
+ next.lineOffset = if next.lastOffset < lineStartOffset then lineStartOffset else - 1
1269
1278
finishNamedToken(IDENTIFIER , target = next)
1270
1279
end loopRest
1271
1280
setStrVal()
@@ -1312,10 +1321,10 @@ object Scanners {
1312
1321
}
1313
1322
end getStringPart
1314
1323
1315
- private def fetchStringPart (multiLine : Boolean ) = {
1324
+ private def fetchStringPart (multiLine : Boolean ) =
1316
1325
offset = charOffset - 1
1326
+ lineOffset = if lastOffset < lineStartOffset then lineStartOffset else - 1
1317
1327
getStringPart(multiLine)
1318
- }
1319
1328
1320
1329
private def isTripleQuote (): Boolean =
1321
1330
if (ch == '"' ) {
@@ -1657,21 +1666,31 @@ object Scanners {
1657
1666
case Run (ch : Char , n : Int )
1658
1667
case Conc (l : IndentWidth , r : Run )
1659
1668
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
- }
1669
+ def <= (that : IndentWidth ): Boolean = (this , that) match
1670
+ case (Run (ch1, n1), Run (ch2, n2)) => n1 <= n2 && (ch1 == ch2 || n1 == 0 )
1671
+ case (Conc (l1, r1), Conc (l2, r2)) => (l1 == l2 && r1 <= r2) || this <= l2
1672
+ case (_, Conc (l2, _)) => this <= l2
1673
+ case _ => false
1672
1674
1673
1675
def < (that : IndentWidth ): Boolean = this <= that && ! (that <= this )
1674
1676
1677
+ def >= (that : IndentWidth ): Boolean = that <= this
1678
+
1679
+ def > (that : IndentWidth ): Boolean = that < this
1680
+
1681
+ def size : Int = this match
1682
+ case Run (_, n) => n
1683
+ case Conc (l, r) => l.size + r.n
1684
+
1685
+ /** Add one level of indentation (one tab or two spaces depending on the last char) */
1686
+ def increment : IndentWidth =
1687
+ def incRun (ch : Char , n : Int ): Run = ch match
1688
+ case ' ' => IndentWidth .Run (' ' , n + 2 )
1689
+ case ch => IndentWidth .Run (ch, n + 1 )
1690
+ this match
1691
+ case Run (ch, n) => incRun(ch, n)
1692
+ case Conc (l, Run (ch, n)) => Conc (l, incRun(ch, n))
1693
+
1675
1694
/** Does `this` differ from `that` by not more than a single space? */
1676
1695
def isClose (that : IndentWidth ): Boolean = this match
1677
1696
case Run (ch1, n1) =>
0 commit comments