lua 脚本
neovim 中引入了 lua 解释器能够直接使用 lua 作为脚本文件。由于 lua 是一个完整的编程语言要比 vimscript 更加的完善因此 neovim 推荐使用 lua 脚本来配置以及 lua 编写插件。
简介
Neovim 内置了一个始终可用的 lua 引擎,并且围绕 lua 提供了一系列的编程接口。为了方便 Neovim 将他们放置在不同的命名空间中:
- vim builtin-functions 和 Ex-commands 被封装到
vim.fn
命名空间下,或者使用vim.cmd()
来使用 - 一些用 C 语言编写的用于远程插件和图形用户界面的Nvim API,他们被封装到
vim.api
命名空间下 - 使用 Lua 编写的用于 Neovim 的lua-stdlib,他们直接被绑定在 vim 下
Note
像 vim.opt
、vim.keymap
这些都属于 lua-stdlib。这里主要讲解的也只是 lua-stdlib。而 vim.fn
通常被认为是隶属于 vim 的
在 nvim 中使用 lua
要在 neovim 中使用 lua 有几种方式:
- 使用
:lua <lua-code>
直接在命令行上运行 lua 代码 - 使用
:source luafile.lua
来运行 lua 脚本
Note
在命令行中使用 lua 命令具有自己的执行环境,多次命令之间无法互通(例如定义变量)
启动是加载 Lua 文件
Neovim 支持使用 init.lua 文件作为配置文件,他会在启动时自动加载他,他通常位于:
而如果想要加载插件和其他 lua 文件,可以放置到 runtimepath 的 lua/
目录中,基于此配置目录可能如下:
~/.config/nvim
|-- after/
|-- ftplugin/ # filetype 插件,特定 filetype 加载
|-- java.lua
|-- lua/
| |-- myluamodule.lua
| |-- other_modules/
| |-- anothermodule.lua
| |-- init.lua
|-- plugin/ # 插件
|-- syntax/ # 语法高亮
|-- init.lua
在 lua 中可以使用 require 来加载其他 lua 模块:
-- 注意不要有 .lua 扩展名
require('other_modules') -- loads other_modules/init.lua
-- loads other_modules/anothermodule.lua
require('other_modules/anothermodule')
-- or
require('other_modules.anothermodule')
如果模块不存在会导致引入错误,因此有一个比较健壮的写法就是:
local ok, mymod = pcall(require, 'module_with_error')
if not ok then
print("Module had an error")
else
mymod.function()
end
在 Lua 中使用 vimscript
所有的 Vim 命令和函数都能够在 Lua 中访问。
vim.cmd
使用 vim.cmd()
来运行任意 Vim 命令(主要是 Ex Command):
-- 运行 Vim Command
vim.cmd("colorscheme habamax")
-- 多行命令可以使用 [[ ]] 来运行
vim.cmd([[
highlight Error guibg=red
highlight link Warning Error
]])
如果想要你编程的方式构建 vim 命令,可以使用 vim.cmd.{fn}
的形式:
vim.cmd.colorscheme("habamax")
vim.cmd.highlight({ "Error", "guibg=red" })
vim.cmd.highlight({ "link", "Warning", "Error" })
vim.fn
所有 Vimscript function 函数(包含builtin-functions和user-functions被绑定在 vim.fn
命令空间下:
print(vim.fn.printf('Hello from %s', 'Lua'))
local reversed_list = vim.fn.reverse({ 'a', 'b', 'c' })
vim.print(reversed_list) -- { "c", "b", "a" }
local function print_stdout(chan_id, data, name)
print(data[1])
end
vim.fn.jobstart('ls', { on_stdout = print_stdout })
注意由于 #
并不是 Lua 的合法标识符,如果要调用 AutoLoad 函数必须:
变量(Variables)
可以使用以下的包装器来设置和读取变量:
lua | vimscript | 说明 |
---|---|---|
vim.g |
g: |
全局变量 |
vim.b |
b: |
当前缓冲区变量 |
vim.w |
w: |
当前窗口变量 |
vim.t |
t: |
当前标签页变量 |
vim.v |
v: |
预定义变量 |
vim.env |
- | 当前会话中定义的环境变量 |
还可以定位特定缓冲区(通过数字)、窗口(通过 window-id)和标签页:
vim.b[2].myvar = 1 -- set myvar for buffer number 2
vim.w[1005].myothervar = true -- set myothervar for window ID 1005
同样某些不能用于标识符的字符串需要通过中括号调用:
Tip
要删除一个变量,只需要将其设置为 nil 即可: vim.g.myvar = nil
更改表中字段值
这个非常特殊,你不能直接修改表中字段的只:
vim.g.some_global_variable.key2 = 400
vim.print(vim.g.some_global_variable)
--> { key1 = "value", key2 = 300 }
如果想要更改必须创建一个中间 lua 表,然后修改后在赋值:
local temp_table = vim.g.some_global_variable
temp_table.key2 = 400
vim.g.some_global_variable = temp_table
vim.print(vim.g.some_global_variable)
--> { key1 = "value", key2 = 400 }
选项(Options)
Neovim 同样为选项提供了包装器:
lua | vim | 说明 |
---|---|---|
vim.opt |
:set |
设置选项 |
vim.opt_global |
:setglobal |
设置全局选项 |
vim.opt_local |
:setlocal |
设置本地选项 |
对于布尔选项:
对于列表选项:
-- set wildignore=*.o,*.a,__pycache__
vim.opt.wildignore = { '*.o', '*.a', '__pycache__' }
-- set listchars=space:_,tab:>~
vim.opt.listchars = { space = '_', tab = '>~' }
-- set formatoptions=njt
vim.opt.formatoptions = { n = true, j = true, t = true }
读取选项
不能直接访问选项的值,必须使用 vim.opt:get()
:
print(vim.opt.smarttab)
--> {...} (big table)
print(vim.opt.smarttab:get())
--> false
vim.print(vim.opt.listchars:get())
--> { space = '_', tab = '>~' }
vim.o
还有一套更为简单的选项读取接口:
lua | vim | 说明 |
---|---|---|
vim.o |
:set |
设置选项 |
vim.go |
:setglobal |
设置全局选项 |
vim.bo |
- | 设置缓冲区选项 |
vim.wo |
- | 设置窗口范围的选项 |
他们能够直接读写:
vim.o.smarttab = false -- :set nosmarttab
print(vim.o.smarttab)
--> false
vim.o.listchars = 'space:_,tab:>~' -- :set listchars='space:_,tab:>~'
print(vim.o.listchars)
--> 'space:_,tab:>~'
vim.o.isfname = vim.o.isfname .. ',@-@' -- :set isfname+=@-@
print(vim.o.isfname)
--> '@,48-57,/,.,-,_,+,,,#,$,%,~,=,@-@'
vim.bo.shiftwidth = 4 -- :setlocal shiftwidth=4
print(vim.bo.shiftwidth)
--> 4
和变量一样,他同样可以通过缓冲区编号、window-id 来针对特定缓冲区:
vim.bo[4].expandtab = true -- sets expandtab to true in buffer 4
vim.wo.number = true -- sets number to true in current window
vim.wo[0].number = true -- same as above
vim.wo[0][0].number = true -- sets number to true in current buffer
-- in current window only
print(vim.wo[0].number) --> true
映射(Mappings)
使用 vim.keymap.set(mode, lhs, rhs, opts)
来创建命令,他接受三个参数分别表示:
mode
: 一个字符串或 table,其中包含映射生效的模式前缀lhs
: 一个字符串,要触发命令的按键rhs
: 一个带有 vim 命令的字符串或者是 lua 函数,如果是空表示禁用键
-- Normal mode mapping for Vim command
vim.keymap.set('n', '<Leader>ex1', '<cmd>echo "Example 1"<cr>')
-- Normal and Command-line mode mapping for Vim command
vim.keymap.set({'n', 'c'}, '<Leader>ex2', '<cmd>echo "Example 2"<cr>')
-- Normal mode mapping for Lua function
vim.keymap.set('n', '<Leader>ex3', vim.treesitter.start)
-- Normal mode mapping for Lua function with arguments
vim.keymap.set('n', '<Leader>ex4', function() print('Example 4') end)
第四个参数包含一些映射行为,他是一个具有四个参数的表:
buffer
: 指定缓冲区映射, 0 或 true 表示当前缓冲区silent
: 如果设置为 true 则禁止输出错误信息expr
: 如果设置为 true 不执行 rhs 而是使用输入作为返回值desc
: 映射描述remap
: 默认是非递归映射,可以设置为 true 来启用递归映射
自动命令(autocommand)
用户命令(usercommand)
Note
要区别用户命令和用户函数,前者可以通过 :user_command
调用,后者用于 vimscript/lua 脚本中
参考
Note
Lua 5.1 是 Neovim Lua 的永久接口