使用 lua 配置 neovim

neovim 0.4.4 to 0.6.1

直到 2 月份我用的都是 nvim 0.4.4,去年 0.5 发布的时候没有更新,然后 2 月初直接更新到了 0.6.1,并且把配置改成了 lua,再陆陆续续用了 2 个月,划个水写点东西记一下。

配置结构

nvim 的配置放在 $HOME/.config/nvim 下,这点没变,只不过启动配置由 vimscript 的 init.vim 改为 lua 的 init.lua,如果需要分成多个模块,则需要在 init.lua 所在目录下创建 lua 目录,把其他模块放入 lua 目录下,init.lua 中通过 require '模块名' 来加载模块。

我的配置比较简单,最终呈现的是这样的目录结构:

1
2
3
4
5
6
7
8
9
10
11
├── init.lua
├── lua
│   ├── colorschema.lua
│   ├── customize.lua
│   ├── keybindings.lua
│   ├── lsp.lua
│   ├── options.lua
│   ├── plugins.lua
│   └── snippets.lua
└── plugin
└── packer_compiled.lua

新配置介绍

在 nvim 里,除了 init.lua,你还可以在命令模式下通过 :lua <lua 语句> 执行 lua 语句,lua 运行时可以通过默认加载的 vim 模块来访问和控制 vim 运行时。

常用模块

  • vim.{o/wo/bo}: vim 选项,例如 :lua vim.wo.number = true 等价于 :set number
  • vim.g:全局变量,相当于 set g:variable = value
  • vim.env: 访问和配置环境变量
  • vim.fn: 访问 vim 运行时函数,例如 :lua print(vim.fn.getenv('HOME')) 等价于 :echo getenv('HOME') 写法
  • vim.api: 提供了一些 api 代替 vim 命令,例如不严格来说,vim 中的 map 对应 vim.api.nvim_set_keymap
  • vim.cmd: 在 lua 中执行 vimscript,例如 :lua vim.cmd('echo getenv("HOME")')
  • vim.lsp: 提供了 lsp 相关的功能,例如::lua vim.lsp.buf.formatting()
  • vim.inspect: 可以把 vim 运行时对象转成可以打印的 lua 对象格式,例如 lua print(vim.inspect(vim.opt.completeopt))

其他没写到的都是因为我没怎么用到。

o/wo/bo 和 opt

vim 中有 3 种范围的选项:global、window-local、buffer-local,在旧版(vimscript 版)里,统一使用 set <option> [ = value ] 语法设置,新版改用带范围的 o/wo/bo 设置选项,并且使用布尔值代替了 no* 选项,并且 nvim 还提供了一种配置方法,那就是 vim.opt,opt 的写法比较像旧版的 set,也有一些区别,我把三种写法都写出来感受一下

vimscript 写法:

1
2
3
4
5
set backup
set number
set shiftwidth=4
set completeopt=menu,menuone,noselect
set shortmess+=c

lua o/wo/bo 写法:

1
2
3
4
5
vim.o.backup = false
vim.wo.number = true
vim.bo.shiftwidth = 4
vim.o.completeopt = 'menu,menuone,noselect'
vim.o.shortmess = vim.o.shortmess .. 'c'

lua opt 写法:

1
2
3
4
5
vim.opt.backup = false
vim.opt.number = true
vim.opt.shiftwidth = 4
vim.opt.completeopt = { 'menu', 'menuone', 'noselect' }
vim.shortmess:append 'c'

用哪种全凭个人喜好

插件

  • 包管理: packer.nvim

之前用的是 vim-plug 管理插件,这次升级也跟着换成了 lua 管理 packer,用的过程中需要注意的是,如果一个插件(参考我配置中使用 cmp-buffer 的方式)没有直接 use,那么它不会触发编译 plugin/packer_compiled.lua,需要在 PackerInstall 后手动 :PackerCompile

  • 语法高亮: nvim-treesitter

nvim 0.5 开始支持 treesitter 高亮,安装配置 nvim-treesitter 插件后开启,插件还能提供折叠和基于语法选择的功能。分析语法需要通过额外的 parser,这个是要额外下载的,通过 :TSInstall <lang> 安装指定语法的 parser,通过 :TSInstallInfo 查看已经安装完成的 parser。

  • lsp 配置: nvim-lspconfig

neovim 团队维护的 lsp 配置,要用自带的 lsp 基本都要安装这个插件,如果自己不太想管理 lsp,可以用 nvim-lsp-installer 插件来管理,如果 lspconfig 目前不支持你用的 lsp server,只能通过 fork 项目在 server_configurations 下增加对应的 lsp server。

  • 自动完成: nvim-cmp 和 luasnip

两个要一起说,因为 nvim-cmp 必须要配置 snippet 的 expand 函数

遇到的一些小问题

  • bufferline 不刷新

我之前一直习惯用 :badd,但是 bufferline 不会刷新,必须要手动 :redrawtabline 才会刷新,改成了 :edit 就可以了

  • luasnip 光标乱飞

我找到了相关 issue,大概原因是使用 snippet 时没有跳转所有位置,那么没被跳转过的位置都会被记录下来,哪怕你离开了这个 snippet 的区域,在下次 jump 还会 jump 回来,解决方法更加邪门,配置 region_check_events 每次光标移动都会触发 luasnip 调用 exit_out_of_region。

  • treesitter 导致代码变乱

触发条件是快速撤回代码,过程中一旦 treesitter 保持,就会让高亮失效,然后代码变乱,暂时没解决。

结尾

我不太懂 lua,不过用到目前感觉影响不大,lua 是一门非常简单易懂的语言,唯一想吐槽的是当 strng 或 table 作为唯一参数可以省略函数括号这个语法糖,让大家给的例子功能一样,写法不一样。

目前整体配置还是没办法完全绕开 vimscript,例如 autocmd 之类,官方正在跟进 api,等 0.7 发布应该基本可以脱离 vimscript 了。

参考资料