実装
実装するに至った背景
筆者は普段、gofmt
やgoimports
を使用しているのですが、importの順序が毎回決まった順序にならず、手動で直すことが何度もあったので、自動でソートしてくれる機能を実装することにしました。
importの順序
importは下記のようにグルーピングして空行を入れて記述することになっています。
package main
import (
// 標準パッケージ
"fmt"
"hash/adler32"
"os"
// サードパーティのライブラリ
"appengine/foo"
"appengine/user"
// 自プロジェクト
"github.com/foo/bar"
"rsc.io/goversion/version"
)
常にこのようにグルーピングとソートが行われればいいのですが、どうやら空行毎に分割してソートしているようで、同じグループの間や、自プロジェクトのimportが標準パッケージに混ざっていても、前後に空行があると正しくグルーピングとソートが行われません。
例えば、
package main
import (
"fmt" // 標準パッケージ
"github.com/foo/bar" // 自分のプロジェクト
"hash/adler32" // 標準パッケージ
"os" // 標準パッケージ
"appengine/foo" // サードパーティのライブラリ
"appengine/user" // サードパーティのライブラリ
"rsc.io/goversion/version" // 自分のプロジェクト
)
このようにバラバラに記述されたimportでも、goimport-fmt
を使えば自動でグルーピングとソートをしてくれます。
インストール
$ go get github.com/istsh/goimport-fmt
実行コマンド
% goimport-fmt -filepath path/to/file.go -ownproject github.com/my-repository/my-project
やっていること
実装はシンプルで、対象のファイルを上から読んでいき、下記の3ブロックに分割します。
- 1行目からimportブロックの直前まで
- importブロック
- importより後
1. 1行目からimportブロックの直前まで
1行目からimportブロックの直前までのコードをそのまま保持しておく。
2. importブロック
今回重要なのはこのブロックです。
importの記述はいろいろありますが、下記の4パターンを想定して実装しました。
パターン1: 1つのパスを1行で記述
import "fmt"
パターン2: 1つのパスを3行で記述
import (
"fmt"
)
パターン3: 複数のパスそれぞれにimportをつけて記述
import "fmt"
import "strconv"
import "github.com/pkg/errors"
import "github.com/my-repository/my-project/log"
パターン4: 複数のパスを、括弧で括って記述
import (
"fmt"
"strconv"
"github.com/pkg/errors"
"github.com/my-repository/my-project/log"
)
パターン1〜3は変更せずに終了させていて、パターン4のような記述の時に自動更新が走ります。
記述されたパッケージを下記の3つに分類します。
- 標準パッケージ
-
$GOROOT/src/xxx
に一致するディレクトリがあるかどうかで判定しています。
-
- 自分のプロジェクト
- 実行時の引数で渡された
ownproject
と前方一致するかどうかで判定しています。
- 実行時の引数で渡された
- サードパーティのライブラリ
- 上記2つに該当しない場合はサードパーティのライブラリだと判定しています。
当初は$GOPATH
配下に一致するディレクトリがあるかどうかで判定しようと思ったのですが、$GO111MODULE
の値によってインストール先が変わるので処理がいくらか複雑になるのと、できるだけ早く処理を完了させたかったので楽しました。
- 上記2つに該当しない場合はサードパーティのライブラリだと判定しています。
3. importより後
importより後のコードをそのまま保持しておく。
GoLandのFileWatchers
私は普段GoLandで開発しているので、GoLandで保存時に自動で実行するように設定しています。
Preferences > Tools > File Watchers
の+
から、下記のように設定を追加。
まとめ
コードレビューで指摘されて、importの順序や空行をいれるだけの修正をし、再度レビュー依頼をするという、レビュアーにとってもレビュイーにとっても無駄なやりとりが何度も起きていたので、いくらか改善されそうです。
他の人にも使ってもらえるように、今後も改修していくかもしれません。