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

Neovim 插件管理器 nvim-plug

2025-02-09
2026-02-01
Eric Wong

如果你也喜欢折腾 Neovim ,可能也会在插件管理器上花不少时间吧。 我从最早的 vundle(Bundle)、plug.vim,到后来的 neobundle.vim、dein.nvim,都使用很长一段时间。 使用最长时间的要算 dein.nvim,这是一个无 UI 界面的插件管理器,我甚至曾经还为该插件写过一个 dein-ui.vim 模仿 plug.vim 的界面。

我写 nvim-plug 的原因其实很简单,我前面写过一篇插件管理器的运行机制《Neovim 和 Vim 插件管理器的实现逻辑》

我想练习一下插件管理器的实现,之前看过 dein.nvim 的一些源码,里面的懒加载实现感觉很有趣。 我想要一个完全可控、异步、并且不限制 UI 表现形式的插件管理器。

项目地址: wsdjeg/nvim-plug

nvim-plug

项目简介

nvim-plug 是一个用 Lua 编写的 Neovim 插件管理器,核心目标是:

  • 插件安装和更新必须是异步的
  • 插件管理过程和 UI 界面分离
  • 支持常用的插件懒加载模式

插件描述规范这一块我主要参考了 dein.nvim,之前用的比较多,也习惯了一些 key 值叫法和意义。

功能特点

完全异步调用命令

所有插件的 clone、pull、build 都是通过 job.nvim 并发执行的, 不会出现卡在安装界面不能操作的情况。你可以根据网络和机器性能自由设置并行任务数量。

支持多种懒加载模式

  • 事件: events
  • 命令: cmds
  • 文件类型: on_ft
  • 按键: on_map
  • Vim 函数: on_func

插件什么时候加载,完全可以在配置文件里面明确,而不需要一堆额外逻辑拼出来。

UI 是可替换的

nvim-plug 并不假设你一定要用某种 UI:

  • 默认是分屏 UI,参考的是 vundle.vim
  • 可以切换为 notify 风格的浮动通知
  • 也可以完全自己实现 UI 回调

插件管理器只负责状态,展示方式由你决定。

支持多种插件类型

除了 Git 仓库,还支持:

  • raw 脚本
  • LuaRocks 包
  • 本地开发路径

一些“不是标准插件,但又需要被管理的仓库”也可以统一处理。

我甚至使用这个插件管理器,管理一些上游的 GitHub 仓库(非插件),我需要定期同步到本地某个指定的位置,方便本地阅读代码。

安装方式

自动安装(推荐)

local dir = vim.fn.stdpath('data') .. '/repos/'

local function bootstrap(repo)
  if vim.fn.isdirectory(dir .. repo) == 0 then
    vim.fn.system({
      'git',
      'clone',
      '--depth',
      '1',
      'https://github.com/' .. repo .. '.git',
      dir .. repo,
    })
  end
  vim.opt.runtimepath:append(dir .. repo)
end

bootstrap('wsdjeg/job.nvim')
bootstrap('wsdjeg/logger.nvim')
bootstrap('wsdjeg/nvim-plug')

使用 LuaRocks

luarocks install nvim-plug

基本配置

require('plug').setup({
  bundle_dir = vim.fn.stdpath('data') .. '/repos',
  raw_plugin_dir = vim.fn.stdpath('data') .. '/repos/raw_plugin',
  max_processes = 5,
  base_url = 'https://github.com',
  ui = 'default',
  clone_depth = 1,
})

添加插件

require('plug').add({
  {
    'wsdjeg/scrollbar.vim',
    events = { 'VimEnter' },
  },
  {
    'wsdjeg/flygrep.nvim',
    cmds = { 'FlyGrep' },
    config = function()
      require('flygrep').setup()
    end,
  },
  {
    type = 'raw',
    url = 'https://gist.githubusercontent.com/.../markdown.vim',
    script_type = 'after/syntax',
  },
})

常用命令

  • :Plug install
  • :Plug update

插件界面的选择

其实关于插件管理器的界面,因为历史原因,一直习惯了 Vundle 的界面模式,包括 Vim 下知名插件 vim-plug 也是使用这样类似的插件管理器界面。 而我之前使用的 dein.vim 没有提供默认的界面,为此我还写过一个插件dein-ui.vim。那么在设计 nvim-plug 这个插件管理器的时候, 自然而然就实现了一个类似于 Vundle 的可视化界面,但是考虑到有可能会有其他操作界面模式的需求,因此 nvim-plug 这个插件管理器的操作界面是设计成界面和逻辑分离的模式。

如果兴趣,也可以实现一个新的插件列表界面,可以通过如下模式进行修改:

--- your custom UI

local function on_ui_update(name, data)
  -- logic
end


require('plug').setup({
  bundle_dir = 'D:/bundle_dir',
  max_processes = 5, -- max number of processes used for nvim-plug job
  base_url = 'https://github.com',
  ui = on_ui_update, -- default ui is notify, use `default` for split window UI
})

上述的 on_ui_update 函数会在插件下载、更新、build 等命令执行过程种被调用,函数调用时被传入两个参数:插件名称、界面更新数据 plugUiData。

The plugUiData is table with following keys:

plugUiData 是一个 Lua table,其键值如下:

键值 描述
clone_done boolead, is true when clone successfully
command string, clone, pull or build
clone_process string, git clone progress, such as 16% (160/1000)
clone_done boolean, git clone exit status
building boolean
build_done boolean
pull_done boolean
pull_process string

写在最后

如果你也喜欢折腾插件管理器,欢迎尝试,有问题欢迎交流反馈。


版权声明:本文为原创文章,遵循 署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)版权协议,转载请附上原文出处链接和本声明。


延生阅读

分享到:

评论