Eric's Blog 时光荏苒,岁月如梭

Lua 与 Vim Script 之间函数相互调用

2024-06-11
Eric Wong

起因

在使用 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

延生阅读

分享到:

评论

目前只支持使用邮件参与评论。