Vimのレジスタは何種類?使い分けとハマりやすい勘違い
ヤンクした文字列を貼り付けようとしたら、さっき消したはずの別の行が出てきて困りました。Vimを使い始めた頃、こういう場面に何度も遭遇しました。原因は単純で、yもdも同じ無名レジスタを使い回すからです。レジスタの仕組みを知らないと、コピーと削除が同じ場所を奪い合っていることに気づけません。
この記事では、Vimのレジスタが全部で何種類あるかを整理したうえで、実務で意識する必要があるものと、名前だけ知っておけば十分なものを分けて説明します。
検証環境:macOS + Vim 9.1.1752 / Neovim 0.12.4。レジスタの分類は両者の:help registersを突き合わせ、:registersの出力は実際に操作してから取得したものです。
目次(7項目)
| キー | これだけ覚える |
|---|---|
| "" | 無名レジスタ。ヤンク・削除が自動的に上書きする |
| "0 | 直近ヤンク専用。削除しても消えない |
| "a〜"z | 自分で使い分ける保管庫 |
| "_ | ブラックホール。書き込んだ内容を破棄する |
レジスタの全体像
Vimの公式ヘルプ(:help registers)は、レジスタを10種類に分類しています。数として数えると合計48個です。:registers(省略形は:reg)を実行すると、今どのレジスタに何が入っているかを一覧で確認できます。
There are ten types of registers
| 種類 | 記法 | 個数 | 役割 |
|---|---|---|---|
| 無名レジスタ | "" | 1 | 直近のヤンク・削除・変更が自動的に入る |
| 番号付きレジスタ | "0 〜 "9 | 10 | ヤンク・削除の履歴 |
| 小削除レジスタ | "- | 1 | 1行未満の削除が入る |
| 名前付きレジスタ | "a 〜 "z | 26 | 自分で使い分ける汎用スロット |
| 読み取り専用レジスタ | ": ". "% | 3 | 直近のコマンド・挿入文字列・ファイル名 |
| 代替バッファレジスタ | "# | 1 | 直前に開いていたファイル名 |
| 式レジスタ | "= | 1 | Vim script式の評価結果を挿入 |
| 選択・ドロップレジスタ | "* "+ "~ | 3 | OSのクリップボードとドラッグ&ドロップ |
| ブラックホールレジスタ | "_ | 1 | 書き込んだ内容を破棄する |
| 検索パターンレジスタ | "/ | 1 | 直近の検索パターンが入る |
読み取り専用レジスタと代替バッファレジスタ("#)は役割が近いので同じ枠で語られがちですが、公式の分類では別の種類として並んでいます。48という数字はこの表の個数を足したものです。
Neovimでは47個になる
Neovimのヘルプでも同じく10種類に分かれていますが、8番目の項目が「The selection registers "* and "+」となっていて、ドロップレジスタ"~がありません。GUIへのドラッグ&ドロップで使うレジスタで、Neovimは対応していないためです。したがって数え方を揃えるとVimとNeovimで1個ずれます。
| Vim | Neovim | |
|---|---|---|
| 種類 | 10種類 | 10種類 |
| 個数 | 48個 | 47個("~が無い) |
48種類と聞くと多く感じますが、日常的に触るのは無名・番号付き・名前付き・ブラックホールの4つくらいで、残りは「そういうものがある」と知っておけば困ったときに調べれば十分です。
:registersの読み方
手元のVim 9.1で、4行のバッファに対してyy(1行ヤンク)→dd(1行削除)→x(1文字削除)を順に実行してから:registersを叩くと、次のようになりました。
Type Name Content
c "" d
l "0 alpha^J
l "1 bravo^J
c "a named-a
c "- d
この5行に、レジスタの仕組みがほぼ全部出ています。最後に実行したxの結果(dという1文字)が無名レジスタに入り、ヤンクしたalphaは"0に、行削除したbravoは"1に、1文字だけの削除は小削除レジスタ"-に、それぞれ分かれて残っています。
左端のType列も見落とされがちですが重要です。lは行単位(linewise)、cは文字単位(charwise)を表し、これがpの貼り付け方を決めます。行単位なら現在行の下に新しい行として入り、文字単位ならカーソルの直後に挿入されます。内容の末尾にある^Jは改行文字の表示で、行単位でヤンクされたことの現れです。
無名レジスタが上書きされる仕組み
冒頭の失敗は、無名レジスタ"の仕様を知らないままpを使ったことが原因でした。yでヤンクしてもdで削除しても、指定したレジスタが無ければ結果は無名レジスタに入ります。つまりヤンクした直後に何かを削除すると、無名レジスタの中身は削除したほうの文字列に置き換わります。pで貼り付けたときに「さっきコピーしたものと違う」と感じたら、まずこれが原因になっている可能性があります。
さらに紛らわしいのが、"ayyのように名前付きレジスタを明示的に指定した場合でも、Vimはその内容を無名レジスタにもコピーする点です。名前付きレジスタは独立した保管場所だと思い込んでいると、直後のpで予想外の文字列が出てきて驚くことになります。
番号付きレジスタで削除の履歴をたどる
"1から"9は削除の履歴で、消したものが何に入るかは削除の大きさで決まります。
| 削除した操作 | 入る先 |
|---|---|
| dd/dap(1行以上) | "1。既存の中身は"2→"9へ押し出される |
| x/行内のdw(1行未満) | "-(小削除レジスタ) |
何行か前に消した文字列を戻したいときは、"3pのように番号を変えながら試すとたどり着けます。
もう一つ覚えておくと便利なのが"0です。これはヤンクだけを記録する専用のレジスタで、削除の影響を受けません。
const a = 1;
│ ← 直前にddで1行削除した
const a = 1;
const a = 1;
ヤンクした行の後で別の行を削除しても、無名レジスタは削除内容で上書きされる一方"0はヤンクした内容を保持し続けます。ヤンクしてから編集作業を挟むことが多い人は、pより"0pを使う癖をつけたほうが事故が減ります。番号付きレジスタの割り当てには細かい例外もあります。こちらで詳しく扱っています。
ブラックホールレジスタで削除を「無かったこと」にする
コピーした文字列を別の行に貼り付けたいだけなのに、貼り付け先の行をddで消すとレジスタの中身が上書きされてしまいます。こういうときに使うのがブラックホールレジスタ"_です。"_ddのように指定すると、削除した内容はどこにも保存されずに消えます。
"ayy " a レジスタに現在行をヤンク
/target
"_dd " 削除するが a も無名レジスタも汚さない
"ap " さっきヤンクした内容を貼り付け
置換系のマッピングを自作するときにも、この"_を経由させると「置換したらクリップボードの中身が消えた」という事故を防げます。
複数箇所に同じ文字列を貼り付ける作業をしていて、途中の削除操作で無名レジスタの中身が消し飛んだことが何度もありました。それに気づいてからは、使い回すヤンク内容は"ayyのように名前付きレジスタへ意識的に逃がすようにしています。地味ですが、これだけでコピペ関連のやり直しはかなり減りました。
クリップボード連携のレジスタは環境差に注意
"*と"+はOSのクリップボードと連動するレジスタで、Vimとターミナルの外にあるアプリの間でコピー&ペーストをしたいときに使います。ただし、これは+clipboard機能付きでビルドされたVim(多くのLinuxディストリビューションのvim-gtk系や、macOS/WindowsのGUI版)でないと機能しません。:echo has('clipboard')で0が返る環境では"*yとしても何も起きないので、まず確認しておくと安心です。WSL環境やSSH越しの作業では、この確認を飛ばしてハマる人をよく見かけます。残りのレジスタは必要になったときに:h registersを引けば十分です。
まとめ
Vimのレジスタは48種類ありますが、全部を覚える必要はありません。無名レジスタが上書きされる仕組みと、"0・名前付きレジスタ・ブラックホールレジスタの使い分けさえ押さえておけば、コピペ関連のトラブルはほとんど防げます。まずは:registersで今のレジスタの中身を眺めてみるところから始めるとよいでしょう。貼り付け側の記法はモードによって変わるので、こちらもあわせて確認しておくと安心です。