Humanity

Edit the world by your favorite way

Vim の terminal でも : や <Esc> でノーマルモードに移りたい。けど完全に潰したくはない

そんな方のための Hack をふと某 slack に公開したらウケが良かったのでブログに書いておきます。

一言で言うと

  • 「シェルのプロンプトが空の場合は他のバッファと同じように振舞う」ようにしたら便利だった
  • ついでにプラグイン化した

という話です。

前置き

Vim の terminal window では動いているプロセスにキー入力が送られるため :コマンドラインウィンドウに移ったり、 <Esc>ノーマルモードに移ることができません。
コマンドラインウィンドウに移るには <C-w>:ノーマルモードに移る*1には <C-w>N を押す必要があります。
これを terminal window かそうでないかで使い分ける必要があり、それが地味にストレスでした。

しかし下記の設定を行うことで上記を意識せずに操作を行うことができます。

" Enter command-line / normal-mode if current line is empty prompt
function! s:is_empty_prompt() abort
  return term_getline(bufnr(''), '.') =~# (&shell =~# 'sh$' ? '\$ $' : '> $')
endfunction
tnoremap <expr> :     <SID>is_empty_prompt() ? "\<C-w>:" : ':'
tnoremap <expr> <Esc> <SID>is_empty_prompt() ? "\<C-w>N" : "<Esc>"

というのを某 slack に共有したらウケが良かったのとウーパールーパーのアイコンの人に勧められたのでプラグイン化してみました。
以下で上記と同じことができます。

" Enter command-line / normal-mode if current line is empty prompt
function! s:empty_prompt_mappings() abort
  call empty_prompt#map(#{lhs: ':', rhs: "<C-w>:"})
  call empty_prompt#map(#{lhs: '<Esc>', rhs: "<C-w>N"})
endfunction
autocmd VimEnter * ++once call s:empty_prompt_mappings()

行数増えてない?というのはもっともなツッコミですが、プラグイン化することで

  • (今後の予定として) PS1 を検知して空のプロンプト行をより正確に検知できるようになる
    • デフォルトだと簡易的に 'shell' オプションが *sh だったら $ で終わる行、そうでなかったら (cmd.exe/powershell を想定して) > で終わる行としてある
  • 名前がつくことで管理しやすく & 外部からわかりやすくなる (ポリシー的な意味で)

という感じでプラグイン化することにしました。

github.com

注意

Vim 8.2 が必要

それ以前の Vim を使っている方は、すみませんが Vim 本体のアップデートをお願いします。

デフォルトの空プロンプトの判定

デフォルトだと「プロンプトが空かどうか」の判定には

  • シェルが *sh 系 (&shell =~# 'sh$') なら $ で終わる行
  • それ以外なら (cmd.exe/powershell を想定して) > で終わる行

を空のプロンプトと判定しています。
そのためプロンプト文字列をいじっている方はうまくいかない場合があります。

その場合は g:empty_prompt#pattern を変更していただくようお願いします (現在行に対する Vim正規表現です)。

*1:正確には Terminal-Normal mode (:help Terminal-Normal)