koturnの日記

普通の人です.ブログ上のコードはコピペ自由です.

Vim scriptのinput()で<Esc>を検出する方法

Vimプラグインを作っているときに,input()<Esc>キーを感知できればいいなと思ったのがきっかけである. どうやら,ここに書いてあるinput()のラッパー関数を使えばOKのようだ.

function! s:input(...) abort
  new
  cnoremap <buffer> <Esc> __CANCELED__<CR>
  let ret = call('input', a:000)
  bwipeout!
  redraw
  if ret =~# '__CANCELED__$'
    throw 'Canceled'
  endif
  return ret
endfunction

このラッパー関数が行っていることは,

  1. 新しいバッファを作成.
  2. バッファローカルなコマンドモードの<Esc>キーのマッピングを定義.
  3. input()関数コール時に,<Esc>キーの入力を検知すると,__CANCELED__<CR>を入力したことになる.例えば,aa<Esc>という入力はaa__CANCELED__という入力と等価になる.
  4. 入力文字列の末尾が__CANCELED__ならば,が押されたと判断し,例外を投げる.

ただし,redrawは必ずしも必要でないことや,<C-c>の割り込みを考慮すると,以下のようにするのがよいだろう. (個人的な好みで,<Esc>時には数値0を,<C-c>時には数値-1を返却するようにしてある.)

function! s:input(...) abort
  new
  cnoremap <buffer> <Esc> __CANCELED__<CR>
  try
    let input = call('input', a:000)
    let input = input =~# '__CANCELED__$' ? 0 : input
  catch /^Vim:Interrupt$/
    let input = -1
  finally
    bwipeout!
    return input
  endtry
endfunction

今回の方法は非常に画期的ではあるが,inputlist()では同様の手法を用いることができなかった....