rcmdnk's blog

Mr.Children/Split The Difference [DVD]

Vimのdiffモード機能はVimを使うべき一つの大きな理由になるくらい便利なものだと思います。

違いを見るためだけでも左右に並べて見れるので diffコマンドなんかよりもよりわかりやすく見ることが出来ます。

そのdiffモード関連のVimの機能やプラグイン等のまとめ。

Sponsored Links

vim -d

2つの似たようなファイルを比べたい時に

$ vim -d a.txt b.txt

とすると2つを比べた状態でファイルを開くことが出来ます。

3つ以上同時に比較することも可能です。

diffモードでは以下のオプションがセットされます。

  • diff: on
  • scrollbind: on
  • cursorbind: on
  • scrollopt: includes “hor”
  • wrap: off
  • foldmethod: “didf”
  • foldcolumn: value from ‘diffopt’, default is 2

Vim, :help diff

vimdiff

vim -d

$ vimdiff a.txt b.txt

vimdiffというコマンドを呼ぶのと同じです。

vimdiffを確認してみるとただのvimへのシンボリックリンクになってると 思いますが、 Vimではコマンド自体の名前をみて追加のオプションを渡す様になっています。

追記:vimdiff: シンボリックリンクを使ってオプション付きのコマンドを渡す方法 - bugfix

実際、

$ cd /tmp && touch a.txt b.txt
$ ln -s /usr/bin/vim ./vimdiff
$ ./vimdiff a.txt b.txt

とかすればdiffモードで立ち上がります。

:diffsplit

すでに立ち上げてる時に他のファイルと比較したいときは コマンドラインモードで

:diffsplit b.txt

diffsplit(またはdiffs)を使って他のファイルを開きます。

diffモードでの操作

  • [c: 次のdiff箇所へ移動。
  • ]c: 前のdiff箇所へ移動。
  • :diffget:diffg: diff行で現在のバッファの内容をもう片方のバッファへコピー。
  • do: “diff obtain”、diff行で現在のバッファの内容をもう片方のバッファへコピー。
  • :diffput:diffpu: diff行で現在のバッファの内容をもう片方のバッファへコピー。
  • dp: “diff put”、diff行で現在のバッファの内容をもう片方のバッファへコピー。
  • diffoff: diffモードを終了する。
  • diffupdate: diff状態をアップデート。

:diffgetdo、及び:diffputdpは同じ操作ですが、 コマンドモードのdiffgetなどは範囲を選んでVisualモードに入ってからも 使うことが出来、離れたdiffも一気にマージすることが可能です。

範囲を選んでからdを押してしまうとその部分が消えてしまうので dodpはVisualモードでは使えません1

3つ以上のファイルを同時に開いている時には、

:diffget 3

または

3do

の様にマージするバッファを指定します。

[c]cdodpをよく使います。

これらのコマンドでのマージは、diffのある行を含む範囲を選んでいるか、 diffのある行、またはその一つ下の行にいる時に出来ます。

相手にしか無い行はそもそも該当行にカーソルが行けませんが 一つ下の行に行くとdoとかが使えます。

diffモードの設定

diffモードでは上に書いたように幾つかの設定が特別に自動でセットされたりしますが diffモードの時だけ設定を変えたい時には &diffをチェックして行います。

1
2
3
4
5
6
7
8
function! SetDiffMode()
  if &diff
    set nospell
  else
    set spell
  endif
endfunction
autocmd VimEnter,FilterWritePre * call SetDiffMode()

こんな感じで。スペルチェックなんかは diffモードでもオンにしておくとかなり煩くなってしまうこともあるのでオフにしています。

VimEnterはVimを開くとき、FilterWritePrediffモードに入るときに該当するのでこれらの時に行うように。

また、diffモードの設定としてdiffoptというパラメーターがあって、 これに以下の様な値を設定することが出来ます。

  • filter: 片方だけに存在する行がある場合、無いファイルの方の行を空ける様になる。
  • context:{n}: 変更がある行間にn行表示する。それ以上ある場合にはfoldされる。設定が無いと6行。
  • icase: 大文字小文字を区別しない。
  • iwhite: スペースの違いは無視。
  • horizontal: 縦分割でdiffモード。
  • vertical: 縦分割でdiffモード。
  • foldcolumn:{n}: diffモード時のfoldcolumn(foldの状況を左側に表示、その幅)の設定。

現在の設定は以下のとおり。

1
set diffopt=filler,vertical

また、diffモードで開いて片方を残してそのまま編集したい時、 diffモードが続いてしまって見難いのでdiffoffをする必要がありますが、 これを自動で行う様に以下の様な設定をしておくと便利です。

1
autocmd WinEnter * if(winnr('$') == 1) && (getbufvar(winbufnr(0), '&diff')) == 1 | diffoff | endif

diffの表示に使われるカラー設定は以下の4つで、

  • DiffAdd: 比較相手に無い行。
  • DiffDelte: 比較相手にあるが自分にない部分。
  • DiffChange: 違いのある行。
  • DiffText: 違いのある部分(必ずDiffChangeに該当する行の中になる)。

好みですが使ってるカラースキーマ(ron)のものが余り気に入らなかったので 以下の様に設定しています。

1
2
3
4
hilight DiffAdd cterm=bold ctermbg=17 gui=bold guibg=slateblue
hilight DiffDelete ctermbg=6 guibg=coral
hilight DiffChange ctermbg=22 guibg=darkgreen
hilight DiffText cterm=bold ctermbg=52 gui=bold guibg=olivedrab

:DiffOrig

Vimデフォルトコマンドではありませんが、 Helpに載ってるコマンドで、

1
2
3
4
if !exists(":DiffOrig")
  command DiffOrig vert new | set bt=nofile | r # | 0d_ | diffthis
        \ | wincmd p | diffthis
endif

の様なDiffOrigというコマンドを作っておくと、

:DiffOrig

とすることで開いたファイルの初期状態からの違いをdiffモードで見ることが出来るようになります。

.vimrcに必須なものの一つ。

linediff.vim

linediff.vimは 任意の二箇所のdiffを表示してくれるプラグイン。

同一ファイル内の2箇所でも可能です。

まず、diffを見たい箇所の一つをVisualモードで選択し、

:LineDiff

コマンドを打ちます。するとその範囲の上と下側にマークがつくので、 その状態でもう一箇所へ移動してVisualモードに入って選択し、 再び

:LineDiff

すると先ほど選択した部分とのdiffが別枠で表示されます。

同じファイル内の同じ様な場所のdiffを見たり、 違うファイルでも一部だけ比較したい時などに便利です。

これを知るまでは見たい部分だけ他のファイルに書きだして vim -dしたりしてましたがこのプラグインで大分作業が楽になりました。

vcscommand.vim

vcscommand.vim はsvnやgit等のバージョン管理システム上のファイルを扱う際に 色々と便利にしてくれるプラグインです。

ファイルのコミットなども出来ますが、 この中にコマンド:VCSVimDiffまたは<Leader>cvで 現在開いているファイルの未コミット分の差分を見ることが出来ます。

基本ターミナルで作業するのでgitコマンドなどをこのプラグインで使うことはありませんが、 このdiff機能はとても便利でよく使います。 無いと困るくらい。

vcscommand.vimの他の機能については以下の記事が詳しいです。

vcscommand.vimを少し便利に使う - Keep It Simple, Stupid

diffchar.vim

diffchar.vim はdiffの表示時に単語単位のハイライトを強化してくれるプラグイン。

通常、diffモードでは行内に違う部分がある場合、 違う部分が初めて出たところから最後に出るところまで全体がDiffTextのハイライト対象になります。

aaa bbb ccc ddd eee fff ggg hhh

aaa bbb xxx ddd eee xxx ggg hhh

を比較した場合、ここではcccfffの部分が違いますが、 ccc ddd eee fffの部分がハイライトされます。

これがdiffchar.vimを入れるとcccfffの部分だけがハイライトされる様になります。

ただ、場合によっては逆に見づらくなることもあるので 使ってみて、という感じです。

最初入れてちょっとあれかな、と思って外してましたが 今はまた入れています。

vim-diff-enhanced

vim-diff-enhanced はdiffを作るアルゴリズムを変更できるプラグイン。

diffモードで、

:PatienceDiff

とすると、Patienceアルゴリズムと言うVimで使っているアルゴリズムとは 違うアルゴリズムでdiffを再描写します。

 :EnhancedDiffDisable

で元に。

GitHubのREADMEにある例だと、 同じような内容の関数がいくつか続くような時、 本来比べてほしくない部分を比較してしまう時がありますが、 Patienceを使うと綺麗に分けてくれています。

これも場合によってはデフォルトの方が良い場合もありますが、 上記のコマンドで簡単に元に戻せるので入れておくと便利です。

READMEにあるように、デフォルトをPatienceにするように

1
2
3
if &diff
  let &diffexpr='EnhancedDiff#Diff("git diff", "--diff-algorithm=patience")'
endif

の設定を.vimrcで行っています。

Sponsored Links
  1. 同じような理由で、:diffgetに対してdg を使えないのは、ddeleteのコマンドでもあるので、dgg (現在いる行から上を全て削除)の一部だったりするため。