Vimのj/kを加速させるサブモード

by
カテゴリ:

Vim駅伝の2024/6/12の記事です。

Vimmerならついなんとはなしにj/kしちゃうこともありますし、<C-D>とか使いなよと分かってても長距離j/kしちゃうこともありますよね。
そういった人向けに、j/kを連打すると移動距離を変えるryhsd/accelerated-jkがあります。

これを見てふと、サブモードを使うと、折り返した行の見掛け通りに上下移動したい時にgjgjgjせずにgjjjする方法を思い出しました。

Vim で折り返し行を簡単に移動できるサブモード・テクニック

これ、うまく使えばjjjjって連打したらj2j4j8jみたくなるマッピング作れるんじゃね……?
というわけでできたのがこちら。

私の怠惰でLua言語だしjのみです……。
kも欲しい人やVimユーザーはよしなに翻訳してください……。

--- accelerate は速度をコントロールする
--- この数式の場合、10連打以上すると、2, 4, 8, 16と加速する
local function accelerate(n)
  return math.min(16, math.max(1, math.floor((n - 10) ^ 2)))
end

--- j を j2<Plug>(j) に上書き
---
--- ただし5j などカウント付きの場合は加速しない
vim.keymap.set("n", "j", function()
    local count = vim.v.count
    if count > 0 then
        return "j"
    end
    return "j2<Plug>(j)"
end, { expr = true })

--- j を連打すると、 2j4j8j12j12j 風になる
---
--- 実際には jj jjjj jjjjjjjj といった具合にしてあげるとぬるぬる動く
vim.keymap.set("n", "<Plug>(j)j", function()
    local speed = accelerate(vim.v.count1)
    return "<Esc>" .. string.rep("j", speed) .. (count + 1) .. "<Plug>(j)"
end, { expr = true })

--- <Plug>(j) を <Nop> にしておくと、ベルが鳴らなくて快適かも
--- Neovim はデフォルトで set belloff=all されてるので、気にしなくてもいいっちゃいい
vim.keymap.set("n", "<Plug>(j)", "<Nop>")

コードをちゃんと読んでいただけた方は謎の<Esc>にお気付きかもしれません。
これをなくすと、jを2発打つだけで20行くらい飛んでくので注意が必要なのです。

たとえば以下のようなマッピングをして、2<space>jすると、4jになると思いきや22jになってしまうのですね。

nnoremap <space>j 2j

<Esc>してあげると、カウントを無視できるようになります。

nnoremap <space>j <Esc>2j

というわけで、あとはmap-<expr>でカウント数をよしなに調整してあげれば良いというわけ。
これで2<space>j4jになります。

nnoremap <expr> <space>j "<Esc>" . 2 * v:count1 . "j"

なお、このマッピング、作ってみたかっただけで使ってはいないのですが、カウントの扱いに対する学びがあってよかったです。
思いついたらやってみるのはいいことですね。

ENJOY!!