如果你也喜欢折腾 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 是一个用 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 |
写在最后
如果你也喜欢折腾插件管理器,欢迎尝试,有问题欢迎交流反馈。