本記事はVim駅伝の5/24の記事です。
leap.nvimについて
easymotion系のNeovimプラグインとしてメジャーどころにはhop.nvimやleap.nvimがあります。
leap.nvimはいわゆるeasymotion系のプラグインで、入力した文字にマッチする箇所にラベル(a, b, c, …)をつけ、ラベルを入力するとその位置にカーソルを移動します。
デフォルトの挙動はeasymotionの2-character search motionに近いもので、2文字にマッチする箇所にラベルをつけます。
2-character search motionとして見た時のeasymotionとleap.nvimの大きな違いは2つ。
- ラベルをつけるタイミング
- easymotionは2文字入力し終えてから
- leap.nvimは1文字目の段階から。ジャンプ先の最初の2文字目くらいは読まずとも頭に入っているだろうから、2文字目を入力する時間でラベルを読み、3文字目としてラベルを入力すれば思考のスピードでジャンプできる、という考え方のよう。
- ラベルをつける位置
easymotionはジャンプするまさしくその位置
leap.nvimはジャンプ先の2文字後。
leap ^に飛びたい場合、「le」と左から文字を読みながら入力すると、3文字目の「a」の位置にラベルがつくので、そのまま目の移動に合わせてラベルを入力できる
合う合わないはありそうですが、個人的にはなかなか合理的に思います。
ところでleap.nvimは単純なモーションプラグインではなく、指定した箇所にラベルをつけて、選んだラベルに対して任意のアクションを実行するためのフレームワークでもあります。
targets
引数を指定すれば、1、2、3行目の1文字目にラベルをつけられますし、action
引数を指定すれば、選択したラベルやターゲットに関する情報を元に任意の操作を実行できます。
require("leap").leap({
targets = {
{ pos = {1, 1} },
{ pos = {2, 1} },
{ pos = {3, 1} },
},
action = function(x) {
vim.print(x.label) -- 選択したラベルの表示
vim.print(x.pos) -- 選択したターゲットの位置
}
})
簡単に拡張できそうな気配がしますね!
leap-search.nvimについて
というわけで指定したパターンを元に、Window内の文字列を検索してラベルをつけ、ジャンプするleap-search.nvimを作ってみました。
正規表現による検索結果へのジャンプ
たとえば以下のように leap.{-}nvim
などと、Vimの正規表現を利用できます。
require("leap-search").leap("leap.{-}nvim")
応用すると直前の検索パターンでハイライトされた箇所にジャンプなんてこともできますね。
/leap.{-}nvim
:lua require("leap-search").leap(vim.fn.getreg("/"))
とても便利なので、私は以下のようにマッピングしています。
vim.keymap.set("n", "gn", function() require("leap-search").leap(vim.fn.getreg("/")) end)
vim.keymap.set("n", "gN", function() require("leap-search").leap(vim.fn.getreg("/"), {}, { backward = true }) end)
ドキュメントがまだ皆無ですが、第二引数がleap-search.nvim独自のオプションのテーブル、第三引数がleap.nvim本体のオプションのテーブルになってます。
様々な検索エンジンの利用
第二引数を弄ると、以下の検索エンジンの組み合わせも可能です。
- Vimの正規表現を用いる
vim.regex
- Lua expressionまたは部分一致を用いる
string.find
- migemoで日本語をローマジ検索する
kensaku.query
- エンジンが依存するkensaku.vimが必要
- 全角文字へのラベルに関するバグを補正するleap-wide.nvimを推奨
たとえば string.find
で部分一致を、 kensaku.query
でローマ時検索を実行するなら以下。
-- 「kensaku」というパターンで「kensaku」にも「検索」にもラベルをつけられる
require("leap-search").leap("kensaku", {
engines = {
{ name = "string.find", plain = true },
{ name = "kensaku.query" },
},
})
対話的な検索
更に、第一引数に検索パターンにnil
を与えると対話的な検索モードに入ります。あいまい検索機能こそありませんが、fuzzy-motion.vimに近い挙動です。
require("leap-search").leap(
nil,
{
engines = {
{ name = "string.find", plain = true },
{ name = "kensaku.query" },
}
},
{
-- 現在のWindow全体を検索対象にする
target_windows = { vim.api.nvim_get_current_win() },
},
)
ENJOY!!