Vimで無名レジスタでchange/delete/yankした時に、イニシャルに相当するレジスタにも値を入れる

by

Vim Advent Calendar 2023の12/17の記事です。
昨日の記事は以下の通りでした。

Vimのレジスタ、使いこなしてますか?
とっても沢山種類があります。

[Vim問題] レジスタは全部で何種類?

いくらか使っているものがあるものの、特に名前付きレジスタはちっとも使い込なせてません。
だって、aからzまで、どれをどこに入れたかわからなくなるので、aくらいしか使い道がない……。

一方、ciwしたら使いたかったレジスタ上書きしちゃったじゃん!みたいなできごともしばしばです。
たいていの場合、"0にyankした結果が、"-ciwした結果が入っているので、なんとでもなるっちゃなるのですが、とっさに思考がついていきません。

というわけで考えました。changeしたらcに、deleteしたらdに、yankしたらyに保存しとけばいいのでは……?と。
単純にマッピングで解決するなら、

nnoremap c "cc
xnoremap c "cc
nnoremap d "dd
xnoremap d "dd
nnoremap y "yy
xnoremap y "yy

といった具合にマッピングすればOKです。
しかしこの方法では、"ayyといった具合に明示的にレジスタを指定できなくなる、無名レジスタが更新されないのでpが使い物にならない、といった問題があります。

expression mappingを使って、指定しているレジスタ(v:register)の中身を見る手もあるでしょうが、すべてにexpression mappingを設定するのも面倒そうです。

実は、TextYankPostというイベントは名前と裏腹(?)に、changeやdeleteでレジスタが更新された場合も発火する上に、v:event変数に契機となったオペレータやレジスタが登録されるので、便利です。
以下のように、無名レジスタが更新された場合に、使用中のオペレータの種類(c/d/y)の名前を冠するレジスタに内容をコピーできます。
これで、あっちゃあ、yankした内容をchangeで上書きしちゃったよと思った時も、yankだからyレジスタで、"ypすればオーケー、まだ慌てる時間じゃない、とスムーズに判断できる……かもしれません。

" Vim script
function! UseEasyRegname()
  if v:event.regname ==# ''
    call setreg(v:event.operator, getreg())
  endif
endfunction

augroup UseEasyRegname
  autocmd!
  au TextYankPost * call UseEasyRegname()
augroup END

ちなみに上記のVim scriptは以下のLuaスクリプトをChatGPTに変換させてから手直ししたものです。

-- Lua
vim.api.nvim_create_autocmd("TextYankPost", {
  group = vim.api.nvim_create_augroup("use-easy-regname", {}),
  callback = function()
    if vim.v.event.regname == "" then
      vim.fn.setreg(vim.v.event.operator, vim.fn.getreg())
    end
  end,
})

なお、この方法にも欠点はあります。

これらのマッピングに大しては強制的にxレジスタやsレジスタを割り当てるのも手でしょう。
Atusyの場合は、以下のようにしています。

Enjoy