LoginSignup
9
5

More than 3 years have passed since last update.

【Go】goimportsによるimportのグルーピングが不満なので実装した

Last updated at Posted at 2019-10-28

実装

github.com/istsh/goimport-fmt

実装するに至った背景

筆者は普段、gofmtgoimportsを使用しているのですが、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. 1行目からimportブロックの直前まで
  2. importブロック
  3. 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の値によってインストール先が変わるので処理がいくらか複雑になるのと、できるだけ早く処理を完了させたかったので楽しました。

3. importより後

importより後のコードをそのまま保持しておく。

GoLandのFileWatchers

私は普段GoLandで開発しているので、GoLandで保存時に自動で実行するように設定しています。

Preferences > Tools > File Watchers
+から、下記のように設定を追加。
Screen Shot 2019-10-29 at 2.22.37.png

まとめ

コードレビューで指摘されて、importの順序や空行をいれるだけの修正をし、再度レビュー依頼をするという、レビュアーにとってもレビュイーにとっても無駄なやりとりが何度も起きていたので、いくらか改善されそうです。
他の人にも使ってもらえるように、今後も改修していくかもしれません。

参考

Go言語でつくるインタプリタ
gofmt.go

9
5
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
5