From e5788a0ff939484c2dbaec854036746cb7f7dcf8 Mon Sep 17 00:00:00 2001 From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com> Date: Sun, 22 Jan 2023 06:26:47 -0800 Subject: [PATCH] FormatOps: handle fewer braces and refined types --- .../org/scalafmt/internal/FormatOps.scala | 30 +++++++++++++++ .../scala/org/scalafmt/internal/Router.scala | 29 ++++++++++---- .../test/resources/scala3/OptionalBraces.stat | 32 ++++++++-------- .../resources/scala3/OptionalBraces_fold.stat | 38 +++++++++---------- .../resources/scala3/OptionalBraces_keep.stat | 26 ++++++------- .../scala3/OptionalBraces_unfold.stat | 35 ++++++++--------- 6 files changed, 115 insertions(+), 75 deletions(-) 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 fd8c4de6e1..04a54fb3b3 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 @@ -2037,6 +2037,36 @@ class FormatOps( def splits = Some(getSplits(ft, t, forceNL = true)) def rightBrace = if (isSeqMulti(t.stats)) treeLast(t) else None }) + case t: Type.Refine => + Some(new OptionalBracesRegion { + def owner = Some(t) + def splits = Some(getSplits(ft, t, forceNL = true)) + def rightBrace = treeLast(t) + }) + case t: Term.ArgClause if tokens.getHead(t) eq ft => + def funcSplit(arg: Term.FunctionTerm)(implicit fl: FileLine) = { + val end = tokens.getLast(arg) + val opt = getOptimalTokenFor(getFuncArrow(arg).getOrElse(end)) + val indent = Num(style.indent.getSignificant - style.indent.main) + Split(Space, 0) + .withSingleLine(opt) + .andPolicy(decideNewlinesOnlyAfterToken(opt)) + .withIndent(indent, end.left, ExpiresOn.After) + } + Some(new OptionalBracesRegion { + def owner = t.parent + def splits = Some(t.values match { + case (tf: Term.FunctionTerm) :: Nil + if !style.newlines.alwaysBeforeCurlyLambdaParams && + t.parent.exists(_.is[Term.Apply]) => + getSplits(ft, t, forceNL = false) match { + case s +: rs if !s.isNL => funcSplit(tf)(s.fileLine) +: rs + case ss => ss + } + case _ => getSplits(ft, t, forceNL = true) + }) + def rightBrace = treeLast(t) + }) case _ => None } } 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 cf869218af..3acfe5fd83 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 @@ -1844,13 +1844,28 @@ class Router(formatOps: FormatOps) { } case Newlines.fold => - val end = - nextSelect.fold(expire)(x => getLastNonTrivialToken(x.qual)) - def exclude = insideBracesBlock(t, end, true) - Seq( - Split(NoSplit, 0).withSingleLine(end, exclude), - Split(NewlineT(alt = Some(NoSplit)), 1) - ) + nextSelect match { + case Some(ns) if (ns.qual match { + case p: Term.Apply if dialect.allowFewerBraces => + p.argClause.tokens.head.is[T.Colon] + case p: Term.Match => + !tokenBefore(p.cases).left.is[T.LeftBrace] + case _ => false + }) => + val nextDot = tokenBefore(ns.nameToken).left + Seq( + Split(Newline, 0) + .withPolicy(decideNewlinesOnlyBeforeClose(nextDot)) + ) + case _ => + val end = + nextSelect.fold(expire)(x => getLastNonTrivialToken(x.qual)) + def exclude = insideBracesBlock(t, end, true) + Seq( + Split(NoSplit, 0).withSingleLine(end, exclude), + Split(NewlineT(alt = Some(NoSplit)), 1) + ) + } } val delayedBreakPolicyOpt = nextSelect.map { selectLike => diff --git a/scalafmt-tests/src/test/resources/scala3/OptionalBraces.stat b/scalafmt-tests/src/test/resources/scala3/OptionalBraces.stat index 47d30fd7d8..bfd8ae1620 100644 --- a/scalafmt-tests/src/test/resources/scala3/OptionalBraces.stat +++ b/scalafmt-tests/src/test/resources/scala3/OptionalBraces.stat @@ -3606,12 +3606,10 @@ object a: List(1, 2, 3).foldLeft(1): case (a, b) => a + b >>> -test does not parse object a: def f(): Unit = List(1, 2, 3).foldLeft(1): - case (a, b) => a + b - ^ + case (a, b) => a + b <<< coloneol in fewer braces 2 maxColumn = 80 === @@ -3622,12 +3620,13 @@ object a: .apply: 12 + 3 >>> -test does not parse object a: def f(): Unit = List(1, 2, 3) - .foo: a => a + 2 - ^ + .foo: a => + a + 2 + .apply: + 12 + 3 <<< coloneol in fewer braces 3 maxColumn = 80 === @@ -3638,12 +3637,12 @@ object a: case _ => otherTerm() >>> -test does not parse object a: def f(): Unit = List(1, 2, 3).foo: - case a: Int => - ^ + case a: Int => + case _ => + otherTerm() <<< match with eol maxColumn = 80 === @@ -3654,10 +3653,12 @@ object a: .apply: 12 + 3 >>> -UNABLE TO FORMAT, -tok=2∙.[77:86] +object a: + def f(): Unit = + List(1, 2, 3).match case _ => a + 2 - ^ + .apply: + 12 + 3 <<< coloneol in refined types maxColumn = 80 === @@ -3665,10 +3666,9 @@ object a: type T = String: type U = Int >>> -test does not parse object a: type T = String: - ^ + type U = Int <<< with in refined types maxColumn = 80 === @@ -3676,8 +3676,6 @@ object a: type T = String with type U = Int >>> -test does not parse object a: type T = String with - type U = Int - ^ + type U = Int diff --git a/scalafmt-tests/src/test/resources/scala3/OptionalBraces_fold.stat b/scalafmt-tests/src/test/resources/scala3/OptionalBraces_fold.stat index ce8e5771a8..89cbe9c8a2 100644 --- a/scalafmt-tests/src/test/resources/scala3/OptionalBraces_fold.stat +++ b/scalafmt-tests/src/test/resources/scala3/OptionalBraces_fold.stat @@ -3432,9 +3432,8 @@ object a: case (a, b) => a + b >>> object a: - def f(): Unit = List(1, 2, 3) - .foldLeft(1): - case (a, b) => a + b + def f(): Unit = List(1, 2, 3).foldLeft(1): + case (a, b) => a + b <<< coloneol in fewer braces 2 maxColumn = 80 === @@ -3445,10 +3444,12 @@ object a: .apply: 12 + 3 >>> -test does not parse object a: - def f(): Unit = List(1, 2, 3).foo: a => a + 2.apply: 12 + 2 - ^ + def f(): Unit = List(1, 2, 3) + .foo: a => + a + 2 + .apply: + 12 + 3 <<< coloneol in fewer braces 3 maxColumn = 80 === @@ -3459,12 +3460,12 @@ object a: case _ => otherTerm() >>> -Idempotency violated object a: - def f(): Unit = List(1, 2, 3) - .foo: - case a: Int => - case _ => otherTerm() + def f(): Unit = + List(1, 2, 3).foo: + case a: Int => + case _ => + otherTerm() <<< match with eol maxColumn = 80 === @@ -3476,9 +3477,11 @@ object a: 12 + 3 >>> object a: - def f(): Unit = List(1, 2, 3).match - case _ => a + 2 - .apply: 12 + 3 + def f(): Unit = List(1, 2, 3) + .match + case _ => a + 2 + .apply: + 12 + 3 <<< coloneol in refined types maxColumn = 80 === @@ -3486,10 +3489,9 @@ object a: type T = String: type U = Int >>> -test does not parse object a: type T = String: - ^ + type U = Int <<< with in refined types maxColumn = 80 === @@ -3497,8 +3499,6 @@ object a: type T = String with type U = Int >>> -test does not parse object a: type T = String with - type U = Int - ^ + type U = Int diff --git a/scalafmt-tests/src/test/resources/scala3/OptionalBraces_keep.stat b/scalafmt-tests/src/test/resources/scala3/OptionalBraces_keep.stat index 64d3febe41..90ae55af50 100644 --- a/scalafmt-tests/src/test/resources/scala3/OptionalBraces_keep.stat +++ b/scalafmt-tests/src/test/resources/scala3/OptionalBraces_keep.stat @@ -3588,12 +3588,10 @@ object a: List(1, 2, 3).foldLeft(1): case (a, b) => a + b >>> -test does not parse object a: def f(): Unit = List(1, 2, 3).foldLeft(1): - case (a, b) => a + b - ^ + case (a, b) => a + b <<< coloneol in fewer braces 2 maxColumn = 80 === @@ -3604,12 +3602,12 @@ object a: .apply: 12 + 3 >>> -Idempotency violated object a: def f(): Unit = List(1, 2, 3).foo: a => - a + 2 - .apply: 12 + 2 + a + 2 + .apply: + 12 + 3 <<< coloneol in fewer braces 3 maxColumn = 80 === @@ -3620,12 +3618,12 @@ object a: case _ => otherTerm() >>> -test does not parse object a: def f(): Unit = List(1, 2, 3).foo: - case a: Int => - ^ + case a: Int => + case _ => + otherTerm() <<< match with eol maxColumn = 80 === @@ -3640,7 +3638,8 @@ object a: def f(): Unit = List(1, 2, 3).match case _ => a + 2 - .apply: 12 + 3 + .apply: + 12 + 3 <<< coloneol in refined types maxColumn = 80 === @@ -3648,10 +3647,9 @@ object a: type T = String: type U = Int >>> -test does not parse object a: type T = String: - ^ + type U = Int <<< with in refined types maxColumn = 80 === @@ -3659,8 +3657,6 @@ object a: type T = String with type U = Int >>> -test does not parse object a: type T = String with - type U = Int - ^ + type U = Int diff --git a/scalafmt-tests/src/test/resources/scala3/OptionalBraces_unfold.stat b/scalafmt-tests/src/test/resources/scala3/OptionalBraces_unfold.stat index 992d38d39e..6e35ca889b 100644 --- a/scalafmt-tests/src/test/resources/scala3/OptionalBraces_unfold.stat +++ b/scalafmt-tests/src/test/resources/scala3/OptionalBraces_unfold.stat @@ -3729,12 +3729,11 @@ object a: List(1, 2, 3).foldLeft(1): case (a, b) => a + b >>> -test does not parse object a: def f(): Unit = List(1, 2, 3).foldLeft(1): - case (a, b) => - ^ + case (a, b) => + a + b <<< coloneol in fewer braces 2 maxColumn = 80 === @@ -3745,10 +3744,12 @@ object a: .apply: 12 + 3 >>> -test does not parse object a: - def f(): Unit = List(1, 2, 3).foo: a => a + 2.apply: 12 + 2 - ^ + def f(): Unit = List(1, 2, 3) + .foo: a => + a + 2 + .apply: + 12 + 3 <<< coloneol in fewer braces 3 maxColumn = 80 === @@ -3759,12 +3760,12 @@ object a: case _ => otherTerm() >>> -test does not parse object a: def f(): Unit = List(1, 2, 3).foo: - case a: Int => - ^ + case a: Int => + case _ => + otherTerm() <<< match with eol maxColumn = 80 === @@ -3781,7 +3782,8 @@ object a: .match case _ => a + 2 - .apply: 12 + 3 + .apply: + 12 + 3 <<< coloneol in refined types maxColumn = 80 === @@ -3789,10 +3791,10 @@ object a: type T = String: type U = Int >>> -test does not parse object a: - type T = String: - ^ + type T = + String: + type U = Int <<< with in refined types maxColumn = 80 === @@ -3800,8 +3802,7 @@ object a: type T = String with type U = Int >>> -test does not parse object a: - type T = String with - type U = Int - ^ + type T = + String with + type U = Int