Neovim 0.13でドット繰り返しが変わる 挿入と選択の両方
挿入モードで矢印キーを押した瞬間に.が使い物にならなくなる、という経験はドットコマンドを使い込むほど当たり前になっていきます。カーソルを動かすと「繰り返しの単位」がそこで切れるからで、自動閉じ括弧のプラグインが<C-g>Uという見慣れない指定を撒いているのもこれが理由です。Neovim 0.13では、この区切り方が根本から変わります。挿入はセッションまるごと、ビジュアルの操作は選択の「意味」ごと繰り返されるようになり、同じキーを打っているのに結果が変わります。
検証環境:nightlyビルドのNVIM v0.13.0-dev-1336+g8c0bf18374、安定版のNVIM v0.12.4、そしてVIM 9.1(いずれもmacOS)の3つで同じキー列を流して比べました。掲載しているバッファの中身はすべて実行結果です。0.13は開発中なので、リリースまでに細部が変わる可能性があります。
| 打ったキー | Vim 9.1 / Neovim 0.12.4 | Neovim 0.13 |
|---|---|---|
| 挿入に矢印キーを混ぜて. | 最後の移動より後だけ | セッション全体 |
viwUして. | 同じ文字数ぶん | その位置の単語ぜんぶ |
viwdして. | 同じ文字数ぶん | その位置の単語ぜんぶ |
目次(7項目)
挿入はセッションまるごと繰り返される
いちばん分かりやすいのは、挿入の途中で矢印キーを押した場合です。aaaという行の先頭でiXRightRightYEscと打つと、どのバージョンでもXaaYaになります。違いが出るのは、次の行で.を押したときです。
" 打ったキー: iX<Right><Right>Y<Esc> そのあと2行目で .
Vim 9.1 / Neovim 0.12.4
XaaYa
Yaaa <- Y しか繰り返されない
Neovim 0.13
XaaYa
XaaYa <- 打ったとおり
従来は、カーソルを動かした時点で繰り返しの記録がリセットされ、その後に打ったYだけが残っていました。0.13では相対的なカーソル移動が挿入の一部として記録されるので、打鍵をそのまま再現します。ヘルプのins-repeatにも、カーソルキー・S-Left・Home・Ctrl-gjなどが挿入の一部になると明記されています。
自動閉じ括弧が素直に繰り返せるようになる
この変更が実際の設定に効いてくるのが、括弧を自動で閉じるマッピングです。素朴に書くとこうなります。
-- 括弧を閉じてカーソルを中へ戻すだけのマッピング
vim.keymap.set('i', '(', '()<Left>')
foo という行の末尾でa(abcEscと打つとfoo (abc)になります。そのまま次の行で.を押した結果が、バージョンで割れます。
Vim 9.1 / Neovim 0.12.4
foo (abc)
barabc <- 括弧が消え、中身だけが入る
Neovim 0.13
foo (abc)
bar (abc) <- そのまま繰り返せる
従来この問題を避けるために使われてきたのが<C-g>Uで、次のカーソル移動でアンドゥの区切りを作らせない指定です。auto-pair系のプラグインが()<C-g>U<Left>のように書いているのはこのためでした。0.13のヘルプは、カーソルキーは.に記録されるようになったのでこれらのマッピングはアンドゥの区切りを避ける目的だけに必要だ、と説明を書き換えています。役割が半分に減ったわけです。
ビジュアルの繰り返しが「意味」で効く
もうひとつが、ビジュアルモードで操作したあとの.です。Vimはこれを「同じ大きさの範囲」に対して繰り返します。3文字選んで大文字にしたなら、次も3文字ぶん大文字にします。0.13は、選択の作り方ごと再現します。
1行目 one two
2行目 elephant two
" 1行目で viwU、2行目の先頭で .
Vim 9.1 / Neovim 0.12.4
ONE two
ELEphant two <- 3文字ぶんだけ
Neovim 0.13
ONE two
ELEPHANT two <- iw が効き直す
削除でも同じで、viwdのあとに.を押すと、従来は3文字消えてphant twoが残りましたが、0.13は単語ごと消えて twoになりました。ヘルプはviwdを例に「カーソル位置の単語を、大きさに関係なく削除する」と書いています。マクロで同じことをしていた場面のうち、単純なものはこれで置き換えられます。
注意点として、選択を作るのに繰り返せない操作を混ぜた場合は従来どおりです。マウス・検索・Exコマンド・スクロールで範囲を伸ばしたときは、1vを打ったのと同じ「同じ大きさの範囲」に対して演算子が適用されます。
従来の挙動に戻すには
どちらの変更にも逃げ道が用意されています。挿入側は、カーソルキーをCtrl-o経由に置き換えると以前と同じ切れ方に戻ります。
" 挿入中のカーソル移動を従来の区切り方に戻す
inoremap <Left> <C-o><Left>
inoremap <Right> <C-o><Right>
ビジュアル側は:help visual-fixed-sizeに、CmdAtomイベントで直前のビジュアル操作を覚えておき、.を1v付きで実行し直すLuaの例が載っています。ただ、こちらは20行ほどのコードになるので、素直に新しい挙動へ慣れるほうが現実的です。
変わらないところ
取り違えやすい点を3つ挙げておきます。まず、挿入中にジャンプした場合は従来どおりです。Ctrl-Homeでファイルの先頭へ飛んでから文字を打つ操作を試したところ、0.12.4も0.13も最後の変更しか繰り返しませんでした。相対的な移動と、位置が飛ぶ移動とで扱いが違います。
次に、直前に挿入したテキストが入る".レジスタは、これまでどおり最後のカーソル移動より後の文字だけを持ちます。上の括弧の例で確認すると、0.13でも中身はabcのままでした。.の記録と".レジスタは別物です。
3つ目に、この変更はNeovim側だけの話です。Vim 9.1で同じキー列を流すと、すべて0.12.4と同じ結果になりました。Vim 9.2にもこの方向の変更は入っていないので、両方を行き来している人は手が食い違います。
なぜ今この変更が入ったのか
背景にあるのは、Neovimが内部で導入した「アトム」という単位です。開発者向けのドキュメントによると、ユーザーの操作1つ(マッピング展開後のキー列)を意味のあるまとまりとして捕まえ、CmdAtomというイベントで流す仕組みが入りました。xはdlとして、挿入はciwfoo<Esc>として記録される、といった具合です。ドット繰り返しの改善はその副産物で、本命は同じ仕組みの上に載るマルチカーソルのほうです。
ただし、マルチカーソル自体はまだ動きません。今回試したnightlyではQを押しても複数カーソルは付かず、ヘルプの:help multicursorも本文がtodoのままでした。リリースノートに項目があっても手元で動くとは限らないので、実際に触って確かめるまでは期待しないほうが無難です。
最初は「.の効きが良くなる」という話を軽く見ていました。実際に括弧のマッピングで試して、0.12.4がbarabcという壊れた結果を返し、0.13がbar (abc)を返すのを並べて見た時点で評価が変わりました。長年これを回避するために<C-g>Uを書いていたわけで、その理由を知らないまま他人の設定からコピーしていた人も多いはずです。一方でビジュアルの繰り返しは、慣れるまで少し怖いと感じます。viwdの.が長い単語を丸ごと消すのは便利ですが、これまで「同じ文字数」で予測していた手が数回空振りしました。
まとめ
Neovim 0.13のドット繰り返しは、挿入では矢印キーを含むセッション全体を、ビジュアルでは選択の作り方そのものを再現するようになります。実測では、矢印キー混じりの挿入がYaaaからXaaYaへ、viwUの繰り返しがELEphantからELEPHANTへと変わりました。自動閉じ括弧のマッピングが.で壊れなくなるのが実利のいちばん大きいところです。戻したい場合は挿入側がCtrl-o経由のマッピング、ビジュアル側がvisual-fixed-sizeのレシピで、Vim側は9.1でも従来の挙動のままでした。標準ファイラの入れ替えと同じく、0.13は手が覚えている操作に触ってくる更新です。