rcmdnk's blog
Last update

Mr.Children/Split The Difference [DVD]

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

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

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

vim -d

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

$ vim -d a.txt b.txt

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

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

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

  • diff: on, diffモードであるかどうかの指標。
  • scrollbind: on, 比べてる2つのウィンドウのスクロールを同期。
  • cursorbind: on, 比べてる2つのウィンドウのカーソル位置を同期。
  • scrollopt: includes “hor”, scrollbindの方法。”hor”(horizontal)で水平方向のスクロールを同期。 元々デフォルトで”ver”(vertical)オプションが入っているので通常縦方向も同期する。
  • wrap: off, 一行が長い時に折り返しをしない様にする。折り返しをすると左右で同期している時に表示が崩れてしまう事があるので一応offが推奨。
  • foldmethod: “diff”, 畳み込みの方法。”diff”は違いがある付近のみを残して畳み込む様にする。
  • foldcolumn: value from ‘diffopt’, default is 2, 左側に示すdiffの状況表示の幅。

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状態をアップデート。

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

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

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

:diffget 3

または

3do

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

[c、]cとdo、dpをよく使います。

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

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

diffモードの設定

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

1
2
3
4
5
6
7
function! SetDiffMode()
  if &diff
    setlocal nospell
    setlocal wrap<
  endif
endfunction
autocmd VimEnter,FilterWritePre * call SetDiffMode()

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

wrapに関しては、違いの箇所を見るのに横にスクロールしないと見えないのは 結構面倒なのでwrapをオンにしています。 (wrap<で<を付けることでグローバルセッティングの値を使います。 普段はオンにしているので。 <無しで常にオンにしてもOK。)

wrapをオンにすると、片方のファイルでwrapが必要ない程短い部分があると 左右の表示がズレてしまうことがあります。 そのような場合にはwrapをオフにした方が良いのですが、 通常比べるファイルでは同じような書式で同じような幅を持った物同士のものが多いので オンにして比べやすい方が便利な場合が多いです。

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

また、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
5
set splitright
if !exists(":DiffOrig")
  command DiffOrig vert new | set bt=nofile | r # | 0d_ | diffthis
        \ | wincmd p | diffthis
endif

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

:DiffOrig

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

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

追記: 2016/03/08

splitrightをしておくと左側に編集中のファイル、右側に元のファイルを表示します。 (デフォルトではこのオプションはオフで、そのままだと右側に編集中のファイルがおかれる。)

追記ここまで

linediff.vim

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

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

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

:LineDiff

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

:LineDiff

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

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

デフォルトの設定だと現在のバッファを後ろに回して 比較する者同士だけの表示に移ります。

作業中のバッファを表示したまま、新たにの方に違いを表示させるには

1
2
let g:linediff_first_buffer_command  = 'leftabove new'
let g:linediff_second_buffer_command = 'rightbelow vertical new'

の様な設定をしておくと便利です。

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

vcscommand.vim

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

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

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

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

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

diffchar.vim

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

追記: 2016/03/08

元々、 vim-scripts/diffchar.vim というvim-scriptsレポジトリにあるものを使っていましたが、 今は作者の人が上の自分のレポジトリに最新版を公開しています。

最新版の方では下に書く様にオプションのデフォルトが変更されていたり ヘルプが入っていたりします。

追記ここまで

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

aaa bbb ccc ddd eee fff ggg hhh

と

aaa bbb xxx ddd eee xxx ggg hhh

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

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

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

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

追記: 2016/03/08

この辺の見づらくなる所は vim-scripts/diffchar.vim でのデフォルトが一文字単位でチェックしてハイライトする所が原因です。

aaaとabaみたいのがある場合にbの部分だけをハイライトするような。 これだけだと別に良いのですが、長い文章で全然違う単語が並んでいるのに、 逆に幾つかの文字はたまたま合うのでそれらを除いた部分がハイライトされる 様な事が起こります。

これはg:DiffUnitというオプションで変更できます。

1
let g:DiffUnit = 'Word1'
  • ‘Word1’ : \w+ word and any \W single character (default)
  • ‘Word2’ : non-space and space words
  • ‘Word3’ : < or > character class boundaries
  • ‘Char’ : any single character
  • ‘CSV(,)’ : separated by characters such as ‘,’, ‘;’, and ‘\t’

以前のvim-scriptsにあるレポジトリのもの(v5.5)では ‘Char’がデフォルトでしたが、 rickhowe/diffchar.vim では’Word1’がデフォルトになっていて、 単語単位でハイライトされるので見やすくなります。

追記ここまで

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 を使えないのは、dがdeleteのコマンドでもあるので、dgg (現在いる行から上を全て削除)の一部だったりするため。 ↩

Sponsored Links

« Macでmas-cliを使わずコマンドラインからApp StoreアプリのIDを調べたりインストールする方法 シェルスクリプトでの配列の初期化の速度 »

}