@@ -219,6 +219,9 @@ object Scanners {
219219 class Scanner (source : SourceFile , override val startFrom : Offset = 0 )(implicit ctx : Context ) extends ScannerCommon (source)(ctx) {
220220 val keepComments : Boolean = ! ctx.settings.YdropComments .value
221221
222+ /** A switch whether operators at the start of lines can be infix operators */
223+ private var allowLeadingInfixOperators = true
224+
222225 /** All doc comments kept by their end position in a `Map` */
223226 private [this ] var docstringMap : SortedMap [Int , Comment ] = SortedMap .empty
224227
@@ -265,12 +268,12 @@ object Scanners {
265268 else IDENTIFIER
266269 }
267270
268- private class TokenData0 extends TokenData
271+ def newTokenData : TokenData = new TokenData {}
269272
270273 /** We need one token lookahead and one token history
271274 */
272- val next : TokenData = new TokenData0
273- private val prev : TokenData = new TokenData0
275+ val next = newTokenData
276+ private val prev = newTokenData
274277
275278 /** a stack of tokens which indicates whether line-ends can be statement separators
276279 * also used for keeping track of nesting levels.
@@ -378,6 +381,30 @@ object Scanners {
378381 next.token = EMPTY
379382 }
380383
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+
381408 /** Insert NEWLINE or NEWLINES if
382409 * - we are after a newline
383410 * - we are within a { ... } or on toplevel (wrt sepRegions)
@@ -389,10 +416,15 @@ object Scanners {
389416 (canStartStatTokens contains token) &&
390417 (sepRegions.isEmpty || sepRegions.head == RBRACE ||
391418 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 || oldSyntax)
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)))
396428 }
397429
398430 postProcessToken()
@@ -1087,8 +1119,6 @@ object Scanners {
10871119 case _ => showToken(token)
10881120 }
10891121
1090- // (does not seem to be needed) def flush = { charOffset = offset; nextChar(); this }
1091-
10921122 /* Resume normal scanning after XML */
10931123 def resume (lastToken : Token ): Unit = {
10941124 token = lastToken
0 commit comments