Neovimの'g@'でLuaの関数を呼び出す
この記事は、Vim Advent Calendar 2022に投稿したものとなります。
前日までの記事は、リンクを参照してください。
g@
オペレーター
vimにはg@
オペレーターという、少し特殊なオペレーターがあります。
このオペレーターは、実行されると入力を待機する状態となり、w
や4$
のようなモーションが入力されると、'operatorfunc'
に設定されているVimScriptの関数が発火されます。
使用例
詳しい使い方は、Vim上で:h map-operator
と調べて出てくるサンプルがわかりやすいです。
nnoremap <expr> <F4> CountSpaces()
xnoremap <expr> <F4> CountSpaces()
" doubling <F4> works on a line
nnoremap <expr> <F4><F4> CountSpaces() .. '_'
function CountSpaces(type = '') abort
if a:type == ''
set opfunc=CountSpaces
return 'g@'
endif
let sel_save = &selection
let reg_save = getreginfo('"')
let cb_save = &clipboard
let visual_marks_save = [getpos("'<"), getpos("'>")]
try
set clipboard= selection=inclusive
let commands = #{line: "'[V']y", char: "`[v`]y", block: "`[\<c-v>`]y"}
silent exe 'noautocmd keepjumps normal! ' .. get(commands, a:type, '')
echom count(getreg('"'), ' ')
finally
call setreg('"', reg_save)
call setpos("'<", visual_marks_save[0])
call setpos("'>", visual_marks_save[1])
let &clipboard = cb_save
let &selection = sel_save
endtry
endfunction
F4
を押すと、CountSpaces
関数が引数なしでコールされ、'opfunc(operatorfunc)'
の設定をした後、g@
という文字列がreturnされます。
<expr>
で関数からreturnされた文字列が評価されるため、g@
オペレーターが発動し、入力待機状態となります。
この状態でモーションを入力すると、'operatorfunc'
に設定したCountSpaces
関数が今度は引数ありでコールされます。
この時の引数は、
- 行単位の
line
- 文字単位の
char
- 矩形の
block
のいずれかの文字列になります。それぞれの説明は難しくなるので、:h g@
を参考していただきたいです。
Luaの関数を'operatorfunc'に設定する
'operatorfunc'
はVim scriptの関数を設定するためのオプションのため、Luaの関数はそのままでは発火させることが出来ません。
そこで、v:lua
というvim変数を利用します。
詳しくは、:h v:lua-call
で確認できますが、このvim変数を利用することで、Lua側で定義されたグローバル関数をv:lua.Somefunc()
のような記述で呼び出すことが出来ます。
lua << EOF
function SomeFunc()
print("こんばんは")
end
EOF
" :call v:lua.SomeFunc() -> こんばんは
これを利用し、'operatorfunc'
にLuaの関数を設定します。
set operatorfunc=v:lua.Luaのグローバル関数
と設定します。
lua << EOF
function OpeFunc(type)
print(string.format("OpeFunc:%s",type))
end
EOF
set operatorfunc=v:lua.OpeFunc
この状態で、g@
とモーションを入力することで、Lua側で定義されているOpeFunc
が引数付きで呼ばれることを確認できます。
また、'tagfunc'
や'omnifunc'
といった他の関数が設定されるオプションも同様に、v:lua
を利用してLuaの関数を設定することが出来るようです。
こちらも、詳しくは:h v:lua-call
で確認できます。
まとめ
g@
は一見クセが強い機能ですが、モーションと組み合わせることによって簡易的な括弧補完を作ったりすることが出来ます。そういった機能をLua側で実装したい際には、v:lua
を使うことで多少なりとも書きやすくなるかもしれません。
Discussion