Scalafmt - code formatter for Scala


0.6.3

Join the chat at https://gitter.im/olafurpg/scalafmt

Any style guide written in English is either so brief that it’s ambiguous, or so long that no one reads it.
-- Bob Nystrom, "Hardest Program I've Ever Written", Dart, Google.

Scalafmt turns the mess on the left into the (hopefully) readable, idiomatic and consistently formatted Scala code on the right.

object FormatMe { List(number) match { case head :: Nil if head % 2 == 0 => "number is even"
  case head :: Nil => "number is not even"
  case Nil => "List is empty" }
  function(arg1, arg2(arg3(arg4, arg5, "arg6"), arg7 + arg8), arg9.select(1, 2, 3, 4, 5, 6)) }
object FormatMe {
  List(number) match {
    case head :: Nil
        if head % 2 == 0 =>
      "number is even"
    case head :: Nil =>
      "number is not even"
    case Nil => "List is empty"
  }
  function(
    arg1,
    arg2(arg3(arg4, arg5, "arg6"),
         arg7 + arg8),
    arg9.select(1, 2, 3, 4, 5, 6))
}

The goal of scalafmt is to produce good enough formatted code so that you can focus on programming instead of manipulating syntax trivia. Scalafmt can be used in several environments such as the command line, text editors and build tools.

It is not a goal to format every single Scala source file under the sun. In particular, scalafmt cannot format deeply nested computer generated code.

Scalafmt is maintained by @olafurpg in his free time. Bug reports, feature requests, questions and PRs are welcome. Complaints and unfriendly attitude is not welcome.

Curious to learn more about scalafmt? Check out this talk:

Installation


IntelliJ

Here is the plugin. You can install it directly from within IntelliJ,

The default shortcut is Ctrl + Shift + L. Undo works, but not redo.

The plugin determines which style to use in this order:

  1. .scalafmt.conf in the project's root directory, if it exists
  2. $HOME/.scalafmt.conf, if it exists
  3. Otherwise, uses default style.
For details on how .scalafmt.conf should look like, see Configuration. The scalafmt IntelliJ plugin has a "Format on save" setting.

NOTE. You may need to enable balloons to get error message: Settings > Appearance & Behavior > Notifications > Scalafmt > Popup "Balloon". You need to first check "Display Balloon Notifications".

CLI

There are several ways to install the CLI.

Coursier

Create a tiny (~12kb) bootstrap script with coursier like this:
coursier bootstrap com.geirsson:scalafmt-cli_2.11:0.6.3 --main org.scalafmt.cli.Cli -o scalafmt
./scalafmt --version # should be 0.6.3
To install coursier, see here. For more details on coursier bootstrap launchers, see here.

Recommended in CI. Put the bootstrap script in your code repository to make sure everyone on your team use the same scalafmt version. To configure which files to format, see project.

Linux/OSX

To install the CLI in Linux or OSX, run:

sudo curl -L -o /usr/local/bin/scalafmt https://raw.githubusercontent.com/scalameta/scalafmt/master/bin/scalafmt_auto && sudo chmod +x /usr/local/bin/scalafmt && scalafmt --upgrade --help

Arch Linux

Install the scalafmt package from the Arch User Repository. Voting on the package will also increase the likelyhood that the package will be merged into the community repo.

To upgrade to the latest release, run scalafmt --upgrade --help. All downloaded artifacts will go to $HOME/.scalafmt-bin.

For instructions on how to use the bash script, run scalafmt --h. Once installed, see --help.

Nailgun

For experimental nailgun support (requires ng client on $PATH), run instead:

sudo curl -L -o /usr/local/bin/scalafmt https://raw.githubusercontent.com/scalameta/scalafmt/master/bin/scalafmt_ng && chmod +x /usr/local/bin/scalafmt && scalafmt --upgrade --help

Nailgun keeps scalafmt running on a local server to avoid the JVM startup penalty and also so scalafmt can benefit from JIT. This makes scalafmt up to 10x faster when formatting a single file from the CLI. The downside to Nailgun is that the setup is complicated and the long-running server needs to be restarted once in awhile.

Help wanted!. For 0.5.0, I would like to migrate scalafmt_ng to python using this python script as a baseline.

Build from source

For the greatest performance and prettiest output.

scalafmt.jar

You can download a pre-compiled jar from the releases page. Next, create a script named scalafmt somewhere in your PATH with the command:
java -jar PATH_TO/scalafmt.jar $@
or, in case of Windows, a script scalafmt.bat with the command:
java -jar PATH_TO/scalafmt.jar %*

Homebrew/Mac OSX

brew install olafurpg/scalafmt/scalafmt
scalafmt --version // should be 0.6.3

Windows

Help wanted!

--help

NOTE. This help page is generated from the master branch and may include flags that have not been published yet.
build commit: 8499ed6aca
build time: Sun Mar 12 21:32:27 CET 2017
scalafmt 0.6.3
Usage: scalafmt [options]

  -h, --help               prints this usage text
  -v, --version            print version 
  -f, --files <value>      file or directory, in which case all *.scala files are formatted.
  --exclude <value>        file or directory, in which case all *.scala files are formatted.
  -c, --config <value>     a file path to .scalafmt.conf.
  --config-str <value>     configuration defined as a string
  --stdin                  read from stdin and print to stdout
  --assume-filename <value>
                           required to format .sbt files with --stdin flag.
  -i, --in-place           write output to file, does nothing if file is not specified
  --test                   test for mis-formatted code, exits with status 1 on failure.
  --migrate2hocon <value>  migrate .scalafmt CLI style configuration to hocon style configuration in .scalafmt.conf
  --diff                   If set, only format edited files in git diff against master.
  --diff-branch <value>    If set, only format edited files in git diff against provided branch.
  --build-info             prints build information
  --quiet                  don't print out stuff to console.
  --debug                  print out diagnostics to console.
  --non-interactive        disable fancy progress bar, useful in ci or sbt plugin.
Examples:
scalafmt # Format all files in the current project, configuration is determined in this order:
         # 1. .scalafmt.conf file in current directory
         # 2. .scalafmt.conf inside root directory of current git repo
         # 3. no configuration, default style
scalafmt --test # throw exception on mis-formatted files, won't write to files.
scalafmt --diff # Format all files that were edited in git diff against master branch.
scalafmt --diff-branch 2.x # same as --diff, except against branch 2.x
scalafmt --stdin # read from stdin and print to stdout
scalafmt --stdin --assume-filename foo.sbt # required to format .sbt files
scalafmt -f Code.scala             # print formatted contents to stdout.
scalafmt -i -f Code1.scala,A.scala # write formatted contents to file.
scalafmt -i -f . --exclude target  # format all files in directory excluding target
scalafmt --config .scalafmt.conf   # read custom style from file
scalafmt --config-str "style=IntelliJ" # define custom style as a flag, must be quoted.
Please file bugs to https://github.com/scalameta/scalafmt/issues
      

sbt

The sbt plugin is a tiny wrapper around the command line interface.

// NOTE. If you use sbt-coursier, make sure you're on version 1.0.0-M15-1.
addSbtPlugin("com.geirsson" % "sbt-scalafmt" % "0.6.3")

Gradle

It is possible to use scalafmt in gradle with the following externally maintained plugins:

Maven

It is possible to use scalafmt in Maven with the following externally maintained plugin:

Vim

NOTE. You pay the JVM startup penalty on every format unless you're using Nailgun.

Standalone library

Add to your dependencies
libraryDependencies += "com.geirsson" %% "scalafmt-core" % "0.6.3"

Use the API like this

scala> org.scalafmt.Scalafmt.format("""
      object FormatMe { List(Split(Space, 0).withPolicy(SingleLineBlock(close)), Split(Newline, 1).withPolicy{ case Decision(t@FormatToken(_, `close`, _), s) => Decision(t, List(Split(Newline, 0)))}.withIndent(2, close, Right)) }
       """).get
res0: java.lang.String = 
"object FormatMe {
  List(
    Split(Space, 0).withPolicy(SingleLineBlock(close)),
    Split(Newline, 1)
      .withPolicy {
        case Decision(t @ FormatToken(_, `close`, _), s) =>
          Decision(t, List(Split(Newline, 0)))
      }
      .withIndent(2, close, Right)
  )
}
"

Help wanted!

Configuration


Configuration for scalafmt is defined in a plain text file .scalafmt.conf using HOCON syntax. To reuse your configuration with IntelliJ, .scalafmt.conf must be placed in the root directory of your project.

Here is an example .scalafmt.conf.

style = defaultWithAlign # For pretty alignment.
maxColumn = 100          # For my wide 30" display.

A note of warning. I personally use the default style, which means that the default style is by far the most tested and supported style. Most of the configuration flags are quite innocent, while some of them work very differently (esp. Scala.js). It is very difficult to guarantee that all configurations options play nicely together so I recommend you try not to go too crazy on this part.

The following sections describe the most common configuration options.

NOTE. If you are using scalafmt as a Standalone library, you can pass in a ScalafmtConfig instance, which is set to ScalafmtStyle.default by default.

style

Option 1: default
// Column 40                           |
// non bin packed parent constructors
object DefaultStyle
    extends Parent
    with SecondParent
    with ThirdParent {
  // non bin packed arguments
  function(
    argument1,
    argument2(argument3, argument4))
  // No vertical alignment
  x match {
    case 1 => 1
    case 11 => 1
  }
}
Option 2: defaultWithAlign
// Column 40                           |
object DefaultWithAlignStyle
    extends Parent
    with SecondParent
    with ThirdParent {
  // vertical alignment
  x match {
    case 1  => 1  // align me
    case 11 => 11 // align me too

    case x => // blank line breaks alignment
  }
  for {
    x  <- List(1)
    xx <- List(1, 2)
  } yield x * xx
  // A whole bunch of other
  // alignment goodies.
  // ...
}
Option 3: IntelliJ
// Column 40                           |
// non bin packed parent constructors
object IntelliJStyle
    extends Parent
    with SecondParent
    with ThirdParent {

  function(argument1, argument2)

  // continuationIndent = 2  # for call + defn site
  // align.openParenCallSite = false
  // danglingParentheses = true
  function(
    argument1,
    argument2,
    argument3,
    argument4
  )

  // openParenCallSite = true
  def foobar(argument1: Type1,
             argument2: Type2): Int =
    argument1 + argument2

}
NOTE. There is a Scala.js style that is super experimental and does not work for complicated code.

maxColumn

Default: 80

continuationIndent.callSite

Default: 2

Example:

function(
  argument1 // indented by 2
)

continuationIndent.defnSite

Default: 4

Same as continuationIndent.callSite except for definition site. Example:

def function(
    argument1: Type1): ReturnType // Indented by 4

align.tokens

Default: off

The style defaultWithAlign has pre-configured a variety of tokens that align together nicely.

// 40 columns                          |
object TokenAlignment {
  x match {
    // Align by => and -> and //
    case 1  => 1  -> 2  // first
    case 11 => 11 -> 22 // second

    // A blank line separates alignment blocks.
    case `ignoreMe` => 111 -> 222
  }

  // Align assignments of similar type.
  def name   = column[String]("name")
  def status = column[Int]("status")
  val x  = 1
  val xx = 22

  // Align sbt module IDs.
  libraryDependencies ++= Seq(
    "org.scala-lang" % "scala-compiler" % scalaVersion.value,
    "com.lihaoyi"    %% "sourcecode"    % "0.1.1"
  )
}

// format: off

Disable formatting for specific regions of code by wrapping them in // format: OFF blocks:
object PrettyMatrix {
  // format: off
  val identity = Array(1, 0, 0,
                       0, 1, 0,
                       0, 0, 1)
  // format: on
}
To disable formatting for a whole file, put the comment at the top of the file.

assumeStandardLibraryStripMargin

Default: false

If true, the margin character | is aligned with the opening triple quote """ in interpolated and raw string literals.

// 40 columns                          |
object StripMargin {
  val example1 =
    s"""Examples:
       |  * one
       |  * two
       |  * $three
       |""".stripMargin

  // pipe character after opening """
  val example2 =
    s"""|Examples:
        |  * one
        |  * two
        |  * $three
        |""".stripMargin
}
NOTE. May cause non-idempotent formatting in rare cases, see #192.

docstrings

Default: ScalaDoc
// docstrings = ScalaDoc
/** Align by second asterisk.
  *
  */

// docstrings = JavaDoc
/** Align by first asterisk.
 *
 */

newlines.alwaysBeforeTopLevelStatements

Default: false
// newlines.alwaysBeforeTopLevelStatements = false
import org.scalafmt
package P {
  object O {
    val x1 = 1
    val x2 = 2
    def A = "A"
    def B = "B"
  }
}

// newlines.alwaysBeforeTopLevelStatements = true
import org.scalafmt

package P {

  object O {
    val x1 = 1
    val x2 = 2

    def A = "A"

    def B = "B"
  }
}

newlines.sometimesBeforeColonInMethodReturnType

Default: true
// Column limit                                                     |
// newlines.sometimesBeforeColonInMethodReturnType = true
implicit def validatedInstances[E](implicit E: Semigroup[E])
  : Traverse[Validated[E, ?]] with ApplicativeError[Validated[E, ?], E] = 2

// newlines.sometimesBeforeColonInMethodReturnType = false
implicit def validatedInstances[E](implicit E: Semigroup[E]): Traverse[
    Validated[E, ?]] with ApplicativeError[Validated[E, ?], E] = 2

align.arrowEnumeratorGenerator

Default: false
// align.arrowEnumeratorGenerator = false
for {
  x <- new Integer {
    def value = 2
  }
} yield x

// align.arrowEnumeratorGenerator = true
for {
  x <- new Integer {
        def value = 2
      }
} yield x

binPackParentConstructors

Default: false
// column limit                        |
// binPackParentConstructors = false
object DefaultStyle
    extends Parent
    with SecondParent
    with ThirdParent {
  // body ...
}

// column limit                        |
// binPackParentConstructors = true
object DefaultStyle
    extends Parent with SecondParent
    with ThirdParent {
  // body ...
}

align.openParenCallSite

Default: true
// Column limit |
// align.openParenCallSite = true
foo(arg1, arg2)

function(arg1, // align by (
         arg2,
         arg3)
function(
  argument1,
  argument2)

// align.openParenCallSite = false
foo(arg1, arg2)
function(
  arg1, // no align by (
  arg2,
  arg3)
function(
  argument1,
  argument2)

lineEndings

Default: unix

includeCurlyBraceInSelectChains

Default: true
// includeCurlyBraceInSelectChains = true
List(1)
  .map { x =>
    x + 2
  }
  .filter(_ > 2)

// includeCurlyBraceInSelectChains = false
List(1).map { x =>
    x + 2
}.filter(_ > 2)
For more details, see this comment.

optIn.breakChainOnFirstMethodDot

Default: true

If true, forces a select chain (pipeline) to break if there is a newline at the start of the chain.

// original
foo
  .map(_ + 1)
  .filter( > 2)

// optIn.breakChainOnFirstMethodDot = true
foo
  .map(_ + 1)
  .filter( > 2)

// optIn.breakChainOnFirstMethodDot = false
foo.map(_ + 1).filter( > 2)
// note. chain starts at .foo() in a.b.foo()

See this comment for further motivation.

newlines.penalizeSingleSelectMultiArgList

Default: true
// newlines.penalizeSingleSelectMultiArgList = true
logger.elem(a,
            b,
            c)

// newlines.penalizeSingleSelectMultiArgList = false
logger
  .elem(a, b, c)

See this comment for further motivation.

binPack.literalArgumentLists

Default: true
// binPack.literalArgumentLists = true
val secret: List[Bit] = List(0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1,
  0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1,
  0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1)

// binPack.literalArgumentLists = false
val secret: List[Bit] = List(
  0,
  0,
  1,
  1,
  // really long list...
  1
)

runner.optimizer.forceConfigStyleOnOffset

Default: 150

Set to -1 to disable. Increase number to require bigger argument bodies to trigger flag.

// Before: Argument body is too big
Account(userName = "user",
        fullName = "user@localhost",
        mailAddress = "",
        password = "",
        isAdmin = false,
        url = None,
        registeredDate = new Date(),
        updatedDate = new Date(),
        lastLoginDate = None,
        image = image,
        isGroupAccount = false,
        isRemoved = false)
// After: "config style"
Account(
  userName = "user",
  fullName = "user@localhost",
  mailAddress = "",
  password = "",
  isAdmin = false,
  url = None,
  registeredDate = new Date(),
  updatedDate = new Date(),
  lastLoginDate = None,
  image = image,
  isGroupAccount = false,
  isRemoved = false
)

Set runner.optimizer.forceConfigStyleMinArgCount = 1 to enable this rule for function calls with only 1 argument (default = 2).

By forcing config style, scalafmt is able to greatly optimize performance eliminating a large number of "search state exploded" errors. See these flame graphs.

rewrite.rules

Default: disabled

To enable a rewrite rule, add it to the config like this rewrite.rules = [SortImports].

AvoidInfix

a success b
a error (b, c)
a map { x =>
  x + 2
}
"o" % "a" % "v" c(D)
future recover {
  case e: Err => 0
} map (_.toString)
a.success(b)
a.error(b, c)
a.map { x =>
  x + 2
}
("o" % "a" % "v").c(D)
future
  .recover {
    case e: Err => 0
  }
  .map(_.toString)
For more examples, see this diff. Configuration contains include/exclude operator filters (those values are build in, and can't be configured outside)

ExpandImportSelectors

import a.{
    b,
    c
  }, h.{
    k, l
  }
  import d.e.{f, g}
  import a.{
      foo => bar,
      zzzz => _,
      _
    }

import a.b
import a.c
import h.k
import h.l
import d.e.f
import d.e.g
import a.{foo => bar, zzzz => _, _}

RedundantBraces

object RedundantBraces {
  def foo = {
    List(1, 2, 3).sum
  }

  // stringInterpolation = true
  q"Hello ${name}"
}
object RedundantBraces {
  def foo =
    List(1, 2, 3).sum

  // stringInterpolation = true
  q"Hello $name"
}
Configuration options and default values:

RedundantParens

Removes redundant parentheses in guard clauses.

object RedundantParenthesis {
  def c(b: List[Int]): List[Int] =
    for {
      a <- b
      if (a.nonEmpty)
    } yield a
}
object RedundantParenthesis {
  def c(b: List[Int]): List[Int] =
    for {
      a <- b
      if a.nonEmpty
    } yield a
}

SortImports

The imports are sorted by the groups: symbols, lower-case, upper-case.

import foo.{Zilch, bar, Random, sand}
import foo.{bar, sand, Random, Zilch}

PreferCurlyFors

Replaces parentheses into curly braces in for comprehensions that contain multiple enumerator generators.

object a {
  for(a <- as; b <- bs if b > 2)
    yield (a, b)
}
object a {
  for {
    a <- as
    b <- bs if b > 2
  } yield (a, b)
}

project

Configure which source files should be formatted in this project.

# Only format files tracked by git.
project.git = true
# manually exclude files to format.
project.excludeFilters = [
   regex1
   regex2
]
# manually include files to format.
project.includeFilters = [
  regex1
  regex2
]

Other

Scalafmt has a ton of internal configuration options which I really don't recommend you rely on. However, just in case, here they are.
maxColumn = 80
onTestFailure = 
indent.rightAssociativeInfixOperatorsLikeLeftAssociative = true
bestEffortInDeeplyNestedCode = false
project.git = false
project.files = []
project.includeFilters = [".*\.scala"
  ".*\.sbt"]
project.excludeFilters = []
align.openParenCallSite = true
align.openParenDefnSite = true
align.ifWhileOpenParen = true
align.arrowEnumeratorGenerator = false
align.tokens = []
align.mixedOwners = false
continuationIndent.callSite = 2
continuationIndent.defnSite = 4
continuationIndent.extendSite = 4
optIn.configStyleArguments = true
optIn.breakChainOnFirstMethodDot = true
optIn.annotationNewlines = false
assumeStandardLibraryStripMargin = false
runner.optimizer.maxEscapes = 16
runner.optimizer.dequeueOnNewStatements = true
runner.optimizer.maxVisitsPerToken = 513
runner.optimizer.acceptOptimalAtHints = true
runner.optimizer.maxDepth = 100
runner.optimizer.forceConfigStyleMinArgCount = 2
runner.optimizer.disableOptimizationsInsideSensitiveAreas = true
runner.optimizer.forceConfigStyleOnOffset = 150
runner.optimizer.recurseOnBlocks = true
runner.optimizer.escapeInPathologicalCases = true
runner.optimizer.pruneSlowStates = true
runner.eventCallback = "<function1>"
runner.parser = scala.meta.internal.parsers.ScalametaParser$$anon$202@454f961d
runner.debug = false
runner.fatalWarnings = false
runner.maxStateVisits = 1000000
runner.ignoreWarnings = false
runner.dialect = Scala211
encoding = UTF-8
newlines.neverInResultType = false
newlines.sometimesBeforeColonInMethodReturnType = true
newlines.alwaysBeforeTopLevelStatements = false
newlines.penalizeSingleSelectMultiArgList = true
newlines.neverBeforeJsNative = false
newlines.alwaysBeforeCurlyBraceLambdaParams = false
version = 0.6.3
unindentTopLevelOperators = false
poorMansTrailingCommasInConfigStyle = false
indentOperator.include = ".*"
indentOperator.exclude = "^(&&|\|\|)$"
docstrings = ScalaDoc
lineEndings = unix
rewrite.rules = []
rewrite.redundantBraces.includeUnitMethods = true
rewrite.redundantBraces.maxLines = 100
rewrite.redundantBraces.stringInterpolation = false
rewrite.neverInfix.includeFilters = ["[\w\d_]+"]
rewrite.neverInfix.excludeFilters = [until
  to
  by
  eq
  ne
  should.*
  contain.*
  must.*
  in
  be
  taggedAs
  thrownBy
  synchronized
  have
  when]
danglingParentheses = false
includeCurlyBraceInSelectChains = true
binPack.literalsInclude = [".*"]
binPack.literalsExclude = [String]
binPack.literalsMinArgCount = 5
binPack.lambdaParameter = false
binPack.literalArgumentLists = true
binPack.defnSite = false
binPack.callSite = false
binPack.parentConstructors = false
indentYieldKeyword = true
importSelectors = noBinPack
verticalMultilineAtDefinitionSite = false
spaces.neverAroundInfixTypes = []
spaces.beforeContextBoundColon = false
spaces.beforeSeqWildcard = false
spaces.afterTripleEquals = false
spaces.inImportCurlyBraces = false
spaces.inParentheses = false

Gotchas


Scalafmt tries to automatically reformat as much as possible. However, sometimes you need to help scalafmt decide how to format your code.

Infix applications

Infix applications are methods calls that use the syntax like a + b instead of a.+(b). Scalafmt preserves your line breaks in infix applications, even if this means the maxColumn setting is not respected.
// column limit |
// if you have long infix appplications
a.b(c) && d.e(f, g, h)
// then scalafmt may format like this
a.b(c) && d.e(
  f, g, h)
// which is ugly. You can fix it by inserting
// a newline after && and it will look like this
a.b(c) &&
  d.e(f, g, h)

Config style

You can use "config style" to tell scalafmt to break a function application.
// Put newline after opening (
// and newline before closing )
// to force one argument on each line.

// OK: Config style
function(
  longerArg1 = defaultValue1,
  longerArg2 = defaultValue2,
  longerArg3 = defaultValue3
)
// NOT Config style
function(longerArg1 = defaultValue1,
         longerArg2 = defaultValue2,
         longerArg3 = defaultValue3)

Community


Related projects

Have a scalafmt related project? Please consider opening a pull request to list it here:

Adopters

Are you using scalafmt? Please consider opening a pull request to list your organization here: Scalafmt was downloaded >10.000 times in November 2016.

FAQ / Troubleshooting


How can I be notified of releases/announcements?

Join the scalafmt mailing list to receive announcements of new releases only. No spam, promise.

Why not Scalariform?

Scalariform does an excellent job of tidying up common formatting errors. However, Finally, scalafmt is my Master's thesis project. I thought it would be a fun challenge to write a code formatter :)

Why is scalafmt so slow?

My benchmarks show that scalafmt is for most common cases around 4-6x slower than scalariform (btw, scalariform is already impressively fast). This means that formatting your average 1.000 LOC file on modern hardware will take around 200ms, which should still feel close enough to instant.

The main feature that makes scalafmt slower than scalariform is the column-width limit. To figure the "best" way to break a long line, Scalafmt may try thousands of different formatting solutions.

I am sure that scalafmt could benefit greatly from micro optimizations. Any help here is appreciated.

Code formatters create unnecessary diffs!

That's not a question, but I agree that code formatters like scalafmt do sometimes increase the size of diffs in code reviews. I still believe it's worth it, considering

  1. Proper formatting helps you catch bugs!
  2. you can enable non-whitespace diffs during code review. For Github, add ?w=1 to the URL to ignore whitespace changes.
  3. git blame has a -w flag to ignore whitespace changes so you can still blame your colleagues for their crappy code.
  4. code is read waaay more often outside of code reviews, for example when you are actually coding.

Is the formatting output stable between releases?

No, the formatting rules will evolve even between PATCH releases. I recommend you inspect the diff for every scalafmt update.

Known issues


Developing code formatters is notoriously hard and scalafmt has been no exception. The following are the biggest known issues with scalafmt:

Deeply nested code

AKA. "Search state exploded"

0.5.0 made big improvements on this issue. In my test corpus, only 13 out of 27.000 source files trigger this error now.

Scalafmt cannot yet format all files with deeply nested functions calls. Deeply nested code is troublesome because the number of possible formatting options grows exponentially with each new layer of nesting. Instead of taking seconds or minutes to complete formatting, scalafmt chooses to bail early and leave the source file unformatted.

There are two workaround if you are affected by this issue:

Other cool code formatters like ClangFormat, dartfmt and rfmt use better techniques to solve this issue, which scalafmt can maybe learn from.

Non-idempotent

Scalafmt is non-idempotent for certain files. See #339. This means you should be careful about enforcing scalafmt in your CI build.

In my test corpus, as of v0.5.0, only 12 files out of 27.000 source files are affected by this issue. If sources are already formatted by scalafmt, no source file in the test corpus triggers non-idempotent formatting.

Pro tip. As awkward as I feel recommending this, you may want to run scalafmt twice on your codebase for the first time you reformat it.

Performance

Scalafmt is 6x slower than Scalariform. For 98% of source files this won't be a problem if you have a decently modern laptop. However, if you only work in files with 4.000 LOC it might be a problem. I'm quite sure that micro-optimizations can squeeze out at least ~2x performance improvements, maybe even more. Moreover, I think incremental formatting has the possibility to increase the performance by several orders of magnitude in interactive IDE environments where scalafmt is invoked on file save.

Changelog


0.6.x

0.6.3

0.6.2

0.6.1

0.6.0

Merged PRs: https://github.com/scalameta/scalafmt/milestone/14?closed=1.

3 million lines reformatted with 0.6.1 compared to 0.5.8: scala-repos#20

0.5.x

0.5.8

Merged PRs: https://github.com/scalameta/scalafmt/milestone/10?closed=1. Big thanks to @dwijnand and @tgodzik for contributing 5 and 2 pull requests each, respectively!

0.5.6

Merged PRs: https://github.com/scalameta/scalafmt/milestone/11?closed=1.

0.5.5

0.5.4

0.5.3

0.5.2

This release was made possible with contributions from 7 new contributors. Big thanks to @avdv, @pjrt, @MasseGuillaume, @mpociecha, @rbellamy, @ysusuk and @dguo-coursera! Also thanks to everyone who reported issues.

0.5.1

0.5.0

0.4.x

0.4.10

0.4.9

0.4.8

0.4.7

0.4.6

0.4.5

0.4.4

0.4.3

Big thanks to @cb372, @phalodi and @caoilte for contributing to this release!

0.4.2

0.4.1

0.4.0

Big thanks to everyone who contributed to this release: @mmatloka, @melrief, @stefanobaghino, @johnynek, @RatanRSur, @Gmadorell, @Synesso, @Lambdista and @easel

0.3.x

0.3.1

0.3.0

I'm really excited to announce this release with lots of new features and improvements.

0.2.x

0.2.12

0.2.11

0.2.10

0.2.9

0.2.8

0.2.7

Skipping, messed up Sonatype release.

0.2.6

0.2.5

0.2.4

0.2.3

0.2.2

NOTE. This release contains a bug, see #192. Please hold off from upgrading.

This release changes the formatting rules for many small cases. I recommend you reformat your whole project in one commit.

0.2.1

0.2.0

0.1.x

0.1.6

0.1.5

0.1.4

0.1.3

0.1.2

0.1.1

This page was last updated on Mar 12, 2017.

Fork me on GitHub