@@ -219,6 +219,9 @@ object Scanners {
219
219
class Scanner (source : SourceFile , override val startFrom : Offset = 0 )(implicit ctx : Context ) extends ScannerCommon (source)(ctx) {
220
220
val keepComments : Boolean = ! ctx.settings.YdropComments .value
221
221
222
+ /** A switch whether operators at the start of lines can be infix operators */
223
+ private var allowLeadingInfixOperators = true
224
+
222
225
/** All doc comments kept by their end position in a `Map` */
223
226
private [this ] var docstringMap : SortedMap [Int , Comment ] = SortedMap .empty
224
227
@@ -265,12 +268,12 @@ object Scanners {
265
268
else IDENTIFIER
266
269
}
267
270
268
- private class TokenData0 extends TokenData
271
+ def newTokenData : TokenData = new TokenData {}
269
272
270
273
/** We need one token lookahead and one token history
271
274
*/
272
- val next : TokenData = new TokenData0
273
- private val prev : TokenData = new TokenData0
275
+ val next = newTokenData
276
+ private val prev = newTokenData
274
277
275
278
/** a stack of tokens which indicates whether line-ends can be statement separators
276
279
* also used for keeping track of nesting levels.
@@ -378,6 +381,30 @@ object Scanners {
378
381
next.token = EMPTY
379
382
}
380
383
384
+ def insertNL (nl : Token ): Unit = {
385
+ next.copyFrom(this )
386
+ // todo: make offset line-end of previous line?
387
+ offset = if (lineStartOffset <= offset) lineStartOffset else lastLineStartOffset
388
+ token = nl
389
+ }
390
+
391
+
392
+ /** A leading symbolic or backquoted identifier is treated as an infix operator
393
+ * if it is followed by at least one ' ' and a token on the same line
394
+ * that can start an expression.
395
+ */
396
+ def isLeadingInfixOperator =
397
+ allowLeadingInfixOperators &&
398
+ (token == BACKQUOTED_IDENT ||
399
+ token == IDENTIFIER && isOperatorPart(name(name.length - 1 ))) &&
400
+ (ch == ' ' ) && {
401
+ val lookahead = lookaheadScanner
402
+ lookahead.allowLeadingInfixOperators = false
403
+ // force a NEWLINE a after current token if it is on its own line
404
+ lookahead.nextToken()
405
+ canStartExpressionTokens.contains(lookahead.token)
406
+ }
407
+
381
408
/** Insert NEWLINE or NEWLINES if
382
409
* - we are after a newline
383
410
* - we are within a { ... } or on toplevel (wrt sepRegions)
@@ -389,10 +416,15 @@ object Scanners {
389
416
(canStartStatTokens contains token) &&
390
417
(sepRegions.isEmpty || sepRegions.head == RBRACE ||
391
418
sepRegions.head == ARROW && token == CASE )) {
392
- next copyFrom this
393
- // todo: make offset line-end of previous line?
394
- offset = if (lineStartOffset <= offset) lineStartOffset else lastLineStartOffset
395
- token = if (pastBlankLine()) NEWLINES else NEWLINE
419
+ if (pastBlankLine())
420
+ insertNL(NEWLINES )
421
+ else if (! isLeadingInfixOperator)
422
+ insertNL(NEWLINE )
423
+ else if (isScala2Mode)
424
+ ctx.warning(em """ Line starts with an operator;
425
+ |it is now treated as a continuation of the expression on the previous line,
426
+ |not as a separate statement. """ ,
427
+ source.atSpan(Span (offset)))
396
428
}
397
429
398
430
postProcessToken()
@@ -1087,8 +1119,6 @@ object Scanners {
1087
1119
case _ => showToken(token)
1088
1120
}
1089
1121
1090
- // (does not seem to be needed) def flush = { charOffset = offset; nextChar(); this }
1091
-
1092
1122
/* Resume normal scanning after XML */
1093
1123
def resume (lastToken : Token ): Unit = {
1094
1124
token = lastToken
0 commit comments