Skip to content

Commit 0aa4936

Browse files
committed
Router: extract rules to Splits, use class lookup
A profiler run revealed that `getSplits` is the largest bottleneck of performance, despite being run only once, almost double that of the part which was previously thought to be the more expensive: BestFirstSearch. Let's refactor away from using a massive `match` to several lookups by the token class.
1 parent 5480025 commit 0aa4936

File tree

14 files changed

+3821
-3011
lines changed

14 files changed

+3821
-3011
lines changed

scalafmt-core/shared/src/main/scala/org/scalafmt/config/Spaces.scala

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,22 +65,11 @@ case class Spaces(
6565
afterKeywordBeforeParen: Boolean = true,
6666
inByNameTypes: Boolean = true,
6767
afterSymbolicDefs: Boolean = false,
68-
private val afterColonInMatchPattern: Spaces.AfterColonInMatchPattern =
68+
afterColonInMatchPattern: Spaces.AfterColonInMatchPattern =
6969
Spaces.AfterColonInMatchPattern.Always,
7070
) {
7171
def isSpaceAfterKeyword(tokenAfter: T): Boolean = afterKeywordBeforeParen ||
7272
!tokenAfter.is[T.LeftParen]
73-
74-
def notAfterColon(owner: meta.Tree): Boolean = owner match {
75-
case x: meta.Pat.Typed => afterColonInMatchPattern match {
76-
case Spaces.AfterColonInMatchPattern.Never => true
77-
case Spaces.AfterColonInMatchPattern.Always => false
78-
case Spaces.AfterColonInMatchPattern.NoAlternatives => x.parent
79-
.is[meta.Pat.Alternative]
80-
}
81-
case _ => false
82-
}
83-
8473
}
8574

8675
object Spaces {

scalafmt-core/shared/src/main/scala/org/scalafmt/internal/BestFirstSearch.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ private class BestFirstSearch private (range: Set[Range])(implicit
2626
/** Precomputed table of splits for each token.
2727
*/
2828
val routes: Array[Seq[Split]] = {
29-
val router = new Router(formatOps)
29+
val router = new Router
3030
val result = Array.newBuilder[Seq[Split]]
3131
tokens.foreach(t => result += router.getSplits(t))
3232
result.result()
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.scalafmt.internal
2+
3+
object Constants {
4+
val ShouldBeNewline = 100000
5+
val ShouldBeSingleLine = 30
6+
val BinPackAssignmentPenalty = 10
7+
val SparkColonNewline = 10
8+
val BracketPenalty = 20
9+
val ExceedColumnPenalty = 1000
10+
// Breaking a line like s"aaaaaaa${111111 + 22222}" should be last resort.
11+
val BreakSingleLineInterpolatedString = 1000 * ExceedColumnPenalty
12+
}

scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala

Lines changed: 14 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
package org.scalafmt.internal
22

3-
import org.scalafmt.config.BinPack
4-
import org.scalafmt.config.IndentOperator
5-
import org.scalafmt.config.Indents
6-
import org.scalafmt.config.Newlines
7-
import org.scalafmt.config.ScalafmtConfig
8-
import org.scalafmt.config.ScalafmtOptimizer
9-
import org.scalafmt.config.TrailingCommas
10-
import org.scalafmt.internal.Policy.NoPolicy
3+
import org.scalafmt.config._
114
import org.scalafmt.rewrite.RedundantBraces
125
import org.scalafmt.util.InfixApp._
136
import org.scalafmt.util._
@@ -158,10 +151,6 @@ class FormatOps(
158151
case _ => false
159152
})
160153

161-
val StartsStatementRight = new FT.ExtractFromMeta[Tree](meta =>
162-
optimizationEntities.statementStarts.get(meta.idx + 1),
163-
)
164-
165154
def parensTuple(ft: FT): TokenRanges = matchingOptLeft(ft)
166155
.fold(TokenRanges.empty)(other => TokenRanges(TokenRange(ft, other)))
167156
def parensTuple(tree: Tree): TokenRanges = parensTuple(getLast(tree))
@@ -900,7 +889,7 @@ class FormatOps(
900889
!isAttachedCommentThenBreak(ftd) =>
901890
d.onlyNewlinesWithoutFallback
902891
}
903-
case _ => NoPolicy
892+
case _ => Policy.NoPolicy
904893
}
905894
findTemplateGroupOnRight { x =>
906895
// this method is called on a `with` or comma; hence, it can
@@ -1232,18 +1221,6 @@ class FormatOps(
12321221
case _ => Space(spaceOk && style.spaces.inParentheses)
12331222
}
12341223

1235-
def getNoSplitBeforeClosing(
1236-
ft: FT,
1237-
commentNL: Modification,
1238-
spaceOk: Boolean = true,
1239-
)(implicit style: ScalafmtConfig): Modification = ft.left match {
1240-
case _: T.Comment =>
1241-
val isDetachedSlc = ft.hasBreak && prev(ft).hasBreak
1242-
if (isDetachedSlc || ft.leftHasNewline) commentNL else Space
1243-
case _: T.RightParen if ft.meta.rightOwner eq ft.meta.leftOwner => NoSplit
1244-
case _ => Space(spaceOk && style.spaces.inParentheses)
1245-
}
1246-
12471224
// look for arrow before body, if any, else after params
12481225
def getFuncArrow(term: Term.FunctionTerm): Option[FT] = tokens
12491226
.tokenBeforeOpt(term.body)
@@ -1739,8 +1716,8 @@ class FormatOps(
17391716
val semi = nextNonCommentSameLineAfter(nft)
17401717
if (semi.noBreak || (semi.left eq x) && !semi.right.is[T.Comment])
17411718
decideNewlinesOnlyAfterToken(semi)
1742-
else NoPolicy
1743-
case _ => NoPolicy
1719+
else Policy.NoPolicy
1720+
case _ => Policy.NoPolicy
17441721
})
17451722
}
17461723

@@ -1768,8 +1745,6 @@ class FormatOps(
17681745
getClosingIfBodyEnclosedAsBlock(body).isDefined
17691746

17701747
object GetSelectLike {
1771-
val OnRight =
1772-
new FT.ExtractFromMeta(m => onRightOpt(m.rightOwner, tokens(m.idx)))
17731748

17741749
private def get(
17751750
ro: Tree,
@@ -1779,16 +1754,10 @@ class FormatOps(
17791754
case _ => None
17801755
}
17811756

1782-
private[FormatOps] def onRightOpt(ro: Tree, ft: => FT): Option[SelectLike] =
1783-
get(ro)(_ =>
1784-
nextNonCommentAfter(ft) match {
1785-
case xft @ FT(_, _: T.KwMatch, _) => Some(next(xft))
1786-
case _ => None
1787-
},
1788-
)
1789-
1790-
def onRightOpt(ft: FT): Option[SelectLike] =
1791-
onRightOpt(ft.meta.rightOwner, ft)
1757+
def onRightOpt(ft: FT): Option[SelectLike] = get(ft.rightOwner) { _ =>
1758+
val nft = nextNonCommentAfter(ft)
1759+
if (nft.right.is[T.KwMatch]) Some(next(nft)) else None
1760+
}
17921761

17931762
def unapply(tree: Tree): Option[SelectLike] =
17941763
get(tree)(x => Some(tokenBefore(x.casesBlock)))
@@ -1843,9 +1812,6 @@ class FormatOps(
18431812
// Optional braces after any token that can start indentation:
18441813
// ) = => ?=> <- catch do else finally for
18451814
// if match return then throw try while yield
1846-
def unapply(ftMeta: FT.Meta)(implicit
1847-
style: ScalafmtConfig,
1848-
): Option[Seq[Split]] = get(tokens(ftMeta.idx)).flatMap(_.splits)
18491815

18501816
def get(
18511817
ft: FT,
@@ -2802,7 +2768,7 @@ class FormatOps(
28022768
case Newlines.fold
28032769
if shouldDangle && !style.binPack.indentCallSiteOnce =>
28042770
decideNewlinesOnlyBeforeCloseOnBreak(close)
2805-
case _ => NoPolicy
2771+
case _ => Policy.NoPolicy
28062772
}
28072773
}
28082774

@@ -2890,10 +2856,10 @@ class FormatOps(
28902856
exclude: TokenRanges,
28912857
): (Option[FT], Policy) = {
28922858
val beforeDelims = getBeforeRightDelims(ft)
2893-
if (beforeDelims eq null) return (None, NoPolicy)
2859+
if (beforeDelims eq null) return (None, Policy.NoPolicy)
28942860

28952861
val afterDelims = getAfterRightDelims(ft)
2896-
if (afterDelims eq null) return (None, NoPolicy)
2862+
if (afterDelims eq null) return (None, Policy.NoPolicy)
28972863

28982864
def closeBreakPolicy() = {
28992865
@tailrec
@@ -2926,7 +2892,7 @@ class FormatOps(
29262892
delayedBreakPolicy(Policy.End >= beforeDelims, exclude)(
29272893
Policy.RelayOnSplit { case (s, nextft) =>
29282894
s.isNL && nextft.idx > beforeDelims.idx // don't need anymore
2929-
}(policy)(NoPolicy),
2895+
}(policy)(Policy.NoPolicy),
29302896
)
29312897

29322898
(afterDelims.right match {
@@ -2962,7 +2928,7 @@ class FormatOps(
29622928
}
29632929
case _ => null
29642930
}) match {
2965-
case null => (None, NoPolicy)
2931+
case null => (None, Policy.NoPolicy)
29662932
case (nft, policyOrOkToBreak) =>
29672933
val policyOpt = policyOrOkToBreak match {
29682934
case Left(policy) => Some(policy)
@@ -2980,7 +2946,7 @@ class FormatOps(
29802946
if !nextNonCommentAfter(afterArg).right.is[T.CloseDelim] =>
29812947
(None, splitOneArgPerLineAfterCommaOnBreak(exclude)(next(afterArg)))
29822948
case _: T.CloseDelim if isCallSite => policyOnRightDelim(afterArg, exclude)
2983-
case _ => (None, NoPolicy)
2949+
case _ => (None, Policy.NoPolicy)
29842950
}
29852951
}
29862952

@@ -3041,21 +3007,6 @@ class FormatOps(
30413007
)(implicit style: ScalafmtConfig, ft: FT): Modification =
30423008
getBracesToParensMod(rb, mod, isWithinBraces)._1
30433009

3044-
@tailrec
3045-
private def getSingleFunctionArg(
3046-
values: List[Tree],
3047-
): Option[Term.FunctionTerm] = values match {
3048-
case (t: Term.FunctionTerm) :: Nil => Some(t)
3049-
case (t: Term.Block) :: Nil if !isEnclosedInBraces(t) =>
3050-
getSingleFunctionArg(t.stats)
3051-
case _ => None
3052-
}
3053-
3054-
val LambdaAtSingleArgCallSite = new FT.ExtractFromMeta(_.leftOwner match {
3055-
case Term.ArgClause(v, None) => getSingleFunctionArg(v)
3056-
case _ => None
3057-
})
3058-
30593010
}
30603011

30613012
object FormatOps {
@@ -3149,24 +3100,6 @@ object FormatOps {
31493100
def alignOpenDelim(implicit clauseSiteFlags: ClauseSiteFlags): Boolean =
31503101
clauseSiteFlags.alignOpenDelim
31513102

3152-
val ImplicitUsingOnLeft =
3153-
new FT.ExtractFromMeta(meta => TreeOps.getImplicitParamList(meta.leftOwner))
3154-
3155-
val WithTemplateOnLeft = new FT.ExtractFromMeta(_.leftOwner match {
3156-
case lo: Stat.WithTemplate => Some(lo.templ)
3157-
case _ => None
3158-
})
3159-
3160-
val TemplateOnRight = new FT.ExtractFromMeta(_.rightOwner match {
3161-
case ro: Template => Some(ro)
3162-
case _ => None
3163-
})
3164-
3165-
val EnumeratorAssignRhsOnLeft = new FT.ExtractFromMeta(_.leftOwner match {
3166-
case x: Enumerator.Assign => Some(x.rhs)
3167-
case _ => None
3168-
})
3169-
31703103
@tailrec
31713104
private def getBlockWithNonSingleTermStat(t: Term.Block): Option[Term.Block] =
31723105
t.stats match {

scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatToken.scala

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -146,16 +146,4 @@ object FormatToken {
146146
}
147147
}
148148

149-
class ExtractFromMeta[A](f: FT.Meta => Option[A]) {
150-
def unapply(meta: FT.Meta): Option[A] = f(meta)
151-
}
152-
153-
val LeftOwner = new ExtractFromMeta(x => Some(x.leftOwner))
154-
val RightOwner = new ExtractFromMeta(x => Some(x.rightOwner))
155-
156-
val LeftOwnerParent =
157-
new ExtractFromMeta(x => Some((x.leftOwner, x.leftOwner.parent)))
158-
val RightOwnerParent =
159-
new ExtractFromMeta(x => Some((x.rightOwner, x.rightOwner.parent)))
160-
161149
}

0 commit comments

Comments
 (0)