Vimの範囲選択(v・V・Ctrl-v)と矩形選択で一括編集
Vimで範囲を選ぶキーはv・V・Ctrl-vの3つです。ここまではどの入門記事にも書いてありますが、実際に手が止まるのはその先で、選び始めの位置を間違えたときに最初からやり直したり、矩形選択で先頭に文字を足したのに一部の行にだけ入らなかったりします。
この記事では、選んだ範囲を作り直さずに直す操作(o・モードの切り替え・gv)と、矩形選択の一括編集が思ったとおりに反映されない条件を、Vim と Neovim の両方で実際にキーを打って確かめました。コピーと貼り付けそのものはVimでコピペする方法(yy・p・Ctrl-r)で扱っているので、ここでは「範囲を選ぶ」側に絞ります。
検証環境:Vim 9.1.1752 と NVIM v0.12.6(macOS)。どちらも設定を読まない状態(vim -Nu NONE / nvim -u NONE)で tmux の中に起動し、キーを1つずつ送って画面を読み取りました。:normal で流した結果は根拠にしていません。掲載している編集結果・モード表示・:echo の値はすべてその実行結果で、2つの版で違いは出ませんでした。
目次(9項目)
| キー | これだけ覚える |
|---|---|
| v V Ctrl-v | 文字・行・矩形。選択中に押すとモードだけ変わる |
| o | 選択の反対の端へカーソルを移す |
| gv | 直前の選択をもう一度選ぶ |
| I / A / $A | 矩形の左に挿入/右に追加/行末に追加 |
| Esc | 矩形の挿入はEscで抜ける。Ctrl-cだと1行目だけ |
v・V・Ctrl-vの違い(文字・行・矩形)
3つのキーは、選んだ範囲をどの単位で扱うかが違います。vは文字単位で、始点から終点までを本文の流れに沿って選びます。Vは行単位で、カーソルが行のどこにあっても行全体が対象になります。Ctrl-vは矩形(ブロック)で、始点と終点を対角にした長方形の範囲を選びます。
| キー | 画面下の表示 | 選ぶ単位 | 向いている場面 |
|---|---|---|---|
| v | -- VISUAL -- | 文字 | 行の途中から途中まで |
| V | -- VISUAL LINE -- | 行 | 関数や段落を行ごと |
| Ctrl-v | -- VISUAL BLOCK -- | 矩形 | 表の1列、複数行の同じ桁 |
showcmd が有効なら、表示の右側に選択の大きさも出ます。vとVでは行数(2行なら2)、Ctrl-vでは「行数x桁数」(2x3 のような形)です。矩形で何桁つかんでいるかは見た目より数字のほうが確実に分かります。
選んだ範囲を直す(o / O と、モードの切り替え)
選択中に別のモードのキーを押すと、選んだ範囲を保ったままモードだけが変わります。1行目の3桁目からv j lと選んでからVを押すと-- VISUAL LINE --に変わり、続けてCtrl-vを押すと-- VISUAL BLOCK --と2x2が出ました。この状態でdを押すと、2行の3〜4桁目だけが消えます。
0123456789 → 01456789
0123456789 → 01456789
0123456789 0123456789
行単位で選んだつもりが列だけ欲しかった、という場面では選び直す必要がありません。逆に、いまと同じモードのキー(vの最中にv)を押すと選択が解除されてノーマルモードに戻ります。Escと同じ扱いです。
範囲を広げたり縮めたりできるのは、カーソルがある側の端だけです。始点を動かしたいときはoで反対の端へカーソルを移します。矩形ではさらにOがあり、同じ行のまま左右の端を入れ替えます。1行目の5桁目からCtrl-v j l lと選んだとき(カーソルは2行目7桁目)、ルーラーの位置は次のように動きました。
| 押したキー | カーソルの位置 | 移った先 |
|---|---|---|
| (選んだ直後) | 2,7 | 右下 |
| o | 1,5 | 対角の左上 |
| O | 2,5 | 同じ行の左下 |
| o O | 1,7 | 右上 |
文字単位のvでOを押してもoと同じ動きになるので、文字と行の選択ではoだけ覚えておけば足ります。「1文字手前から選ぶべきだった」というときは、o hで始点を1つ左へ広げられます。vで456を選び、o h dと押すと3456が消えました。
テキストオブジェクトで一気に選ぶ(viw / vip / vi( と重ね押し)
選択中は、ciwやdi(で使うテキストオブジェクトがそのまま範囲の指定になります。viwで単語、vipで段落、vi(で括弧の中です。括弧や引用符の中身を書き換えるだけならci" や ci( で括弧・引用符の中身を書き換える方法のほうが手数は少なく、ビジュアルモードを経由する利点は、選んだ範囲を目で見てから操作を決められることです。
選択中に同じテキストオブジェクトをもう一度押すと、範囲がさらに広がります。どう広がるかはオブジェクトごとに違ったので、yでヤンクして:echo string(@")で中身を見ました。
| キー | 元の文とカーソル | 選ばれた範囲 |
|---|---|---|
| vi( | call(a, (b, c), d) の b | 'b, c' |
| vi( i( | 同上 | 'a, (b, c), d' |
| vi( a( | 同上 | '(b, c)' |
| viw iw iw | foo bar baz qux の f | 'foo bar' |
| vaw aw | 同上 | 'foo bar ' |
| vip | 2行の段落の1行目 | 4〜5行目 |
| vip ip | 同上 | 4〜6行目(下の空行まで) |
| vip ip ip | 同上 | 4〜7行目(次の段落まで) |
括弧は重ねるたびに1つ外側の括弧へ移るので、入れ子の深いコードで「この引数の外側の括弧の中身全部」を選ぶときに便利です。一方iwは、単語の間の空白も1つの「語」として数えます。3回押してfoo barまでしか広がらないのはそのためで、foo・空白・barで3つです。ipも同じ考え方で、段落と段落の間の空行が1つの塊として数えられます。単語を2つ足したいならawを重ねるほうが直感に合います。
もう一度同じ範囲を選ぶ(gv と '<,'>)
選択をEscで抜けたあとでも、gvで直前の範囲を選び直せます。1行目から2行にわたってvで選び、Escで抜けてgg $とカーソルを遠くへ動かしてからgvを押すと、同じ範囲が-- VISUAL --で戻ってきました。インデントを>で1段ずらしたら選択が外れてしまい、もう1段ずらしたいという場面でよく使います(インデント操作そのものはインデントを増減する方法(>> / << / shiftwidth)にまとめています)。
選択の始点と終点は、マーク'<と'>として残ります。上の選択のあとで確かめると、行と桁はこうでした。
:echo getpos("'<")[1:2] getpos("'>")[1:2]
[1, 3] [2, 4]
選択中に:を押すと、コマンドラインには:'<,'>が自動で入ります。このマークを範囲にしてExコマンドが走るので、:'<,'>s/a/b/gのように選んだ行だけを置換できます。
ただしExコマンドは行単位で動きます。Ctrl-vで2行の2〜3桁目だけを選んでから:s/a/X/gを実行すると、選んでいない桁も含めて2行のaが全部置き換わりました。選んだ範囲の内側だけに効かせたいときは、パターンに\%Vを付けます。
" aaaaaa の2行で、2〜3桁目を矩形選択してから実行
:'<,'>s/a/X/g " XXXXXX(行全体が変わる)
:'<,'>s/\%Va/X/g " aXXaaa(選んだ桁だけ)
選択した範囲に効くコマンド(y d c > < J u U r)
選んだあとに押すキーは、ノーマルモードのオペレータとほぼ同じです。違うのは、範囲をモーションで指定せずにすでに選んであることだけで、dなら選んだ範囲を消し、yならヤンクします。ノーマルモードとは別の意味になるキーもあるので、実際の結果を並べます。
| キー | 選択中の働き | 実際の結果 |
|---|---|---|
| U | 大文字にする | Hello → HELLO |
| u | 小文字にする(アンドゥではない) | Hello → hello |
| ~ | 大文字と小文字を入れ替える | Hello World → hELLO wORLD |
| J | 選んだ行を空白1つで連結 | 3行 → Hello World foo bar baz |
| gJ | 空白を入れずに連結 | 3行 → Hello Worldfoo barbaz |
| r{文字} | 選んだ文字を全部その1文字に | 矩形2桁 → abcZZf |
| > | 右へずらす(矩形なら左端の桁から) | 0123456789 → 0123 456789 |
いちばん間違えやすいのはuです。ノーマルモードではアンドゥなので、選択したまま「戻そう」と押すと小文字化が走ります。大文字化と小文字化の使い分けは大文字・小文字を変換する方法でも扱っています。
矩形での>は、行頭ではなく選んだ矩形の左端に空白を入れます。上の結果は:set expandtab shiftwidth=4のときで、5桁目の前に空白が4つ入りました。設定を読まない素の状態(noexpandtab・shiftwidth=8)では、同じ操作でタブ1つと空白4つが入りました。表の列を右へ寄せる用途なら、expandtabを有効にしてからにしたほうが崩れません。
矩形選択で複数行をまとめて編集する(I / A / $A / c)
矩形選択のいちばんの使いどころは、複数行の同じ位置に同じ文字を入れることです。行頭にコメント記号を付ける、表の列の右側に区切りを足す、といった作業が1回の入力で済みます。使うのは次の4つです。
| キー | 入る位置 |
|---|---|
| I | 矩形の左端の前 |
| A | 矩形の右端の後ろ |
| $A | 各行の行末(Ctrl-vのあと$で右端を行末にしてからA) |
| c | 選んだ矩形を消して、そこへ入力 |
どれも、挿入している間は1行目にしか文字が見えません。Escで挿入モードを抜けた瞬間に、残りの行へ同じ文字が入ります。入力中に他の行が変わらないので失敗したように見えますが、抜けるまで待てば反映されます。
abcdef abcdef abcdef
abcXdef abcXdef abcXdef
この操作はVim道場の矩形で選んでまとめて編集する練習で、ブラウザの上でそのまま試せます。行の長さがそろっていれば4つとも素直に動きますが、そろっていないときと、抜け方を変えたときに結果が分かれます。次の節がその話です。
矩形の一括編集が反映されない条件(短い行・Esc と Ctrl-c)
行の長さがそろっていないテキストで、4つの操作を試しました。2行目だけが短いabcdef / ab / abcdefの3行で、4桁目(dの位置)からCtrl-v jjと選んでいます。cだけはlを足してdeの2桁を選びました。表示は空白を.に置き換えています。
| 操作 | Escで抜けた | Ctrl-cで抜けた |
|---|---|---|
| IX | abcXdefababcXdef | abcXdefababcdef |
| AX | abcdXefab..XabcdXef | abcdXefababcdef |
| $AX | abcdefXabXabcdefX | (試していません) |
| cXY | abcXYfababcXYf | abcXYfababcf |
短い行の扱いは、操作によって3通りに分かれました。Iとcは、矩形の桁まで届いていない行には何もしません。Aは逆で、届かない行を空白で埋めてから矩形の右端の後ろに入れます。$Aは桁をそろえず、各行の行末にそのまま足します。ヘルプ(:help v_b_I)にも「行が矩形の中まで伸びている場合に限る。短い行は変更されない」とあり、:help v_b_AにはIとAの違いは意図したものだと書かれています。
行頭にコメント記号を足すつもりで、インデントの位置に合わせてIを使うと、空行やインデントの浅い行だけ記号が付かないのはこのためです。全行の末尾にセミコロンやカンマを足すときは、Aではなく$Aを使います。Aだと短い行のあとに空白が詰められ、行末に余計な空白が残ります。
「届いている」の判定は1桁でも入っていれば成り立ちます。2行目をabcdにして4〜5桁目を選ぶと、2行目は4桁目のdしか矩形にかかっていませんが、IならabcXd、cならabcXYとなり、どちらも反映されました。
もう1つの条件は抜け方です。Ctrl-cで挿入を抜けると、IもAも1行目にしか入らず、他の行は元のままでした。cはさらに厄介で、他の行の選択部分は消えているのに、入力した文字は入っていません(3行目がabcf)。ヘルプのi_CTRL-Cは「挿入モードを抜ける。短縮入力を確認せず、InsertLeave を発生させない」と書いているだけで、矩形への反映には触れていませんが、Vim 9.1 と Neovim 0.12.6 のどちらでも同じ結果でした。Ctrl-cをEscの代わりに使う癖がある人は、矩形の挿入だけはEscで抜けてください。
複数の場所を同時に書き換える手段は、矩形選択のほかにもあります。行の位置がそろっていない箇所をまとめて直したいなら、マルチカーソルと標準機能での代用の比較が参考になります。
この記事のテーマを決めた段階のメモには、:normalで流してみたら短い行にもIの文字が入り、Ctrl-cで抜けても反映された、と書いてありました。ヘルプと逆なので自分でもvim -esの:normalで流したところ、今度は1行目にすら何も入らず、どちらが正しいのか分からなくなりました。tmuxの中で起動してキーを1つずつ送る形に切り替えてからは、ヘルプどおりの結果がVimでもNeovimでも毎回そろったので、本文の表はすべてその結果です。もう1つ意外だったのはcをCtrl-cで抜けたときで、3行目のdeが消えたのに何も入らず、1行目だけ書き換わった状態で残りました。uで1回戻せば元に戻りますが、気づかずに先へ進むと消えた2文字の行方が分からなくなります。
まとめ
範囲は作り直さずに直せます。モードを間違えたら選択したままv・V・Ctrl-vを押し替え、始点を動かしたいならo、外れた選択はgvで戻します。括弧や段落はテキストオブジェクトを重ねて押すと外側へ広がり、選んだ範囲は'<と'>に残るので:でそのままExコマンドに渡せます。Exコマンドは行単位なので、矩形の内側だけに効かせるなら\%Vを付けます。
矩形の一括編集は、短い行に対してIとcは何もせず、Aは空白で埋め、$Aは行末に足します。どれも挿入をEscで抜けたときだけ残りの行に反映され、Ctrl-cで抜けると1行目にしか入りません。