vimのカスタマイズとTips

バッドノウハウ、あるいはフォースの暗黒面

これは何?

ここのカスタマイズに関する記事は主に某社での新人さんとのやりとりから生まれた物で、必ずしも有用かどうかはわかりませんが、一部でも役に立つことが有れば幸いです。
基本的にIME関係以外はWindowsでもLinux(Ubuntu)でも共通で使えます。

viの簡単な使い方は以下にあります。
はじめてのvi



困ったときは


Windowsのネットワーク共有で読み込みが遅くなる

ネットワーク共有しているファイルの編集が遅い

<目次へ>


長い行が表示されない

vim/gvimは長い行があると、画面に収まらない時には表示されません。
表示させたい場合は.vimrcに以下を追加して下さい。
"画面最後の行をできる限り表示する。
set display=lastline

<目次へ>

他のWindowsエディタから移行する人のために


行の移動を表示行で移動したい

デフォルトはこれで便利なこともあるのですが、特に日本語の場合は長くて折り返しされていると非常に面倒なことになります。
キーボードマクロには物理行移動でないと困る場合が多いので<C-p>,<C-n>を使いましょう。
以下を.vimrcに追加してください。
"カーソルを表示行で移動する。物理行移動は<C-n>,<C-p>
nnoremap j gj
nnoremap k gk
nnoremap <Down> gj
nnoremap <Up>   gk
h l が行末を超えることが出来るように設定できます。
以下の例では whichwrap で カーソルキーの ← → が行末、行頭を超えることが出来るように設定してから h l にマッピングしています。
"カーソルキーで行末/行頭の移動可能に設定。
set whichwrap=b,s,[,],<,>
nnoremap h <Left>
nnoremap l <Right>
"l を <Right>に置き換えて、折りたたみを l で開くことができるようにする。
if has('folding')
  nnoremap <expr> l foldlevel(line('.')) ? "\<Right>zo" : "\<Right>"
endif
こうすると l で移動している時には行末を超えることが出来ますが、 d999l のように他のコマンドからモーション指定した場合は行末までしか削除されません。
999l では行末を超えて移動します。

<目次へ>


スペースキーは一画面移動にしてみる試み

さらにShift+Spaceで一画面逆スクロールみたいにするとまるでブラウザみたいです。
これはブラウザを立ち上げてテキストファイルを読んで、必要になったら挿入モードに移行する様なイメージで、意外とvimに慣れるためのいいステップかもしれないと思います。
早い話がvimはテキストエディタではなく、テキスト用ブラウザなのだと考えるとしっくりくるかもしれません。
これは個人的にはvimを使う大きな理由になっています。
だってスクロールするのにスペースキーだけですむのって楽だと思いませんか。

とりあえずスペースキーは押しやすいのは間違いないのでよく考えて好きな機能に割り当てましょう。
私は<Space>,<S-Space>で一画面単位スクロール、g<Space>でファイルの最終行へ、Alt+<Space>で空白挿入にしています。
g<Space>だけmapなのはGがモーション(移動指定)だからです。
nnoremap <SPACE>   <PageDown>
nnoremap <S-SPACE> <PageUp>
map      g<SPACE>  G
nnoremap <M-SPACE> i<SPACE><ESC><Right>

vnoremap <SPACE>   <C-d>
vnoremap <S-SPACE> <C-u>

<目次へ>


その他の雑多な設定

"ヘルプ
nnoremap <F1> K
"現在開いているvimスクリプトファイルを実行。
nnoremap <F8> :source %<CR>
"強制全保存終了を無効化。
nnoremap ZZ <Nop>
"検索後画面の中心に。
nmap n nzz
nmap N Nzz
  • ヘルプはF1などに割り当ててKはなにか有用なことにリマップして使いましょう
  • .vimrcを書き換えた時<F8>を押すと終了→立ち上げしなくても設定が反映されます。
  •  ノーマルモードでZZは単純に危険なので慣れないうちは無効にしておいた方がよいのではないかと思います。
    これは取り返しの付かない(アンドゥ出来ない)結果になるコマンドだからです。
    ノーマルモードにいることに気がつかず、ZZガンダムが……と書こうとしただけで他のファイルまで全て上書き保存して終了されたら結構ダメージ大きくないですか?
    (7.3以降では終了後のアンドゥが可能です。「Persistent undoの設定」)

<目次へ>


ノーマルモード


縦に連番を入力する

vim/gvimで縦に連続した番号へ置換したいときがあります。

fuga[0]
fuga[0]
fuga[0]

これを次のように変更したりする場合です。

fuga[0]
fuga[1]
fuga[2]

適当なのが見あたらなかったので、適当に作ってみたものを使用しています。
(注意) Vim 8.0以降ではビジュアルモードで選択してからg<C-a>, g<C-x>で連番を作れるようになりました。


.vimrc(_vimrc) に追加して使います。
nnoremap <silent> co :ContinuousNumber <C-a><CR>
vnoremap <silent> co :ContinuousNumber <C-a><CR>
command! -count -nargs=1 ContinuousNumber let snf=&nf|set nf-=octal|let cl = col('.')|for nc in range(1, <count>?<count>-line('.'):1)|exe 'normal! j'.nc.<q-args>|call cursor('.', cl)|endfor|unlet cl|unlet snfカーソルを連番に置換したい数値に合わせて 3co と実行すると、縦に3行分が連番に置換されます。
ビジュアルモードで選択してから co だと選択行が連番に変更されます。
<C-a>を使用しているので、連番変更の対象になるのはカーソル位置から最も近い右側の数字です。
必ずしも数値の上でなければいけないという事はありません。
ただし対象範囲は全て同じ数字(開始値)にしておく必要があります。
また puyo1, puyo2, ...のような数値にも有効です。
fuga[2][1]
fuga[2][1]
fuga[2][1]

puyo1
puyo1
puyo1

数値が不揃いの場合は

  1. <C-v>で数値部分を矩形選択
  2. s0 などとして連番の初期値に置換
  3. gvで範囲再選択
  4. co
という感じに、整形してから連番変更すると良いのではないかと思います。
ビジュアルモードでは <C-a> や <C-x> に割り当てるとそれっぽいかもしれません。
vnoremap <silent> <C-a>   :ContinuousNumber <C-a><CR>
vnoremap <silent> <C-x>   :ContinuousNumber <C-x><CR>
(注意)
連番処理に <C-a>を使用しているため、 04 のように最初が0で始まる数値は8進数として扱われます。
8進数処理が必要でない場合は nrformatsから8進数を認識しないよう設定すると良いかもしれません。
set nrformats-=octal


< 目次へ>


カーソル位置の単語をヤンクした単語に置換

個人的によくやるのがこれです。

ciyはカーソル位置の単語をヤンクした文字列に置き換えます。
ciyはテキストオブジェクトなのでカーソルが単語内の何処にあっても使えます。
cyはカーソル位置以降の単語部分をヤンクした単語に置換します。

一度cyやciyを実行した後、n.で繰り返せる用に、検索レジスタが置換された文字列と置き換わります。
nnoremap <silent> ciy ciw<C-r>0<ESC>:let@/=@1<CR>:noh<CR>
nnoremap <silent> cy   ce<C-r>0<ESC>:let@/=@1<CR>:noh<CR>
vnoremap <silent> cy   c<C-r>0<ESC>:let@/=@1<CR>:noh<CR>
  1. 置換に使いたい文字列をヤンクしておきます。
  2. 置換したい単語まで移動します。
  3. cyかciyを実行します。
  4. カーソル位置の単語がヤンクした文字列に置換されます。
  5. 必要ならn.で繰り返します。
同様にヤンクした文字を貼り付けます。
nnoremap gy "0P
この場合はカーソル位置の前に挿入したいことが多いので、Pで貼り付けています。
単純にpだとレジスタの内容が入れ替わっていることも多いのでヤンクレジスタを指定して貼り付けます。

<目次へ>


ヒストリ貼り付け

:di でレジスタの状態を見てみます。
"0 はヤンクした文字列専用のレジスタ
"1 - "9 は削除した文字列が削除順に並んでいます。
9個削除レジスタがあるのは意味があります。
何回か適当な行を削除してみてから
"1P.....
とノーマルモードで入力してみてください。
ヒストリ順に貼り付けになっているのがわかると思います。
"1P.u.u.u
などとやると、削除文字列を選んで貼り付けることが出来ます。
もちろん"3Pみたいに番号を直接指定して貼り付けも出来ます。
コピーしたい行が分散している時、それぞれの場所で削除→アンドゥを繰り返してから実行してやると便利です。

<目次へ>


ヒストリ貼り付け2

前回のヒストリ貼り付けですが削除文字列しか対応していません。
dduで削除→アンドゥを繰り返してから貼り付けするのもいいのですが、コピーしたいところを何カ所かヤンクして貼り付けできると便利だと思いませんか。
次のスクリプトはヤンクした行を削除レジスタにコピーする物です。
nmap <silent> Y   Y:CpR0toR1<CR>
vmap <silent> Y   Y:CpR0toR1<CR>
vmap <silent> y   y:CpR0toR1<CR>
omap <silent> y   y:CpR0toR1<CR>
"行単位のみ
command! CpR0toR1 if @0 =~ "\<NL>"|let @9=@8|let @8=@7|let @7=@6|let @6=@5|let @5=@4|let @4=@3|let @3=@2|let @2=@1|let @1=@0|endif
これでヤンクした行も削除レジスタに入るようになり、
"1P.....
でヤンクした行でもヒストリ貼り付けができるようになります。

ヒストリ貼り付けを多用したり、単語単位でヤンクした物まで反映して欲しいなら、"yankring.vim"のようなスクリプトを使用する方がいいかもしれません。
http://www.vim.org/scripts/script.php?script_id=1234

<目次へ>


検索レジスタに文字列設定

検索で#を使っていますか?
私は使っていません。
n N で繰り返す時、逆になって面倒くさいからです。

なので * を押した時検索文字列の設定と表示のみを行い、方向は次からn Nで指定するようにしています。
#の場合はカーソル位置から単語の末尾まで検索、g*では非単語単位の検索になるように設定しました。
また文字列選択中には選択した文字を検索文字列に設定するようにしています。
"カーソル位置の単語を単語単位の検索文字列に設定
nnoremap <silent> *  :<C-u>call MySetSearch('""yiw', 'word')<CR>:let &hlsearch=&hlsearch<CR>
"カーソル位置の単語を非単語単位の検索文字列に設定
nnoremap <silent> g* :<C-u>call MySetSearch('""yiw')<CR>:let &hlsearch=&hlsearch<CR>
"カーソル位置から単語の末尾までを検索文字列に設定
nnoremap <silent> #  :<C-u>call MySetSearch('""ye')<CR>:let &hlsearch=&hlsearch<CR>

"文字列選択中なら選択文字列を検索レジスタに設定。
vnoremap <silent> * :<C-u>call MySetSearch('""vgvy')<CR>:let &hlsearch=&hlsearch<CR>
vnoremap <silent> # :<C-u>call MySetSearch('""vgvy')<CR>:let &hlsearch=&hlsearch<CR>

""""""""""""""""""""""""""""""
"検索ワードをセットする。
"何か追加パラメータが設定されていたら、単語単位検索に。
""""""""""""""""""""""""""""""
function! MySetSearch(cmd, ...)
  let saved_reg = @"
  if a:cmd != ''
    silent exec 'normal! '.a:cmd
  endif
  let pattern = escape(@", '\\/.*$^~[]')
  let pattern = substitute(pattern, '\n$', '', '')
  if a:0 > 0
    let pattern = '\<'.pattern.'\>'
  endif
  let @/ = pattern
  let @" = saved_reg
endfunction


<目次へ>



最後に編集したコマンドをもう一度実行

これはかなり重要と思います。

. で編集を繰り返すのはよく知られていると思います。
同様に & で最後のsコマンドを繰り返せます。
他にもたとえばgvはビジュアルモードで最後に選択した範囲を再選択してくれます。
oで選択範囲の先頭と後尾に移動します。

また vimにはたくさんの"最後の編集位置"への移動コマンドがあります。
gi `` '' `. 等々覚えきれないぐらい有ります。
map gb `.zz
nnoremap <C-g> g;
nnoremap g; g,
個人的には
gb    : 最後の編集位置へ移動
<C-g> : 編集位置をさかのぼる
で落ち着きました。

最後に編集したバッファへ切り替えたい事もよくあると思います。
:b#で切替もいいのですが、<C-^>でも切り替えられます。
"window分割していないとき、<C-w><C-w>で裏バッファへ切り替え。
nnoremap <silent> <C-w><C-w> :<C-u>call MyWincmdW()<CR>
nnoremap <silent> <C-w>w :<C-u>call MyWincmdW()<CR>
function! MyWincmdW()
  let pn = winnr()
    silent! wincmd w
  if pn == winnr()
    silent! b#
  endif
endfunction

<目次へ>


内容が空のファイルを保存したら自動で削除する

vim/gvimで開いたファイルを削除したい時があります。
:Explore や shellコマンドでもいいんですが、単純にファイルの中身を全削除して保存したらファイル自体も削除するように設定しています。

以下を .vimrcに追加します。
augroup BUFWRITE_POSTDELETE
  au!
  autocmd BufWritePost * call BufWritePostDelete()
augroup END

function! BufWritePostDelete()
  let crlen = 0
  if &binary == 0
    let crlen = &ff=='dos' ? 2 : 1
  endif
  if getfsize(expand('%:p')) <= crlen
    call delete(expand('%:p'))
  endif
endfunction

ファイルの中身に関わらず削除してはいけないファイルがある場合は、特定の拡張子の場合(例えば txt)だけ中身のないファイルを削除するようにした方がよいかもしれません。
augroup BUFWRITE_POSTDELETE
  au!
  autocmd BufWritePost *.txt call BufWritePostDelete()
augroup END


<目次へ>


vimで現在編集中のバッファのファイル名を変更する

以下を .vimrcに追加します。
command! -nargs=+ -bang -complete=file Rename let pbnr=fnamemodify(bufname('%'), ':p')|exec 'f '.escape(<q-args>, ' ')|w<bang>|call delete(pbnr)

変更したいファイル名を指定して実行します。
:Rename newfilename
!を付けると強制保存して変更します。
:Rename! newfilename

<目次へ>


現在のファイル名を無名レジスタにコピー

if has('win32')
  nnoremap <silent> gkf :let @*=expand('%:p')<CR>:echo "Copy filename to noname register."<CR>
elseif has('unix')
  nnoremap <silent> gkf :let @"=expand('%:p')<CR>:echo "Copy filename to noname register."<CR>
endif

もしくは挿入モードで<C-r>=に続けて好きな関数を実行して貼り付け可能なのでそちらでも良いかもしれません。
i<C-r>=expand('%:p')<CR>


変更されている時のみ保存

変更がないのに保存するとタイムスタンプのみ変更されて、あまりよろしくありません。
:update
なら変更のない時は保存されません。
キーマップに定義も出来ます。
"バッファ変更時のみ保存
nnoremap <silent> <C-s> :<C-u>update<CR>
.gvimrcに以下を付け加えると強制保存と、更新時のみ保存をメニューでも使い分けられます。
silent! aunmenu &File.保存(&S)
amenu <silent> 10.340 &File.保存(&W)<Tab>:w  :if expand('%') == ''<Bar>browse confirm w<Bar>else<Bar>confirm w<Bar>endif<CR>
amenu <silent> 10.341 &File.更新時保存(&S)<Tab>:update  :if expand('%') == ''<Bar>browse confirm w<Bar>else<Bar>confirm update<Bar>endif<CR>

<目次へ>


Diff

:DiffOrig
現バッファと差分表示は便利です。最後に保存してからの編集箇所がわかります。

:Diff #
裏バッファと差分表示もなかなか便利です。
差分を取りたいファイルを開いて、裏バッファになるようにしてから実行します。
Diff 3 の様にバッファ番号指定も出来ます。
"現バッファの差分表示。
command! DiffOrig vert new | set bt=nofile | r # | 0d_ | diffthis | wincmd p | diffthis
"ファイルまたはバッファ番号を指定して差分表示。#なら裏バッファと比較
command! -nargs=? -complete=file Diff if '<args>'=='' | browse vertical diffsplit|else| vertical diffsplit <args>|endif
DiffOrigは公式のvimrcサンプルに書かれています。

<目次へ>



IMEがONの時IMEをOFFにしてfコマンド実行

fコマンドは大変便利ですが、私は基本アルファベットしか検索しません。
なのでfコマンドを実行する時にIMEは自動でOFFにしています
nnoremap <silent> f :set iminsert=0<CR>f
nnoremap <silent> F :set iminsert=0<CR>F

<目次へ>

挿入モード



挿入モードのキーをカスタマイズする

本来vimの挿入モードは長居をするところではありません。
とはいえ挿入モードだけでほとんどの入力編集を可能にすることも出来ます。
特に日本語を使用する場合などはノーマルモードでの利点が無くなってくるので、ある程度編集可能にしておいた方が便利な時もあります。

若干Emacs風味なvimのキー設定」にまとめました。

<目次へ>


日本語を使いやすくする

vimで日本語を使いやすくする」にまとめました。

<目次へ>


IMEの状態でカーソル色を変えたい

CursorIMを利用してカーソル色を変える方法は
vim/gvimの色設定について」の「IMEの状態でカーソルの色を変更する」にまとめました。

<目次へ>

index