💬

10行のVim scriptで実現する快適?Conventional Commits

2021/12/08に公開2

この記事は Vim Advent Calendar 2021 の8日目に向けたものです。

TL;DR

  • .gitmessage とちょっとした Vim script を用意して Vim 上での Conventional Commits をサポートするようにしたら捗った話

はじめに

みなさん、良いコミットメッセージを書いているでしょうか?
私は全然です。。

わかりづらくないように気をつけつつも毎回「こんな感じで良いのかなぁ」と悩みながら書いていました。

そこで重い腰を上げて Conventional Commits を導入しようと思ったのですが、Conventional Commits をサポートするいい感じの Vim プラグインが見つからず、Commitizen を見てみるもセットアップが若干手間だったことと今の Git ワークフロー的に Not for me だったりしてピンとくるものがない状態でした。

欲しい機能はたいしてないので、では作ってしまいましょう!というのが今回のお話です。

欲しい機能

私は普段の Git 操作には Tigiberianpig/tig-explorer.vim を使っていて、コミットメッセージも Vim 上で入力しています。
なので Vim 上で Conventional Commits をサポートしてくれることが最低条件です。

その他、必要だと思ったのは以下のような機能です。

  • コミットの型は選択したい
    • 型を覚えたくない。極力何も考えずに選べるようにしたい
  • 余計なアクションを必要としない
    • 例えば型の候補を出すためのマッピングが必要になると、それを忘れる/面倒になって使わなくなる
    • 基本的にものぐさなので最小のアクションで済ませたい
  • Conventional Commits を強制しない
    • 本末転倒感はあるがテスト用のコミットなどは適当なメッセージをつけておいて、あとで squash なり reword したいので、強制されたくない

方針

基本的には .gitmessage で定義したコミットメッセージテンプレートで型などを管理する方針にしました。
理由は以下の通りです。

  • 型はポップアップなどで選択させることもできるが、「ポップアップを表示させる」という追加のアクションが必要
    • またポップアップ上では表示できる情報が限られる
      • どういうときにどの型を使うのかといった補足情報を載せづらい
  • .gitmessage はそのまま Vim のバッファに展開されるので、選択肢を表示するための余計なアクションが不要
    • Git に標準で搭載されている機能を使うのでセットアップが楽
    • 仮にチーム内でコミットの型を共有したい場合でも共有しやすい

用意した .gitmessage は以下のようなものです。
フォーマットとしては # 型 型の説明 のようにコミットの型と説明をスペース区切りにしたコメント行にしています。
コメントなのは選択肢を無視してコミットメッセージを書けるようにするためです。
「型の説明」があることでどういう時にどの型を使えばいいかを覚える必要をなくしています。

# feat      feature
# fix       bug fix
# deps      dependencies
# breaking  breaking changes
# docs      documentation
# style     formatting, missing semi colons, etc
# refactor  refactoring
# test      when adding missing tests
# chore     maintain

# [optional body]

# [optional footer]

# ==========
# Conventional Commits
# - https://www.conventionalcommits.org/ja/v1.0.0/
# AngularJS Git Commit Message Conventions
# - https://gist.github.com/stephenparish/9941e89d80e2bc58a153#allowed-type

なお Conventional Commits の仕様としては型に付随してオプショナルな scope も書けますが、今回の .gitmessage では考慮していません。将来的に Conventional Commits を使いこなしてきて必要になったら考えます。

.gitmessage はコミット時のテンプレートとして展開されるように、以下のように git config の commit.template として登録しておいてください。

$ git config --global commit.template /PATH/TO/.gitmessage

作ったもの

肝心の Vim script は以下になります。
runtimepath 配下に ftplugin/gitcommit.vim というファイルを作成してコピペしてもらえれば動くはずです。

function! s:select_type() abort
  let line = substitute(getline('.'), '^#\s*', '', 'g')
  let title = printf('%s: ', split(line, ' ')[0])

  silent! normal! "_dip
  silent! put! =title
  silent! startinsert!
endfunction

nnoremap <buffer> <CR><CR> <Cmd>call <SID>select_type()<CR>

runtimepath とは?という方は :set rtp コマンドを実行すれば確認できます。

やっていることは単純でカーソル配下の行にあるコミット型だけを残して挿入モードに入るだけです。
私は <CR><CR> にマッピングしていますが、ここは好みに応じて変えていただければと思います。

たったこれだけですが(個人的に)面倒なことが無いので、途中でやめることなく続けられています。
もっと長く使う中で不満は出てくるかもしれませんが、現状では満足度はかなり高いです。

最後に

Vim で便利機能を用意したいとなると真っ先に Vim プラグインを作らないと!となって及び腰になってしまう方も少なくないと思います。
しかし一般的に配布されている Vim プラグインの形を取らなくても、(プラグインといえばプラグインではありますが) こんな形で拡張可能であることも知れると「Vim 楽しいかも?」と思えるかもしれません。
こういったちょっとした拡張を簡単にできるのが Vim の良いところかと思います。

Discussion

kawarimidollkawarimidoll

非常に参考になりました!便利に使わせていただいております
記事で紹介されていたものをアレンジして、ftplugin/gitcommit.vimを使わなくても同様の動きを作れました
ご参考になれば…

define in .vimrc or init.vim
augroup select-commit-title
  autocmd!
  autocmd FileType *commit nnoremap <buffer> <CR><CR>
        \  <Cmd>silent! execute 'normal! ^w"zdiw"_dip"zPA: ' <bar> startinsert!<CR>
augroup END
uochanuochan

とてもスマートですね!ありがとうございます!!