diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala index 93e49d6de5..c78d572d92 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala @@ -149,7 +149,7 @@ class FormatOps( case c: T.Comment if start.noBreak && (!start.left.is[T.LeftParen] || - tokens.isBreakAfterRight(start)) => Some(c) + tokens.hasBreakAfterRightBeforeNonComment(start)) => Some(c) case _ => Some(start.left) } }.fold(_.right, identity) @@ -163,8 +163,9 @@ class FormatOps( case _: T.Comma | _: T.LeftParen | _: T.Semicolon | _: T.RightArrow | _: T.Equals => None case _: T.RightParen if start.left.is[T.LeftParen] => None - case c: T.Comment if start.noBreak && tokens.isBreakAfterRight(start) => - Some(c) + case c: T.Comment + if start.noBreak && + tokens.hasBreakAfterRightBeforeNonComment(start) => Some(c) case _ if !style.newlines.formatInfix && start.noBreak && isInfix => None case _ => Some(start.left) } @@ -1240,7 +1241,7 @@ class FormatOps( def isDone(x: FormatToken) = x.hasBlankLine || x.right.end >= end @tailrec def iter(x: FormatToken): FormatToken = { - val nft = tokens.nextNonCommentSameLine(next(x)) + val nft = tokens.nextNonCommentSameLineAfter(x) if (isDone(nft)) nft else if (!nft.right.is[T.Comment]) ft // original else iter(nft) @@ -1266,7 +1267,7 @@ class FormatOps( case _: T.Comment => val isDetachedSlc = ft.hasBreak && tokens.isBreakAfterRight(ft) if (isDetachedSlc || ft.rightHasNewline) null else Space - case _ => Space(style.spaces.inParentheses && spaceOk) + case _ => Space(spaceOk && style.spaces.inParentheses) } // look for arrow before body, if any, else after params @@ -1636,7 +1637,7 @@ class FormatOps( if (!ft.right.is[T.Comment]) splitsFunc(ft) else if (ft.hasBreak) Seq(nlSplitFunc(0).forThisLine) else { - val nextFt = nextNonCommentSameLine(next(ft)) + val nextFt = tokens.nextNonCommentSameLineAfter(ft) val splits = if (nextFt.noBreak) splitsFunc(nextFt) else { diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatTokens.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatTokens.scala index b6df9dbd93..d33ef3db58 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatTokens.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatTokens.scala @@ -170,6 +170,9 @@ class FormatTokens(leftTok2tok: Map[TokenHash, Int])(val arr: Array[FormatToken] final def nextNonCommentSameLine(curr: FormatToken): FormatToken = findToken(curr, next)(ft => ft.hasBreak || !ft.right.is[Token.Comment]) + final def nextNonCommentSameLineAfter(curr: FormatToken): FormatToken = + nextNonCommentSameLine(next(curr)) + final def nextNonComment(curr: FormatToken): FormatToken = findToken(curr, next)(!_.right.is[Token.Comment]) @@ -288,13 +291,21 @@ class FormatTokens(leftTok2tok: Map[TokenHash, Int])(val arr: Array[FormatToken] @inline def isBreakAfterRight(ft: FormatToken): Boolean = next(ft).hasBreakOrEOF + @inline + def hasBreakAfterRightBeforeNonComment(ft: FormatToken): Boolean = + nextNonCommentSameLineAfter(ft).hasBreakOrEOF + + @inline + def hasBreakBeforeNonComment(ft: FormatToken): Boolean = ft.hasBreak || + hasBreakAfterRightBeforeNonComment(ft) + @inline def isRightCommentThenBreak(ft: FormatToken): Boolean = ft.right - .is[Token.Comment] && isBreakAfterRight(ft) + .is[Token.Comment] && hasBreakAfterRightBeforeNonComment(ft) @inline - def isRightLikeSingleLineComment(ft: FormatToken): Boolean = - isRightCommentThenBreak(ft) && !ft.rightHasNewline + def isRightCommentWithBreak(ft: FormatToken): Boolean = ft.right + .is[Token.Comment] && hasBreakBeforeNonComment(ft) @inline def isAttachedCommentThenBreak(ft: FormatToken): Boolean = ft.noBreak && diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala index a40a82b813..b6ed2f7d49 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala @@ -1869,7 +1869,8 @@ object FormatWriter { case x => x } - val slc = tokens.isRightLikeSingleLineComment(ft) + val slc = ft.right.is[T.Comment] && tokens.isBreakAfterRight(ft) && + !ft.rightHasNewline val code = if (slc) "//" else ft.meta.right.text floc.style.alignMap.get(code).flatMap { matchers => // Corner case when line ends with comment diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala index b569029b24..9ccc187e6d 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala @@ -1149,8 +1149,7 @@ class Router(formatOps: FormatOps) { val rightIsComment = right.is[T.Comment] val mustUseNL = onlyConfigStyle || style.newlines.keepBreak(newlines) || - rightIsComment && - (newlines != 0 || nextNonCommentSameLine(next(ft)).hasBreak) + tokens.isRightCommentWithBreak(ft) val noSplitModification = if (rightIsComment && !mustUseNL) getMod(ft) else baseNoSplitMod val nlMod = if (rightIsComment && mustUseNL) getMod(ft) else Newline @@ -1192,8 +1191,7 @@ class Router(formatOps: FormatOps) { val nlOnly = flags.dangleForTrailingCommas || flags.configStyle != ConfigStyle.None || style.newlines.keepBreak(newlines) || scalaJsStyleNL || - rightIsComment && - (newlines != 0 || nextNonCommentSameLine(next(ft)).hasBreak) + tokens.isRightCommentWithBreak(ft) def findComma(ft: FormatToken) = findFirstOnRight[T.Comma](ft, close) @@ -1955,7 +1953,7 @@ class Router(formatOps: FormatOps) { if (style.newlines.getBeforeMultiline eq Newlines.unfold) CtrlBodySplits .checkComment(ft, nlSplitFunc) { ft => if (ft.right.is[T.LeftBrace]) { - val nextFt = nextNonCommentSameLine(next(ft)) + val nextFt = tokens.nextNonCommentSameLineAfter(ft) val policy = decideNewlinesOnlyAfterToken(nextFt.left) Seq(Split(Space, 0, policy = policy)) } else Seq(nlSplitFunc(0)) @@ -1987,7 +1985,7 @@ class Router(formatOps: FormatOps) { case FormatToken(_, _: T.KwThen | _: T.KwDo, _) => if (style.newlines.sourceIgnored || noBreak()) Seq( Split(Space, 0) - .withOptimalToken(nextNonCommentSameLine(next(ft)).left), + .withOptimalToken(tokens.nextNonCommentSameLineAfter(ft).left), Split(Newline, 1), ) else Seq(Split(Newline, 0)) @@ -2120,7 +2118,7 @@ class Router(formatOps: FormatOps) { val policy = if (bodyBlock || tokens.isAttachedCommentThenBreak(arrowFt)) NoPolicy else if (isCaseBodyEnclosedAsBlock(postArrowFt, owner)) { - val postParenFt = nextNonCommentSameLine(next(postArrowFt)) + val postParenFt = tokens.nextNonCommentSameLineAfter(postArrowFt) val lparen = postParenFt.left val rparen = matching(lparen) if (postParenFt.right.start >= rparen.start) defaultPolicy @@ -2162,7 +2160,7 @@ class Router(formatOps: FormatOps) { if (style.newlines.keepBreak(newlines)) Seq(Split(Newline, 0)) else { val arrow = getCaseArrow(rightOwner.asInstanceOf[Case]).left - val afterIf = nextNonCommentSameLine(next(ft)) + val afterIf = tokens.nextNonCommentSameLineAfter(ft) val noSplit = if (style.newlines.keepBreak(afterIf)) { val indent = Indent(style.indent.main, arrow, ExpiresOn.Before) @@ -2422,13 +2420,14 @@ class Router(formatOps: FormatOps) { } ft match { case FormatToken(_: T.BOF, _, _) => splits - case FormatToken(_, _: T.Comment, _) if tokens.isBreakAfterRight(ft) => + case FormatToken(_, _: T.Comment, _) if rhsIsCommentedOutIfComment(ft) => + splitsAsNewlines(splits.map(_.withNoIndent)) + case FormatToken(_: T.Comment, _, _) => + if (ft.noBreak) splits else splitsAsNewlines(splits) + case FormatToken(_, _: T.Comment, _) + if tokens.hasBreakAfterRightBeforeNonComment(ft) => if (ft.noBreak) splits.map(_.withMod(Space)) - else if (!rhsIsCommentedOut(ft)) splitsAsNewlines(splits) - else splitsAsNewlines(splits.map(_.withNoIndent)) - // Only newlines after inline comments. - case FormatToken(_: T.Comment, _, _) if ft.hasBreak => - splitsAsNewlines(splits) + else splitsAsNewlines(splits) case ft if ft.meta.formatOff && ft.hasBreak => splitsAsNewlines(splits) case FormatToken(_: T.Equals, r: T.KwMacro, _) if dialect.allowSignificantIndentation => @@ -2451,10 +2450,8 @@ class Router(formatOps: FormatOps) { def expire = getLastToken(body) if (ft.right.is[T.LeftBrace]) // The block will take care of indenting by 2 Seq(Split(Space, 0).withIndents(spaceIndents)) - else if ( - ft.right.is[T.Comment] && - (ft.hasBreak || nextNonCommentSameLine(next(ft)).hasBreak) - ) Seq(CtrlBodySplits.withIndent(Split(Space.orNL(ft.noBreak), 0), ft, body)) + else if (tokens.isRightCommentWithBreak(ft)) + Seq(CtrlBodySplits.withIndent(Split(Space.orNL(ft.noBreak), 0), ft, body)) else if (isJsNative(body)) Seq(Split(Space, 0).withSingleLine(expire)) else if (style.newlines.forceBeforeAssign(ft.meta.leftOwner)) Seq(CtrlBodySplits.withIndent(Split(Newline, 0), ft, body)) diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/rewrite/PreferCurlyFors.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/rewrite/PreferCurlyFors.scala index 0903c4376f..4cd4ef9a7c 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/rewrite/PreferCurlyFors.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/rewrite/PreferCurlyFors.scala @@ -74,10 +74,8 @@ private class PreferCurlyFors(implicit val ftoks: FormatTokens) else null case _: Token.Semicolon - if !style.rewrite.preferCurlyFors.removeTrailingSemicolonsOnly || { - val next = ftoks.next(ft) - next.hasBreak || ftoks.isRightCommentThenBreak(next) - } => + if !style.rewrite.preferCurlyFors.removeTrailingSemicolonsOnly || + ftoks.hasBreakAfterRightBeforeNonComment(ft) => ft.meta.rightOwner match { case _: Term.For | _: Term.ForYield => removeToken case _ => null diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/util/TokenOps.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/util/TokenOps.scala index e17f089d2c..33b3ed53c8 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/util/TokenOps.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/util/TokenOps.scala @@ -70,8 +70,13 @@ object TokenOps { def withNoIndent(ft: FormatToken): Boolean = ft.between.lastOption .exists(_.is[LF]) + @inline def rhsIsCommentedOut(ft: FormatToken): Boolean = ft.right.is[Comment] && - withNoIndent(ft) && isSingleLineIfComment(ft.right) + rhsIsCommentedOutIfComment(ft) + + @inline + def rhsIsCommentedOutIfComment(ft: FormatToken): Boolean = withNoIndent(ft) && + isSingleLineIfComment(ft.right) @inline def isLeftCommentThenBreak(ft: FormatToken): Boolean = ft.left diff --git a/scalafmt-tests/src/test/resources/scala3/Extension.stat b/scalafmt-tests/src/test/resources/scala3/Extension.stat index 069a9fa2df..781ca085f4 100644 --- a/scalafmt-tests/src/test/resources/scala3/Extension.stat +++ b/scalafmt-tests/src/test/resources/scala3/Extension.stat @@ -126,8 +126,9 @@ maxColumn = 40 extension [A](cat: Cat) /* new feature */ // or this def a = 1 >>> -extension [A](cat: Cat) - /* new feature */ // or this +extension [A]( + cat: Cat +) /* new feature */ // or this def a = 1 <<< extension comments with embedded NL maxColumn = 40 @@ -136,8 +137,7 @@ extension [A](cat: Cat) /* * new feature */ // or this def a = 1 >>> -extension [A](cat: Cat) - /* +extension [A](cat: Cat) /* * new feature */ // or this def a = 1 <<< extension comments brace