The Scruff style
Scruff aims for consistency, generality, readability, and reducing git diffs.
This document describes the current formatting style.
How Scruff wraps lines
Scruff ignores previous formatting, and applies uniform horizontal whitespace to your code based on max-column (default 100). Scruff uses 2 whitespaces for indentation, like most Scala styles.
Short expressions
Scruff tries to print one full expression or a statement per line. If this fits the allotted max-column, it will flatten the lines together.
// original input
object A:
val a = List(1, 2)
val b = List(
1,
2
)
val c =
List(1, 2)
becomes:
// scruffed
object A:
val a = List(1, 2)
val b = List(1, 2)
val c = List(1, 2)
Expressions that can fold in half
When a definition doesn't fit on one line, Scruff first tries folding it in half at the =:
def compileIncremental(setup: Setup, previous: PreviousResult): CompileResult = compileWith(setup, previous, inputs, reporter)
becomes
// scruffed
def compileIncremental(setup: Setup, previous: PreviousResult): CompileResult =
compileWith(setup, previous, inputs, reporter)
Long expressions
If a function call still doesn't fit after folding, Scruff puts one argument per line, with a trailing comma:
val result = doSomething(compileInputs, logger, reporter, progress, cacheDirectory, extraOptions, previousResult)
becomes
// scruffed
val result = doSomething(
compileInputs,
logger,
reporter,
progress,
cacheDirectory,
extraOptions,
previousResult,
)
Named arguments
A function call with named arguments, or with a DSL containing = signs, puts one argument per line:
val conn = connect(host = "localhost", port = 8080)
lazy val action = project
.settings(name := "action", Test / fork := true)
becomes
// scruffed
val conn = connect(
host = "localhost",
port = 8080,
)
lazy val action = project
.settings(
name := "action",
Test / fork := true,
)
Call chain (fluent interface)
A chain of two or more method calls and field selections stays on one line if it fits. Otherwise, Scruff breaks before each ., starting from the first call. Field selection, like xs.dependencies, stay with the receiver:
val names = xs.dependencies.filter(_.isTransitive).map(_.moduleName).filterNot(excluded).distinct.sorted.take(limit)
becomes
// scruffed
val names = xs.dependencies
.filter(_.isTransitive)
.map(_.moduleName)
.filterNot(excluded)
.distinct
.sorted
.take(limit)
Chained infix expression
A chain of infix expressions stays on one line if it fits. Otherwise, Scruff breaks before each && or || operator, so the operators start the lines:
val skip = isCi && isPullRequest || offline && cache.isEmpty || dryRun && warningCount + errorCount == 0
becomes
// scruffed
val skip = isCi && isPullRequest
|| offline && cache.isEmpty
|| dryRun && warningCount + errorCount == 0
Scala 3 dialect
Scala 3 is the default dialect of Scruff formatter:
dialect = "scala3"
Scala 3: Indentation syntax
"scala3" diealect replaces braces with indentation where Scala 3 allows it. This covers the bodies of classes, objects, and definitions:
object A {
def foo(x: Int): String =
x match {
case 1 => "one"
case _ => "many"
}
}
becomes
// scruffed
object A:
def foo(x: Int): String = x match
case 1 => "one"
case _ => "many"
Scala 3: Fewer braces (colon argument) syntax
"scala3" dialect replaces a braced lambda argument with a colon argument (SIP-44):
val sizes = files.map { file =>
val bytes = Files.size(file)
bytes / 1024
}
becomes
// scruffed
val sizes = files.map: file =>
val bytes = Files.size(file)
bytes / 1024
Scala 3: Control syntax
"scala3" dialect rewrites to Scala 3 control syntax:
val label = if (count > 1) "many" else "one"
val names = for (file <- files) yield file.getName
becomes
// scruffed
val label = if count > 1 then "many" else "one"
val names = for file <- files yield file.getName
Scala 3: Miscellaneous rewrites
*for wildcard imports, andasfor renames in imports and exports?for type wildcardsxs*for vararg splicesusingforimplicitparameter clauses&for compound types, and commas between parents afterextends- End markers added to definitions spanning 20 or more lines, and removed from shorter ones
Scala 2 dialects
Scruff also supports the following Scala 2 dialects:
scala213source3,scala212source3, andsbt1(Scala 2 with-Xsource:3): Scruff rewrites imports, type wildcards, and vararg splices to the Scala 3 syntax, and keeps braces, parentheses,implicit, andwith.scala213: Scruff rewrites Scala 3 syntax back to Scala 2 syntax, such as braces, parentheses,_wildcard imports,=>renames, andxs: _*splices.
In both, a Boolean chain broken across lines is wrapped in parentheses, since a Scala 2 line can't start with an operator:
// scruffed
val shouldPublish = (isRelease
&& !isSnapshot
&& credentials.nonEmpty
&& repository.isDefined
&& testsPassed)
Comments
Scruff does not format comment contents.
Scaladoc
Scruff will, however, unify the Scaladoc comments with gutter asterisks aligned in column two:
// scruffed
/** Compiles the sources.
*
* @param sources the files to compile
*/
def compile(sources: Seq[File]): Unit = ()
format: off
Code in between // format: off and // format: on are not formatted:
// format: off
object AnythingGoes {}
// format: on