Skip to content

Commit

Permalink
FormatOps: handle fewer braces and refined types
Browse files Browse the repository at this point in the history
  • Loading branch information
kitbellew committed Jan 23, 2023
1 parent e985b9c commit a83bb16
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 174 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2037,6 +2037,34 @@ 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))
Split(Space, 0)
.withSingleLine(opt)
.andPolicy(decideNewlinesOnlyAfterToken(opt))
}
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
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ object Indent {
case x => new IndentImpl(x, expire, expiresAt)
}

@inline def empty: Indent = Empty
case object Empty extends Indent {
override def withStateOffset(offset: Int): Option[ActualIndent] = None
override def switch(trigger: Token, on: Boolean): Indent = this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,12 @@ class Router(formatOps: FormatOps) {
val indent = // don't indent if the body is empty `{ x => }`
if (isEmptyFunctionBody(leftOwner) && !right.is[T.Comment]) 0
else if (leftOwner.is[Template]) 0 // { applied the indent
else style.indent.main
else
leftOwner.parent match {
case Some(ArgClauseParent(p: Term.Apply)) if isFewerBraces(p) =>
style.indent.getSignificant
case _ => style.indent.main
}

def noSingleLine = {
// for constructors with empty args lambda
Expand Down Expand Up @@ -1718,6 +1723,23 @@ class Router(formatOps: FormatOps) {
val expire = getLastEnclosedToken(expireTree)
val indentLen = style.indent.main

val nextDotIfSig = nextSelect.flatMap { ns =>
val ok = ns.qual match {
case p: Term.Apply => isFewerBraces(p)
case p: Term.Match => !tokenBefore(p.cases).left.is[T.LeftBrace]
case _ => false
}
if (ok) Some(tokenBefore(ns.nameToken)) else None
}
def forcedBreakOnNextDotPolicy = nextSelect.map { selectLike =>
val tree = selectLike.tree
Policy.before(selectLike.nameToken) {
case d @ Decision(FormatToken(_, _: T.Dot, m), _)
if m.rightOwner eq tree =>
d.onlyNewlinesWithoutFallback
}
}

def breakOnNextDot: Policy =
nextSelect.fold(Policy.noPolicy) { selectLike =>
val tree = selectLike.tree
Expand Down Expand Up @@ -1828,29 +1850,29 @@ class Router(formatOps: FormatOps) {
if (prevSelect.isEmpty && nextSelect.isEmpty)
Seq(Split(NoSplit, 0), Split(Newline, 1))
else {
val forcedBreakPolicy = nextSelect.map { selectLike =>
val tree = selectLike.tree
Policy.before(selectLike.nameToken) {
case d @ Decision(FormatToken(_, _: T.Dot, m), _)
if m.rightOwner eq tree =>
d.onlyNewlinesWithoutFallback
}
}
Seq(
Split(NoSplit, 0).withSingleLine(expire, noSyntaxNL = true),
Split(NewlineT(alt = Some(NoSplit)), 1)
.withPolicyOpt(forcedBreakPolicy)
.withPolicyOpt(forcedBreakOnNextDotPolicy)
)
}

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)
)
if (nextDotIfSig.isEmpty) {
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)
)
} else {
val policy = forcedBreakOnNextDotPolicy
Seq(
Split(NoSplit, 0).andPolicyOpt(policy),
Split(NewlineT(alt = Some(NoSplit)), 1).withPolicyOpt(policy)
)
}
}

val delayedBreakPolicyOpt = nextSelect.map { selectLike =>
Expand All @@ -1866,11 +1888,13 @@ class Router(formatOps: FormatOps) {
}

// trigger indent only on the first newline
val indent = Indent(indentLen, expire, After)
val nlIndent = Indent(indentLen, expire, After)
val spcIndent = nextDotIfSig
.fold(Indent.empty)(x => Indent(indentLen, x.left, ExpiresOn.Before))
val willBreak = nextNonCommentSameLine(tokens(t, 2)).right.is[T.Comment]
val splits = baseSplits.map { s =>
if (willBreak || s.isNL) s.withIndent(indent)
else s.andFirstPolicyOpt(delayedBreakPolicyOpt)
if (willBreak || s.isNL) s.withIndent(nlIndent)
else s.andFirstPolicyOpt(delayedBreakPolicyOpt).withIndent(spcIndent)
}

if (prevSelect.isEmpty) splits
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,7 @@ object ParamClauseParent {
case p => p
}
}

object ArgClauseParent {
def unapply(t: Member.ArgClause): Option[Tree] = t.parent
}
Original file line number Diff line number Diff line change
Expand Up @@ -1004,4 +1004,7 @@ object TreeOps {
case _ => None
})

def isFewerBraces(tree: Term.Apply)(implicit dialect: Dialect): Boolean =
dialect.allowFewerBraces && tree.argClause.tokens.head.is[Colon]

}
70 changes: 34 additions & 36 deletions scalafmt-tests/src/test/resources/scala3/OptionalBraces.stat
Original file line number Diff line number Diff line change
Expand Up @@ -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 1, main > sig
maxColumn = 80
indent.main = 4
Expand All @@ -3621,12 +3619,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
===
Expand All @@ -3639,12 +3635,15 @@ 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
.foo: a =>
2 + a
.apply:
12 + 3
<<< coloneol in fewer braces 2, main > sig
maxColumn = 80
indent.main = 4
Expand All @@ -3658,12 +3657,15 @@ 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
.foo: a =>
2 + a
.apply:
12 + 3
<<< coloneol in fewer braces 3
maxColumn = 80
===
Expand All @@ -3674,12 +3676,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()
<<< coloneol in fewer braces 3, main > sig
maxColumn = 80
indent.main = 4
Expand All @@ -3691,12 +3693,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
===
Expand All @@ -3709,13 +3711,14 @@ object a:
.apply:
12 + 3
>>>
test does not parse
object a:
def f(): Unit =
List(1, 2, 3).match
case _ => a + 2
.foo: a => 2 + a
^
case _ => a + 2
.foo: a =>
2 + a
.apply:
12 + 3
<<< match with eol, main > sig
maxColumn = 80
indent.main = 4
Expand All @@ -3729,24 +3732,24 @@ object a:
.apply:
12 + 3
>>>
test does not parse
object a:
def f(): Unit =
List(1, 2, 3).match
case _ => a + 2
.foo: a => 2 + a
^
case _ => a + 2
.foo: a =>
2 + a
.apply:
12 + 3
<<< coloneol in refined types
maxColumn = 80
===
object a:
type T = String:
type U = Int
>>>
test does not parse
object a:
type T = String:
^
type U = Int
<<< coloneol in refined types, main > sig
maxColumn = 80
indent.main = 4
Expand All @@ -3755,22 +3758,19 @@ 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
===
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
<<< with in refined types, main > sig
maxColumn = 80
indent.main = 4
Expand All @@ -3779,8 +3779,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
Loading

0 comments on commit a83bb16

Please sign in to comment.