Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid empty lines in the beginning and end of blocks #1431

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 25 additions & 3 deletions docs/configuration.md
Expand Up @@ -82,6 +82,28 @@ val example2 =
|""".stripMargin
```

## Edition

The `edition` setting allows Scalafmt users to stay on older default settings
while upgrading to the latest Scalafmt release.

The use-case for configuring the `edition` setting is when you want to upgrade
to the latest Scalafmt version (for example, to enjoy a bug fix in the parser)
but need more time to deal with the latest changes in the formatting output.

The end goal for users should be to remove the `edition` setting from
`.scalafmt.conf` in order to enjoy the latest improvements to the formatting
output.

```scala mdoc:defaults
edition
```

The `edition` setting is formatted as `"$year-$month"` and should be interpreted
as: "use the default settings at that given date". For example the setting
`edition = 2019-10` means that the default settings from October 2019 should be
used.

## Indentation

### `continuationIndent.callSite`
Expand Down Expand Up @@ -138,9 +160,9 @@ x match { // false for case arrows
```

> **Pro tip**: Enable this setting to minimize git diffs/conflicts from
> renamings and other refactorings, without having to ignore whitespace
> changes in diffs or use `--ignore-all-space` to avoid conflicts when
> git merging or rebasing.
> renamings and other refactorings, without having to ignore whitespace changes
> in diffs or use `--ignore-all-space` to avoid conflicts when git merging or
> rebasing.

#### `align=some`

Expand Down
@@ -0,0 +1,24 @@
package org.scalafmt.config

import metaconfig.{Conf, ConfDecoder, ConfEncoder, Configured}

case class Edition(year: Int, month: Int)

object Edition {
val Latest = Edition(Int.MaxValue, Int.MaxValue)
implicit val ordering: Ordering[Edition] =
Ordering.by[Edition, (Int, Int)](e => e.year -> e.month)
lazy val format = "(\\d{4})-(\\d{1,2})".r

implicit val decoder: ConfDecoder[Edition] =
ConfDecoder.instanceExpect("'$year-$month', for example '2019-08'") {
case Conf.Str(format(year, month)) =>
Configured.ok(Edition(year.toInt, month.toInt))
case Conf.Str("latest") => Configured.ok(Latest)
}

implicit val encoder: ConfEncoder[Edition] =
ConfEncoder.instance(
edition => Conf.Str(f"${edition.year}%d-${edition.month}%02d")
)
}
Expand Up @@ -110,7 +110,8 @@ case class Newlines(
beforeImplicitKWInVerticalMultiline: Boolean = false,
alwaysBeforeElseAfterCurlyIf: Boolean = false,
alwaysBeforeMultilineDef: Boolean = true,
avoidAfterYield: Boolean = true
avoidAfterYield: Boolean = true,
avoidEmptyLinesAroundBlock: Option[Boolean] = None
) {
val reader: ConfDecoder[Newlines] = generic.deriveDecoder(this).noTypos
}
Expand Down
Expand Up @@ -9,6 +9,8 @@ import metaconfig.Configured._
import org.scalafmt.util.LoggerOps
import org.scalafmt.util.ValidationOps

import Edition.ordering._

/** Configuration options for scalafmt.
*
* @param version The version of scalafmt to use for this project. Currently not used,
Expand Down Expand Up @@ -159,7 +161,8 @@ case class ScalafmtConfig(
verticalAlignMultilineOperators: Boolean = false,
onTestFailure: String = "",
encoding: Codec = "UTF-8",
project: ProjectFiles = ProjectFiles()
project: ProjectFiles = ProjectFiles(),
edition: Edition = Edition.Latest
) {
private implicit val runnerReader = runner.reader
private implicit val projectReader = project.reader
Expand Down Expand Up @@ -189,6 +192,9 @@ case class ScalafmtConfig(
continuationIndent.callSite,
continuationIndent.defnSite
)

val avoidEmptyLinesAroundBlock: Boolean =
newlines.avoidEmptyLinesAroundBlock.getOrElse(edition > Edition(2019, 10))
}

object ScalafmtConfig {
Expand Down
Expand Up @@ -184,6 +184,7 @@ class Router(formatOps: FormatOps) {
selfAnnotation.nonEmpty
val nl: Modification =
if (isSelfAnnotation) newlines2Modification(formatToken)
else if (style.avoidEmptyLinesAroundBlock) NewlineT()
else NewlineT(shouldGet2xNewlines(tok, style, owners))

val (startsLambda, lambdaPolicy, lambdaArrow, lambdaIndent) =
Expand Down Expand Up @@ -347,6 +348,11 @@ class Router(formatOps: FormatOps) {
)
}

case FormatToken(_, T.RightBrace(), _)
if style.avoidEmptyLinesAroundBlock && newlines > 1 =>
Seq(
Split(NoSplit, 0)
)
case FormatToken(_, T.RightBrace(), _) =>
Seq(
Split(xmlSpace(rightOwner), 0),
Expand Down
1 change: 0 additions & 1 deletion scalafmt-tests/src/test/resources/default/Advanced.stat
Expand Up @@ -223,7 +223,6 @@ callStatement = js.If(genIsInstanceOf(callTrg, rtClass.tpe), {
rawApply
} else {
val reflBoxClassPatched = {

def isIntOrLongKind(kind: TypeKind) = kind match {
case _: INT | LONG => true
case _ => false
Expand Down
1 change: 0 additions & 1 deletion scalafmt-tests/src/test/resources/default/Apply.stat
Expand Up @@ -249,7 +249,6 @@ class ResolutionCopier(x: Int) {
}
>>>
class ResolutionCopier(x: Int) {

def visitClassDeclaration(node: ClassDeclaration): Boolean = {
val toNode = this._toNode.asInstanceOf[ClassDeclaration];
javaBooleanAnd(
Expand Down
1 change: 0 additions & 1 deletion scalafmt-tests/src/test/resources/default/Class.stat
Expand Up @@ -62,7 +62,6 @@ class MediaStream() extends EventTarget {
}
>>>
class MediaStream() extends EventTarget {

/**
*/
val ended: Boolean = js.native
Expand Down
5 changes: 0 additions & 5 deletions scalafmt-tests/src/test/resources/default/Comment.stat
Expand Up @@ -10,7 +10,6 @@
}
>>>
{

def cancel(reason: Any): js.Promise[Any] = js.native // comment

/**
Expand All @@ -29,7 +28,6 @@ object KeyValue {
}
>>>
object KeyValue {

// ================================
// 6.3.1 Special Key Values

Expand All @@ -45,7 +43,6 @@ object KeyValue {
}
>>>
object KeyValue {

// comment
/** This key value */
final val Unidentified = "Unidentified"
Expand Down Expand Up @@ -74,7 +71,6 @@ object D {
}
>>>
object D {

private val decompressedPrefixes: Seq[(String, String)] =
compressedPrefixes map { case (a, b) => (b, a) }

Expand All @@ -94,7 +90,6 @@ object Primes {
}
>>>
object Primes {

// prime sieve; use instead of the test or stream when you know the
// upper bound of the primes you will need.
// en.wikipedia.org/wiki/Sieve_of_Eratosthenes
Expand Down
1 change: 0 additions & 1 deletion scalafmt-tests/src/test/resources/default/DefDef.stat
Expand Up @@ -9,7 +9,6 @@ object a {
}
>>>
object a {

/**
* Returns True is this state will always return better formatting than other.
*/
Expand Down
1 change: 0 additions & 1 deletion scalafmt-tests/src/test/resources/default/Fidelity.source
Expand Up @@ -10,7 +10,6 @@ object ScalaJSGroupID {
}
>>>
object ScalaJSGroupID {

def auto_impl(c: Context { type PrefixType = ScalaJSGroupID })()
: Expr[CrossGroupArtifactID] = {
reify {}
Expand Down
4 changes: 0 additions & 4 deletions scalafmt-tests/src/test/resources/default/Idempotency.stat
Expand Up @@ -86,7 +86,6 @@ val bindingFuture = Http().bindAndHandleSync({
>>>
{
{

{
{
{
Expand All @@ -103,7 +102,6 @@ val bindingFuture = Http().bindAndHandleSync({
}
}
}

}
<<< rendering #339
{{
Expand All @@ -130,7 +128,6 @@ val bindingFuture = Http().bindAndHandleSync({
RequestRenderingContext(request, hostHeader, sendEntityTrigger)
}
}

})
}
}
Expand Down Expand Up @@ -169,7 +166,6 @@ val bindingFuture = Http().bindAndHandleSync({
})
new ResponseParsingMerge(rootParser)
}

})
}
}
Expand Down
Expand Up @@ -132,7 +132,6 @@ object A {
}
>>>
object A {

/** test */
def foo(): Int = 0
// bar
Expand Down
@@ -0,0 +1,55 @@
maxColumn = 80
newlines.avoidEmptyLinesAroundBlock = true

<<< should remove empty lines at the beginning of a def block
def other: String = {

"Result"
}
>>>
def other: String = {
"Result"
}

<<< should remove empty lines at the beginning and end of a def block
def other: String = {

"Result"

}
>>>
def other: String = {
"Result"
}

<<< should remove empty lines at the beginning and end of a val block
def other: String = {

val aux = {

"Result"

}

aux

}
>>>
def other: String = {
val aux = {
"Result"
}

aux
}

<<< should remove empty lines at the end of a block
def other: String = {
"Result"


}
>>>
def other: String = {
"Result"
}
Expand Up @@ -15,14 +15,12 @@ object a {
}
>>>
object a {

val x: Int => String = {
case 2 =>
"two"
case _ =>
"other"
}

}
<<< case clauses with stuff
object a {
Expand All @@ -41,7 +39,6 @@ object a {
}
>>>
object a {

val x: Int => String = {
case 2 =>
println("TWO")
Expand All @@ -50,7 +47,6 @@ object a {
// Nice
"other"
}

}
<<< case clauses to unit
object a {
Expand All @@ -66,12 +62,10 @@ object a {
}
>>>
object a {

val x: Int => Unit = {
case 0 =>
case 1 =>
case 2 =>
case _ => println("?")
}

}
Expand Up @@ -15,15 +15,13 @@ object a {
}
>>>
object a {

def x(i: Int): String =
if (i == 0)
"zero"
else if (i < 0)
"sub-zero"
else
"other"

}
<<< if-else with stuff
object a {
Expand All @@ -50,7 +48,6 @@ object a {
}
>>>
object a {

def x(i: Int): String =
if (i == 0)
/* what */
Expand Down Expand Up @@ -88,7 +85,6 @@ object a {
}
>>>
object a {

val x: Int => Unit =
if (i == 0)
println("zero")
Expand All @@ -98,7 +94,6 @@ object a {
def y(i: Int) =
if (i > 0)
println("yes!")

}
<<< nested if
object a {
Expand Down