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 260be8e035..112b377956 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 @@ -246,8 +246,8 @@ class FormatOps( implicit fileLine: FileLine, ): Policy = Policy ? (comma.right.is[T.Comment] && comma.noBreak) || delayedBreakPolicy(Policy.End < comma, exclude) { - Policy.onRight(comma, prefix = "NL->A[,]") { case Decision(`comma`, ss) => - getOneArgPerLineSplitsAfterComma(comma.right, ss) + Policy.onlyFor(comma, prefix = "NL->A[,]") { + getOneArgPerLineSplitsAfterComma(comma.right, _) } } @@ -292,10 +292,9 @@ class FormatOps( getTemplateGroups(template).flatMap(iter) } - def getBreakBeforeElsePolicy(beforeElse: FT): Policy = Policy.End <= - beforeElse ==> Policy.onRight(beforeElse, prefix = "ELSE") { - case d @ Decision(`beforeElse`, _) => d - .onlyNewlinesWithFallback(Split(Newline, 0)) + def getBreakBeforeElsePolicy(beforeElse: FT): Policy = Policy + .onlyFor(beforeElse, prefix = "ELSE") { + Decision.onlyNewlinesWithFallback(_, Seq(Split(Newline, 0))) } def getBreakBeforeElsePolicy(term: Term.If): Policy = getElseToken(term) @@ -1620,8 +1619,7 @@ class FormatOps( val split = nlSplitFunc(0).forThisLine Seq(if (rhsIsCommentedOut(nextFt)) split.withNoIndent else split) } - val policy = Policy - .onRight(nextFt, "CBCMT") { case Decision(`nextFt`, _) => splits } + val policy = Policy.onlyFor(nextFt, "CBCMT")(_ => splits) Seq(Split(Space, 0, policy = policy)) } @@ -2731,10 +2729,8 @@ class FormatOps( def noNLPolicy(): Policy = { def close = next(ftBeforeClose) - if (scalaJsStyle) - Policy(Policy.End >= ftBeforeClose, prefix = "tuckSJS") { - case Decision(`ftBeforeClose`, ss) => Decision.noNewlineSplits(ss) - } + if (scalaJsStyle) Policy + .onlyFor(ftBeforeClose, prefix = "tuckSJS")(Decision.noNewlineSplits) else style.newlines.source match { case Newlines.keep if closeBreak => decideNewlinesOnlyBeforeClose(close) case Newlines.fold @@ -2877,10 +2873,9 @@ class FormatOps( findLastApplyAndNextSelect(x.tree, cfg.encloseSelectChains) Right(canStartSelectChain(x, nextSelect, expireTree)) case Newlines.keep => - Left(Policy.onRight(afterDelims, "BP1L.NL") { - case Decision(`afterDelims`, ss) => Decision - .onlyNewlineSplits(ss) - .map(_.preActivateFor(SplitTag.SelectChainBinPackNL)) + Left(Policy.onlyFor(afterDelims, "BP1L.NL") { + Decision.onlyNewlineSplits(_) + .map(_.preActivateFor(SplitTag.SelectChainBinPackNL)) }) case _ => Right(true) } diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Policy.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Policy.scala index 71caff3b3f..b944c0bd39 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Policy.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Policy.scala @@ -118,6 +118,13 @@ object Policy { )(f: Pf)(implicit fileLine: FileLine): Policy = apply(End > exp, prefix, noDequeue, rank)(f) + def onlyFor(on: FT, prefix: String, noDequeue: Boolean = false, rank: Int = 0)( + f: Seq[Split] => Seq[Split], + )(implicit fileLine: FileLine): Policy = Policy.End <= on ==> + Policy.onRight(on, s"$prefix[${on.idx}]", noDequeue, rank) { + case Decision(`on`, ss) => f(ss) + } + abstract class Clause(implicit val fileLine: FileLine) extends Policy { val endPolicy: End.WithPos def prefix: String 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 a149b854a9..ab3d811fbf 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 @@ -394,11 +394,9 @@ class Router(formatOps: FormatOps) { // copy logic from `( ...`, binpack=never, defining `slbSplit` val slbParensPolicy = Policy ? (slbMod eq noSplitMod) || { val beforeClose = prev(close) - Policy.End <= beforeClose ==> - Policy.onRight(beforeClose, "BracesToParens") { - case Decision(`beforeClose`, ss) => ss - .flatMap(s => if (s.isNL) None else Some(s.withMod(NoSplit))) - } + Policy.onlyFor(beforeClose, "BracesToParens") { + _.flatMap(s => if (s.isNL) None else Some(s.withMod(NoSplit))) + } } val exclude = slbParensExclude.getOrElse(TokenRanges.empty) val slbPolicy = @@ -411,15 +409,12 @@ class Router(formatOps: FormatOps) { val pend = getSlbEndOnLeft(getLast(p)) def pendSlb(s: Split) = s .withSingleLine(pend, noSyntaxNL = true, extend = true) - Policy.End <= close ==> - Policy.onRight(close, s"RB-ELSE[${pend.idx}]") { - case Decision(`close`, ss) => - if (ss.exists(_.isNL)) ss - .map(s => if (s.isNL) s else pendSlb(s)) - else ss.flatMap { s => - Seq(pendSlb(s), s.withMod(Newline).withPenalty(1)) - } + Policy.onlyFor(close, s"RB-ELSE[${pend.idx}]") { ss => + if (ss.exists(_.isNL)) ss.map(s => if (s.isNL) s else pendSlb(s)) + else ss.flatMap { s => + Seq(pendSlb(s), s.withMod(Newline).withPenalty(1)) } + } } else decideNewlinesOnlyAfterClose(close) } Split(slbMod, 0).withSingleLine( @@ -476,16 +471,16 @@ class Router(formatOps: FormatOps) { val (mod, policy) = singleLineSplitOpt match { case Some(slbSplit) if singleLineSplit.isIgnored => val arrSplit = slbSplit.withMod(Space) - slbMod -> Policy.onRight(lambdaExpire, s"FNARR($arrSplit)") { - case Decision(`lambdaExpire`, ss) => - var hadNoSplit = false - val nlSplits = ss.flatMap { s => - // penalize NL one extra, for closing brace - if (s.isNL) - Some(s.andPolicy(nlPolicy).withPenalty(nlArrowPenalty)) - else { hadNoSplit = true; None } - } - if (hadNoSplit) arrSplit +: nlSplits else nlSplits + val fnarrDesc = s"FNARR($nlArrowPenalty;$arrSplit)" + slbMod -> Policy.onlyFor(lambdaExpire, fnarrDesc) { ss => + var hadNoSplit = false + val nlSplits = ss.flatMap { s => + // penalize NL one extra, for closing brace + if (s.isNL) + Some(s.andPolicy(nlPolicy).withPenalty(nlArrowPenalty)) + else { hadNoSplit = true; None } + } + if (hadNoSplit) arrSplit +: nlSplits else nlSplits } case _ => noSplitMod -> (decideNewlinesOnlyAfterToken(lambdaExpire) ==> nlPolicy) @@ -905,12 +900,11 @@ class Router(formatOps: FormatOps) { ) val spacePolicy = SingleLineBlock(lambdaToken) ==> { - def before = Policy.End < close ==> Policy.onLeft(close, "NODANGLE") { - case Decision(`beforeClose`, _) => - val bc = beforeClose.left - if (bc.is[T.Comment]) - if (bc.text.startsWith("//")) Nil else Seq(Split(Space, 0)) - else Seq(Split(Space(style.spaces.inParentheses), 0)) + def before = Policy.onlyFor(beforeClose, "NODANGLE") { _ => + val bc = beforeClose.left + if (bc.is[T.Comment]) + if (bc.text.startsWith("//")) Nil else Seq(Split(Space, 0)) + else Seq(Split(Space(style.spaces.inParentheses), 0)) } Policy ? lambdaIsABlock || Policy.RelayOnSplit.by(Policy.End <= lambdaLeft.getOrElse(close))( @@ -1770,24 +1764,23 @@ class Router(formatOps: FormatOps) { def forcedBreakOnNextDotPolicy(implicit fileLine: FileLine) = beforeNextDotOpt.map(decideNewlinesOnlyAfterToken(_)) - def getClassicNonFirstBreakOnDot( - dot: FT, - )(implicit fileLine: FileLine): Policy = Policy.End <= dot ==> - Policy.onRight(dot, "NEXTSEL2NL") { case Decision(`dot`, s) => - val filtered = s.flatMap { x => - val y = x.activateFor(SplitTag.SelectChainSecondNL) - if (y.isActive) Some(y) else None - } - if (filtered.isEmpty) Seq.empty - else { - val minCost = math.max(0, filtered.map(_.costWithPenalty).min - 1) - filtered.map { x => - val p = x.policy.filter(!_.isInstanceOf[PenalizeAllNewlines]) - implicit val fileLine = x.fileLineStack.fileLineHead - x.copy(penalty = x.costWithPenalty - minCost, policy = p) - } + def getClassicNonFirstBreakOnDot(dot: FT)(implicit + fileLine: FileLine, + ): Policy = Policy.onlyFor(dot, "NEXTSEL2NL") { s => + val filtered = s.flatMap { x => + val y = x.activateFor(SplitTag.SelectChainSecondNL) + if (y.isActive) Some(y) else None + } + if (filtered.isEmpty) Seq.empty + else { + val minCost = math.max(0, filtered.map(_.costWithPenalty).min - 1) + filtered.map { x => + val p = x.policy.filter(!_.isInstanceOf[PenalizeAllNewlines]) + implicit val fileLine = x.fileLineStack.fileLineHead + x.copy(penalty = x.costWithPenalty - minCost, policy = p) } } + } def classicNonFirstBreakOnNextDot(implicit fileLine: FileLine): Policy = beforeNextDotOpt.map(getClassicNonFirstBreakOnDot) @@ -2326,13 +2319,12 @@ class Router(formatOps: FormatOps) { ) val lmod = NewlineT(noIndent = rhsIsCommentedOut(postParen)) val lsplit = Seq(Split(lmod, 0).withIndents(lindents)) - val rsplit = Seq(Split(Newline, 0)) Policy.onRight(postParen, prefix = "CASE[(]") { case Decision(`postParen`, _) => lsplit // fires only if there's a comment between lparentFt and postParentFt case Decision(`lparen`, _) => Seq(Split(Space, 0)) - } ==> Policy.afterRight(rparen, prefix = "CASE[)]") { - case Decision(`rparen`, _) => rsplit + } ==> Policy.onlyFor(rparen, prefix = "CASE[)]") { _ => + Seq(Split(Newline, 0)) } } } diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/util/PolicyOps.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/util/PolicyOps.scala index 1382a25520..40bab1f81a 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/util/PolicyOps.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/util/PolicyOps.scala @@ -49,6 +49,10 @@ object PolicyOps { )) } + def penalizeOneNewline(on: FT, penalty: Int)(implicit + fileLine: FileLine, + ): Policy = Policy.onlyFor(on, s"PNL+$penalty")(_.penalizeNL(penalty)) + def penalizeNewlineByNesting(before: FT, after: FT)(implicit fileLine: FileLine, ): Policy = Policy.End < before ==> Policy.beforeLeft(after, prefix = "PNL()") { @@ -256,12 +260,10 @@ object PolicyOps { val unindent = Indent(indent, rt, ExpiresOn.After) val triggeredIndent = Indent.before(unindent, trigger) val triggerUnindent = Policy - .onLeft(rt, prefix = "UNIND{") { case Decision(`lt`, s) => - s.map(_.withIndent(triggeredIndent)) - } + .onlyFor(lt, prefix = "UNIND{")(_.map(_.withIndent(triggeredIndent))) val cancelUnindent = delayedBreakPolicy(Policy.End <= lt) { - Policy.onRight(lt, rank = 1, prefix = "UNIND}") { // use rank to apply after policy above - case Decision(`lt`, s) => s.map(_.switch(trigger, false)) + Policy.onlyFor(lt, rank = 1, prefix = "UNIND}") { // use rank to apply after policy above + _.map(_.switch(trigger, false)) } } policy ==> triggerUnindent & cancelUnindent