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 f6d4193896..ef4772cc43 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 @@ -620,6 +620,84 @@ class Router(formatOps: FormatOps) { ) withSlbSplit else getFolded(beforeMultiline eq Newlines.keep) } + + // Given decl + case FT(_: T.KwGiven, _, FT.LeftOwner(gvn: Stat.GivenLike)) => + if (style.newlines.unfold && gvn.paramClauseGroups.nonEmpty) { + val nonSlbPolicy = gvn.paramClauseGroups.flatMap { pcg => + if (pcg.tparamClause.values.isEmpty) pcg.paramClauses + else pcg.tparamClause +: pcg.paramClauses + }.foldLeft(Policy.noPolicy) { case (policy, pc) => + val pcLast = getLast(pc) + val pcPolicy = Policy ? pcLast.left.is[T.RightArrow] && + decideNewlinesOnlyAfterToken(nextNonCommentSameLine(pcLast)) + policy ==> pcPolicy + } + if (nonSlbPolicy.isEmpty) Seq(Split(Space, 0)) + else Seq( + Split(Space, 0) + .withSingleLine(getSlbEndOnLeft(getLast(gvn.paramClauseGroups.last))), + Split(Space, 1, policy = nonSlbPolicy), + ) + } else Seq(Split(Space, 0)) + + // Given conditional arrow + case FT( + left: T.RightArrow, + _, + FT.LeftOwnerParent( + pcg: Member.ParamClauseGroup, + Some(gvn: Stat.GivenLike), + ), + ) => + val nlOnly = !style.newlines.sourceIgnored && hasBreak() + def spaceSplit(implicit fl: FileLine) = Split(nlOnly, 0)(Space) + val nextParamClause = pcg.paramClauses.find(_.pos.start > left.start) + .orElse(gvn.paramClauseGroups.dropWhile(_ ne pcg) match { + case `pcg` :: pcgNext :: _ => + val tpc = pcgNext.tparamClause + if (tpc.nonEmpty) Some(tpc) else pcgNext.paramClauses.headOption + case _ => None + }) + nextParamClause.fold { + gvn match { + case gvn: Defn.Given => binPackParentConstructorSplits( + isFirstCtor = true, + owners = Set(gvn.templ), + rhs = gvn.templ.inits.headOption, + lastFt = templateDerivesOrCurlyOrLastNonTrivial(gvn.templ), + indentLen = style.indent.extendSite, + extendsThenWith = gvn.templ.inits.lengthCompare(1) > 0, + ) + case _ => + val end = gvn match { + case gvn: Tree.WithBody => tokenBefore(gvn.body) + case _ => getLast(gvn) + } + val noSlb = gvn match { + case gvn: Tree.WithDeclTpe => gvn.decltpe match { + case t: Type.Tuple => t.args.lengthCompare(1) > 0 + case _ => false + } + case _ => false + } + Seq( + spaceSplit.withSingleLine(getSlbEndOnLeft(end), ignore = noSlb), + Split(Newline, 1) + .withIndent(style.indent.main, end, ExpiresOn.After), + ) + } + } { npc => + val nextArrow = + getSlbEndOnLeft(nextAfterNonCommentSameLine(getLast(npc))) + val noSlb = npc.values.lengthCompare(1) != 0 + Seq( + spaceSplit.withSingleLine(nextArrow, ignore = noSlb), + Split(Newline, 1) + .withIndent(style.indent.main, nextArrow, ExpiresOn.After), + ) + } + // New statement case FT(_: T.Semicolon, _, StartsStatementRight(stmt)) if !stmt.is[Term.EndMarker] => diff --git a/scalafmt-tests/shared/src/test/resources/scala3/OptionalBraces.stat b/scalafmt-tests/shared/src/test/resources/scala3/OptionalBraces.stat index 0c111b5555..632b83f9ba 100644 --- a/scalafmt-tests/shared/src/test/resources/scala3/OptionalBraces.stat +++ b/scalafmt-tests/shared/src/test/resources/scala3/OptionalBraces.stat @@ -7965,3 +7965,28 @@ lazy val onlyImplicitOrTypeParams = paramss.forall( sym.isType || sym.is(Implicit) || sym.is(Given) } ) +<<< scala-3.6 given 1 +maxColumn = 40 +=== +given [A] => + Seq[A] = foo +>>> +given [A] => + Seq[A] = foo +<<< scala-3.6 given 2 +maxColumn = 40 +=== +given [A] => (Seq[A] => + List[A]) => List[A] = foo +>>> +given [A] => (Seq[A] => List[A]) => + List[A] = foo +<<< scala-3.6 given 3 +maxColumn = 40 +=== +given foo: (a: A) => + [B, C <: AnyRef] => (b: B[A], C) => List[A] = foo +>>> +given foo: (a: A) => + [B, C <: AnyRef] => (b: B[A], C) => + List[A] = foo diff --git a/scalafmt-tests/shared/src/test/resources/scala3/OptionalBraces_fold.stat b/scalafmt-tests/shared/src/test/resources/scala3/OptionalBraces_fold.stat index 331565af7d..5f95f2840c 100644 --- a/scalafmt-tests/shared/src/test/resources/scala3/OptionalBraces_fold.stat +++ b/scalafmt-tests/shared/src/test/resources/scala3/OptionalBraces_fold.stat @@ -7645,3 +7645,26 @@ lazy val onlyImplicitOrTypeParams = paramss.forall( lazy val onlyImplicitOrTypeParams = paramss.forall(_.exists { sym => sym.isType || sym.is(Implicit) || sym.is(Given) }) +<<< scala-3.6 given 1 +maxColumn = 40 +=== +given [A] => + Seq[A] = foo +>>> +given [A] => Seq[A] = foo +<<< scala-3.6 given 2 +maxColumn = 40 +=== +given [A] => (Seq[A] => + List[A]) => List[A] = foo +>>> +given [A] => (Seq[A] => List[A]) => + List[A] = foo +<<< scala-3.6 given 3 +maxColumn = 40 +=== +given foo: (a: A) => + [B, C <: AnyRef] => (b: B[A], C) => List[A] = foo +>>> +given foo: (a: A) => [B, C <: AnyRef] => + (b: B[A], C) => List[A] = foo diff --git a/scalafmt-tests/shared/src/test/resources/scala3/OptionalBraces_keep.stat b/scalafmt-tests/shared/src/test/resources/scala3/OptionalBraces_keep.stat index e37ab1fc3d..8e638ec443 100644 --- a/scalafmt-tests/shared/src/test/resources/scala3/OptionalBraces_keep.stat +++ b/scalafmt-tests/shared/src/test/resources/scala3/OptionalBraces_keep.stat @@ -7988,3 +7988,28 @@ lazy val onlyImplicitOrTypeParams = paramss.forall( sym.isType || sym.is(Implicit) || sym.is(Given) } ) +<<< scala-3.6 given 1 +maxColumn = 40 +=== +given [A] => + Seq[A] = foo +>>> +given [A] => + Seq[A] = foo +<<< scala-3.6 given 2 +maxColumn = 40 +=== +given [A] => (Seq[A] => + List[A]) => List[A] = foo +>>> +given [A] => (Seq[A] => List[A]) => + List[A] = foo +<<< scala-3.6 given 3 +maxColumn = 40 +=== +given foo: (a: A) => + [B, C <: AnyRef] => (b: B[A], C) => List[A] = foo +>>> +given foo: (a: A) => + [B, C <: AnyRef] => (b: B[A], C) => + List[A] = foo diff --git a/scalafmt-tests/shared/src/test/resources/scala3/OptionalBraces_unfold.stat b/scalafmt-tests/shared/src/test/resources/scala3/OptionalBraces_unfold.stat index 9c62482900..68aa462713 100644 --- a/scalafmt-tests/shared/src/test/resources/scala3/OptionalBraces_unfold.stat +++ b/scalafmt-tests/shared/src/test/resources/scala3/OptionalBraces_unfold.stat @@ -8286,3 +8286,26 @@ lazy val onlyImplicitOrTypeParams = paramss.forall( sym.isType || sym.is(Implicit) || sym.is(Given) } ) +<<< scala-3.6 given 1 +maxColumn = 40 +=== +given [A] => + Seq[A] = foo +>>> +given [A] => Seq[A] = foo +<<< scala-3.6 given 2 +maxColumn = 40 +=== +given [A] => (Seq[A] => + List[A]) => List[A] = foo +>>> +given [A] => (Seq[A] => List[A]) => + List[A] = foo +<<< scala-3.6 given 3 +maxColumn = 40 +=== +given foo: (a: A) => + [B, C <: AnyRef] => (b: B[A], C) => List[A] = foo +>>> +given foo: (a: A) => [B, C <: AnyRef] => + (b: B[A], C) => List[A] = foo diff --git a/scalafmt-tests/shared/src/test/scala/org/scalafmt/FormatTests.scala b/scalafmt-tests/shared/src/test/scala/org/scalafmt/FormatTests.scala index 65183fa31f..c92af9be18 100644 --- a/scalafmt-tests/shared/src/test/scala/org/scalafmt/FormatTests.scala +++ b/scalafmt-tests/shared/src/test/scala/org/scalafmt/FormatTests.scala @@ -144,7 +144,7 @@ class FormatTests extends FunSuite with CanRunTests with FormatAssertions { val explored = Debug.explored.get() logger.debug(s"Total explored: $explored") if (!onlyUnit && !onlyManual) - assertEquals(explored, 1208582, "total explored") + assertEquals(explored, 1209508, "total explored") val results = debugResults.result() // TODO(olafur) don't block printing out test results. // I don't want to deal with scalaz's Tasks :'(