Neovim配置——从入门到放弃

前言

本来这个教程想分成十几篇来写的,但实在是懒,就放在这一篇里吧,有什么更新也会直接添加到文章后面。

本篇博文声明:

  1. 本篇博客不以完整的教程为目的,仅作为一个提纲性的参考
  2. 默认读者对vim/neovim有一定的了解,或者已经用了一段时间了,但是想系统性的调整自己的配置文件
  3. 任何第三方的博客都是二手资料,一般都是不如官方文档的,除非实在写的太烂,比如luasnip:),如果你看这篇博客看不懂,直接去翻插件的文档,如果英文看不懂,就算翻译一下总能看懂的,总比到处问强
  4. 不要盲目添加插件,没有完美的配置,只有适合自己的配置,在使用的过程中添加/删除插件,修改配置
  5. 天天改配置不如多写几行代码:(

关于用现成的配置比如LazyVim, LunarVim,还是完全自己配,这个完全看个人

  • 不想折腾就用现成的,开箱即用,然后加上一些自定义配置,缺点是出了问题不知道在哪
  • 自己配,知根知底,相对麻烦一些,但实际上也没那么麻烦

我的配置在github,个人使用,仅供参考。
放几张效果图:

启动页 文件搜索
image image
定义/引用查找 目录树/大纲
image image

我的neovim配置结构

我的配置没有完全放弃对vim的支持,这部分用viml来设置,主要是一些基础的配置,比如高亮行,设置行号等等,还有一些基础的按键设置,目的是保留对部分服务器的简单支持,直接copy就行,当然,如果是自己的开发机的话,也可以通过rsync同步的方式把配置上传到开发机上。

配置的基本目录结构如下:

~/.config/nvim/
├── debug.lua
├── init.lua neovim配置入口
├── lazy-lock.json 插件版本信息
├── lua
│   ├── autoload/ 自己写的自动加载的脚本(插件)
│   ├── helper/ 一些工具函数
│   └── plugins 插件目录
│   ├── dap/ 调试
│   ├── edit/ 编辑类
│   ├── lang/ 特定语言类,例如tex, c/cpp, python这样分类
│   ├── lsp/ lsp相关
│   ├── tools/ 工具类插件
│   ├── ui/ ui美化类插件
│   └── unused/ 暂时不用的插件,不加载
├── my-snippets/ 我的代码片段
├── README.md
├── stylua.toml lua格式化
├── tasks.ini 代码运行全局配置
├── test/ 各种乱七八糟的文件,测试效果用的
└── vimrc 基础配置

配置文件入口为 ~/.config/nvim/init.lua,里面先加载vimrc,然后才是用lua写的插件配置。
插件管理器用的是folke大神写的lazy.nvim,目前主流的Neovim预配置项目都采用了这个管理器,推荐,具体用法在后面介绍。

vim.cmd.source(vim.fn.stdpath("config") .. "/vimrc")
require("helper.path")
require("helper.lazy").setup({
spec = {
-- 经过这种import 方式导入插件的lua 文件必须返回一个table
{ import = "plugins.ui" },
{ import = "plugins.edit" },
{ import = "plugins.lsp" },
{ import = "plugins.dap" },
{ import = "plugins.tools" },
{ import = "plugins.lang" },
{ import = "autoload" },
},
dev = {
path = "~/Projects/neovim/",
},
})

同步配置

服务器连接github比较困难的情况下,可以用rsync把本地配置同步到远程,享受几乎一样的体验

sync_nvim() {
local server=$1
rsync ~/.vimrc $server:~/.vimrc
rsync -av --progress --delete --exclude test/ ~/.config/nvim/ $server:~/.config/nvim/
rsync -av --progress --exclude .git/ ~/.local/share/nvim/lazy/ $server:~/.local/share/nvim/lazy/
}

用法

sync_nvim ubuntu@192.168.1.2
# 或者
sync_nvim server_alias

基础配置

基础配置在~/.config/nvim/vimrc,neovim会先加载这个文件,vim如果使用的话通过软链接的方式ln -s ~/.config/nvim/vimrc ~/.vimrc

这个文件大概分为这几个部分

  • options 常用设置选项
  • keymaps 无插件按键映射
  • autocmd 设置了几个自动命令
  • gui config 主要是字体设置(不常用)
  • vim only config 主要是vim内置的目录树netrw和apt安装的vim-airline
    image

由于这些配置具有一定的个人喜好,所以仅列出部分,详情可以去翻我的仓库

options

" 编码
set encoding=utf-8
set fileencodings=utf-8,gb2312,gbk,gb18030,latin1
set fileformat=unix
set fileformats=unix,dos
" 缩进与格式
filetype indent on
set autoindent
set smarttab
set cindent
set shiftwidth=4
set tabstop=4
set expandtab
set softtabstop=4
set backspace=eol,start,indent
" 搜索
set hlsearch
set incsearch
set ignorecase
set smartcase

keymaps

这里先祭出一张忘了从哪抄来的表格

Mode | Norm | Ins | Cmd | Vis | Sel | Opr | Term | Lang | ~
Command +------+-----+-----+-----+-----+-----+------+------+ ~
[nore]map | yes | - | - | yes | yes | yes | - | - |
n[nore]map | yes | - | - | - | - | - | - | - |
[nore]map! | - | yes | yes | - | - | - | - | - |
i[nore]map | - | yes | - | - | - | - | - | - |
c[nore]map | - | - | yes | - | - | - | - | - |
v[nore]map | - | - | - | yes | yes | - | - | - |
x[nore]map | - | - | - | yes | - | - | - | - |
s[nore]map | - | - | - | - | yes | - | - | - |
o[nore]map | - | - | - | - | - | yes | - | - |
t[nore]map | - | - | - | - | - | - | yes | - |
l[nore]map | - | yes | yes | - | - | - | - | yes |
Modes
normal_mode = "n",
insert_mode = "i",
visual_mode = "v",
visual_block_mode = "x",
term_mode = "t",
command_mode = "c",

下面列出部分映射

" 优化jk设置
nnoremap j gj
nnoremap k gk
vnoremap j gj
vnoremap k gk
" alt+hjkl移动行
" mini.move
nnoremap <M-j> :m +1<CR>==
vnoremap <M-j> :m '>+1<CR>gv=gv
nnoremap <M-k> :m -2<CR>==
vnoremap <M-k> :m '<-2<CR>gv=gv
nnoremap <M-h> <<
vnoremap <M-h> <gv
nnoremap <M-l> >>
vnoremap <M-l> >gv
" H, L jump to line home / end
nnoremap H ^
nnoremap L $
vnoremap H ^
vnoremap L $
onoremap H ^
onoremap L $
" 切换buffer
nnoremap <C-n> <cmd>bnext<CR>
nnoremap <C-p> <cmd>bprevious<CR>
" 简单的自动括号实现,给vim用的
if !has('nvim')
inoremap ( ()<Left>
inoremap <expr> ) getline(line('.'))[col('.')-1]==')' ? '<Right>' : ')'
inoremap [ []<Left>
inoremap <expr> ] getline(line('.'))[col('.')-1]==']' ? '<Right>' : ']'
inoremap { {}<Left>
inoremap <expr> } getline(line('.'))[col('.')-1]=='}' ? '<Right>' : '}'
inoremap < <><Left>
inoremap <expr> > getline(line('.'))[col('.')-1]=='>' ? '<Right>' : '>'
" inoremap ' ''<Left>
" inoremap <expr> ' getline(line('.'))[col('.')-1]=="'" ? '<Right>' : "'"
" inoremap " ""<Left>
" inoremap <expr> " getline(line('.'))[col('.')-1]=='"' ? '<Right>' : '"'
" inoremap ` ``<Left>
" inoremap <expr> ` getline(line('.'))[col('.')-1]=='`' ? '<Right>' : '`'
endif

autocmd

augroup _general_settings
autocmd!
"这些文件类型按q退出
autocmd FileType qf,help,man,checkhealth,startuptime nnoremap <silent><buffer> q <cmd>close<CR>
" json等文件tab设置为2,并高亮所在列
autocmd FileType json,json5,yaml setlocal shiftwidth=2 tabstop=2 softtabstop=2 cursorcolumn
" neovim复制文本后高亮
if has('nvim')
autocmd TextYankPost * silent!lua require('vim.highlight').on_yank({ timeout = 500 })
endif
autocmd BufWinEnter * set formatoptions-=cro
autocmd CursorHold * set nohlsearch
augroup end
" 剪贴板设置
" wsl用户,在vim中复制后同步到系统剪贴板
" 从系统剪贴板复制到vim中用终端的Ctrl-Shift-V
if has('wsl')
let s:clip = '/mnt/c/Windows/System32/clip.exe'
if executable(s:clip)
augroup WSLYank
autocmd!
autocmd TextYankPost * if v:event.operator ==# 'y' | call system(s:clip, @0) | endif
augroup end
endif
else
set clipboard=unnamedplus
endif

gui font

这里主要是针对gvim字体设置

if has('gui_running')
" 去掉gvim的标题栏
set guioptions-=m
set guioptions-=T
" 设置字体,注意不同系统的分隔符还不一样
if has('win32') || has('mac') || has('nvim')
set guifont=JetBrainsMono\ Nerd\ Font:10,Consolas:10
else
set guifont=JetBrainsMono\ Nerd\ Font\ 10,Consolas\ 10,DejaVu\ Sans\ Mono\ 10
endif
endif

插件管理器 Lazy.nvim

Lazy.nvim是一个非常方便,模块化管理的插件管理器,现在主流的neovim配置都是用的这个。

自己配置neovim,第一个设置的肯定是插件管理器。还是强调一下无论什么插件,基本用法在官方文档里都是有的。

Lazy.nvim 文档

lazy.nvim有一段自动安装的脚本,这个你放在配置的开头,然后在启动lazy.nvim,我自己呢是做了一个简单的包装

local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system(
"git clone --filter=blob:none --branch=stable https://github.com/folke/lazy.nvim.git " .. lazypath
)
end
vim.opt.rtp:prepend(lazypath)

设置lazy.nvim

require("helper.lazy").setup({
spec = {
-- 经过这种import 方式导入插件的lua 文件必须返回一个table
{ import = "plugins.ui" }, -- 对应~/.config/nvim/lua/plugins/ui 目录
{ import = "plugins.edit" },-- 对应~/.config/nvim/lua/plugins/edit目录
{ import = "plugins.lsp" },
{ import = "plugins.dap" },
{ import = "plugins.tools" },
{ import = "plugins.lang" },
{ import = "autoload" }, -- 对应~/.config/nvim/lua/autoload 目录
},
})

然后把插件文件放进这些目录里就行,lazy.nvim会自动加载,不过每个插件文件需要满足一定的格式,每个文件必须返回一个table,例如

return {
"folke/lazy.nvim", version = "*"
}

一个文件也可以包含多个插件

return {
{ "folke/lazy.nvim", version = "*" },
{ "williamboman/mason.nvim", version = "*", config = true },
...
}

常用的字段:

  • dependencies 依赖插件,会先加载
  • version 选择的插件版本(tag)常用version="*",即只使用打tag的release版本,相对稳定些
  • commit 插件的某个版本commit id,用于某些更新频繁,不太稳定的插件
  • event 触发event之后加载
  • keys 快捷键映射,同时也作为插件加载的依据
  • cmd 需要执行命令时加载
  • opts 插件的opts,会自动传给require('...').setup(opts)
  • config 与opts类似
    如果同时使用config字段和opts字段,opts字段会不生效,需要改成:
opts = { ... },
config = function(_, opts)
require('plugin').setup(opts) -- 上面的opts也可以直接放到这里
-- other setttings
end,

设置event, keys, cmd之后默认是懒加载模式,如果需要在启动的时候加载,可以指定lazy=false

模糊搜索 fzf-lua/Telescope/Leaderf/vim-clap...

插件管理器之后,我认为最重要的插件应该是模糊搜索类插件,这类插件在我心中的地位,甚至超过了输入补全和lsp
这类插件可以非常方便的进行文件搜索和跳转,全项目范围的字符模糊搜索,同时一般也集成了内置比如查看按键映射,高亮,最近打开的文件等等功能,还有集成了lsp/dap等扩展功能,非常强大,非常方便

文件搜索 mru
image image
字符串搜索 keymap查询
image image

我把这类插件放在第二个来介绍,还不仅仅是因为上面这些强大的功能,主要是因为可以非常方便的搜索文档。
image

不知道某个插件怎么用?搜一下文档
不知道某个内置函数怎么用?搜一下文档
不知道neovim有什么内置函数可以用?搜一下文档
有这个功能之后,可以节省你很多的时间。

vim/neovim生态下的模糊搜索插件主要有以下几个:

  • telescope.nvim 纯neovim,使用人数最多,插件比较多
  • fzf-lua,和telescope差不多,但我感觉好用一点
  • LeaderF,同时支持vim/neovim,优势在于支持gtags,缺点是不支持lsp,gtags可以作为lsp以外的补充。(依赖python,模糊搜索算法是作者自己设计的,不输fzf)
  • vim-clap,和leaderf类似,同样是中国人开发,支持vim/neovim,这个插件我了解不多

这些插件在功能性上应该都差不多,选一个自己用的顺手的就行。我现在用的是fzf-lua

return {
"ibhagwan/fzf-lua",
dependencies = {
"nvim-tree/nvim-web-devicons",
},
event = { "VeryLazy" },
enabled = vim.pathlib.executable("fzf"),
opts = {
"default",
winopts = {
preview = {
border = "noborder",
vertical = "up:50%",
horizontal = "right:50%",
delay = 50,
},
},
files = {
path_shorten = 3,
},
diagnostics = {
split = "belowright new",
},
previewers = {
bat = {
cmd = vim.pathlib.executable("batcat") and "batcat" or "bat",
},
},
},
keys = {
{ "<leader>f/", "<cmd>FzfLua <CR>", desc = "FzfLua self" },
{ "<leader>ff", "<cmd>FzfLua files<CR>", desc = "files" },
{ "<leader>fb", "<cmd>FzfLua buffers<CR>", desc = "buffers" },
{ "<leader>fl", "<cmd>FzfLua live_grep<CR>", desc = "live grep" },
{ "<leader>fh", "<cmd>FzfLua help_tags<CR>", desc = "help" },
{ "<leader>fH", "<cmd>FzfLua highlights<CR>", desc = "highlights" },
{ "<leader>fm", "<cmd>FzfLua oldfiles<CR>", desc = "mru" }, -- mru: most recent used
{ "<leader>fc", "<cmd>FzfLua commands<CR>", desc = "commands" },
{ "<leader>fj", "<cmd>FzfLua jumps<CR>", desc = "jumplist" },
{ "<leader>fk", "<cmd>FzfLua keymaps<CR>", desc = "keymaps" },
{ "<leader>fq", "<cmd>FzfLua quickfix<CR>", desc = "quickfix" },
{ "<leader>fw", "<cmd>FzfLua grep_cword<CR>", desc = "cword" },
{ "<leader>fa", "<cmd>lua require('helper.asynctask').fzf_select()<CR>", desc = "asynctask" },
{ "<leader>d", "<cmd>FzfLua lsp_document_diagnostics<CR>", desc = "lsp_document_diagnostics" },
{ "<leader>fd", "<cmd>FzfLua lsp_definitions<CR>", desc = "lsp_definition" },
{ "<leader>fr", "<cmd>FzfLua lsp_references<CR>", desc = "lsp_references" },
{ "<leader>fi", "<cmd>FzfLua lsp_implementations<CR>", desc = "lsp_implementations" },
{ "<leader>fs", "<cmd>FzfLua lsp_document_symbols<CR>", desc = "lsp_document_symbols" },
{ "<leader>fS", "<cmd>FzfLua lsp_workspace_symbols<CR>", desc = "lsp_workspace_symbols" },
{ "<C-f>", "<cmd>FzfLua grep_curbuf<CR>", desc = "lines" },
},
}

在进行各个插件的按键映射的时候最好遵循一套自己的逻辑,在模糊搜索功能下主要以<leader>f作为前缀(我的leader键是空格),然后有些用的非常频繁的可以作为个例,比如我这里的<C-f>,算是使用windows留下的习惯吧。

输入补全 cmp

cmp是neovim中的一个补全框架,说时候配置比较繁琐,如果追求省事儿,可以选用coc.nvim,老牌插件了,也是国人开发的,开箱即用

cmp比较轻量级,但是文档冗长,经常出现意义不明的选项=_=

cmp插件本身只是一个框架,需要source来配合,例如:

return {
"hrsh7th/nvim-cmp",
dependencies = {
-- cmp-nvim-lsp in lsp/init.lua
-- cmp-luasnip in edit/luasnip.lua
"hrsh7th/cmp-cmdline",
"hrsh7th/cmp-buffer",
"hrsh7th/cmp-path",
},
config = function()
--
end,
}

配置及说明

--我在cmp配置文件开头有一个CMP_SOURCES 的表
CMP_SOURCES = {
luasnip = { name = "luasnip", menu = "[Snip]" },
nvim_lsp = {
name = "nvim_lsp",
menu = "[LSP]",
entry_filter = function(entry)
return require("cmp").lsp.CompletionItemKind.Snippet ~= entry:get_kind()
end,
},
path = { name = "path", menu = "[Path]" },
buffer = { name = "buffer", menu = "[Buf]" },
cmdline = { name = "cmdline", menu = "[Cmd]" },
}
-- cmp的配置
config = function()
local cmp = require("cmp")
local compare = require("cmp.config.compare")
-- 这里设置的是插入模式下的补全source
cmp.setup({
-- 补全菜单和文档框,是否要有边框,以及高亮组等
window = {
completion = cmp.config.window.bordered({
border = "none",
winhighlight = "Normal:Normal,FloatBorder:Normal,CursorLine:MyCmpSel", -- 重点是CursorLine
}),
documentation = cmp.config.window.bordered({
border = "none",
}),
},
-- 补全菜单的格式,见下面的效果图
formatting = {
fields = { "abbr", "kind", "menu" },
expandable_indicator = false,
format = function(entry, vim_item)
vim_item.menu = CMP_SOURCES[entry.source.name].menu
return vim_item
end,
},
-- global setting and can be overwritten in sources
experimental = { ghost_text = false },
sources = {
CMP_SOURCES.nvim_lsp,
CMP_SOURCES.luasnip,
CMP_SOURCES.buffer,
CMP_SOURCES.path,
},
-- 补全项的排序算法权重,我也比较迷惑
sorting = {
comparators = {
compare.score,
compare.kind,
compare.exact,
compare.length,
compare.offset,
compare.sort_text,
},
},
-- snippet settting in luasnip
mapping = cmp.mapping.preset.insert({
-- expand snippet setting in luasnip
["<C-u>"] = cmp.mapping.scroll_docs(-4),
["<C-d>"] = cmp.mapping.scroll_docs(4),
-- ["<C-n>"] = require("cmp_rime").mapping.select_next_item,
-- ["<C-p>"] = require("cmp_rime").mapping.select_prev_item,
-- toggle cmp 菜单
["<C-Space>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.abort()
else
cmp.complete()
end
end),
-- 回车选中
["<CR>"] = cmp.mapping.confirm({ select = true, behavior = cmp.ConfirmBehavior.Replace }),
}),
})
-- 搜索模式
for _, cmd_type in ipairs({ "/", "?" }) do
cmp.setup.cmdline(cmd_type, {
mapping = cmp.mapping.preset.cmdline(),
sources = {
CMP_SOURCES.buffer,
},
})
end
-- 命令行模式
cmp.setup.cmdline(":", {
mapping = cmp.mapping.preset.cmdline(),
sources = {
-- 有path 的时候屏蔽cmd
vim.tbl_extend("force", CMP_SOURCES.path, { group_index = 1 }),
vim.tbl_extend("force", CMP_SOURCES.cmdline, { group_index = 2 }),
},
})
end,

效果:

image image
image image

LSP

在这里不解释LSP是什么,只介绍怎么配置。

如果使用的是coc,基本开箱即用,易用性也挺好的,如果是用的neovim官方的lsp,那还需要少量的配置。

用到的插件有

  • neovim/nvim-lspconfig:neovim官方lsp插件
  • hrsh7th/cmp-nvim-lsp:用于cmp补全的source
  • williamboman/mason.nvimwilliamboman/mason-lspconfig.nvim:管理下载的插件
  • folke/neodev.nvim:这个可能
  • nvimdev/lspsaga.nvim:基于lsp协议实现查找定义,引用等的插件
  • 其他lsp应用类的插件

管理lsp的部分基本照抄就行了

-- ~/.config/nvim/lua/plugins/lsp/init.lua
local on_attach = function(client, bufnr)
vim.api.nvim_buf_set_option(bufnr, "omnifunc", "v:lua.vim.lsp.omnifunc")
-- 禁用lsp 语义高亮
-- client.server_capabilities.semanticTokensProvider = nil
end
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities.textDocument.completion.completionItem = {
documentationFormat = { "markdown", "plaintext" },
-- snippet禁用无效,要在cmp里设置
snippetSupport = false,
}
return {
"neovim/nvim-lspconfig",
dependencies = {
{ "folke/neodev.nvim", version = "*", config = true, lazy = true },
"hrsh7th/cmp-nvim-lsp",
{ "williamboman/mason-lspconfig.nvim", version = "*", config = true },
},
event = { "BufReadPre", "BufNewFile" },
config = function()
require("mason-lspconfig").setup_handlers({
function(server_name)
local configs = {
on_attach = on_attach,
capabilities = capabilities,
root_dir = require("lspconfig").util.root_pattern(vim.g.ROOT_MARKERS),
}
if vim.pathlib.file_exist(("~/.config/nvim/lua/plugins/lsp/settings/%s.lua"):format(server_name)) then
local spec_configs = require(("plugins.lsp.settings.%s"):format(server_name))
configs = vim.tbl_deep_extend("force", configs, spec_configs)
end
require("lspconfig")[server_name].setup(configs)
end,
})
end,
}

mason.nvim是一个用于下载neovim下第三方工具的一个插件,可以用于下载安装lsp、dap、语法检查工具linter、格式化工具等等,相比于手动apt下载,管理更加方便。与其配套的有

  • mason-lspconfig.nvim
  • mason-nvim-dap.nvim
  • mason-null-ls.nvim
    我都用到了,除了方便安装,还可以通过配置做到即装即用,不用再手动的vim.lsp.start

上面这段配置做到了全局和局部的结合,用mason-lspconfig.nvim启动会带上默认的配置,如果需要修改,将配置写到~/.config/nvim/lua/plugins/lsp/settings/这个目录下,从这里读取的配置会覆盖全局设置和lsp默认设置,比如clangd:

-- ~/.config/nvim/lua/plugins/lsp/settings/clangd.lua
return {
capabilities = {
offsetEncoding = { "utf-16" }, -- 解决全局的utf8编码问题
},
cmd = {
"clangd",
-- 同时开启的任务数量
"-j 8",
-- 开启clang-tidy
"--clang-tidy",
-- xxxxxxxxx其他设置
},
-- 有些lsp可以用过这个字段来进行设置
settings = {
}
}

大部分lsp的默认设置已经可以用了,只需要少量的需要进行调整

nvimdev这个插件是用于写lua配置和插件用的,主要功能是设置了lua_ls这个lsp和runtimepath,使得写lua配置的时候可以对neovim配置的lua的api和lua插件进行补全和提示,需要在lua_ls启动之前加载,所以这个插件作为lspconfig的依赖了

lspsaga.nvim是lsp应用类的插件,基于lsp协议实现的定义/引用查找,查看文档等功能
模糊查找的插件比如telescopefzf-lua也都内置了这些功能

查看error/warning信息的插件trouble.nvim,用的人也比较多。

代码运行、调试、测试

代码运行 asynctask.vim

代码运行类的插件只推荐asynctask.vim这个插件,非常的优雅。

调试 dap

这个比较坑

测试 neotest

编辑类插件

注释 Comment.nvim

注释类插件我用的是Comment.nvim,我习惯用<leader>c/<leader>C前缀。

return {
"numToStr/Comment.nvim",
event = { "BufRead" },
version = "*",
opts = {
padding = true,
sticky = true,
ignore = "^$",
toggler = {
line = "<leader>cc",
block = "<leader>CC",
},
opleader = {
line = "<leader>c",
block = "<leader>C",
},
extra = {
above = "<leader>cO",
below = "<leader>co",
eol = "<leader>cA",
},
mappings = {
basic = true,
extra = true,
},
},
}

nvim-autopair

nvim-surround

格式化 null-ls

代码片段 luasnip

其他

工具类插件

大纲解析 aerial

目录树 nvim-tree

悬浮终端 toggleterm

gitsigns

diffview.nvim

flash.nvim

mini.nvim

美化类插件

主题

dashboard

noice

lualine

dressing

indent-blankline

其他

luasnip

vimtex

弃用的插件

posted @   lavateinn  阅读(9193)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示