.vimrcに昔記述した,カーソルを移動せず,ファイルの全ての行末のホワイトスペースを消去するコマンドを,範囲指定できるように書き直したときにハマった話をする. 以下のコマンドを,
function! s:delete_trailing_whitespace() abort let cursor = getcurpos() silent keepjumps %s/\s\+$//ge call histdel('search', -1) call setpos('.', cursor) endfunction command! -bar DeleteTrailingWhitespace call s:delete_trailing_whitespace()
関数のrange識別を用いて,次のように書き直した.
function! s:delete_trailing_whitespace() abort range let cursor = getcurpos() execute 'silent keepjumps' a:firstline ',' a:lastline 's/\s\+$//ge' call histdel('search', -1) call setpos('.', cursor) endfunction command! -bar -range=% DeleteTrailingWhitespace <line1>,<line2>call s:delete_trailing_whitespace()
しかし,このコマンドを :DeleteTrailingWhitespace
のように実行する(範囲は1行目から最終行)と,カーソルがファイルの1行目に移動してしまう.
(-range=%
は,範囲指定をしないときは,1行目から最終行が指定されたことになる)
どうやら,range識別をつけた関数内における getcurpos()
で得られるのは,指定された範囲の最初の位置であるらしく,関数を呼び出した時点でのカーソル位置が得られるわけではない.
そのため,次のように,関数の引数として範囲を渡すように書き換えた.
(引数名に firstline
と lastline
を用いることはできない)
function! s:delete_trailing_whitespace(line1, line2) abort let cursor = getcurpos() execute 'silent keepjumps' a:line1 ',' a:line2 's/\s\+$//ge' call histdel('search', -1) call setpos('.', cursor) endfunction command! -bar -range=% DeleteTrailingWhitespace call s:delete_trailing_whitespace(<line1>, <line2>)
これならば,関数内で呼び出された getcurpos()
で得られるのは,関数を呼び出した時点でのカーソル位置である.
今までrange識別を用いた関数を書いたことがなかったので,このような挙動をすることがわかっていなかった. もっとスマートな解決方法があるのかもしれない.