NeoVim配置美化完整流程

Neovim 配置美化完整流程


环境说明

项目 说明
操作系统 Ubuntu 20.04 / macOS
终端 Windows Terminal + WSL2 / iTerm2
Neovim版本 v0.7+
配置需求 流畅的 GitHub 连接(用于拉取插件)

一、配置文件整体结构

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
.
├── init.lua # 配置入口文件
└── lua
├── autocmds.lua # 自动命令
├── basic.lua # 基础配置
├── colorscheme.lua # 主题配置
├── keybindings.lua # 快捷键设置
├── lsp
│ ├── cmp.lua # 代码补全配置
│ ├── config # 各语言服务器配置
│ │ ├── bash.lua
│ │ ├── css.lua
│ │ ├── html.lua
│ │ ├── json.lua
│ │ ├── lua.lua
│ │ ├── markdown.lua
│ │ ├── pyright.lua
│ │ ├── rust.lua
│ │ └── ts.lua
│ ├── formatter.lua # 代码格式化
│ ├── null-ls.lua # 格式化/诊断
│ ├── setup.lua # LSP 初始化
│ └── ui.lua # UI 美化
├── plugin-config
│ ├── bufferline.lua # 顶部标签页
│ ├── comment.lua # 注释插件
│ ├── dashboard.lua # 启动页面
│ ├── gitsigns.lua # Git 增强
│ ├── indent-blankline.lua # 缩进线
│ ├── lualine.lua # 底部状态栏
│ ├── nvim-autopairs.lua # 自动括号
│ ├── nvim-tree.lua # 文件浏览器
│ ├── nvim-treesitter.lua # 语法高亮
│ ├── project.lua # 项目管理
│ ├── surround.lua # 成对编辑
│ ├── telescope.lua # 模糊搜索
│ ├── toggleterm.lua # 终端
│ ├── vimspector.lua # 调试
│ └── which-key.lua # 快捷键提示
├── plugins.lua # 插件管理
└── utils
├── fix-yank.lua
├── global.lua
└── im-select.lua

文件说明

文件 说明
init.lua 整个配置的入口文件,负责引用所有其他模块
basic.lua 基础配置,对默认配置的重置
colorscheme.lua 主题皮肤配置
keybindings.lua 快捷键设置,所有插件的快捷键
plugins.lua 插件安装管理
lsp/ 内置 LSP 功能配置,包括编程语言与语法提示
plugin-config/ 第三方插件的独立配置文件
utils/ 常见问题的修改,包括输入法切换等

二、安装 Neovim

2.1 卸载旧版本(可选)

1
sudo apt-get remove neovim

2.2 Ubuntu 安装

1
2
3
4
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:neovim-ppa/unstable
sudo apt-get update
sudo apt-get install neovim

2.3 macOS 安装

1
brew install neovim

2.4 验证版本

1
2
nvim --version
# 确保版本为 0.7 及以上

2.5 配置别名

1
2
3
4
5
6
# ~/.bashrc
alias vim='nvim'
alias vi='nvim'
alias v='nvim'

source ~/.bashrc

三、配置 Nerd Fonts

3.1 下载字体

官网:Nerd Fonts

下载 FiraCodeNerdFont-Regular.ttf,双击安装即可。

3.2 配置终端字体

在终端设置中选择安装的 Nerd Font 字体。


四、配置入口 init.lua

~/.config/nvim/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
-- 基础设置
require('basic')

-- 快捷键映射
require("keybindings")

-- Packer 插件管理
require("plugins")

-- 主题设置
require("colorscheme")

-- 插件配置
require("plugin-config.nvim-tree")
require("plugin-config.bufferline")
require("plugin-config.lualine")
require("plugin-config.dashboard")
require("plugin-config.project")
require("plugin-config.nvim-treesitter")

-- 内置 LSP
require("lsp.setup")
require("lsp.cmp")
require("lsp.ui")
require("lsp.null-ls")

五、基础配置 basic.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
-- utf8
vim.g.encoding = "UTF-8"
vim.o.fileencoding = 'utf-8'

-- jkhl 移动时光标周围保留8行
vim.o.scrolloff = 8
vim.o.sidescrolloff = 8

-- 使用相对行号
vim.wo.number = true
vim.wo.relativenumber = true

-- 高亮所在行
vim.wo.cursorline = true

-- 显示左侧图标指示列
vim.wo.signcolumn = "yes"

-- 右侧参考线
vim.wo.colorcolumn = "80"

-- 缩进2个空格等于一个Tab
vim.o.tabstop = 2
vim.bo.tabstop = 2
vim.o.softtabstop = 2
vim.o.shiftround = true
vim.o.shiftwidth = 2
vim.bo.shiftwidth = 2

-- 空格替代tab
vim.o.expandtab = true
vim.bo.expandtab = true

-- 新行对齐当前行
vim.o.autoindent = true
vim.bo.autoindent = true
vim.o.smartindent = true

-- 搜索大小写
vim.o.ignorecase = true
vim.o.smartcase = true
vim.o.hlsearch = false
vim.o.incsearch = true

-- 命令行高度
vim.o.cmdheight = 2

-- 文件修改自动加载
vim.o.autoread = true
vim.bo.autoread = true

-- 禁止折行
vim.wo.wrap = false

-- 光标移动
vim.o.whichwrap = '<,>,[,]'

-- 允许隐藏buffer
vim.o.hidden = true

-- 鼠标支持
vim.o.mouse = "a"

-- 禁止备份文件
vim.o.backup = false
vim.o.writebackup = false
vim.o.swapfile = false

-- 更新间隔
vim.o.updatetime = 300
vim.o.timeoutlen = 500

-- 分屏位置
vim.o.splitbelow = true
vim.o.splitright = true

-- 自动补全
vim.g.completeopt = "menu,menuone,noselect,noinsert"

-- 样式
vim.o.background = "dark"
vim.o.termguicolors = true
vim.opt.termguicolors = true

-- 不可见字符
vim.o.list = true
vim.o.listchars = "space:·"

-- 补全增强
vim.o.wildmenu = true
vim.o.shortmess = vim.o.shortmess .. 'c'
vim.o.pumheight = 10
vim.o.showtabline = 2
vim.o.showmode = false

-- 复制粘贴联通系统粘贴板
vim.o.clipboard = "unnamedplus"

配置项说明

配置 说明
vim.g.{name} 全局变量
vim.b.{name} 缓冲区变量
vim.w.{name} 窗口变量
vim.bo.{option} buffer-local 选项
vim.wo.{option} window-local 选项
vim.opt 通用选项设置

六、快捷键设置 keybindings.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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
vim.g.mapleader = " "
vim.g.maplocalleader = " "

local map = vim.api.nvim_set_keymap
local opt = { noremap = true, silent = true }

-- 取消 s 默认功能
map("n", "s", "", opt)

-- windows 分屏快捷键
map("n", "sv", ":vsp<CR>", opt)
map("n", "sh", ":sp<CR>", opt)
map("n", "sc", "<C-w>c", opt)
map("n", "so", "<C-w>o", opt)

-- Alt + hjkl 窗口之间跳转
map("n", "<A-h>", "<C-w>h", opt)
map("n", "<A-j>", "<C-w>j", opt)
map("n", "<A-k>", "<C-w>k", opt)
map("n", "<A-l>", "<C-w>l", opt)

-- 左右比例控制
map("n", "<C-Left>", ":vertical resize -2<CR>", opt)
map("n", "<C-Right>", ":vertical resize +2<CR>", opt)
map("n", "s,", ":vertical resize -20<CR>", opt)
map("n", "s.", ":vertical resize +20<CR>", opt)

-- 上下比例
map("n", "sj", ":resize +10<CR>", opt)
map("n", "sk", ":resize -10<CR>", opt)

-- Terminal相关
map("n", "<leader>t", ":sp | terminal<CR>", opt)
map("n", "<leader>vt", ":vsp | terminal<CR>", opt)
map("t", "<Esc>", "<C-\\><C-n>", opt)
map("t", "<A-h>", "<C-\\><C-N><C-w>h", opt)
map("t", "<A-j>", "<C-\\><C-N><C-w>j", opt)
map("t", "<A-k>", "<C-\\><C-N><C-w>k", opt)
map("t", "<A-l>", "<C-\\><C-N><C-w>l", opt)

-- visual模式下缩进
map("v", "<", "<gv", opt)
map("v", ">", ">gv", opt)

-- 上下移动选中文本
map("v", "J", ":move '>+1<CR>gv-gv", opt)
map("v", "K", ":move '<-2<CR>gv-gv", opt)

-- 上下滚动
map("n", "<C-j>", "4j", opt)
map("n", "<C-k>", "4k", opt)
map("n", "<C-u>", "9k", opt)
map("n", "<C-d>", "9j", opt)

-- 粘贴不复制
map("v", "p", '"_dP', opt)

-- 退出
map("n", "q", ":q<CR>", opt)
map("n", "qq", ":q!<CR>", opt)
map("n", "Q", ":qa!<CR>", opt)

-- insert 模式跳转
map("i", "<C-h>", "<ESC>I", opt)
map("i", "<C-l>", "<ESC>A", opt)

-- 代码注释插件
pluginKeys.comment = {
toggler = { line = "gcc", block = "gbc" },
opleader = { line = "gc", block = "gb" },
}

-- nvim-cmp 自动补全
pluginKeys.cmp = function(cmp)
return {
["<A-.>"] = cmp.mapping(cmp.mapping.complete(), { "i", "c" }),
["<A-,>"] = cmp.mapping({ i = cmp.mapping.abort(), c = cmp.mapping.close() }),
["<C-k>"] = cmp.mapping.select_prev_item(),
["<C-j>"] = cmp.mapping.select_next_item(),
["<CR>"] = cmp.mapping.confirm({ select = true, behavior = cmp.ConfirmBehavior.Replace }),
["<C-u>"] = cmp.mapping(cmp.mapping.scroll_docs(-4), { "i", "c" }),
["<C-d>"] = cmp.mapping(cmp.mapping.scroll_docs(4), { "i", "c" }),
}
end

-- LSP 快捷键
pluginKeys.mapLSP = function(mapbuf)
mapbuf("n", "<leader>rn", "<cmd>lua vim.lsp.buf.rename()<CR>", opt)
mapbuf("n", "<leader>ca", "<cmd>lua vim.lsp.buf.code_action()<CR>", opt)
mapbuf("n", "gd", "<cmd>lua vim.lsp.buf.definition()<CR>", opt)
mapbuf("n", "gh", "<cmd>lua vim.lsp.buf.hover()<CR>", opt)
mapbuf("n", "gD", "<cmd>lua vim.lsp.buf.declaration()<CR>", opt)
mapbuf("n", "gi", "<cmd>lua vim.lsp.buf.implementation()<CR>", opt)
mapbuf("n", "gr", "<cmd>lua vim.lsp.buf.references()<CR>", opt)
mapbuf("n", "gp", "<cmd>lua vim.diagnostic.open_float()<CR>", opt)
mapbuf("n", "gk", "<cmd>lua vim.diagnostic.goto_prev()<CR>", opt)
mapbuf("n", "gj", "<cmd>lua vim.diagnostic.goto_next()<CR>", opt)
mapbuf("n", "<leader>f", "<cmd>lua vim.lsp.buf.format()<CR>", opt)
end

-- TypeScript 快捷键
pluginKeys.mapTsLSP = function(mapbuf)
mapbuf("n", "gs", ":TSLspOrganize<CR>", opt)
mapbuf("n", "gr", ":TSLspRenameFile<CR>", opt)
mapbuf("n", "gi", ":TSLspImportAll<CR>", opt)
end

-- nvim-tree 快捷键
pluginKeys.nvimTreeList = {
{ key = { "<CR>", "o", "<2-LeftMouse>" }, action = "edit" },
{ key = "v", action = "vsplit" },
{ key = "h", action = "split" },
{ key = "i", action = "toggle_custom" },
{ key = ".", action = "toggle_dotfiles" },
{ key = "<F5>", action = "refresh" },
{ key = "a", action = "create" },
{ key = "d", action = "remove" },
{ key = "r", action = "rename" },
{ key = "x", action = "cut" },
{ key = "c", action = "copy" },
{ key = "p", action = "paste" },
{ key = "s", action = "system_open" },
}

七、插件配置

7.1 安装 Packer

1
git clone --depth 1 https://github.com/wbthomason/packer.nvim ~/.local/share/nvim/site/pack/packer/start/packer.nvim

7.2 plugins.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
local packer = require("packer")
packer.startup(function(use)
-- Packer 可以管理自己本身
use 'wbthomason/packer.nvim'

-- -------------------- colorschemes --------------------
use("folke/tokyonight.nvim")

-- -------------------- 文件浏览器 --------------------
use({ "kyazdani42/nvim-tree.lua", requires = "kyazdani42/nvim-web-devicons" })

-- -------------------- 顶部标签页 --------------------
use({ "akinsho/bufferline.nvim", requires = { "kyazdani42/nvim-web-devicons", "moll/vim-bbye" } })

-- -------------------- 底部状态栏 --------------------
use({ "nvim-lualine/lualine.nvim", requires = "kyazdani42/nvim-web-devicons" })
use("arkav/lualine-lsp-progress")

-- -------------------- 模糊搜索 --------------------
use { 'nvim-telescope/telescope.nvim', requires = { "nvim-lua/plenary.nvim" } }
use("ahmedkhalf/project.nvim")

-- -------------------- 启动页面 --------------------
use("glepnir/dashboard-nvim")

-- -------------------- 语法高亮 --------------------
use({ "nvim-treesitter/nvim-treesitter", run = ":TSUpdate" })

-- -------------------- LSP --------------------
use("williamboman/mason.nvim")
use({ "williamboman/mason-lspconfig.nvim" })
use("neovim/nvim-lspconfig")
use("jose-elias-alvarez/null-ls.nvim")

-- -------------------- 代码补全 --------------------
use("hrsh7th/nvim-cmp")
use("hrsh7th/vim-vsnip")
use("hrsh7th/cmp-vsnip")
use("hrsh7th/cmp-nvim-lsp")
use("hrsh7th/cmp-buffer")
use("hrsh7th/cmp-path")
use("hrsh7th/cmp-cmdline")
use("rafamadriz/friendly-snippets")

-- -------------------- UI 美化 --------------------
use("tami5/lspsaga.nvim")

-- -------------------- 调试 --------------------
use("CRAG666/code_runner.nvim")
use("windwp/nvim-autopairs")
use("numToStr/Comment.nvim")
use("ur4ltz/surround.nvim")

-- -------------------- TypeScript 增强 --------------------
use({ "jose-elias-alvarez/nvim-lsp-ts-utils", requires = "nvim-lua/plenary.nvim" })
use("b0o/schemastore.nvim")

-- -------------------- Rust 增强 --------------------
use("simrat39/rust-tools.nvim")
end)

7.3 Packer 命令

命令 说明
:PackerCompile 重新生成编译的加载文件
:PackerClean 清除不用的插件
:PackerInstall 安装缺失的插件
:PackerUpdate 更新并安装插件
:PackerSync 更新 + 编译
:PackerLoad 立刻加载 opt 插件

7.4 自动安装

1
2
3
4
5
6
7
-- lua/plugins.lua 末尾添加
pcall(vim.cmd, [[
augroup packer_user_config
autocmd!
autocmd BufWritePost plugins.lua source <afile> | PackerSync
augroup end
]])

八、主题配置 colorscheme.lua

1
2
3
4
5
6
local colorscheme = "tokyonight"
local status_ok, _ = pcall(vim.cmd, "colorscheme " .. colorscheme)
if not status_ok then
vim.notify("colorscheme " .. colorscheme .. " 没有找到!")
return
end

推荐主题

  • tokyonight
  • monokai.nvim
  • dracula
  • nord

九、插件配置详解

9.1 nvim-tree.lua

lua/plugin-config/nvim-tree.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
local status, nvim_tree = pcall(require, "nvim-tree")
if not status then return end

nvim_tree.setup({
git = { enable = false },
update_cwd = true,
update_focused_file = { enable = true, update_cwd = true },
filters = { dotfiles = true, custom = { 'node_modules' } },
view = {
width = 40,
side = 'left',
hide_root_folder = false,
number = false,
relativenumber = false,
signcolumn = 'yes',
},
actions = {
open_file = {
resize_window = true,
quit_on_open = true,
},
},
system_open = { cmd = 'wsl-open' },
})

vim.cmd([[
autocmd BufEnter * ++nested if winnr('$') == 1 && bufname() == 'NvimTree_' . tabpagenr() | quit | endif
]])

9.2 bufferline.lua

lua/plugin-config/bufferline.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
local status, bufferline = pcall(require, "bufferline")
if not status then return end

bufferline.setup({
options = {
close_command = "Bdelete! %d",
right_mouse_command = "Bdelete! %d",
offsets = { { filetype = "NvimTree", text = "File Explorer", highlight = "Directory" } },
diagnostics = "nvim_lsp",
diagnostics_indicator = function(count, level, diagnostics_dict, context)
local s = " "
for e, n in pairs(diagnostics_dict) do
local sym = e == "error" and " " or (e == "warning" and " " or "")
s = s .. n .. sym
end
return s
end,
},
})

快捷键:

1
2
3
4
5
6
map("n", "<C-h>", ":BufferLineCyclePrev<CR>", opt)
map("n", "<C-l>", ":BufferLineCycleNext<CR>", opt)
map("n", "<C-w>", ":Bdelete!<CR>", opt)
map("n", "<leader>bl", ":BufferLineCloseRight<CR>", opt)
map("n", "<leader>bh", ":BufferLineCloseLeft<CR>", opt)
map("n", "<leader>bc", ":BufferLinePickClose<CR>", opt)

9.3 lualine.lua

lua/plugin-config/lualine.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
local status, lualine = pcall(require, "lualine")
if not status then return end

lualine.setup({
options = {
theme = "tokyonight",
component_separators = { left = "|", right = "|" },
section_separators = { left = " ", right = "" },
},
extensions = { "nvim-tree", "toggleterm" },
sections = {
lualine_c = {
"filename",
{ "lsp_progress", spinner_symbols = { " ", " ", " ", " ", " ", " " } },
},
lualine_x = { "filesize", "fileformat", "encoding", "filetype" },
},
})

9.4 nvim-treesitter.lua

lua/plugin-config/nvim-treesitter.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
local status, treesitter = pcall(require, "nvim-treesitter.configs")
if not status then return end

treesitter.setup({
ensure_installed = { "json", "html", "css", "vim", "lua", "javascript", "typescript", "tsx", "rust", "c", "cpp" },
highlight = { enable = true, additional_vim_regex_highlighting = false },
incremental_selection = {
enable = true,
keymaps = {
init_selection = "<CR>",
node_incremental = "<CR>",
node_decremental = "<BS>",
scope_incremental = "<TAB>",
},
},
indent = { enable = true },
})

vim.opt.foldmethod = "expr"
vim.opt.foldexpr = "nvim_treesitter#foldexpr()"
vim.opt.foldlevel = 99

9.5 dashboard.lua

lua/plugin-config/dashboard.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
local status, db = pcall(require, "dashboard")
if not status then return end

db.setup({
theme = 'doom',
config = {
header = {
[[ ]],
[[██████╗ ███████╗████████╗████████╗███╗ ███╗██████╗ ]],
[[██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝████╗ ████║██╔══██╗]],
[[██████╔╝█████╗ ██║ ██║ ██╔████╔██║██████╔╝]],
[[██╔══██╗██╔══╝ ██║ ██║ ██║╚██╔╝██║██╔═══╝ ]],
[[██║ ██║███████╗ ██║ ██║ ██║ ╚═╝ ██║██║ ]],
[[╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ]],
},
center = {
{ icon = " ", desc = "Projects", action = "Telescope projects" },
{ icon = " ", desc = "Recently files", action = "Telescope oldfiles" },
{ icon = " ", desc = "Edit keybindings", action = "edit ~/.config/nvim/lua/keybindings.lua" },
{ icon = " ", desc = "Edit Projects", action = "edit ~/.local/share/nvim/project_nvim/project_history" },
},
footer = {},
},
})

9.6 nvim-autopairs.lua

lua/plugin-config/nvim-autopairs.lua

1
2
3
4
5
6
7
8
9
10
11
local status, autopairs = pcall(require, "nvim-autopairs")
if not status then return end

autopairs.setup({
check_ts = true,
ts_config = { lua = { "string" }, javascript = { "template_string" }, java = false },
})

local cmp_autopairs = require("nvim-autopairs.completion.cmp")
local cmp = require("cmp")
cmp.event:on("confirm_done", cmp_autopairs.on_confirm_done())

9.7 comment.lua

lua/plugin-config/comment.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
local status, comment = pcall(require, "Comment")
if not status then return end

comment.setup({
padding = true,
sticky = true,
toggler = { line = "gcc", block = "gbc" },
opleader = { line = "gc", block = "gb" },
extra = { above = "gcO", below = "gc", eol = "gcA" },
mappings = {
extra = false,
},
})

vim.api.nvim_set_keymap("n", "<leader>/", "<cmd>lua require('Comment').toggle()<CR>", { noremap = true })
vim.api.nvim_set_keymap("v", "<leader>/", "<cmd>lua require('Comment').toggle()<CR>", { noremap = true })

9.8 surround.lua

lua/plugin-config/surround.lua

1
2
3
4
local status, surround = pcall(require, "surround")
if not status then return end

surround.setup({ style_separator_prefix = " " })

快捷键:

快捷键 说明
ds + ( ) } " 删除成对符号
cs + ( ) } " 修改成对符号
ys + 动作 + 符号 添加成对符号

9.9 telescope.lua

lua/plugin-config/telescope.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
local status, telescope = pcall(require, "telescope")
if not status then return end

telescope.setup({
defaults = {
initial_mode = "insert",
selection_strategy = "reset",
color_devkit = true,
file_sorter = sorters.get_fuzzy_sorter,
file_ignore_patterns = { "node_modules", ".git", "dist", "target", "__pycache__" },
generic_sorter = sorters.get_fuzzy_sorter,
winblend = 0,
border = {},
borderchars = { "─", "│", "─", "│", "╭", "╮", "╯", "╰" },
color_devkit = true,
path_display = { "shorten" },
set_env = { ["COLORTERM"] = "truecolor" },
file_previewer = previewers.vim_buffer_cat.new,
grep_previewer = previewers.vim_buffer_vimgrep.new,
qflist_previewer = previewers.vim_buffer_qflist.new,
},
extensions_list = { "themes", "terms", "projects" },
})

快捷键:

1
2
3
4
5
6
7
8
9
map("n", "<C-p>", "<cmd>Telescope projects<CR>", opt)
map("n", "<C-f>", "<cmd>Telescope live_grep<CR>", opt)
map("n", "<C-s>", "<cmd>Telescope grep_string<CR>", opt)
map("n", "<leader>ff", "<cmd>Telescope find_files<CR>", opt)
map("n", "<leader>fb", "<cmd>Telescope buffers<CR>", opt)
map("n", "<leader>fh", "<cmd>Telescope help_tags<CR>", opt)
map("n", "<leader>fo", "<cmd>Telescope oldfiles<CR>", opt)
map("n", "<leader>fc", "<cmd>Telescope commands<CR>", opt)
map("n", "<leader>fp", "<cmd>Telescope project<CR>", opt)

9.10 toggleterm.lua

lua/plugin-config/toggleterm.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
local status, toggleterm = pcall(require, "toggleterm")
if not status then return end

toggleterm.setup({
size = 12,
open_mapping = [[<F5>]],
hide_numbers = true,
shade_filetypes = {},
shade_terminals = true,
shading_factor = 2,
start_in_insert = true,
insert_mappings = true,
persist_size = false,
direction = "float",
close_on_exit = true,
shell = vim.o.shell,
float_opts = { border = "curved", winblend = 3, highlights = { border = "Normal", background = "Normal" } },
})

function _G.set_terminal_keymaps()
vim.api.nvim_buf_set_keymap(0, "t", "<esc>", [[<C-\><C-n>]], { noremap = true })
vim.api.nvim_buf_set_keymap(0, "t", "<A-h>", [[<C-\><C-n><C-w>h]], { noremap = true })
vim.api.nvim_buf_set_keymap(0, "t", "<A-j>", [[<C-\><C-n><C-w>j]], { noremap = true })
vim.api.nvim_buf_set_keymap(0, "t", "<A-k>", [[<C-\><C-n><C-w>k]], { noremap = true })
vim.api.nvim_buf_set_keymap(0, "t", "<A-l>", [[<C-\><C-n><C-w>l]], { noremap = true })
end

vim.cmd("autocmd! TermOpen term://* lua set_terminal_keymaps()")

local Terminal = require("toggleterm.terminal").Terminal
local htop = Terminal:new({ cmd = "htop", hidden = true, direction = "float" })
function _HTOP_HIDE() htop:toggle() end
vim.api.nvim_set_keymap("n", "<F4>", "<cmd>lua _HTOP_HIDE()<CR>", { silent = true })

9.11 lspsaga.nvim

lua/lsp/ui.lua

1
2
3
4
5
6
7
8
local status, saga = pcall(require, "lspsaga")
if not status then return end

saga.init_lsp_saga({
loader = {
load_charsets_async = true,
},
})

9.12 project.lua

lua/plugin-config/project.lua

1
2
3
4
5
6
7
8
9
10
11
12
local status, project = pcall(require, "project_nvim")
if not status then return end

project.setup({
detection_methods = { "lsp", "pattern" },
patterns = { ".git", "package.json", "CMakeLists.txt", "Makefile" },
})

local status_telescope, _ = pcall(require, "telescope._extensions")
if status_telescope then
pcall(require("telescope").load_extension, "project")
end

9.13 gitsigns.lua

lua/plugin-config/gitsigns.lua

1
2
3
4
local status, gitsigns = pcall(require, "gitsigns")
if not status then return end

gitsigns.setup()

快捷键:

快捷键 说明
]c 跳至下一个差异
[c 跳至上一个差异
<leader>gs Git 显示

9.14 indent-blankline.lua

lua/plugin-config/indent-blankline.lua

1
2
3
4
5
6
7
8
9
local status, indent = pcall(require, "indent_blankline")
if not status then return end

indent.setup({
space_char_blankline = " ",
show_current_context = true,
show_current_context_start = true,
context_pattern_backup = {},
})

十、LSP 配置

10.1 setup.lua

lua/lsp/setup.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
local lspconfig = require("lspconfig")
local protocol = require("vim.lsp.protocol")
local protocol_callbacks = protocol.callbacks
local method_is_available = vim.lsp.handlers["textDocument/hover"]

vim.lsp.handlers["textDocument/hover"] = function(_, result, method, ...)
if vim.tbl_isempty(result or {}) then
return method_is_available(_, result, method, ...)
end
return protocol_callbacks.hover(_, result, method, ...)
end

protocol.symbol_name_provider = {
work_done_progress = false,
}

vim.cmd([[
function! LspSymbol(name, icon) abort
let l:symbols = {
\ 'Text': '🔤',
\ 'Method': '🍔',
\ 'Function': '🍞',
\ 'Variable': '🍜',
\ 'Interface': '🍙',
\ 'File': '🍚',
\ 'Module': '🍛',
\ 'Property': '🍣',
\ 'Field': '🍤',
\ 'Enum': '🍥',
\ 'Keyword': '🍣',
\ 'Constant': '🍣',
\ 'Class': '🍣',
\ 'Struct': '🍣',
\ 'Event': '🍣',
\ 'Operator': '🍣',
\ 'Ref': '🍣',
\ 'TypeParameter': '🍣',
\ 'Parameter': '🍣',
\ 'StaticMethod': '🍣',
\ 'Namespace': '🍣',
\ }
return get(l:symbols, a:name, '📖')
endfunction

augroup LspSymbol_highlight
autocmd!
autocmd WinEnter * silent! lua if vim.tbl_isempty(vim.lsp.buf_get_active_clients()) == false then vim.cmd('highlight LspSymbol guifg=#b4d51c')| endif
augroup END
]])

local list = {
"pyright",
"rust_analyzer",
"tsserver",
"gopls",
"jsonls",
"html",
"cssls",
"julials",
"bashls",
"dockerls",
"yamlls",
"vimls",
"cmake",
"lemminx",
}

for _, server in pairs(list) do
lspconfig[server].setup({
flags = { debounce_text_changes = 500 },
capabilities = require("lsp.cmp").capabilities,
on_attach = function(client, bufnr)
require("lsp.ui").on_attach(client, bufnr)
end,
})
end

10.2 cmp.lua

lua/lsp/cmp.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
local lsp_status_ok, cmp_config = pcall(require, "copilot_cmp.config")
if not lsp_status_ok then
local status_cmp, cmp = pcall(require, "cmp")
if not status_cmp then
return
end

local status_cmp_lsp, cmp_lsp = pcall(require, "cmp_nvim_lsp")
if not status_cmp_lsp then
return
end

local snip_status_ok, luasnip = pcall(require, "luasnip")
if not snip_status_ok then
return
end

local border = {
{ "╭", "LspSagaBorderTitle" },
{ "─", "LspSagaBorderTitle" },
{ "╮", "LspSagaBorderTitle" },
{ "│", "LspSagaBorderTitle" },
{ "╯", "LspSagaBorderTitle" },
{ "─", "LspSagaBorderTitle" },
{ "╰", "LspSagaBorderTitle" },
{ "│", "LspSagaBorderTitle" },
}

local options = {
window = {
completion = {
border = border,
},
documentation = {
border = border,
},
},
snippet = {
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},
mapping = require("keybindings").pluginKeys.cmp(cmp),
sources = cmp.config.sources({
{ name = "nvim_lsp", priority = 1000 },
{ name = "vsnip", priority = 750 },
{ name = "buffer", priority = 500, max_item_count = 10 },
{ name = "path", priority = 250 },
}),
formatting = {
format = function(entry, vim_item)
local lsp_source_icons = {
vscode = " ",
nvimg = " ",
}
vim_item.kind = string.format("%s %s", lsp_source_icons[entry.source.name] or " ", vim_item.kind)
vim_item.menu = ({
nvim_lsp = "[LSP]",
vsnip = "[SNIP]",
buffer = "[BUF]",
path = "[PATH]",
})[entry.source.name]
vim_item.icons = string.format("%s %s", lsp_source_icons[entry.source.name] or " ", vim_item.kind)
return vim_item
end,
},
confirm_opts = {
behavior = cmp.ConfirmBehavior.Replace,
select = false,
},
}

cmp.setup("n", { "<leader>rn", "LspSource" })
cmp.setup(options)

cmp.setup.cmdline("/", {
mapping = cmp.mapping.preset.cmdline(),
sources = {
{ name = "buffer" },
},
})

cmp.setup.cmdline(":", {
mapping = cmp.mapping.preset.cmdline(),
sources = cmp.config.sources({
{ name = "path" },
}, {
{ name = "cmdline" },
}),
})
end

10.3 formatter.lua

lua/lsp/formatter.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
local status, null_ls = pcall(require, "null-ls")
if not status then
return
end

local formatting = null_ls.builtins.formatting
local diagnostics = null_ls.builtins.diagnostics

null_ls.setup({
debug = false,
sources = {
formatting.prettier.with({ extra_filetypes = { "toml", "tsx", "jsx" } }),
formatting.black.with({ extra_args = { "--fast" } }),
formatting.stylua,
formatting.shfmt,
formatting.codespell,
diagnostics.eslint,
formatting.eslint,
},
on_attach = function(client, bufnr)
if client.supports_method("textDocument/formatting") then
vim.api.nvim_clear_autocmds({ group = augroup, buffer = bufnr })
vim.api.nvim_create_autocmd("BufWritePre", {
group = augroup,
buffer = bufnr,
callback = function()
vim.lsp.buf.format({ async = true, bufnr = bufnr })
end,
})
end
end,
})

十一、常见问题

11.1 Neovim 输入法切换

安装 im-select

1
npm install -g im-select

配置 lua/utils/im-select.lua

1
2
3
-- 自动切换输入法
vim.g.im_select_enable = 1
vim.g.im_select_default = 1

11.2 WSL2 打开 Windows 文件管理器

安装 wsl-open

1
npm i -g wsl-open

11.3 禁用自动备份

1
2
3
4
-- ~/.config/nvim/init.lua
vim.o.backup = false
vim.o.writebackup = false
vim.o.swapfile = false

11.4 配置查找与帮助

命令 说明
:h {subject} 查看帮助
:h <C-d> 列出所有主题
:h <option> 查看选项含义
:checkhealth 检查健康状态