Vimで括弧・引用符の中身を書き換える(ci" / ci( / ci{ / ci[)
文字列リテラルの中身や関数の引数を書き換えるとき、範囲をビジュアル選択してから削除するのは手数が多いです。ci系のコマンドを使えば、カーソルがブロックのどこにあっても、囲みの中身だけを一発で書き換えられます。
| キー | これだけ覚える |
|---|---|
| ci" / ci( / ci{ / ci[ | 囲みの中身をカーソル位置に関係なく書き換え |
| di( | 中身を削除するだけ(インサートモードに入らない) |
| ca( | 記号自体も含めて書き換え |
| 2ci( | 1つ外側のブロックを対象にする |
基本の考え方:オペレータ+テキストオブジェクト
ci"は「変更」のオペレータcと、カーソルのある"ブロックの中身を指すテキストオブジェクトi"の組み合わせです。カーソルが"ブロックの中のどこにあっても、その内側全体が対象になります。
foo(a, b)
foo(x)
ci" " "..."の中身を変更
ci( " (...)の中身を変更
ci{ " {...}の中身を変更
ci[ " [...]の中身を変更
丸括弧はci)、波括弧はciBのような別名でも同じ範囲を指せます。どの記法を使っても結果は同じなので、押しやすいほうを選べば十分です。
ci{記号}で中身全体を対象にできます。位置合わせに気を遣わなくてよいのが最大の利点です。用途別の例
| コマンド | 使いどころ |
|---|---|
| ci" | 文字列リテラルの中身を丸ごと書き換える |
| ci( | 関数呼び出しの引数を丸ごと入れ替える |
| ci{ | JSON/オブジェクトリテラルの中身を書き換える |
| ci[ | 配列・リストの要素を書き換える |
削除だけしたい場合はdi
di"
di(
di{
di[
cをdに変えれば、中身を削除するだけでインサートモードには入りません。書き換えず消すだけでよいときはこちらを使います。
ネストしている場合の挙動
括弧が入れ子になっている場合、ci(はカーソルに最も近い(内側の)括弧を対象にします。外側の括弧全体を対象にしたいときは、カーソルを外側の括弧の位置まで動かしてから実行する必要があります。ネストが深いコードでは、対応する括弧を目で追う%コマンドと組み合わせて、まず外側の位置を確認してから編集すると確実です。
カウントを前置きすると、階層を1段ずつ外側へ広げられます。
2ci( " 1つ外側の()ブロックの中身を変更
入れ子の深いコードでカーソルを動かさずに範囲を広げたいときは、この数値指定のほうがカーソル移動より速いことが多いです。
a系との違い
i(inner)は記号自体を含まない中身だけが対象ですが、a(around)に変えると記号自体も含めて対象になります。
ca" " "を含めて丸ごと削除・変更
ca( " (と)を含めて丸ごと削除・変更
記号ごと消してしまいたいときはa系、中身だけ書き換えたいときはi系、と使い分けます。
Pythonの辞書リテラルを書き換えるとき、以前は中身を目で選択してから削除していましたが、ci{を覚えてからはカーソルがブロック内のどこにあっても一発で書き換えられるようになりました。特にci(は関数の引数を丸ごと差し替えるときに使用頻度が高く、手が勝手に動くレベルまで身についています。
まとめ
括弧・引用符の中身を書き換えるならci"/ci(/ci{/ci[。カーソルの位置合わせが不要になる分、ビジュアル選択より速く正確に編集できます。HTMLタグの中身を対象にしたい場合は専用のcitコマンドを使います。対応する括弧まで飛ぶ%や、外側へ広げるカウント指定を併用すると、入れ子の深い場所でも目的の階層を狙えます。同じテキストオブジェクトの考え方そのものはcwとciwの違いで説明しています。カーソルが囲みの外にあっても、後ろに括弧が控えていればci(は効きます。行頭からでも、括弧が次の行にあっても、前方を探しに行って中身を掴みます。効かないのは括弧を通り過ぎたとき、つまり閉じ括弧より後ろにカーソルがある場合です。反応しないと感じたら、行き過ぎていないかを疑うと早く切り分けられます。