@@ -60,16 +60,19 @@ object Scanners {
6060 /** the base of a number */
6161 var base : Int = 0
6262
63- def copyFrom (td : TokenData ): Unit = {
63+ def copyFrom (td : TokenData ): this . type = {
6464 this .token = td.token
6565 this .offset = td.offset
6666 this .lastOffset = td.lastOffset
6767 this .lineOffset = td.lineOffset
6868 this .name = td.name
6969 this .strVal = td.strVal
7070 this .base = td.base
71+ this
7172 }
7273
74+ def saveCopy : TokenData = newTokenData.copyFrom(this )
75+
7376 def isNewLine = token == NEWLINE || token == NEWLINES
7477 def isStatSep = isNewLine || token == SEMI
7578 def isIdent = token == IDENTIFIER || token == BACKQUOTED_IDENT
@@ -86,12 +89,14 @@ object Scanners {
8689
8790 def isOperator =
8891 token == BACKQUOTED_IDENT
89- || token == IDENTIFIER && isOperatorPart(name(name.length - 1 ) )
92+ || token == IDENTIFIER && isOperatorPart(name.last )
9093
9194 def isArrow =
9295 token == ARROW || token == CTXARROW
9396 }
9497
98+ def newTokenData : TokenData = new TokenData {}
99+
95100 abstract class ScannerCommon (source : SourceFile )(using Context ) extends CharArrayReader with TokenData {
96101 val buf : Array [Char ] = source.content
97102 def nextToken (): Unit
@@ -264,11 +269,10 @@ object Scanners {
264269 if (idx >= 0 && idx <= lastKeywordStart) handleMigration(kwArray(idx))
265270 else IDENTIFIER
266271
267- def newTokenData : TokenData = new TokenData {}
268-
269272 /** We need one token lookahead and one token history
270273 */
271274 val next = newTokenData
275+ val last = newTokenData
272276 private val prev = newTokenData
273277
274278 /** The current region. This is initially an Indented region with zero indentation width. */
@@ -385,6 +389,7 @@ object Scanners {
385389 /** Produce next token, filling TokenData fields of Scanner.
386390 */
387391 def nextToken (): Unit =
392+ last.copyFrom(this )
388393 val lastToken = token
389394 val lastName = name
390395 adjustSepRegions(lastToken)
@@ -433,7 +438,7 @@ object Scanners {
433438 // in backticks and is a binary operator. Hence, `x` is not classified as a
434439 // leading infix operator.
435440 def assumeStartsExpr (lexeme : TokenData ) =
436- (canStartExprTokens.contains(lexeme.token) || lexeme.token == COLONeol )
441+ (canStartExprTokens.contains(lexeme.token) || lexeme.token == COLONfollow )
437442 && (! lexeme.isOperator || nme.raw.isUnary(lexeme.name))
438443 val lookahead = LookaheadScanner ()
439444 lookahead.allowLeadingInfixOperators = false
@@ -483,7 +488,7 @@ object Scanners {
483488 if (nextChar == ch)
484489 recur(idx - 1 , ch, n + 1 , k)
485490 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) ))
487492 recur(idx - 1 , nextChar, 1 , k1)
488493 }
489494 else recur(idx - 1 , ' ' , 0 , identity)
@@ -523,7 +528,7 @@ object Scanners {
523528 *
524529 * The following tokens can start an indentation region:
525530 *
526- * : = => <- if then else while do try catch
531+ * : = => <- if then else while do try catch
527532 * finally for yield match throw return with
528533 *
529534 * Inserting an INDENT starts a new indentation region with the indentation of the current
@@ -638,7 +643,8 @@ object Scanners {
638643 currentRegion.knownWidth = nextWidth
639644 else if (lastWidth != nextWidth)
640645 val lw = lastWidth
641- errorButContinue(spaceTabMismatchMsg(lw, nextWidth))
646+ val msg = spaceTabMismatchMsg(lw, nextWidth)
647+ if rewriteToIndent then report.warning(msg) else errorButContinue(msg)
642648 if token != OUTDENT then
643649 handleNewIndentWidth(currentRegion, _.otherIndentWidths += nextWidth)
644650 if next.token == EMPTY then
@@ -1266,6 +1272,7 @@ object Scanners {
12661272 putChar(ch) ; nextRawChar()
12671273 loopRest()
12681274 else
1275+ next.lineOffset = if next.lastOffset < lineStartOffset then lineStartOffset else - 1
12691276 finishNamedToken(IDENTIFIER , target = next)
12701277 end loopRest
12711278 setStrVal()
@@ -1312,10 +1319,10 @@ object Scanners {
13121319 }
13131320 end getStringPart
13141321
1315- private def fetchStringPart (multiLine : Boolean ) = {
1322+ private def fetchStringPart (multiLine : Boolean ) =
13161323 offset = charOffset - 1
1324+ lineOffset = if lastOffset < lineStartOffset then lineStartOffset else - 1
13171325 getStringPart(multiLine)
1318- }
13191326
13201327 private def isTripleQuote (): Boolean =
13211328 if (ch == '"' ) {
@@ -1657,21 +1664,31 @@ object Scanners {
16571664 case Run (ch : Char , n : Int )
16581665 case Conc (l : IndentWidth , r : Run )
16591666
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- }
1667+ def <= (that : IndentWidth ): Boolean = (this , that) match
1668+ case (Run (ch1, n1), Run (ch2, n2)) => n1 <= n2 && (ch1 == ch2 || n1 == 0 )
1669+ case (Conc (l1, r1), Conc (l2, r2)) => (l1 == l2 && r1 <= r2) || this <= l2
1670+ case (_, Conc (l2, _)) => this <= l2
1671+ case _ => false
16721672
16731673 def < (that : IndentWidth ): Boolean = this <= that && ! (that <= this )
16741674
1675+ def >= (that : IndentWidth ): Boolean = that <= this
1676+
1677+ def > (that : IndentWidth ): Boolean = that < this
1678+
1679+ def size : Int = this match
1680+ case Run (_, n) => n
1681+ case Conc (l, r) => l.size + r.n
1682+
1683+ /** Add one level of indentation (one tab or two spaces depending on the last char) */
1684+ def increment : IndentWidth =
1685+ def incRun (ch : Char , n : Int ): Run = ch match
1686+ case ' ' => IndentWidth .Run (' ' , n + 2 )
1687+ case ch => IndentWidth .Run (ch, n + 1 )
1688+ this match
1689+ case Run (ch, n) => incRun(ch, n)
1690+ case Conc (l, Run (ch, n)) => Conc (l, incRun(ch, n))
1691+
16751692 /** Does `this` differ from `that` by not more than a single space? */
16761693 def isClose (that : IndentWidth ): Boolean = this match
16771694 case Run (ch1, n1) =>
0 commit comments