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 51a02d725d..abe156420a 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 @@ -1759,12 +1759,14 @@ class FormatOps( }) // Redundant () delims around case statements - def isCaseBodyEnclosedAsBlock(ft: FormatToken, caseStat: CaseTree)(implicit - beforeMultiline: Newlines.SourceHints, - ): Boolean = { + def getClosingIfCaseBodyEnclosedAsBlock( + postArrowFt: FormatToken, + caseStat: CaseTree, + )(implicit beforeMultiline: Newlines.SourceHints): Option[FormatToken] = { val body = caseStat.body - (ft.noBreak || beforeMultiline.ignoreSourceSplit) && - body.eq(ft.meta.rightOwner) && isBodyEnclosedAsBlock(body) + val ok = body.eq(postArrowFt.meta.rightOwner) && + (beforeMultiline.ignoreSourceSplit || postArrowFt.noBreak) + if (ok) getClosingIfBodyEnclosedAsBlock(body) else None } // Redundant () delims around body 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 4f6aaf2424..04a73210d1 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 @@ -543,7 +543,8 @@ class Router(formatOps: FormatOps) { ) implicit val beforeMultiline = style.newlines.getBeforeMultiline if ( - isCaseBodyABlock(nft, owner) || isCaseBodyEnclosedAsBlock(nft, owner) + isCaseBodyABlock(nft, owner) || + getClosingIfCaseBodyEnclosedAsBlock(nft, owner).isDefined ) Seq(baseSplit) else if (nft.right.is[T.KwCase]) Seq(nlSplit(nft)(0)) else if (hasBreak() && !beforeMultiline.ignoreSourceSplit) @@ -2185,29 +2186,30 @@ class Router(formatOps: FormatOps) { if (bodyBlock || (arrowFt ne postArrowFt) && postArrowFt.hasBreak) NoPolicy else { + // postArrowFt points to non-comment after arrowFt + // possibly on next line without intervening comments implicit val beforeMultiline = style.newlines.getBeforeMultiline - if (!isCaseBodyEnclosedAsBlock(postArrowFt, owner)) + getClosingIfCaseBodyEnclosedAsBlock(postArrowFt, owner).fold { Policy ? beforeMultiline.in(Newlines.fold, Newlines.keep) || defaultPolicy - else { - val postParenFt = nextNonCommentSameLineAfter(postArrowFt) - val lparen = postParenFt.left - val rparen = matching(lparen) - if (postParenFt.right.start >= rparen.start) defaultPolicy - else { - val indent = style.indent.main - val lindents = Seq( - Indent(indent, rparen, Before), - Indent(-indent, expire, After), - ) - val lmod = NewlineT(noIndent = rhsIsCommentedOut(postParenFt)) - val lsplit = Seq(Split(lmod, 0).withIndents(lindents)) - val rsplit = Seq(Split(Newline, 0)) - Policy.after(lparen, prefix = "CASE[(]") { - case d: Decision if d.formatToken eq postParenFt => lsplit - } ==> Policy.on(rparen, prefix = "CASE[)]") { - case d: Decision if d.formatToken.right eq rparen => rsplit - } + } { rparenFt => + val lparentFt = next(postArrowFt) + val postParenFt = nextNonCommentSameLine(lparentFt) + val rparen = rparenFt.right + val indent = style.indent.main + val lindents = Seq( + Indent(indent, rparen, Before), + Indent(-indent, expire, After), + ) + val lmod = NewlineT(noIndent = rhsIsCommentedOut(postParenFt)) + val lsplit = Seq(Split(lmod, 0).withIndents(lindents)) + val rsplit = Seq(Split(Newline, 0)) + Policy.after(postParenFt.left, prefix = "CASE[(]") { + case Decision(`postParenFt`, _) => lsplit + // fires only if there's a comment between lparentFt and postParentFt + case Decision(`lparentFt`, _) => Seq(Split(Space, 0)) + } ==> Policy.on(rparen, prefix = "CASE[)]") { + case Decision(`rparenFt`, _) => rsplit } } } diff --git a/scalafmt-tests/src/test/resources/newlines/source_classic.stat b/scalafmt-tests/src/test/resources/newlines/source_classic.stat index 10e9871ae5..b60999cd49 100644 --- a/scalafmt-tests/src/test/resources/newlines/source_classic.stat +++ b/scalafmt-tests/src/test/resources/newlines/source_classic.stat @@ -9206,7 +9206,14 @@ object a { } } >>> -org.scalafmt.util.FormatException: java.util.NoSuchElementException: Missing matching token index Comment( c1a) [43..49) @2:18: `// c1a` +object a { + foo match { + case bar => ( // c1a + // c2a + qux + ) + } +} <<< #4133 case body enclosed, no break before lparen, break after lparen object a { foo match { diff --git a/scalafmt-tests/src/test/resources/newlines/source_fold.stat b/scalafmt-tests/src/test/resources/newlines/source_fold.stat index 34dff997b8..bc15149863 100644 --- a/scalafmt-tests/src/test/resources/newlines/source_fold.stat +++ b/scalafmt-tests/src/test/resources/newlines/source_fold.stat @@ -8578,7 +8578,14 @@ object a { } } >>> -org.scalafmt.util.FormatException: java.util.NoSuchElementException: Missing matching token index Comment( c1a) [43..49) @2:18: `// c1a` +object a { + foo match { + case bar => ( // c1a + // c2a + qux + ) + } +} <<< #4133 case body enclosed, no break before lparen, break after lparen object a { foo match { @@ -8610,7 +8617,14 @@ object a { } } >>> -org.scalafmt.util.FormatException: java.util.NoSuchElementException: Missing matching token index Comment( c1a) [49..55) @3:8: `// c1a` +object a { + foo match { + case bar => ( // c1a + // c2a + qux + ) + } +} <<< #4133 case body enclosed, break before lparen, comment after arrow object a { foo match { diff --git a/scalafmt-tests/src/test/resources/newlines/source_keep.stat b/scalafmt-tests/src/test/resources/newlines/source_keep.stat index cf16c3a2ac..2ac16375a1 100644 --- a/scalafmt-tests/src/test/resources/newlines/source_keep.stat +++ b/scalafmt-tests/src/test/resources/newlines/source_keep.stat @@ -9004,7 +9004,14 @@ object a { } } >>> -org.scalafmt.util.FormatException: java.util.NoSuchElementException: Missing matching token index Comment( c1a) [43..49) @2:18: `// c1a` +object a { + foo match { + case bar => ( // c1a + // c2a + qux + ) + } +} <<< #4133 case body enclosed, no break before lparen, break after lparen object a { foo match { diff --git a/scalafmt-tests/src/test/resources/newlines/source_unfold.stat b/scalafmt-tests/src/test/resources/newlines/source_unfold.stat index 58edcba457..c501132168 100644 --- a/scalafmt-tests/src/test/resources/newlines/source_unfold.stat +++ b/scalafmt-tests/src/test/resources/newlines/source_unfold.stat @@ -9302,7 +9302,14 @@ object a { } } >>> -org.scalafmt.util.FormatException: java.util.NoSuchElementException: Missing matching token index Comment( c1a) [43..49) @2:18: `// c1a` +object a { + foo match { + case bar => ( // c1a + // c2a + qux + ) + } +} <<< #4133 case body enclosed, no break before lparen, break after lparen object a { foo match { @@ -9334,7 +9341,14 @@ object a { } } >>> -org.scalafmt.util.FormatException: java.util.NoSuchElementException: Missing matching token index Comment( c1a) [49..55) @3:8: `// c1a` +object a { + foo match { + case bar => ( // c1a + // c2a + qux + ) + } +} <<< #4133 case body enclosed, break before lparen, comment after arrow object a { foo match {