我在入Linux坑之前就已经在Windows下折腾vim配置, 当时是使用GitBash改装的PosixShell环境做开发, 因为厌倦了Windows下复杂的环境配置. 我的第一份.vimrc在仅仅使用了3个月后便被我抛弃, 虽然当时没少花时间配置. 至今这份文件还躺在我的旧Nvim配置仓库的根目录下. 后来在B站看到帕特里柯基老师的视频, 基于他的Nvim配置抄写改装了一个配置, 也就是上一个仓库. 这份配置文件我用了将近一年. 虽然在用了大概半年的时候我就有了自己重新编写配置的想法, 却因为AstroNvim的配置实在太好用, 于是一直咕咕.

直到AstroNvim上个月从V4迁移到V5, 有很多的BreakingChange, 导致这个配置Bug频发, 我才想起重新写一份配置. 至今这份配置已经完全替代了之前基于AstroNvim的配置. 我裁减了大部分配置, 只保留了不到30个插件, 启动速度只有短短的30ms左右 (这个启动时间和机器有关系)

新的配置仓库已经开放, 也欢迎来抄我的配置或提出Issue

Nvim Start Page

Editor Workspace

0.环境配置

第一个需要解决的问题是环境隔离, 在新的配置能够完全 Bootstrap 之前, 我们还是需要老的 neovim 配置. 这就需要将原来的 neovim 缓存隔离起来, 因为在调试过程中会产生缓存, 可能会对原来的缓存产生影响

Note

如果选择使用 Vscode 或其他的编辑器, 那么在写新的配置的过程中不需要这个环境. 但是在其他的时候这个环境隔离的机制还是有 用的.

Neovim 的缓存文件路径主要由下面三个环境变量控制, 主要的原理就是设置他们, 并让他们指向隔离的缓存目录

1
2
3
XDG_DATA_HOME
XDG_STATE_HOME
XDG_CACHE_HOME

下面的 venv 用来启动一个修改了环境变量的Bash, 调试工作将在Shell里进行.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#! /bin/bash

RED='\033[0;31m'
CYAN='\033[36m'
BOLD='\033[1m'
RESET='\033[0m'

[ -z ${VIRTUAL_ENV+x} ] || {
echo ""
echo -e $RED $BOLD "[!] Entering nested debug shell is not allowed" $RESET
exit 1
}


export SCRIPT_DIR=$(dirname "$(realpath "$0")")
export TEST_ENV=$SCRIPT_DIR/nvim-cache
mkdir -p $TEST_ENV/{share,state,cache}
export PATH="$SCRIPT_DIR:$PATH"
export VIRTUAL_ENV=nvim-dev
echo ""
echo -e $CYAN $BOLD "[Note]: Please run \`vi\` to launch nvim and enter \`exit\` to exit debug shell" $RESET
ZDOTDIR=$(mktemp -d)
cat > "$ZDOTDIR/.zshrc" <<'EOF'
[[ -f ~/.zshrc ]] && source ~/.zshrc
alias vi="$(which nv)"
EOF
ZDOTDIR="$ZDOTDIR" zsh
rm -rf "$ZDOTDIR"
echo ""
echo -e $CYAN $BOLD "[OK] Leaving debug shell..." $RESET

下面的 nv 是启动文件, 在 venv 启动的调试Shell中输入 nvim-debug 就能使用当前仓库配置启动Nvim

1
2
3
4
5
6
7
#! /bin/bash

export XDG_DATA_HOME=$TEST_ENV/share
export XDG_STATE_HOME=$TEST_ENV/state
export XDG_CACHE_HOME=$TEST_ENV/cache
export NVIM_CONFIG_DEV=1
nvim -u $SCRIPT_DIR/init.lua "$@"

现在可以开始编写init.lua, 我的配置使用Lazy作为插件管理器, 事实上大部分的Nvim配置都使用Lazy. 我们现在的init.lua 中会带有一些”插入性”的代码, 用来辅助进行环境隔离.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
-- DEBUG MODE
-- NOTE: 这里会检测环境变量决定是否进入调试模式
local debug_mode = vim.env.NVIM_CONFIG_DEV

-- NOTE: 在调试模式下把目前的路径加入
local function set_rtpath()
vim.opt.rtp:prepend(vim.env.SCRIPT_DIR)
end

-- NOTE: 重载vim.fn.stdpath函数, 让其在调试模式下返回修改过的路径
if debug_mode == '1' then
local project_root = vim.env.SCRIPT_DIR
---@diagnostic disable: duplicate-set-field
vim.fn.stdpath = function(what)
if what == 'config' then
return project_root
else
return vim.fn.call('stdpath', { what })
end
end
set_rtpath()
vim.api.nvim_create_autocmd('VimEnter', {
once = true,
callback = function()
vim.defer_fn(function()
vim.notify('Entered DEBUG mode', vim.log.levels.WARN, { title = 'Config' })
end, 200)
end,
})
end
-- DEBUG MODE

-- Load user defined settings after Lazy initialization
vim.api.nvim_create_autocmd('User', {
pattern = 'LazyVimStarted',
callback = function()
vim.schedule(function()
-- DEBUG MODE
-- NOTE: 这里再次加载是因为 Lazy 会重写 rtpath
if debug_mode == '1' then
set_rtpath()
end
-- DEBUG MODE

-- NOTE: 这里是在 Lazy 加载完插件之后加载的其他配置

require('config.keymaps').apply()
require('config.options').apply()
require('config.autocmd').apply()
require('config.diagnostics').apply()
require('config.lsp').apply()
require('config.ssh_mode').apply()
require('config.pairs').apply()
end)
end,
})

-- NOTE: 这里是在 Lazy 加载其他插件之前加载的配置
require('config.preload').apply()

-- set lazy path
local lazypath = vim.fn.stdpath 'data' .. '/lazy/lazy.nvim'
---@diagnostic disable: undefined-field
if not (vim.uv or vim.loop).fs_stat(lazypath) then
local lazyrepo = 'https://github.com/folke/lazy.nvim.git'
local out = vim.fn.system { 'git', 'clone', '--filter=blob:none', '--branch=stable', lazyrepo, lazypath }
if vim.v.shell_error ~= 0 then
error('Error cloning lazy.nvim:\n' .. out)
end
end ---@diagnostic disable-next-line: undefined-field
vim.opt.rtp:prepend(lazypath)

-- import plugins
require('lazy').setup {
-- all the plugins' configure files should be put under `lua/plugins`
spec = {
-- NOTE: 插件被放置在 lua/plugins 目录下
{ import = 'plugins' },
},
-- }, --[[@as LazySpec]] {
-- Configure any other `lazy.nvim` configuration options here

install = {
colorscheme = { 'catppuccin' },
},
ui = {
backdrop = 100,
width = 0.8,
height = 0.8,
border = 'rounded',
},
performance = {
rtp = {
-- disable some rtp plugins, add more to your liking
disabled_plugins = {},
},
},
config = function()
-- apply options and keymaps
-- must be put here as hook because plugin loading is async
end,
---@diagnostic disable: undefined-doc-name
} --[[@as LazyConfig]]

现在就可以正常的编写和调试 neovim 配置了

关于这个隔离脚本的其他用处

有时候我需要对某些特殊的工作, 使用改动过的 neovim 配置. 例如在调试 lsp 的时候需要使用 nc 代替正常的 lsp, 使用套接字和被代理 的 lsp 进行通信, 以监测通信内容.

可以拉取一份配置到需要的目录下, 并进行修改, 之后启动调试 shell, 利用调试 shell 运行改动过的配置来完成对 lsp 的调试

调试 lsp

这个调试脚本可以在调试 shell 中加载给定的配置启动 neovim, 设置NVIM_APPNAME 环境变量可以达到差不多的效果, 但是限制是这个配置必须放在~/.config/$NVIM_APPNAME下, 无法完全自由的指定配置的目录.缓存数据 等也都在对应的~/.local/share/$NVIM_APPNAME~/.local/state/$NVIM_APPNAME下.