hg commitを実行すると、コミットメッセージを編集するためのエディタが起動します。
このエディタにvimを使う場合に、いくつかの設定を行うことでプラスアルファの機能を追加しようという話です。
ありていに言うと committia がすてきな感じなのでMercurialでも同じようなことができないか、という。
コミット対象のファイルを開く
そのまま開く
コミット時には、以下のようなテキストが一時ファイルに出力され、オープンされます。
HG: コミットログを入力してください。'HG:' で始まる行は無視されます。
HG: メッセージが空のままならコミットを中止します。
HG: --
HG: ユーザ: Iwata
HG: ブランチ 'default'
HG: tortoisehg/hgqt/repotreeitem.py を変更
HG: tortoisehg/hgqt/run.py を変更
このように、コミット対象のファイルがテキスト上に最初から表示されているので、そこにカーソルを合わせて <C-w>f
、<C-w>gf
などとすることで簡単にそのファイルを開くことができます。
が、カレントディレクトリがリポジトリルートになっていないとこれは使えません。
Mercurial自体はリポジトリルートをカレントディレクトリに設定した状態でエディタを起動してくれるのですが、Windows版のvimだと、ファイルパスを引数に与えて起動した場合カレントディレクトリがそのファイルの親ディレクトリに変わってしまうようです。
なので、この挙動を回避するために、vim/gvimを直接起動する代わりにこんな感じのバッチを起動するようにします。
gvim -c ":e %1"
unified diffを開く
以下のようなfunctionを用意して、ファイルパス上での ;d
でunified形式のdiffを表示できるようにします。(同じタブ内でウインドウを分割して表示します)
" カーソル下のファイルに対するdiffをunified diff形式で表示
nmap <buffer> ;d :call ShowHgDiffUnified(expand('<cfile>'))<CR>
function! s:find_window(pattern)
for i in range(1, winnr('$'))
if bufname(winbufnr(i)) =~ a:pattern
return i
endif
endfor
return 0
endfunction
function! ShowHgDiffUnified(path) abort
let win = s:find_window('^\[hg diff\]')
if win == 0
botright vnew
else
execute win . 'wincmd w'
endif
setlocal filetype=diff buftype=nofile modifiable
silent! %delete
silent! execute 'file [hg diff] ' . fnamemodify(a:path, ':t')
silent! execute 'r!hg diff ' . a:path
setlocal nomodifiable
endfunction
- この辺のvimscriptはMercurialのコミットメッセージを編集する時だけ読み込まれればいいので、起動用バッチで
gvim -c ":e %1" -S hgcommit.vim
みたいに指定するか、autocmd FileType hgcommit
に引っかければいいと思います。
side by side diffを開く
さらに以下の設定で、同じく ;D
で別タブにvimdiffを表示できるようにします。
" カーソル下のファイルに対するvimdiffを新しいタブに表示
nmap <buffer> ;D :call ShowHgDiffSideBySide(expand('<cfile>'))<CR>
function! ShowHgDiffSideBySide(path) abort
execute 'tabedit ' . a:path
let encoding = &encoding
vnew
setlocal modifiable buftype=nofile
execute 'setlocal fileencoding=' . encoding
silent! execute 'file ' . fnamemodify(a:path, ':t')
silent! execute 'r!hg cat ' . a:path
silent! 1delete _
execute 'doautocmd BufNewFile ' . a:path
setlocal nomodifiable
windo diffthis
endfunction
コミットメッセージの挿入
GUIクライアントだと、最近入力したコミットメッセージを一覧から選択して挿入する機能があったりします。
正直あまり使いませんが、せっかくなのでこれもUnite sourceとして実装しておきましょう。
" 最近のコミットメッセージを表示するUnite source
let s:unite_last_messages = {
\ 'name' : 'last-messages',
\ 'default_action' : { '*' : 'insert' },
\ }
function! s:unite_last_messages.gather_candidates(args, context) abort
let log = system('hg log -r "last(all(), 20)" --template "{desc}\n___SEP___\n"')
let encoded = iconv(log, 'cp932', &encoding)
let splitted = split(encoded, '\n___SEP___\n')
return map(splitted, '{
\ "word": split(v:val, "\n")[0],
\ "source": "last-messages",
\ "kind": "word",
\ "action__text": v:val,
\ }')
endfunction
call unite#define_source(s:unite_last_messages)
unlet s:unite_last_messages
nmap <buffer> ;m :Unite last-messages<CR>
これは単純に最新20コミットを対象にして検索する例ですが、「authorが自分」などの条件を加えてもいいかもしれません。
Windows + win32mbcs環境を前提にしているので文字コード変換(iconv)をかけていますが、Linux環境でvimのencodingもutf8ならこれは不要です。
TODO
commit --amendへの対応
diffの機能がcommit --amend時には無力なので、そこを何とかする。
と言ってもエディタ側からは通常のコミットなのかamendなのかは分からないので、両方のモードを用意して手動でスイッチする感じ?