从零开始的neovim0.12(1) -- Bootstrap
zpack.nvim
众所周知neovim0.12中加入了伟大的内置包管理器 vim.pack。
寒假时候看了 tduyng的博客 后从 kickstart + lazy.nvim (现在的kickstart也是vim.pack了) 迁移到了 vim.pack。
然而博主在使用了三个月之后发现没有自带的懒加载还是太难受了, 需要自己手写懒加载事件,多几个懒加载的插件后简直地狱。
于是博主在朋友的推荐下又使用了三个月的 lz.n 作为懒加载器,不过还是要自己手搓各种spec才能比较通用。
那么,有没有什么通用的方案呢?难道我们还是继续用lazy比较好吗?
于是我选择了 zpack.nvim, 个人感觉是 lazy.nvim 的lite版本,但是大部分功能都是有的,字段也基本通用。
bootstrap
vim.pack.add({ { src = "https://github.com/zuqini/zpack.nvim" } })
vim.g.mapleader = " " -- <Space> as leader key
vim.g.maplocalleader = "," -- <,> as local leader (used by grug-far etc.)
require("zpack").setup({
defaults = {
confirm = false,
lazy = true,
},
})只需要9行,如果在意的话也可以把setup里的参数去掉, 代码量远低于lazy那一坨手动操作git的代码。
是给vim.pack的参数,zpack 使用vim.pack来安装自身,这个boolean false的话会有个弹窗让你确认下载。confirm
虽然文档没有写,但是实际上是存在的,实测通过 defaults.lazy 进行bench启动时间要低5毫秒左右(个人55插件的配置)vim-startuptime -vimpath nvim -warmup 3 -count 40 | head -20
配置好插件管理器后的世界
安装好插件管理器后,嗯…,并没有任何变化。

所以我们开始安装插件。
在vim.pack中,插件在安装后会自动加载插件目录下plugin目录下面的lua和vim文件, 一般是定义各种事件和user command,这是第一步加载,由vim.pack的 load 函数完成,load的默认实现回去调用加载函数加载plugin/目录下的文件。 第二步是用户手动加载,方式是, setup只是一个约定好的函数名称,它接收一个插件规定好的spec,例如我们刚刚 zpack 的里面的require("module_name").setup()和里面的字段就是一个{}类型(zpack 作为一个插件管理器同时也还是一个插件喵)。 当然很多插件只需要第一步足矣了,而有些插件因为 lazy 加载或者load被替换等原因没有第一步加载,只加载了第二步也可能出问题。zpack.Config
但是,手动require是比较朴素的方式,它会按照插件配置的方式直接加载 $${plugin_dir}/lua 下面的代码文件(实际上是所有的插件的lua目录都被加进了 rtp , require会找 rtp 下对应的模块,require函数的参数是lua模块), 如果这个过程很消耗时间的话就会拖慢neovim的启动时间,同时很多插件写的不规范导致 加载plugin/下的代码也会消耗很长时间, 这也是我们需要诸如lazy.nvim、zpack 这样的支持懒加载的插件管理器的原因。
我们使用了zpack,所以接下来我会讲讲zpack 加载插件的方式。
在 zpack 的setup中,有一个spec参数,里面放的类型是zpack.Spec[], 也就是zpack插件spec的数组,具体的定义在 Spec Reference 中。这里翻译了其中几个注释,应该比较好理解。
{
-- Plugin source (provide exactly one)
-- Plugin short name. Expands to https://github.com/{user/repo}
-- unix默认是`~/.local/share/nvim/site/pack/core/opt/`
[1] = "user/repo",
src = "https://...", -- Custom git URL or local path,下载codeberg、gitlab等地方插件
dir = "/path/to/plugin", -- Local plugin directory (lazy.nvim compat, ~ expanded, mapped to src)
url = "https://...", -- http格式的git仓库,用来下载在codeberg、gitlab等地方的插件,也可以填本地路径
-- Dependencies
dependencies = string|string[]|zpack.Spec|zpack.Spec[], -- Plugin dependencies
-- Loading control
enabled = true|false|function, -- Enable/disable plugin
cond = true|false|function(plugin), -- Condition to load plugin
lazy = true|false, -- Force eager loading when false (auto-detected)
priority = 50, -- Load priority (higher = earlier, default: 50)
-- Plugin configuration
-- 我们默认是使用setup给一个插件传一个对应的spec
-- 每次都要去插件目录看lua模块名称很麻烦,opts的功能是扫描插件目录下lua
-- 自动require("module_name").setup(opts)
opts = {}, -- Options passed to setup(), triggers auto-setup
-- opts = function(plugin, opts) return {} end, -- Can also be a function
-- Lifecycle hooks
init = function(plugin) end, -- Runs before plugin loads, useful for certain vim plugins
-- 可以在这个函数里手动require,如果一个插件需要比较复杂的行为可以使用
-- 或者插件不使用setup这个函数而是用其他的
-- opts就是zpack的spec里的opts,会自动传参,可以在config函数里调用opts
config = function(plugin, opts) end, -- Runs after plugin loads, receives resolved opts
-- config = true, -- Calls require(main).setup({})
build = string|function(plugin), -- Build command or function
-- Lazy loading triggers (auto-sets lazy=true unless overridden)
-- All triggers can also be functions that receive zpack.Plugin and return the respective type
event = string|string[]|zpack.EventSpec|(string|zpack.EventSpec)[]|function(plugin), -- Autocommand event(s). Supports 'VeryLazy' and inline patterns: "BufReadPre *.lua"
pattern = string|string[], -- Global fallback pattern(s) for all events
cmd = string|string[]|function(plugin), -- Command(s) to create
keys = zpack.KeySpec|zpack.KeySpec[]|function(plugin), -- Keymap(s) to create
ft = string|string[]|function(plugin), -- FileType(s) to lazy load on
-- Source control (version for `vim.pack.add`, string|vim.VersionRange)
version = "main", -- Git branch, tag, or commit
-- version = vim.version.range("1.*"), -- Or semver range via vim.version.range()
-- Source control (lazy.nvim compat, mapped to version)
sem_version = "^1.0.0", -- Semver string (corresponds to lazy.nvim spec's version), auto-wrapped to vim.version.range()
branch = "main", -- Git branch
tag = "v1.0.0", -- Git tag
commit = "abc123", -- Git commit
-- Plugin metadata
name = "my-plugin", -- Custom plugin name (optional, overrides auto-derived name)
-- 如果一个插件的lua目录下有多个模块,可以用main来指定
main = "module.name", -- Explicit main module (auto-detected if not set)
module = false, -- Disable module-based lazy loading for this plugin
-- Spec imports
import = "plugins.lsp", -- Import from lua/{path}/*.lua and lua/{path}/*/init.lua
}最下面有个import参数,可以让我们不用手写lua文件加载的代码就可以轻松实现模块化。 它的默认值是plugins,也是就加载所有 /.config/lua/plugins下的lua模块,约定是这些模块要返回一个 ,然后zpack会自动合并spec。zpack.Spec|zpack.Spec[]
这里我们有两种方案,第一种是
spec = {
{ import = "plugins.code" },
{ import = "plugins.completion" },
{ import = "plugins.deps" },
{ import = "plugins.edit" },
{ import = "plugins.ui" },
},手动指定plugins目录下所有lua模块,这些模块要么是一个文件夹,这样就会扫描每个文件夹下面的lua文件, 要么是一个lua文件,返回spec|spec[]
而第二种是不手动指定 zpack 的 spec,使用默认的plugins作为导入的spec模块。 这样的话,我们按理说只能在plugins目录下放lua文件,不能继续嵌套了。 但是可以利用lua模块的特性,前面说了一个lua模块是一个文件夹或者同名的lua文件, 文件夹的话会去找文件夹下的init.lua,我们只需要在对应文件夹下的init.lua里import就可以了。 例如在 /.config/nvim/lua/plugins/code/init.lua 中
---@module "zpack"
---@type zpack.Spec|zpack.Spec[]
return {
{ import = "plugins.code" },
}