Vimのヤンクはレジスタ領域に保持され、その名の通りVimにとっての一次記憶の領域であり、作業者の海馬の拡張領域とも言える。非常に重要な機能の一つであるが、これまで上手く活用できていなかったので少し気の利いたものを考えてみた。
用語整理
- ヤンク ...
yy
でお馴染みのVimにおけるコピーのこと - レジスタ ... ヤンクしたものが保持される領域。
0
〜9
a
〜z
に加えて+
*
/
など特殊なものをある - クリップボード ... OS上のコピペで使う領域。ざっくりと「クリップボード」と「プライマリ」の2種類ある(後述)
OSのクリップボードについての基礎知識
私がLinuxしか持ってないのでLinuxを前提とする。まず先にその辺を説明。Linuxで他のOSと同様にブラウザなどでテキスト選択して<Ctrl> + C
を押すとそのテキスト片は「クリップボード」に保持される。<Ctrl> + V
でペーストできる、みんなが知ってるクリップボードである。
問題はもう片方の「プライマリ」である。こいつはテキスト選択しただけで、その選択部分が保持される領域である。昔はX orgにはこれしかなかったが、時代を経て”死に機能”になってしまったものである。これを出力するときにわかりやすいのはターミナルである。基本的に「マウスのホイールクリック」で出力できるはずである。
Vimのクリップボード拡張
Vimにはクリップボード拡張があり、これによりOSのクリップボードとVimのレジスタを連携させることができる。
拡張なし
set clipboard=
SSHとかでGUIなしのサーバーで触るときはこの状態が多いかもしれない。ヤンクした内容は ""
(無名レジスタ)に入り、ペースト時も ""
から貼り付けられる。
unnamedplus
set clipboard=unnamedplus
これがLinuxでは「クリップボード」との連携を意味し、ヤンクした内容は"+
に保持される。"+
はOSの「クリップボード」と共有された状態になっていて、ブラウザなどで<Ctrl> + C
を押すと、その内容が"+
にも入ってくる。逆にlet @+ = 'hoge'
と書き込めば、他のアプリで<Ctrl> + V
をおせばhoge
を貼り付けられる。
unnamed
set clipboard=unnamed
これはLinuxでは「プライマリ」との連携を意味する。ヤンクした内容は "*
に保持される。なのでヤンクしたものをターミナルで中クリックすればその内容をターミナルに貼り付けたりできる。
ややこしいことに、macOSやWindowsではこっちがOSのクリップボードと連携している。なので.vimrc
を書くときはOSを判別して
if has('win32') || has('win64') || has('mac')
set clipboard=unnamed
else
set clipboard=unnamedplus
endif
というように書くのが良い。
ヤンク履歴を管理する
下のような仕様を考える
- ヤンクしたものを
"+
→"a
→"b
→"c
→"d
→……とずらす - 改行のみ、空文字は無視する
"wyy
のようにレジスタを指定した場合も適切に動く
実装するとこんな感じ
function! ShiftRegister() abort
if &clipboard =~? 'unnamedplus'
silent let l:clipboard = getreg('+')
elseif &clipboard =~? 'unnamed'
let l:clipboard = getreg('*')
else
let l:clipboard = getreg('"')
endif
if l:clipboard ==# '' || l:clipboard ==# "\<NL>"
return
endif
" 番号のレジスタだと削除時に使われるのでアルファベットの前半を使う
let l:list= ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
if @a != l:clipboard
let l:i = len(l:list) - 1
while l:i > -1
call setreg(l:list[l:i], (l:i == 0 ? l:clipboard : getreg(l:list[l:i - 1])), 'v')
let l:i -= 1
endwhile
endif
endfunction
function! RegisterPrefix(key) abort
call ShiftRegister()
" `"wyy` のようにレジスタを指定するとv:registerには`'w'`が入ってくる
return '"' . v:register . a:key
endfunction
" 基本操作を書き換える
noremap <silent> <expr> y RegisterPrefix('y')
noremap <silent> <expr> d RegisterPrefix('d')
noremap <silent> <expr> c RegisterPrefix('c')
noremap <silent> <expr> Y RegisterPrefix('Y')
noremap <silent> <expr> D RegisterPrefix('D')
noremap <silent> <expr> C RegisterPrefix('C')
nnoremap <silent> <expr> yy RegisterPrefix('yy')
nnoremap <silent> <expr> dd RegisterPrefix('dd')
" リターンで行をコピーできるようにする
nnoremap <silent> <expr> <C-m> RegisterPrefix('yy')
vnoremap <silent> <expr> <C-m> RegisterPrefix('y')
onoremap <silent> <expr> <C-m> RegisterPrefix('y')
こんな感じのを追加。割と便利。