読者です 読者をやめる 読者になる 読者になる

vimの挿入モードで矢印キーを打つとA, B, C, Dとかなってしまう環境で移動するための設定

Vim

以下で書いた方法は良くないようです. 最後の追記を必ず参照して下さい.



こんにちは. 端末vimしか使えない者です.


問題点: 端末vimの挿入モードで矢印キーを打つとこうなる

解決策: ノーマルモードでのO[A-D]を, 挿入モードに戻るキーにマップする
Dとか入ってしまうのは, 例えば左矢印は<ESC>ODってなってるため.
すなわち,

  1. 挿入モードからノーマルモードに戻る
  2. ノーマルモードのOで一行上に新しい行を作り挿入モードに移動する.
  3. Dを挿入する

という3つの段階で, Dが挿入される.
この場合, ノーマルモードでのODを「挿入モードに戻る+Left」にマップすれば良い.

nnoremap OA gi<Up>
nnoremap OB gi<Down>
nnoremap OC gi<Right>
nnoremap OD gi<Left>

giというのは, ノーマルモードに戻る前の瞬間の挿入位置に戻るマッピング.
詳しくは:h gi.
どうしてiとかaとかじゃダメかというのは, 実際にそれでmapして確認してみて下さい.
thincaさんにLingrで`^っていうのを教えていただいた. help `^を見て, giを見つけました.
ありがとうございます.
(後, <ESC>OAとかを何かにマップするのはとても賢いやり方とはいえない)

更なる問題点: neocomplcacheの補完がいちいち発動してうざい

CursorMovedIか, CursorHoldIかどっちかで発動してしまう.
矢印キーで動いている途中は補完しないでほしい.

解決策: neocomplcache#cancel_popupを使う

function! s:goback_insert(key)
  return "gi" . a:key . neocomplcache#cancel_popup()
endfunction
nnoremap <expr> OA <SID>goback_insert("\<Up>")
nnoremap <expr> OB <SID>goback_insert("\<Down>")
nnoremap <expr> OC <SID>goback_insert("\<Right>")
nnoremap <expr> OD <SID>goback_insert("\<Left>")


Home, End, Del, PageUp, PageDownもそれぞれH, F, 3~, 5~, 6~みたいになってしまう.
次で解決できる.

nnoremap <expr> OF <SID>goback_insert("\<End>")
nnoremap <expr> OH <SID>goback_insert("\<Home>")
nnoremap <expr> [3~ <SID>goback_insert("\<Del>")
nnoremap <expr> [5~ <SID>goback_insert("\<PageUp>")
nnoremap <expr> [6~ <SID>goback_insert("\<PageDown>")

ただし, ノーマルモードのOが死ぬので, Oをよく使う人は注意.
私はOはほとんど使わないからいいのだけど(oをよく使う).


最近, 挿入モードで移動するのにはまってる.
大体はEmacsキーバインドを参考にしている.
Vimmerとしてあるまじき行為かもしれない.
でも結構使いやすくて慣れるとヤバイ.
実際ヤバイし移動も速いし編集も高速になる.
挿入モードで動くキーバインドの設定を載せておく.

imap <expr><C-o> neosnippet#expandable_or_jumpable() ?
      \ "\<Plug>(neosnippet_expand_or_jump)" : "\<ESC>o"
function! s:cancel_popup(key)
  return a:key . neocomplcache#cancel_popup()
endfunction
inoremap <expr> <C-p> <SID>cancel_popup("\<Up>")
inoremap <expr> <C-n> <SID>cancel_popup("\<Down>")
inoremap <expr> <C-b> <SID>cancel_popup("\<Left>")
inoremap <expr> <C-f> <SID>cancel_popup("\<Right>")
inoremap <expr> <C-e> <SID>cancel_popup("\<End>")
inoremap <expr> <C-a> <SID>cancel_popup("\<Home>")
inoremap <expr> <C-d> <SID>cancel_popup("\<Del>")
inoremap <expr> <C-h> <SID>cancel_popup("\<BS>")
inoremap <expr> <Up> <SID>cancel_popup("\<Up>")
inoremap <expr> <Down> <SID>cancel_popup("\<Down>")
inoremap <expr> <Left> <SID>cancel_popup("\<Left>")
inoremap <expr> <Right> <SID>cancel_popup("\<Right>")
function! s:goback_insert(key)
  return "gi" . a:key . neocomplcache#cancel_popup()
endfunction
nnoremap <expr> OA <SID>goback_insert("\<Up>")
nnoremap <expr> OB <SID>goback_insert("\<Down>")
nnoremap <expr> OC <SID>goback_insert("\<Right>")
nnoremap <expr> OD <SID>goback_insert("\<Left>")
nnoremap <expr> OF <SID>goback_insert("\<End>")
nnoremap <expr> OH <SID>goback_insert("\<Home>")
nnoremap <expr> [3~ <SID>goback_insert("\<Del>")
nnoremap <expr> [5~ <SID>goback_insert("\<PageUp>")
nnoremap <expr> [6~ <SID>goback_insert("\<PageDown>")

とりあえず, 挿入モードで矢印キーを押して誤爆することはなくなる.
もし矢印キーをnopにmapするような硬派な人ならば, nnoremap OA giみたいにしておくと良いだろう.
端末vimで困ってる人はお試しあれ.

(追記)neocomplcacheが勝手に補完しようとするのを抑えるには

次の設定でも抑えられるようです. (確認していないけど)

let g:neocomplcache_enable_insert_char_pre=1

この記事の意味が半減してしまった...
Shougoさんありがとうございます.


まとめると, 次の設定で十分です.

  let g:neocomplcache_enable_insert_char_pre = 1

inoremap <C-p> <Up>
inoremap <C-n> <Down>
inoremap <C-b> <Left>
inoremap <C-f> <Right>
inoremap <C-e> <End>
inoremap <C-a> <Home>
inoremap <C-d> <Del>
inoremap <C-x> <Del>
inoremap <C-h> <BS>
inoremap <Up> <Up>
inoremap <C-_> <ESC>ugi
inoremap <C-\> <ESC>ugi
nnoremap OA gi<Up>
nnoremap OB gi<Down>
nnoremap OC gi<Right>
nnoremap OD gi<Left>
nnoremap OF gi<End>
nnoremap OH gi<Home>
nnoremap [3~ gi<Del>
nnoremap [5~ gi<PageUp>
nnoremap [6~ gi<PageDown>

追記 (2014/01/12)

k_takataさんやh_eastさんに指摘を受けました.
マッピングでやる方法は良くないようです.

つまり,

set notimeout
set ttimeout
set timeoutlen=100

あるいは

set <xUp>=^[OA
set <xDown>=^[OB
set <xRight>=^[OC
set <xLeft>=^[OD

という設定で解決します. (^[は<C-V><Esc>)
ただし,

if has('unix') && !has('gui_running')
  inoremap <silent> <ESC> <ESC>
endif

という設定を書いている人は, これを消さなくてはいけません.
要は, 挿入モードでの<ESC>へのマッピングは良くないのです.
(プラグインが<ESC>にマッピングしている場合は, 作者を殴りに行きましょう)
この設定は, デフォルトのtimeoutlenが1000であるために, エスケープキーで直ぐにノーマルモードに戻れないという理由で使われる設定です.
ttimeoutを設定し、ttimeoutlenを短くすると, この設定も不要になるというわけです.