はじめに
この記事はVim Advent Calendar 2021カレンダー2の18日目の記事です。
設定を面倒くさがってsystemctl poweroff
を毎回手打ちしている中、14日目の記事を見ていて思いました。
電源ボタンを作ろうと。
紹介
電源を切るボタンと言えばこれと作ったものがこちらです。
機能は見ての通りです。
見なれた(?)ボタンの側に書いてあるキーを押して操作します。クリックはできません。
これでvimからPCの電源が切れて便利ですね。
見た目以上の機能は特に何もないのでここからは使用した機能について紹介します。
nvim_buf_add_highlight()
floating windowに色を付けるためにnvim_buf_add_highlight()
を使用しています。
通常のハイライトと違い場所を指定して色を付けられるのでお絵書きするのに便利です。
今回だと意味はないですが、matchaddpos()
と違い編集によるテキスト位置の移動に追従します。
使い方は引数を見るとわかるように簡単です。
nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, {col_start}, {col_end})
- {buffer}
- バッファハンドル。0でカレントバッファ。
- {ns_id}
- ネームスペースID。
nvim_create_namespace()
で作成すると一括でハイライトを消したりできます。 - {hl_group}
- ハイライトグループの名前。
- {line}
- 0始まりな行数。
- {col_start}
- 開始位置(バイト単位)。
- {col_end}
- 終了位置。-1を指定すると行末まで。
例として下記を実行すると、カレントバッファの3行目の5~10バイト目にある文字の背景が青色になります。
highlight Blue ctermbg=blue guibg=blue
call nvim_buf_add_highlight(0, 0, 'Blue' 2, 5, 10)
vim.highlight.range()
nvim_buf_add_highlight()
は行単位でしたが、こちらは行をまたぐ範囲版です。
Luaのvimモジュール内にあるのでLuaから使うかv:lua
経由で呼び出します。
vim.highlight.range({bufnr}, {ns}, {higroup}, {start}, {finish}, {rtype}, {inclusive})
最初の3つの引数は同じで{rtype}
と{inclusive}
は省略できます。
- {start}, {finish}
- それぞれ
{line, col}
のタプルです。{start}
から{finish}
までにある文字すべてにハイライトを当てます。 - {rtype}
-
:h setreg()
に書かれているように種類を選べます。(しかし、どれを試しても範囲に違いはなくよくわかりません。中身を見ると、blockwiseのときに非ASCII文字を調節しているようです。)デフォルトはcharacterwise。 - {inclusive}
-
{finish}
で指定したところを含むか含まないか。デフォルトはfalse。
例を試してみます。
vim.highlight.range(0,0,'TODO', {1, 2}, {2, 8})
0000000000
1111111111
2222222222
3333333333
これを実行すると、2行目の3文字目から3行目の7文字目までがハイライトされます。
{inclusive}
をtrueにすると、8文字目までになります。
vim.loop
vimにはtimer
機能があり、処理の実行を遅らせたり繰り返したりできます。
もちろんLuaからも使えますがLuaにはluvがあり似たようなことができます。
libuvをLuaから使えるようにしたもので、Neovimでもすべての機能が使えるようです。
ネットワークやファイルシステム等のイベントも扱えるようですが今回はtimerだけを紹介します。
luvの関数はNeovimのヘルプには載っていないのでluvのドキュメントを見る必要があります。
timerの使い方はvimと似ています。
timer_start(timer, timeout, repeat, callback)
callback
には関数を渡しますがLuaにはラムダ式がないので少し面倒です。
local timer = vim.loop.new_timer()
timer:start(1000, 0, function()
vim.api.nvim_command('echomsg "test"')
end)
通常は上の例でいいのですがこの場合はエラーになります。
vim.api
の関数は遅延されるためそのままではコールバックの中で使えません。
vim.schedule_wrap()
という関数が用意されているのでこれでラップします。
local timer = vim.loop.new_timer()
timer:start(1000, 0, vim.schedule_wrap(function()
vim.api.nvim_command('echomsg "test"')
end))
他にも、nミリ秒後に1回だけ実行したい時にはこれという関数が用意されています。
defer_fn({fn}, {timeout})
こちらはコールバック内でvim.api
関数をそのまま使うことができます。
上の例を書き直すとこうなります。
vim.defer_fn(function ()
vim.api.nvim_command('echomsg "test"')
end
, 1000
)
おわりに
Floating Windowは楽しいですね。
LuaのおかげかNeovimのドキュメントやAPIがずいぶんと更新されていていじりやすくなったと感じます。
これからもネタプラグインが増えることを楽しみにしています。