Vimの番号付きレジスタ「"0〜"9」の仕組みを正確に理解する
数回前に削除した内容を"3pで戻そうとしたら、期待と違う内容が出てきました。番号付きレジスタは「削除するたびに1つずつずれる」という理解だけでは説明できない例外がいくつかあります。
| キー | これだけ覚える |
|---|---|
| "0 | 最後にヤンクした内容(削除の影響を受けない) |
| "1 | 直近の削除・変更(条件を満たすもの) |
| "2〜"9 | 1つ前の"1〜"8がシフトしたもの |
| "- | 1行未満の削除(小削除レジスタ) |
基本ルール
レジスタの全体像で触れたとおり、"0はヤンク専用で削除の影響を受けません。"1〜"9は削除・変更のたびに1つずつシフトしていく履歴で、新しい削除が"1に入ると、それまでの"1は"2へ、"2は"3へと押し出されます。
line A
line B
line C
│ ← ddで2行削除した直後
line C
line A
2回削除した時点で"1は「line B」ですが、1回目に削除した「line A」は"2にシフトしています。何行か前に消した内容を戻したいときは、番号を1つずつ変えながら:registersで中身を確認すると確実です。ここでは"1〜"9がどんな条件で更新されるかを正確に見ていきます。
原則:1行以上の削除だけが対象
基本ルールでは、削除・変更した範囲が1行以上のときだけ"1に入ります。xや行内のdwのように1行未満の削除は、"1ではなく小削除レジスタ"-に入ります。ここまでは多くの人が理解している範囲です。
例外:特定のモーションは1行未満でも"1に入る
ややこしいのはここからで、次のモーションと組み合わせた削除は、結果が1行未満であっても"-だけでなく"1にも入ります。
% " 対応する括弧まで
( " 前の文へ
) " 次の文へ
` " マークへのジャンプ
/ " 検索(前方)
? " 検索(後方)
n " 次の検索マッチ
N " 前の検索マッチ
{ " 前の段落へ
} " 次の段落へ
| 削除の範囲 | 使ったモーション | "- | "1 |
|---|---|---|---|
| 1行未満 | 通常(x dw など) | 入る | 入らない |
| 1行未満 | 上記の特殊モーション | 入る | 入る |
| 1行以上(複数行にまたがる) | どれでも | 入らない | 入る |
たとえばd/foo<Enter>で検索位置までを削除した場合、その範囲が1行に収まっていれば"-と"1の両方に同じ内容が入ります。一方、検索先が別の行で削除範囲が複数行にまたがると、通常の複数行削除と同じ扱いになり"1だけが更新されて"-は変化しません。「特殊モーション="-に入らない」ではなく、「行をまたぐかどうか」で"-の有無が決まる点を押さえておくと安心です。
:registersで実際に確認する
理屈だけで覚えようとせず、:registers(省略形:reg)で実際にどのレジスタに何が入っているかを確認しながら操作すると理解が早まります。何度か削除や検索絡みの操作を試して、"1と"-のどちらに何が入るかを目で確認しておくと、ルールを暗記しなくても感覚で判断できるようになります。正確な条件は公式ヘルプ(:h registers)にも記載があります。
実務での影響
この違いを知らないと、「複数行にまたがる検索絡みの削除を"-pで戻そうとしたら見つからない」という混乱につながります。検索やジャンプを伴う削除を取り消したいときは、まず"1pを試し、それでも見つからなければ番号を1つずつ増やしながら探す、という手順を覚えておくと安心です。番号付きレジスタは自動的に内容がシフトしていくので、特定の内容を確実に残しておきたいなら名前付きレジスタに退避させたほうが安全です。
検索でかなり先の行までまとめて削除した内容を、後から"-pで復元しようとして見つからず困ったことがあります。削除範囲が複数行にまたがっていたせいで"-ではなく"1にしか入っていなかった、というのが原因でした。理屈を知らないままだと「Vimの挙動が気まぐれだ」と誤解しかねない典型例だと思います。
まとめ
番号付きレジスタは単純な「1行以上か未満か」だけでなく、モーションの種類によっても振り分けが変わります。検索・ジャンプ系のモーションを使った削除は"1を優先して探すと確実です。