起因
在使用 Neovim 之前,一直使用的是 Vim Script 写 Vim 脚本。随着 Neovim 的出现,以及 Lua 的速度优势,于是将之前写的插件使用 Lua 进行重写了。 在这里记录一些从 Vim Script 切换到 Lua 遇到的一些问题,以及前后兼容的写法。
基本兼容逻辑
个人比较看重脚本的向后兼容,在非必要的情况下,对外的接口函数不会改变,通过以下一个示例大致展示了如何做到前后版本的脚本兼容。
比如,早期的 Vim Script 脚本实现的脚本如下:
autoload/test.vim
function! test#test(name)
echo "hello " . name
endfunction
那么,对于 Neovim 新版本的支持,可以使用 Lua 实现类似的功能:
lua/test.lua
local M = {}
function M.test(name)
print('hello ' .. name)
end
return M
为了让前面的 test.vim 使用 Lua 函数,可以做如下修改:
function! test#test(name)
if has('nvim')
lua require('test').test(vim.api.nvim_eval('name'))
else
echo "hello " . name
endif
endfunction
以上函数存在一个问题,就是每次调用时,都会去检测一次版本,可以将脚本修改为:
if has('nvim')
function! test#test(name)
lua require('test').test(vim.api.nvim_eval('name'))
endfunction
else
function! test#test(name)
echo "hello " . name
endfunction
endif
这样一来,版本检测仅仅在这个脚本文件被读取载入时才执行一次,在这之后 test#test(name)
这个函数的本体仅限于一行代码。
lua require('test').test(vim.api.nvim_eval('name'))
Vim Script 中调用 Lua
上述的示例中只有一种调用方式,即使用 :lua
命令,该命令使用有两种方式。
lua print('hello')
lua << EOF
print('hello')
EOF
此外,使用 :lua
命令,如果遇到 Vim Script 函数原先需要有返回值的话,比较麻烦,可以使用 luaeval()
函数,比如:
function! test#test(name)
call luaeval("require('test').test(vim.api.nvim_eval('name'))")
" 也可以使用 return 返回值
return luaeval("require('test').test(vim.api.nvim_eval('name'))")
endfunction
Neovim 0.5.0 增加了 v:lua
,可以更加方便地在 Vim Script 中调用 Lua, 示例如下:
function! test#test(name)
" 这里可以直接使用参数,而不需要使用 nvim_eval
return v:lua.require('test').test(a:name)
endfunction
Lua 中调用 Vim Script 函数
Neovim 提供了 vim.fn
以及 nvim_call_function(fn, argv)
。
-- built-in functions
vim.fn.executable(var)
-- User autoload function
vim.fn['test#test'](name)
对于 Vim 的 funcref 变量,可以使用如下方式调用:
function! s:test_hello(name)
endfunction
let s:fn = function('s:test_hello')
function! test#run(fn)
if type(a:fn) == 2
let fn = string(a:fn)[10:-3]
elseif type(a:fn) == type('')
let fn = a:fn
endif
call v:lua.require('test').run(fn)
endf