vimユーザなら超絶便利なciw (1単語を削除して挿入)等のオペレータを多用していることと思います。
一応説明すると、vimにおけるオペレータ( c, d など)とは、ノーマルモードで押下したあと「モーション」入力待ちになり、与えられたモーションに対して所定の操作 (削除して挿入, 削除) を行うものです。モーションとしてはhjklやwなどの移動コマンド、もしくはiwやawといったテキストオブジェクトが使えます。
しかしヤンクした内容を貼り付けようとすると、P(貼り付け)してからDb(前方単語削除)などやや面倒な操作が必要になります。 (多分)
そこで、「レジスタの内容で置き換える」オペレータ <C-K> を定義します。オペレータと殆ど同様に使えるv (キャラクタ単位ビジュアル)でも <C-K> により「現在選択中の領域」をレジスタで置き換えられるように、vnoremapも併せて定義します。
nnoremap <silent> <C-K> :set opfunc=ReplaceMotion<CR>g@
vnoremap <silent> <C-K> :<C-U>call ReplaceMotion('', 1)<CR>
function! ReplaceMotion(type, ...)
let sel_save = &selection
let &selection = "inclusive"
let reg_save = @@
let mark_save = getpos("'a")
if a:0 " visual mode
silent exe "normal! '>$"
if getpos("'>") == getpos('.')
silent exe 'normal! `<"_d`>"_d$"0p`<'
else
silent exe 'normal! `>lma`<"_d`a"0P`<'
endif
elseif a:type == 'char' " char motion
silent exe "normal! ']$"
if getpos("']") == getpos('.')
silent exe 'normal! `["_d`]"_d$"0p`['
else
silent exe 'normal! `]lma`["_d`a"0P`['
endif
endif
let &selection = sel_save
let @@ = reg_save
call setpos("'a", mark_save)
endfunction
これを.vimrcにコピペすれば、
<C-K>3w → 現在位置から3単語をレジスタの内容で置換 <C-K>i( → 現在のカーソル位置を含む括弧内部をレジスタの内容で置換 (ビジュアル選択)<C-K> → 選択部分をレジスタの内容で置換
などの操作が可能になります。
デモの動画を撮ってみました。
- 「test, yank, words」をヤンクして<C-K>i(によりfunctionの引数内を置換
- vimをヤンクして<C-K>iwによりemacsを置換
- ビジュアル選択中に<C-K>を押下して選択部分をレジスタで置換
スクリプトの解説をすると、
nnoremapの方では、ノーマルモードでの<C-K>をオペレータとして定義しています。これはオペレータを実行するコマンドg@を利用していて、g@{motion}とするとモーションの開始/終了範囲がマーク[、]として設定された上でopfuncに指定したコールバックが呼ばれます。
<C-K>ではopfuncを設定し、{motion}の所はユーザに任せることで独自のオペレータ定義を可能にしています。
vnoremapではオペレータではなく、単にマーク<, >を利用して同様の動作を実装しています。if文の中でさらに分岐があるのは、削除オペレータdがexclusive=終端を含まない開区間操作であるためです。似たようなことを4つも書いているのであまり綺麗ではありませんが…
追記: 20090706
Re: vimでモーション(or選択範囲)をレジスタの内容で置き換えるオペレータ @ 7bit - while ("im the true Vim master"); - vimグループ
で突っ込まれた内容を踏まえるなどしてちょっと綺麗 & レジスタ指定を可能に しました。
また、削除オペレータがexclusiveという表現は正しくないようで、オペレータのペンディングに対してexclusive/inclusiveがある模様。dwとdvwで比較すると、dvwの方は終端も含んでいる。たぶんそういうことかな…

No Comments yet »
コメント RSS TrackBack URI
コメントをどうぞ