从零开始的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的代码。

confirm是给vim.pack的参数,zpack 使用vim.pack来安装自身,这个boolean false的话会有个弹窗让你确认下载。

defaults.lazy是插件默认是否启用懒加载的参数,和 lazy.nvim 不同, zpack.nvim 默认是不对插件懒加载的,所以我们需要设置这个参数为lazy

配置好插件管理器后的世界

安装好插件管理器后,嗯…,并没有任何变化。 plain_neovim 所以我们开始安装插件。

在vim.pack中,插件在安装后会自动加载插件目录下plugin目录下面的lua和vim文件, 一般是定义各种事件和user command,这是第一步加载,由vim.pack的 load 函数完成,load的默认实现回去调用加载函数加载plugin/目录下的文件。 第二步是用户手动加载,方式是require("module_name").setup(), setup只是一个约定好的函数名称,它接收一个插件规定好的spec,例如我们刚刚 zpack 的里面的{}和里面的字段就是一个zpack.Config类型(zpack 作为一个插件管理器同时也还是一个插件喵)。 当然很多插件只需要第一步足矣了,而有些插件因为 lazy 加载或者load被替换等原因没有第一步加载,只加载了第二步也可能出问题。

但是,手动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" },
}


本博客基于 Typst 与 Tola 构建
输入关键词开始搜索