Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Format things with scalafmt

Format things with scalafmt

Rikito Taniguchi

November 10, 2018
Tweet

More Decks by Rikito Taniguchi

Other Decks in Technology

Transcript

  1. Format things
    with scalafmt
    Scala 関西 Summit 2018
    @tanishiking

    View Slide

  2. こんにちは
    - Twitter: tanishiking
    - GitHub: tanishiking
    - Hatena: tanishiking24
    株式会社はてな(京都オフィス)で働いています。
    Perl/TypeScript/Scala/Golang
    LinterやFormatterなどの開発支援ツールに興味があります。

    View Slide

  3. Agenda
    1. コードフォーマッタを導入する利点
    2. scalafmt の紹介
    ○ scalariform との違い / scalameta
    3. scalafmt の使い方
    4. Configuration
    ○ とりあえずこの辺設定すればOKというものをいくつか紹介
    します。

    View Slide

  4. Coding style どうしてる?
    - 特にルールはない
    - コードレビューでスタイルについて議論し
    てる?
    - チームにスタイルガイドがある?
    - フォーマッタをすでに導入している?

    View Slide

  5. 特にルールはない
    - 誰も気にしてないならそれはそれで良い
    - プロジェクトに人が増えるとカオスになるかも...?
    - 個々人が暗黙的なルールに従っている?

    View Slide

  6. コードレビューで何とかする
    レビュアーによって好みが違うかも
    スタイルに関する議論がレビューのたびに起こる
    - 「スペースよりタブを使うべきだ」
    - 「この行長すぎるので適当なところで改行してほしいです」
    - 「if の後にスペースがほしいです」
    - 「インデントの数がおかしそう」
    - 「import の順番アルファベット順にしたくないですか?」
    - 「=> を上の行の => と揃えてほしい」

    View Slide

  7. スタイルガイドを整備しても
    “any style guide written in English is
    either so brief that it’s ambiguous, or so
    long that no one reads it.”
    Bob Nystrom
    The Hardest Program I've Ever Written
    http://journal.stuffwithstuff.com/2015/09/08/the-hardest-program-ive-ever-written/

    View Slide

  8. エディタの設定を共有する?
    人によって使っているエディタや
    IDEは違う!

    View Slide

  9. Code Formatter!!
    “Spend more time discussing important
    issues in code review and less time on
    code style. Scalafmt formats code so that
    it looks consistent between people on
    your team.”
    scalafmt.org

    View Slide

  10. scalafmt
    - scala(とsbt)のためのコードフォーマッター
    - scalaプログラムの解析にscalametaを利用
    - 多機能!
    - プログラムの最大カラム数を指定できる(JSのprettierとか
    にもあるよね)
    - コメントとかのalignもできる!(gofmtとかもできるよね)
    - (両方あってすごい)

    View Slide

  11. scalariform or scalafmt
    - scalariform
    - 独自実装されたscalaのparserを利用
    - 2.12のsyntaxには未対応
    - scalafmt
    - scalametaというscalaのコードを解析するライブラリを利用
    - 最新のscalaのsyntaxにも対応、Dottyにも対応予定
    - maxColumn

    View Slide

  12. Install scalafmt
    - CLI
    - coursier
    - brew
    - SBT Plugin
    - sbt-scalafmt (こっちが公式)
    - neo-sbt-scalafmt
    - Editor/IDE plugin
    - IntelliJ plugin
    - vim-autofmt

    View Slide

  13. Install scalafmt (おすすめ)
    - CLI
    - coursier (coursier bootstrap で作ったlauncherをgit管理)
    - brew
    - SBT Plugin
    - sbt-scalafmt (こっちが公式)
    - neo-sbt-scalafmt
    - Editor/IDE plugin
    - IntelliJ plugin
    - vim-autofmt

    View Slide

  14. Configurations
    https://scalameta.org/scalafmt/docs/configuration.html
    - かなり多い。主要なものだけ紹介します。
    - maxColumn
    - continuationIndent
    - align
    - RewriteRules
    - verticalMultiline
    - trailingCommas

    View Slide

  15. maxColumn (default: 80)
    // before
    class ExtendTest extends A with B with C with D
    // after (maxCoulumn=20)
    class ExtendTest
    extends A
    with B
    with C
    with D
    ※必ず指定した値以内に収まるわけではないことに注意

    View Slide

  16. continuationIndent
    インデントのスペース数に関する設定
    - callSite (default: 2)
    - defnSite (default: 4)
    - extendsSite (default: 4)

    View Slide

  17. continuationIndent.callSite (default: 2)
    // before
    function(
    foo: Type,
    bar: Type
    )
    // after
    function(
    foo: Type,
    bar: Type
    )

    View Slide

  18. continuationIndent.defnSite (default: 4)
    // before
    def function(
    foo: Type,
    bar: Type
    )
    // after
    def function(
    foo: Type,
    bar: Type
    )

    View Slide

  19. continuationIndent.extendSite (default: 4)
    // before
    class UserProfile
    extends Profile
    with SomeTrait
    // after
    class UserProfile
    extends Profile
    with SomeTrait

    View Slide

  20. align
    「=」「<-」「%%」「//」などを縦方向で揃える設定
    - none
    - some (default)
    - more
    - most
    align.tokens で詳細を設定できるが、上記の4種類で十分

    View Slide

  21. align = none
    // before
    x match {
    case "foo" => 1 // comment
    case "hoge" => 123 // comment
    case "foobar" => 12345 // comment
    }
    // after
    x match {
    case "foo" => 1 // comment
    case "hoge" => 123 // comment
    case "foobar" => 12345 // comment
    }

    View Slide

  22. align = some (default)
    // before
    x match {
    case "foo" => 1 // comment
    case "hoge" => 123 // comment
    case "foobar" => 12345 // comment
    }
    // after (case match の => のみ align する)
    x match {
    case "foo" => 1 // comment
    case "hoge" => 123 // comment
    case "foobar" => 12345 // comment
    }

    View Slide

  23. align = more
    // before
    x match {
    case "foo" => 1 -> 1 // comment
    case "hoge" => 123 -> 123 // comment
    case "foobar" => 12345 -> 12345 // comment
    }
    // after (// や -> も)
    x match {
    case "foo" => 1 -> 1 // comment
    case "hoge" => 123 -> 123 // comment
    case "foobar" => 12345 -> 12345 // comment
    }

    View Slide

  24. align = more
    // before
    val foo = 1
    val hoge = 2
    libraryDependencies ++= Seq(
    "org.scala-lang" % "scala-compiler" % scalaVersion.value,
    "com.tanishiking" %% "scalaunfmt" % "0.0.1"
    )
    // after (= や sbtの % も)
    val foo = 1
    val hoge = 2
    libraryDependencies ++= Seq(
    "org.scala-lang" % "scala-compiler" % scalaVersion.value,
    "com.tanishiking" %% "scalaunfmt" % "0.0.1"
    )

    View Slide

  25. align = most
    // before
    for {
    x <- List()
    yyy = 2
    } yield {
    // ...
    }
    // after (<- と = をもalign)
    for {
    x <- List()
    yyy = 2
    } yield {
    // ...
    }

    View Slide

  26. RewriteRules
    - AvoidInfix
    - RedundantBraces
    - RedundantParens
    - SortModifiers
    - SortImports
    他にもいくつか...

    View Slide

  27. RewriteRules - AvoidInfix
    // before
    foo toList map { x =>
    x should have length 5
    }
    // after
    foo.toList.map { x =>
    (x should have).length(5)
    }

    View Slide

  28. RewriteRules - RedundantBraces
    // before
    val x: Int => String = {
    case 2 => { "two" }
    case _ => { "other" }
    }
    // after
    val x: Int => String = {
    case 2 => "two"
    case _ => "other"
    }

    View Slide

  29. RewriteRules - RedundantParens
    // before
    for {
    a <- b
    if ((b ++ b).length >= 2)
    } yield a
    // after
    for {
    a <- b
    if (b ++ b).length >= 2
    } yield a

    View Slide

  30. RewriteRules - SortModifiers
    // before
    final lazy private implicit val x = 42
    lazy final implicit private val y = 42
    // after
    implicit final private lazy val x = 42
    implicit final private lazy val y = 42
    sortModifiers.order = [“implicit”, “final” … ] で順番の設定も可能

    View Slide

  31. RewriteRules - SortImports
    // before
    import a.b.{c, a, b}, k.{
    g, f
    }
    // after
    import a.b.{a, b, c}, k.{f, g}

    View Slide

  32. verticalMultiline
    1.6.0-RC1 からの機能
    カラム数が `maxColumn` を超えたときのための設定
    - newlineBeforeImplicitKW
    - newlineAfterImplicitKW
    - newlineAfterOpenParen

    View Slide

  33. newlineBeforeImplicitKW (default: false)
    // before
    override def load()(implicit taskCtx: Context,
    ec: ExecutionContext
    ): Future[Seq[A] Or B]
    // after
    override def load()(
    implicit taskCtx: Context,
    ec: ExecutionContext
    ): Future[Seq[A] Or B]

    View Slide

  34. newlineAfterImplicitKW (default: false)
    // before
    override def load()(implicit taskCtx: Context,
    ec: ExecutionContext
    ): Future[Seq[A] Or B]
    // after
    override def load()(implicit
    taskCtx: Context,
    ec: ExecutionContext
    ): Future[Seq[A] Or B]

    View Slide

  35. trailingCommas
    - 1.6.0-RC1 からの新機能
    - 1.6.0-RC4時点ではまだ不安定、1.6.0 までお待ちを...
    - never
    - preserve (default)
    - always

    View Slide

  36. 結局どういう設定が良いのか
    作者いわくおすすめはdefault
    個人的には今日紹介した設定を好みに合わせて設定するのがオ
    ススメ

    View Slide

  37. diffをできるだけ少なくしたい?
    https://github.com/mikr/whatstyle
    $ python whatstyle.py --formatter scalafmt
    --ignoreopts IGNOREOPTS
    - diffの少ない設定を生成してくれる
    - 他のフォーマッタ(rustfmt, scalariform, clang-format な
    ど)にも対応
    - フォーマッタ本体と独立している

    View Slide

  38. diffをできるだけ少なくしたい?
    https://github.com/tanishiking/scalaunfmt
    maxColumn = [80, 100]
    align = [some, more, most]
    continuationIndent.callSite = [2, 4]
    continuationIndent.defnSite = [2, 4]
    verticalMultiline.newlineAfterImplicitKW = [true, false]
    $ scalaunfmt --config .scalaunfmt.conf test1.scala test2.scala
    - 設定の候補を(ホワイトリスト形式で)選べる
    - JVM warmup によるボトルネックがなく高速(whatstyle比)

    View Slide

  39. scalafmt is not perfect
    - https://gitter.im/scalameta/scalafmt
    - https://github.com/scalameta/scalafmt/issues
    質問はGitter、バグ報告はissueで

    View Slide

  40. まとめ
    - コードフォーマッタを導入することでstyleに関する議論や注意
    を減らし、本質的な問題に注目できる。
    - scalafmtの導入は
    - coursier bootstrap
    - sbt-scalafmt
    - おすすめ設定は基本default、好みで align,
    continuationIndent など
    - 質問はgitterまで!

    View Slide

  41. FAQ
    - JVM起動がボトルネックでscalafmtの起動が遅い
    - nailgun
    - SubstrateVM
    - githookでscalafmtしたい
    - https://medium.com/zyseme-technology/code-formatting-scalafmt-a
    nd-the-git-pre-commit-hook-3de71d099514
    - sbtマルチプロジェクトで設定共有したい
    - common project で scalafmt.conf を生成するタスクを scalafmtConfigに依存させると吉
    - コンパイル時にscalafmt走らせたい
    - scalafmtOnCompile (sbt-scalafmt)

    View Slide