Slide 1

Slide 1 text

Format things with scalafmt Scala 関西 Summit 2018 @tanishiking

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

スタイルガイドを整備しても “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/

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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 ※必ず指定した値以内に収まるわけではないことに注意

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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 }

Slide 22

Slide 22 text

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 }

Slide 23

Slide 23 text

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 }

Slide 24

Slide 24 text

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" )

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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” … ] で順番の設定も可能

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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]

Slide 34

Slide 34 text

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]

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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比)

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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)