Skip to content
Go back
Table of Contents

從零開始的 neovim 配置

我在入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 缓存隔离起来, 因为在调试过程中会产生缓存, 可能会对原来的缓存产生影响

第一個需要解決的問題是環境隔離, 在新的配置能夠完全 Bootstrap 之前, 我們還是需要老的 neovim 配置, 這就需要將原來的 neovim 緩存隔離起來. 因為在調試過程中會產生緩存, 可能會對原來的緩存產生影響.

Note

如果選擇使用 Vscode 或者其他的編輯器, 那麼在寫新的配置的過程中不需要這個環境. 但是這個環境隔離的機制還有其他的作用, 後 文將予以說明

Neovim 緩存文件的路徑主要由下面三個環境變量控制, 主要的原理就是設置他們, 並讓他們指向隔離的緩存目錄

XDG_DATA_HOME
XDG_STATE_HOME
XDG_CACHE_HOME

下面的 venv 用來啟動一個修改了環境變量的 Zsh, 調試工作可以在 Shell 裡進行

#! /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 就能使用當前倉庫配置啟動 neovim

#! /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 "$@"

[Note]

值得注意的是, 環境變量在啟動前才進行注入, 防止在調試 Shell 中因為環境變量產生錯誤

現在可以開始編寫init.lua,我的配置使用Lazy作為插件管理器, 事實上大部分的Nvim配置都使用Lazy. 我們現在的init.lua 中會帶有一些”插入性”代碼,用來輔助進行環境隔離。

-- 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下.