neovim + luaで閉じタグの補完まわりを良い感じにする
閉じタグ補完だけなら調べればいくらでも出てくるが、改行時に開いたり閉じタグの重複防止なんか考えだすと結構詰まるので。 まあプラグインとか使えば一発で解決するけど、vimmer心としては自分で使う物は自分で作りたくなるよね。
準備
クロージャのリスト(テーブル)、カーソル下の文字を取得する関数、あとin演算子的な何かとその他必要な物を用意する。
これらを前提とし、以後省略する。
local opts = { silent = true, expr = true } local keymap = vim.keymap.set local enclosures = { parentheses = { '(', ')', '()' }, brackets = { '[', ']', '[]' }, braces= { '{', '}', '{}' }, chevrons = { '<', '>', '<>' }, } local function get_pos(n) local col = api.nvim_win_get_cursor(0)[2] return string.sub(api.nvim_get_current_line(), col + n, col + 1) end local function inoperator(n, v) for _, f in pairs(enclosures) do if f[n] == data then return true end end end
閉じタグを補完する
luaでキーマップを設定する時はvim.keymap.set
を使う。
基本はこれ。
keymap('i', '(', '()<left>', { silent=true })
for等で書き直す。luaの関数を使うので、expr=true
を追加する。
すくなくとも自分の環境では<left>
が使えないので<esc>i
を使う。
for _, f in pairs(enclosures) do keymap('i', f[1], '\'' .. f[3] .. '<esc>i\'', opts) end
機能の近い物
対応する閉じタグをまとめて消す
中身が空の時だけやってくれれば良いので、カーソル下から2文字取得して分岐する。
このための要素をテーブルの3つめに入れてあるので、これを参照する。0-indexedぽい。
local function remover() local cp = get_pos(0) if inoperator(3, cp) == true then return '<bs><del>' else return '<bs>' end end keymap('i', '<bs>', remover, opts)
クロージャの中で改行した時に良い感じに開く
先程の関数と同じ事をして、戻り値を改行等にするだけ。
これについて調べると二回改行して上行ってインデントというパターンが多く見られるが、改行してインサートを抜けて<S-o>で良い。
ドットリピートに影響するので、それが問題になる場合は戻り値を替える。
local function indenter() local cp = get_pos(0) if inoperator(3, cp) == true then return '\n<esc><s-o>' else return '\n' end end keymap('i', '<return>', indenter, opts)
クロージャ内で空白を入力した時に二つ入れる
これも同じ方法でできるのでついでに加えておく。
local function spacer() local cp = get_pos(0) if inoperator(3, cp) == true then return '<space><space><left>' else return '<space>' end end keymap('i', '<space>', spacer, opts)
上の三つをまとめる
さすがに長いので一つにまとめる。
とりあえずテーブルを作ってforに入れれば動く。
local key = { bs = { '<bs>', '<bs><del>' }, rt = { '<return>', '\n<esc><s-o>' }, sp = { '<space>', '<space><space><left>' }, } for _, f in pairs(key) do local function closing() local cp = get_pos(0) if inoperator(3, cp) == true then return f[2] else return f[1] end end keymap('i', f[1], closing, opts) end
閉じタグの重複を避ける
補完してくれるのは嬉しいが、たとえば引数を受け取らない関数を書く時に())
となったりして鬱陶しい。
そうでなくても、右キーは遠いしインサート抜けてlとかするよりは手動で入力してしまった方が早い。
これを解決するために、閉じタグを入力した時カーソル下に同じタグがあった場合に<right>
を入力したい。
for _, f in pairs(enclosures)do local function closure() local cp = get_pos(1) if cp == f[2] then return '<right>' else return f[2] end end keymap('i', f[2], closure, opts) end
雑だしもっと良い方法がありそう、でも動いてるのでひとまずは良し。