Opinionated code formatter for Scala
- Ólafur Geirsson, @olafurpg
// Before
UserRepo hasEmail me.id flatMap { user =>
AccountRepo hasStatement user.id flatMap { statement =>
BalanceRepo hasBalance statement.id
}
}
// After
for {
user <- UserRepo.hasEmail(me.id)
statement <- AccountRepo.hasStatement(user.id)
balance <- BalanceRepo.hasBalance(statement.id)
} yield balance
Wright et al., “Large-Scale Automated Refactoring Using ClangMR.”
// Columns 80 |
case class Split(modification: Modification,
cost: Int,
ignoreIf: Boolean = false,
indents: Vector[Indent[Length]],
policy: Policy = NoPolicy,
penalty: Boolean = false,
optimalAt: Option[OptimalToken] = None)
// Columns 80 |
case class Split(modification: Modification,
cost: Int,
ignoreIf: Boolean = false,
indents: Vector[Indent[Length]] = Vector.empty[Indent[Length]],
policy: Policy = NoPolicy,
penalty: Boolean = false,
optimalAt: Option[OptimalToken] = None)(implicit val line: sourcecode.Line)
// Columns 80 |
case class Split(
modification: Modification,
cost: Int,
ignoreIf: Boolean = false,
indents: Vector[Indent[Length]] = Vector.empty[Indent[Length]],
policy: Policy = NoPolicy,
penalty: Boolean = false,
optimalAt: Option[OptimalToken] = None)(implicit val line: sourcecode.Line)
"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.
Scala files | 295 |
---|---|
Lines of code | 17.493 |
Time to format | 23s |
Diff | +2,672 −2,372 |
Longest line | 136 chars |
private[data] trait XorTMonadCombine[F[_], L] extends MonadCombine[XorT[F, L, ?]] with XorTMonadFilter[F, L] with XorTSemigroupK[F, L] {
-
scalafmt command:
scalafmt --maxColumn 100 --continuationIndentCallSite 2 --javaDocs --files . -i
- implicit def constSemigroup[A: Semigroup, B]: Semigroup[Const[A, B]] = new Semigroup[Const[A, B]] {
- ...
- }
+ implicit def constSemigroup[A : Semigroup, B]: Semigroup[Const[A, B]] =
+ new Semigroup[Const[A, B]] {
+ ...
+ }
- Arbitrary(Gen.oneOf(
- getArbitrary[A].map(Eval.now(_)),
- getArbitrary[A].map(Eval.later(_)),
- getArbitrary[A].map(Eval.always(_))))
+ Arbitrary(
+ Gen.oneOf(getArbitrary[A].map(Eval.now(_)),
+ getArbitrary[A].map(Eval.later(_)),
+ getArbitrary[A].map(Eval.always(_))))
- /**
- * Lift a function into the context of an Arrow
- */
+ /**
+ * Lift a function into the context of an Arrow
+ */
- def traverse[A: Arbitrary, B: Arbitrary, C: Arbitrary, M: Arbitrary, X[_]: Applicative, Y[_]: Applicative](implicit
- ArbFA: Arbitrary[F[A]],
- ArbXB: Arbitrary[X[B]],
- ArbYB: Arbitrary[Y[B]],
- ArbYC: Arbitrary[Y[C]],
- M: Monoid[M],
- ...
+ def traverse[A : Arbitrary,
+ B : Arbitrary,
+ C : Arbitrary,
+ M : Arbitrary,
+ X[_]: Applicative,
+ Y[_]: Applicative](implicit ArbFA: Arbitrary[F[A]],
+ ArbXB: Arbitrary[X[B]],
+ ArbYB: Arbitrary[Y[B]],
+ ArbYC: Arbitrary[Y[C]],
+ M: Monoid[M],
+ ...
trait AllInstances
- extends FunctionInstances
- with StringInstances
- with EitherInstances
- with ListInstances
- with OptionInstances
- with SetInstances
- with StreamInstances
+ extends FunctionInstances with StringInstances with EitherInstances with ListInstances
+ with OptionInstances with SetInstances with StreamInstances
Scala files | 743 |
---|---|
Lines of code | 63.228 |
Time to format | 51s |
Diff | +3,870 −3,174 |
Longest line | 147 chars |
protected def FormFuResult[A, B: Writeable: ContentTypeOf](form: Form[A])(err: Form[A] => Fu[B])(op: A => Fu[Result])(implicit req: Request[_]) =
-
scalafmt command:
scalafmt --maxColumn 100 --continuationIndentCallSite 2 --style defaultWithAlign --files . -i
//
- protected def SocketOptionLimited[A: FrameFormatter](consumer: TokenBucket.Consumer, name: String)(f: Context => Fu[Option[(Iteratee[A, _], Enumerator[A])]]) =
-
+ protected def SocketOptionLimited[A : FrameFormatter](
+ consumer: TokenBucket.Consumer, name: String)(
+ f: Context => Fu[Option[(Iteratee[A, _], Enumerator[A])]]) =
- WS.url(url).get().map(_.body).mon(_.security.proxy.request.time).flatMap { str =>
- ...
- }.addEffects( ... )
+ WS.url(url)
+ .get()
+ .map(_.body)
+ .mon(_.security.proxy.request.time)
+ .flatMap { str =>
+ ...
+ }
+ .addEffects( ... )
- case (((((((((((((nbUsers, ranks), nbPlaying), nbImported), crosstable), ratingChart), nbFollowing), nbFollowers), nbBlockers), nbPosts), isDonor), trophies), insightVisible), playTime) =>
-
+ case (((((((((((((nbUsers, ranks), nbPlaying), nbImported), crosstable),
+ ratingChart),
+ nbFollowing),
+ nbFollowers),
+ nbBlockers),
+ nbPosts),
+ isDonor),
+ trophies),
+ insightVisible),
+ playTime) =>
- Ok.chunked(Enumerator.outputStream(env.pngExport(game))).withHeaders(
- CONTENT_TYPE -> "image/png",
- CACHE_CONTROL -> "max-age=7200")
+ Ok.chunked(Enumerator.outputStream(env.pngExport(game)))
+ .withHeaders(CONTENT_TYPE -> "image/png",
+ CACHE_CONTROL -> "max-age=7200")
+Ok(
+ Json.obj(
+ "api" -> Json.obj("current" -> api.currentVersion,
+ "olds" -> api.oldVersions.map { old =>
+ Json.obj("version" -> old.version,
+ "deprecatedAt" -> old.deprecatedAt,
+ "unsupportedAt" -> old.unsupportedAt)
+ })
+ )) as JSON
+ Ok(
+ Json.obj(
+ "api" -> Json.obj("current" -> api.currentVersion,
+ "olds" -> api.oldVersions.map { old =>
+ Json.obj("version" -> old.version,
+ "deprecatedAt" -> old.deprecatedAt,
+ "unsupportedAt" -> old.unsupportedAt)
+ })
+ )) as JSON
brew install olafurpg/scalafmt/scalafmt
scalafmt.jar
via Github releases.Tokenizer: String => scala.meta.Tokens
scala> import scala.meta._
scala> """object Main extends App { world =>
println(s"Hello $world!")
}
""".tokenize.get
res3: Tokens = Tokens(
BOF (0..0),
object (0..6),
(6..7),
Main (7..11),
(11..12),
extends (12..19),
(19..20),
App (20..23),
(23..24),
{ (24..25),
(25..26),
world (26..31),
...
Parser: String => scala.meta.Tree
scala> import scala.meta._
scala> """object Main extends App { world =>
println(s"Hello $world!")
}
""".parse[Stat].get.show[Structure]
res9: String = """
Defn.Object(Nil, Term.Name("Main"),
Template(Nil, Seq(Ctor.Ref.Name("App")),
Term.Param(Nil, Term.Name("world"), None, None),
Some(Seq(Term.Apply(Term.Name("println"),
Seq(Term.Interpolate(Term.Name("s"),
Seq(Lit("Hello "),
Lit("!")),
Seq(Term.Name("world")))))))))
"""
// 50 columns |
object BestFirstSearch { // 1 penalty
DBObject(User(Name("Martin", "Odersky"), // 5 penalty
Language("Scala")), // 3 penalty
Address("Lausanne", "Switzerland")) // 0 penalty
} // 0 penalty
//----------
// 9 total
// 50 columns |
object BestFirstSearch { // 1 penalty
DBObject(User(Name("Martin", "Odersky"), Language("Scala")), // 1002 penalty
Address("Lausanne", "Switzerland")) // 0 penalty
} // 0 penalty
//-------------
// 1002 total
// 50 columns |
object BestFirstSearch {
DBObject(User(Name("Martin", "Odersky"), Language("Scala")), Address("Lausanne", "Switzerland"))
DBObject(
User(Name("Martin", "Odersky"), Language("Scala")), Address("Lausanne", "Switzerland"))
DBObject(User(Name("Martin", "Odersky"), Language("Scala")),
Address("Lausanne", "Switzerland"))
DBObject(
User(Name("Martin", "Odersky"), Language("Scala")),
Address("Lausanne", "Switzerland"))
DBObject(User(Name("Martin", "Odersky"),
Language("Scala")),
Address("Lausanne", "Switzerland"))
}
ast(code) == ast(format(code))
format(code) == format(format(code))
See #192.
object VerticalAlignment {
def name = column[String]("name")
def status = column[Int]("status")
for {
dao <- olafur \/> "Can't find olafur"
user <- dao.user \/> "Join failed: no user object"
}
libraryDependencies ++= Seq(
"org.scalameta" % "scalameta" % "0.1.0-RC4-M10",
"com.lihaoyi" %% "sourcecode" % "0.1.1"
)
}
git diff
.
- Visit documentation: scalafmt.org
- See slides: geirsson.com/assets/flatmap
- Contribute with PRs and reporting issues.
- Follow @olafurpg on Twitter.
- Chat on Gitter.