环境:Ubuntu 20.04
宿主机:windows (windows terminal)WSL2
NVIM:v0.10.0-dev
配置Neovim 需要保证流畅的github环境(以便于快速拉取插件),可以使用代理或是配置Github SSH key
本篇博客是学习掘金小册的学习笔记,有兴趣的朋友可以直接点击下文链接了解详情
小册链接:Neovim 配置实战:从0到1打造自己的IDE - nshen - 掘金小册 (juejin.cn)
成品示例图
配置文件整体结构 ├── LICENSE ├── README.md ├── init.lua └── lua ├── autocmds.lua ├── basic.lua ├── colorscheme.lua ├── keybindings.lua ├── lsp │ ├── cmp.lua │ ├── config │ │ ├── bash.lua │ │ ├── emmet.lua │ │ ├── html.lua │ │ ├── json.lua │ │ ├── lua.lua │ │ ├── markdown.lua │ │ ├── pyright.lua │ │ ├── rust.lua │ │ └── ts.lua │ ├── formatter.lua │ ├── null-ls.lua │ ├── setup.lua │ └── ui.lua ├── plugin-config │ ├── bufferline.lua │ ├── comment.lua │ ├── dashboard.lua │ ├── gitsigns.lua │ ├── 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 文件夹:
是对 Neovim 内置 LSP 功能的配置,包括常见编程语言与语法提示等。
config : 文件夹包含各种语言服务器单独的配置文件。
setup.lua : 内置 LSP 的配置。
cmp.lua : 语法自动补全补全的配置,包括各种补全源,与自定义代码段。
ui.lua: 对内置 LSP 功能增强和 UI 美化。
formatter.lua: 独立代码格式化功能。
配置 Nerd Fonts 官网:Nerd Fonts - Iconic font aggregator, glyphs/icons collection, & fonts patcher
下载字体文件FiraCodeNerdFont-Regular.ttf
下载之后双击ttf文件点击安装即可添加到系统字体文件中
配置步骤:
最后点击保存
安装 Neovim 旧版本卸载:sudo apt-get remove neovim
官网:Installing Neovim · neovim/neovim Wiki (github.com)
sudo apt-get install software-properties-common sudo add-apt-repository ppa:neovim-ppa/unstable sudo apt-get update sudo apt-get install neovim
执行nvim --version
,确保版本必须为 0.7 及以上。
执行nvim ~/.bashrc
alias vim='nvim' alias vi='nvim' alias v='nvim'
执行source ~/.bashrc
重新加载配置文件
配置入口 init.lua ~/.config/nvim/init.lua
写入如下内容:
require
函数在 Lua 中用于加载一个模块,而这些模块通常位于 runtimepath
中的 lua/
目录下,也就是我们的 ~/.config/nvim/lua/
目录。
所以上边的代码,就是加载 ~/.config/nvim/lua/basic.lua
文件(注意:require 里没有 .lua
扩展名)。当然也可以创建 ~/.config/nvim/lua/basic/
目录,在目录下边创建 init.lua
文件也是可以成功加载的。
创建基础配置文件~/.config/nvim/lua/basic.lua
,并添加如下内容:
-- 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 -- 命令行高为2,提供足够的显示空间 vim.o.cmdheight = 2 -- 当文件被外部程序修改时,自动加载 vim.o.autoread = true vim.bo.autoread = true -- 禁止折行 vim.wo.wrap = false -- 光标在行首尾时<Left><Right>可以跳到下一行 vim.o.whichwrap = '<,>,[,]' -- 允许隐藏被修改过的buffer vim.o.hidden = true -- 鼠标支持 vim.o.mouse = "a" -- 禁止创建备份文件 vim.o.backup = false vim.o.writebackup = false vim.o.swapfile = false -- smaller updatetime vim.o.updatetime = 300 -- 设置 timeoutlen 为等待键盘快捷键连击时间500毫秒,可根据需要设置 vim.o.timeoutlen = 500 -- split window 从下边和右边出现 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 -- Dont' pass messages to |ins-completin menu| vim.o.shortmess = vim.o.shortmess .. ' c' -- 补全最多显示10行 vim.o.pumheight = 10 -- 永远显示 tabline vim.o.showtabline = 2 -- 使用增强状态栏插件后不再需要 vim 的模式提示 vim.o.showmode = false
说明
vim.g.{name}
: 全局变量
vim.b.{name}
: 缓冲区变量
vim.w.{name}
: 窗口变量
vim.bo.{option}
: buffer-local 选项
vim.wo.{option}
: window-local 选项
一般来说,全部设置在 vim.opt
下也是可以的,例如 vim.opt.number = true
也是有效的,只是我们上边设置到了比较详细位置而已,具体每个变量的分类可以在 :help 文档中查看。
快捷键设置 创建lua/keybindings.lua
vim.g.mapleader = " " vim.g.maplocalleader = " " local map = vim.api.nvim_set_keymap-- 复用 opt 参数 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) map("n" , "<C-Down>" , ":resize +2<CR>" , opt) map("n" , "<C-Up>" , ":resize -2<CR>" , opt) -- 等比例 map("n" , "s=" , "<C-w>=" , 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) -- ctrl u / ctrl + d 只移动9行,默认移动半屏 map("n" , "<C-u>" , "9k" , opt) map("n" , "<C-d>" , "9j" , opt) -- 在visual 模式里粘贴不要复制 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)
最后在init.lua
中引入快捷键文件
-- 快捷键映射 require("keybindings" )
插件配置 目前在 Neovim 最常见的插件管理器主要有 vim-plug 和 packer 两个。
安装步骤(请参照官网步骤):
执行git clone --depth 1 https://github.com/wbthomason/packer.nvim\ ~/.local/share/nvim/site/pack/packer/start/packer.nvim
安装成功后添加配置文件lua/plugins.lua
,添加如下内容
local packer = require("packer" )packer.startup( function (use) -- Packer 可以管理自己本身 use 'wbthomason/packer.nvim' -- 你的插件列表... end)
init.lua
中添加如下内容
-- Packer 插件管理 require("plugins" )
配置生效后,Neovim 会增加以下命令。
:PackerCompile
: 每次改变插件配置时,必须运行此命令或 PackerSync
, 重新生成编译的加载文件
:PackerClean
: 清除所有不用的插件
:PackerInstall
: 清除,然后安装缺失的插件
:PackerUpdate
: 清除,然后更新并安装插件
:PackerSync
: 执行 PackerUpdate
后,再执行 PackerCompile
:PackerLoad
: 立刻加载 opt 插件
通过上边的说明,我们观察到 :PackerSync
命令包含了 :PackerUpdate
和:PackerCompile
,而 :PackerUpdate
又包含了 :PackerClean
和 :PackerInstall
流程。
所以通常情况下,无论安装 还是更新 插件,我只需要下边这一条命令就够::PackerSync
每次修改完 lua/plugins.lua
这个文件后,保存退出,重新打开并调用 :PackerSync
就可以了
添加自动安装 lua/plugins.lua
文件,在最后添加
-- 每次保存 plugins.lua 自动安装插件 pcall( vim.cmd, [[ augroup packer_user_config autocmd! autocmd BufWritePost plugins.lua source <afile> | PackerSync augroup end ]] )
注意:保证自己可以ping 通 github 的情况下,但是仍旧执行:PackerSync
失败,按 R 多次拉取失败插件,如果依旧失败则在错误插件处点击回车,查看问题,具体问题具体分析,我一般多试几次 R 就会成功
配置主题 init.lua
中添加如下内容:
-- 主题设置 (新增) require("colorscheme" )
lua/colorscheme.lua
添加
local colorscheme = "tokyonight" local status_ok, _ = pcall(vim.cmd, "colorscheme " .. colorscheme)if not status_ok then vim.notify("colorscheme " .. colorscheme .. " 没有找到!" ) return end
安装第三方主题 lua/plugins.lua
文件
packer.startup({ function (use) -- Packer 可以升级自己 use("wbthomason/packer.nvim" ) --------------------- colorschemes -------------------- -- tokyonight use("folke/tokyonight.nvim" ) ------------------------------------------------------- -- 略... })
:w
保存 p
退出
其他主体配色链接:neovim-colorscheme · GitHub Topics
lua/plugins.lua
添加插件配置后,修改 lua/colorscheme.lua
内 colorscheme
变量为相应的主题即可
侧边栏文件浏览器 相关插件:
lua/plugins.lua
中添加
packer.startup({ function (use) -- Packer 可以升级自己 use("wbthomason/packer.nvim" ) -------------------------- plugins ------------------------------------------- -- nvim-tree (新增) use({ "kyazdani42/nvim-tree.lua" , requires = "kyazdani42/nvim-web-devicons" }) ...略
配置 nvim-tree lua/plugin-config/nvim-tree.lua
local status, nvim_tree = pcall(require, "nvim-tree" )if not status then vim.notify("没有找到 nvim-tree" ) return end -- 列表操作快捷键 local list_keys = require('keybindings' ).nvimTreeListnvim_tree.setup({ -- 不显示 git 状态图标 git = { enable = false , }, -- project plugin 需要这样设置 update_cwd = true , update_focused_file = { enable = true , update_cwd = true , }, -- 隐藏 .文件 和 node_modules 文件夹 filters = { dotfiles = true , custom = { 'node_modules' }, }, view = { -- 宽度 width = 40, -- 也可以 'right' side = 'left' , -- 隐藏根目录 hide_root_folder = false , -- 自定义列表中快捷键 mappings = { custom_only = false , list = list_keys, }, -- 不显示行数 number = false , relativenumber = false , -- 显示图标 signcolumn = 'yes' , }, actions = { open_file = { -- 首次打开大小适配 resize_window = true , -- 打开文件时关闭 quit_on_open = true , }, }, -- wsl install -g wsl-open -- https://github.com/4U6U57/wsl-open/ system_open = { cmd = 'wsl-open' , -- mac 直接设置为 open }, }) -- 自动关闭 vim.cmd([[ autocmd BufEnter * ++nested if winnr('$' ) == 1 && bufname() == 'NvimTree_' . tabpagenr() | quit | endif ]])
上边代码就是 nvim-tree 的配置,可以根据注释进行修改。这里简单解释一下,值得注意的是 system_open
项,如果想要在 WSL
中用 Windows 系统默认设置打开文件,需要使用 Node.js
全局安装一个 wsl-open
包,使用命令 npm install -g wsl-open
,如果不需要这个功能,也可以不用安装。 如果不是 Windows 系统也就不需要安装。
nvim-tree 初始化支持很多参数,如果想知道还有哪些其他的参数,可以运行 :h nvim-tree.setup
调出帮助文档查看。
lua/keybindings.lua
添加
-- 插件快捷键 local pluginKeys = {}-- nvim-tree -- alt + m 键打开关闭tree map("n" , "<A-m>" , ":NvimTreeToggle<CR>" , opt) -- 列表快捷键 pluginKeys.nvimTreeList = { -- 打开文件或文件夹 { key = {"<CR>" , "o" , "<2-LeftMouse>" }, action = "edit" }, -- 分屏打开文件 { key = "v" , action = "vsplit" }, { key = "h" , action = "split" }, -- 显示隐藏文件 { key = "i" , action = "toggle_custom" }, -- 对应 filters 中的 custom (node_modules) { key = "." , action = "toggle_dotfiles" }, -- Hide (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" }, } return pluginKeys
init.lua
添加
-- 插件配置 require("plugin-config.nvim-tree" )
但是出现问题view.mappings.list has been deprecated in favour of on_attach.please run
,应该是某些插件更新:相关链接:Migrating To on_attach · nvim-tree/nvim-tree.lua Wiki (github.com)
表明view.mappings
and remove_keymaps
已经启用需要移除
解决方案:
注释掉view.mappings
执行:NvimTreeGenerateOnAttach
,复制nvim-tree-on-attach.lua
中的内容粘贴到nvim-tree.lua
中(需要添加在setup之前)
添加
require("nvim-tree" ).setup({ ... on_attach = on_attach, })
问题解决~
顶部标签页和底部信息栏 相关插件:
顶部标签页 lua/plugins.lua
packer.startup({ function (use) -- Packer 可以升级自己 use("wbthomason/packer.nvim" ) -------------------------- plugins ------------------------------------------- -- nvim-tree use({ "kyazdani42/nvim-tree.lua" , requires = "kyazdani42/nvim-web-devicons" }) -- bufferline (新增) use({ "akinsho/bufferline.nvim" , requires = { "kyazdani42/nvim-web-devicons" , "moll/vim-bbye" }}) ...略
:w
保存,自动安装,安装完整按 q
退出
出现GnuTLS recv error (-110) 问题
解决方案:
apt-get install gnutls-bin git config --global http.sslVerify false git config --global http.postBuffer 1048576000 sudo update-alternatives --set fakeroot /usr/bin/fakeroot-tcp
这里我增加了一个 moll/vim-bbye 依赖,因为这个插件安装后会增加一个 :Bdelete
命令,相比内置的 :bdelete
, 它删除 buffer 的同时,并不会搞乱布局 。 待会儿我们会配置 Bdelete
为关闭 Tab 的命令。
新建lua/plugin-config/bufferline.lua
文件并添加内容
local status, bufferline = pcall(require, "bufferline" )if not status then vim.notify("没有找到 bufferline" ) return end -- bufferline 配置 -- https://github.com/akinsho/bufferline.nvim bufferline.setup({ options = { -- 关闭 Tab 的命令,这里使用 moll/vim-bbye 的 :Bdelete 命令 close_command = "Bdelete! %d" , right_mouse_command = "Bdelete! %d" , -- 侧边栏配置 -- 左侧让出 nvim-tree 的位置,显示文字 File Explorer offsets = { { filetype = "NvimTree" , text = "File Explorer" , highlight = "Directory" , text_align = "left" , }, }, -- 使用 nvim 内置 LSP 后续课程会配置 diagnostics = "nvim_lsp" , -- 可选,显示 LSP 报错图标 ---@diagnostic disable-next-line: unused-local 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, }, })
lua/keybindings.lua
-- bufferline -- 左右Tab切换 map("n" , "<C-h>" , ":BufferLineCyclePrev<CR>" , opt) map("n" , "<C-l>" , ":BufferLineCycleNext<CR>" , opt) -- 关闭 --"moll/vim-bbye" 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)
init.lua
添加
require("plugin-config.bufferline" )
底部信息栏 lua/plugins.lua
-- lualine (新增) use({ "nvim-lualine/lualine.nvim" , requires = { "kyazdani42/nvim-web-devicons" } }) use("arkav/lualine-lsp-progress" )
新建lua/plugin-config/lualine.lua
-- 如果找不到lualine 组件,就不继续执行 local status, lualine = pcall(require, "lualine" )if not status then vim.notify("没有找到 lualine" ) return end lualine.setup({ options = { theme = "tokyonight" , component_separators = { left = "|" , right = "|" }, -- https://github.com/ryanoasis/powerline-extra-symbols section_separators = { left = " " , right = "" }, }, extensions = { "nvim-tree" , "toggleterm" }, sections = { lualine_c = { "filename" , { "lsp_progress" , spinner_symbols = { " " , " " , " " , " " , " " , " " }, }, }, lualine_x = { "filesize" , { "fileformat" , -- symbols = { -- unix = '' , -- e712 -- dos = '' , -- e70f -- mac = '' , -- e711 -- }, symbols = { unix = "LF" , dos = "CRLF" , mac = "CR" , }, }, "encoding" , "filetype" , }, }, })
init.lua
添加
require("plugin-config.lualine" )
模糊搜索快速查找文件 相关插件:nvim-telescope/telescope.nvim: Find, Filter, Preview, Pick. All lua, all the time. (github.com)
lua/plugins.lua
-- telescope (新增) use { 'nvim-telescope/telescope.nvim' , requires = { "nvim-lua/plenary.nvim" } }
:checkhealth telescope
检查依赖情况,这里通常会提示 ripgrep
不存在,因为 telescope
依赖以下两个项目。
解决方案:
sudo apt install ripgrep sudo add-apt-repository ppa:x4121/ripgrep sudo apt-get update sudo apt install ripgrep npm install -g fd-find
配置 node 环境 node 配置源,需要哪个版本可以将16替换为相应版本
curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash -
安装
sudo apt-get update -y sudo apt install -y nodejs
如果不想要自己配置指定版本源的情况下,可以直接执行
sudo apt update -y sudo apt install nodejs npm
nvm 官网:nvm-sh/nvm: Node Version Manager - POSIX-compliant bash script to manage multiple active node.js versions (github.com)
git clone nvm仓库时遇到如下错误
GnuTLS recv error (-110): The TLS connection 。。。
我是因为Ipv6的问题,可以git clone -4 指定使用Ipv4连接
但是执行ap-get update
遇到问题:
Err:12 http://ppa.launchpad.net/x4121/ripgrep/ubuntu focal Release 404 Not Found [IP: 185.125.190.52 80] Reading package lists… E: The repository ‘http://ppa.launchpad.net/x4121/ripgrep/ubuntu focal Release’ does not have a Release file.
这是因为之前我执行过sudo add-apt-repository ppa:x4121/ripgrep
所以可以直接手动删除
解决方案(自动移除再更新):
sudo apt-add-repository -r ppa:x4121/ripgrep sudo apt update -q
手动删除
cd /etc/apt/sources.list.drm x4121-ubuntu-ripgrep-focal.list
后面我在使用更新插件包时爆出:Could not resolve host github.com
解决1(失败):
git config --global --unset http.proxy git config --global --unset https.proxy
解决2(成功):
我直接ping了下网络,发现不只是github连不上,而是整个WSL网络都不能使用,在宿主机中我查看WSL网络配置,发现未修复之前两个红框中的ip地址是没有的
直接在宿主机上使用管理员权限执行netsh winsock reset
重启网络即可(注意:不需要重启)
下面解决方案没有试过,仅作为备用方案
解决3:
自定义启动页与项目列表 当我们在命令行中输入 nvim
不带任何路径并敲击回车的时候,就会打开这个界面,通常我们会在这个界面中配置最常用功能,比如打开最近编辑过的文件,快速打开项目文件夹,快速修改快捷键等。
使用官方配置的图像: Ascii Header Text · nvimdev/dashboard-nvim Wiki (github.com)
使用字体生成器:Text to ASCII Art Generator (TAAG) (patorjk.com)
同样 plugins.lua 中添加:
-- dashboard-nvim (新增) use("glepnir/dashboard-nvim" )
创建 lua/plugin-config/dashboard.lua ,并添加:
local status, db = pcall(require, "dashboard" )if not status then vim.notify("没有找到 dashboard" ) return end db.setup({ theme = 'doom' , config = { header = { [[ ]], [[ ]], [[██████╗ ███████╗████████╗████████╗███╗ ███╗██████╗ ]], [[██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝████╗ ████║██╔══██╗]], [[██████╔╝█████╗ ██║ ██║ ██╔████╔██║██████╔╝]], [[██╔══██╗██╔══╝ ██║ ██║ ██║╚██╔╝██║██╔═══╝ ]], [[██║ ██║███████╗ ██║ ██║ ██║ ╚═╝ ██║██║ ]], [[╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ]], [[ ]], [[ [ version : 1.0.0 ] ]], [[ ]], }, --your 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 = {} --your footer } })
但是里面的 Telescope projects
并不是 telescope 内置的命令。需要安装ahmedkhalf/project.nvim
插件,执行如下步骤
步骤 添加插件
-- project use("ahmedkhalf/project.nvim" )
确保lua/plugin-config/nvim-tree.lua
文件中有如下代码:
nvim_tree.setup({ --- 上略 -- project plugin 需要这样设置 update_cwd = true , update_focused_file = { enable = true , update_cwd = true , }, -- 下略 }
创建lua/plugin-config/project.lua
local status, project = pcall(require, "project_nvim" )if not status then vim.notify("没有找到 project_nvim" ) return end -- nvim-tree 支持 vim.g.nvim_tree_respect_buf_cwd = 1 project.setup({ detection_methods = { "pattern" }, patterns = { ".git" , "_darcs" , ".hg" , ".bzr" , ".svn" , "Makefile" , "package.json" , ".sln" }, }) local status, telescope = pcall(require, "telescope" )if not status then vim.notify("没有找到 telescope" ) return end pcall(telescope.load_extension, "projects" )
init.lua
require("plugin-config.dashboard" ) require("plugin-config.project" )
手动编辑 project_history
列表 执行:lua print(require("project_nvim.utils.path").historyfile)
我得到了~/.local/share/nvim/project_nvim/project_history
这个文件,直接编辑即可
添加
以后的话可以方便编写nvim项目配置 :)
语法高亮安装及配置 相关插件:nvim-treesitter/nvim-treesitter: Nvim Treesitter configurations and abstraction layer (github.com)
添加插件
-- treesitter (新增) use({ "nvim-treesitter/nvim-treesitter" , run = ":TSUpdate" })
手动安装Language parser 运行 :TSInstallInfo
命令查看 language parsers 列表与安装状态
安装::TSInstall javascript
卸载::TSUninstall javascript
安装后会在 packer 中生成相应语言的so文件
自动安装配置 lua/plugin-config/nvim-treesitter.lua
local status, treesitter = pcall(require, "nvim-treesitter.configs" )if not status then vim.notify("没有找到 nvim-treesitter" ) return end treesitter.setup({ -- 安装 language parser -- :TSInstallInfo 命令查看支持的语言 ensure_installed = { "json" , "html" , "css" , "vim" , "lua" , "javascript" , "typescript" , "tsx" }, -- 启用代码高亮模块 highlight = { enable = true , additional_vim_regex_highlighting = false , }, })
init.lua
require("plugin-config.nvim-treesitter" )
稍微说下里面 nvim-treesitter 中的配置
增量选择模块 当你的光标在一个语法结点上时,可以设置一个增加键和一个减少键,敲击这两个,在表现上为不断外扩和收缩选中代码。
增加和减少结点的快捷键设置成了 回车 和 退格 。通过不断的按 Enter 选择区域会从里层不断外扩, Backspace 则相反不断内收。
-- 启用增量选择模块 incremental_selection = { enable = true , keymaps = { init_selection = "<CR>" , node_incremental = "<CR>" , node_decremental = "<BS>" , scope_incremental = "<TAB>" , }, },
代码缩进 在 visual 模式中按 = 即可实现代码缩进
-- 启用代码缩进模块 (=) indent = { enable = true , },
代码折叠 快捷键中设置了zc
组合键来折叠 {}
中的内容,还使用 zo
组合键来打开对应的折叠。
-- 开启 Folding 模块 vim.opt.foldmethod = "expr" vim.opt.foldexpr = "nvim_treesitter#foldexpr()" -- 默认不要折叠 -- https://stackoverflow.com/questions/8316139/how-to-set-the-default-to-unfolded-when-you-open-a-file vim.opt.foldlevel = 99
执行:TSModuleInfo
查看是否成功
如果出现问题先执行TSUpdate
看是否可以解决,无法解决查看官方issues或wiki
LSP 基础配置 目的:实现类似IDE的智能提示、语法检查和解析、悬停文档等交互体验
定义:LSP (Language Server Protocol) 语言服务协议,该协议定义了在编辑器或IDE与语言服务器之间使用的协议,该语言服务器提供了例如自动补全,转到定义,查找所有引用等的功能;语言服务器索引格式的目标是支持在开发工具中进行丰富的代码导航或者一个无需本地源码副本的WebUI。
开启 neovim 内置LSP 执行:h lsp
查看quick start,里面的步骤如下
使用包管理器安装语言服务器
为每一种语言服务器配置LSP客户端
配置快捷键(keymaps)和自动命令(autocmds)
配置客户端 安装插件:neovim/nvim-lspconfig: Quickstart configs for Nvim LSP (github.com)
配置语言服务器 安装插件:williamboman/nvim-lsp-installer: Further development has moved to https://github.com/williamboman/mason.nvim!
配置插件:
--------------------- LSP -------------------- use("williamboman/nvim-lsp-installer" ) -- Lspconfig use({ "neovim/nvim-lspconfig" })
安装 LSP Server 运行:LspInstallInfo
,点击 i 即可安装相应的 Server ,进入之后安装 Lua server
从最上面的介绍得知作者未来会用 mason.nvim 取代 nvim-lsp-installer ,mason.nvim 项目支持更多的 server 安装,不仅仅是LSP Server 还支持 DAP servers、 linters、formatters 等等超过 150 个包,100+ 种编程语言,并且升级也是非常简单的
但是安装失败,直接重新安装即可成功
但是有时遇到could not find executable "unzip" in path
问题,可以使用如下命令
sudo apt-get install zip unzip
但是安装成功之后发现问题,我从issues中了解到是因为lua server的仓库地址发生变化
鉴于nvim-lsp-installer 出现的各种问题,现在使用mason.nvim 代替nvim-lsp-installer
步骤:
运行:LspUninstallAll
卸载nvim-lsp-installer
plugins.lua 替换 nvim-lsp-installer 插件为 mason.nvim 和 mason-lspconfig.nvim
use({ "williamboman/mason.nvim" }) use({ "williamboman/mason-lspconfig.nvim" })
lua/lsp/setup.lua
相应部分替换为(这个文件后面会创建,现在先讲下)
local status, mason = pcall(require, "mason" )if not status then vim.notify("没有找到 mason" ) return end local status, mason_config = pcall(require, "mason-lspconfig" )if not status then vim.notify("没有找到 mason-lspconfig" ) return end local status, lspconfig = pcall(require, "lspconfig" )if not status then vim.notify("没有找到 lspconfig" ) return end -- :h mason-default-settings -- ~/.local/share/nvim/mason mason.setup({ ui = { icons = { package_installed = "✓" , package_pending = "➜" , package_uninstalled = "✗" , }, }, }) -- mason-lspconfig uses the `lspconfig` server names in the APIs it exposes - not `mason.nvim` package names -- https://github.com/williamboman/mason-lspconfig.nvim/blob/main/doc/server-mapping.md mason_config.setup({ ensure_installed = { "lua_ls" , }, }) -- 安装列表 -- { key: 服务器名, value: 配置文件 } -- key 必须为下列网址列出的 server name,不可以随便写 -- https://github.com/williamboman/nvim-lsp-installer local servers = { lua_ls = require("lsp.config.lua" ), -- lua/lsp/config/lua.lua } for name, config in pairs(servers) do if config ~= nil and type (config) == "table" then -- 自定义初始化配置文件必须实现on_setup 方法 config.on_setup(lspconfig[name]) else -- 使用默认参数 lspconfig[name].setup({}) end end require("lsp.ui" )
保存之后默认安装到 ~/.local/share/nvim/mason
执行:Mason
即可打开,操作基本和上面的一样,只不过多了 1,2,3,4,5 是上边TAB快捷键,按 2
即可切换到 LSP
Mason 仓库地址:Mason
配置 LSP Server 创建一个新的目录 lua/lsp/
专门存放 lsp 相关的配置,使用:h mason-lspconfig-automatic-server-setup
查看如何配置setup,或是直接看官网
创建lua/lsp/setup.lua
local status, mason = pcall(require, "mason" )if not status then vim.notify("没有找到 mason" ) return end local status, mason_config = pcall(require, "mason-lspconfig" )if not status then vim.notify("没有找到 mason-lspconfig" ) return end local status, lspconfig = pcall(require, "lspconfig" )if not status then vim.notify("没有找到 lspconfig" ) return end -- :h mason-default-settings -- ~/.local/share/nvim/mason mason.setup({ ui = { icons = { package_installed = "✓" , package_pending = "➜" , package_uninstalled = "✗" , }, }, }) -- mason-lspconfig uses the `lspconfig` server names in the APIs it exposes - not `mason.nvim` package names -- https://github.com/williamboman/mason-lspconfig.nvim/blob/main/doc/server-mapping.md mason_config.setup({ ensure_installed = { "lua_ls" , }, }) -- 安装列表 -- { key: 服务器名, value: 配置文件 } -- key 必须为下列网址列出的 server name,不可以随便写 -- https://github.com/williamboman/nvim-lsp-installer local servers = { lua_ls = require("lsp.config.lua" ), -- lua/lsp/config/lua.lua } for name, config in pairs(servers) do if config ~= nil and type (config) == "table" then -- 自定义初始化配置文件必须实现on_setup 方法 config.on_setup(lspconfig[name]) else -- 使用默认参数 lspconfig[name].setup({}) end end require("lsp.ui" )
配置 lua Server 创建/lua/lsp/config/lua.lua
-- https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md local runtime_path = vim.split(package.path, ';' )table.insert(runtime_path, 'lua/?.lua' ) table.insert(runtime_path, 'lua/?/init.lua' ) local opts = { settings = { Lua = { runtime = { -- Tell the language server which version of Lua you're using (most likely LuaJIT in the case of Neovim) version = ' LuaJIT', -- Setup your lua path path = runtime_path, }, diagnostics = { -- Get the language server to recognize the `vim` global globals = { ' vim' }, }, workspace = { -- Make the server aware of Neovim runtime files library = vim.api.nvim_get_runtime_file(' ', true), checkThirdParty = false, }, -- Do not send telemetry data containing a randomized but unique identifier telemetry = { enable = false, }, }, }, flags = { debounce_text_changes = 150, }, on_attach = function(client, bufnr) -- 禁用格式化功能,交给专门插件插件处理 client.resolved_capabilities.document_formatting = false client.resolved_capabilities.document_range_formatting = false local function buf_set_keymap(...) vim.api.nvim_buf_set_keymap(bufnr, ...) end -- 绑定快捷键 require(' keybindings').mapLSP(buf_set_keymap) -- 保存时自动格式化 vim.cmd(' autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync()') end, } -- 查看目录等信息 return { on_setup = function(server) server:setup(opts) end, }
配置 LSP 快捷键 lua/keybindings.lua
-- lsp 回调函数快捷键设置 pluginKeys.mapLSP = function (mapbuf) -- rename mapbuf("n" , "<leader>rn" , "<cmd>lua vim.lsp.buf.rename()<CR>" , opt) -- code action mapbuf("n" , "<leader>ca" , "<cmd>lua vim.lsp.buf.code_action()<CR>" , opt) -- go xx 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) -- diagnostic 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.formatting()<CR>" , opt) -- 没用到 -- mapbuf('n' , '<leader>q' , '<cmd>lua vim.diagnostic.setloclist()<CR>' , opt) -- mapbuf("n" , "<C-k>" , "<cmd>lua vim.lsp.buf.signature_help()<CR>" , opt) -- mapbuf('n' , '<space>wa' , '<cmd>lua vim.lsp.buf.add_workspace_folder()<CR>' , opt) -- mapbuf('n' , '<space>wr' , '<cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>' , opt) -- mapbuf('n' , '<space>wl' , '<cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>' , opt) -- mapbuf('n' , '<space>D' , '<cmd>lua vim.lsp.buf.type_definition()<CR>' , opt) end
init.lua
-- 内置LSP (新增) require("lsp.setup" )
这里记录下:使用:verbose map可以查看所有生效的快捷键
基于 LSP 代码补全及自定义代码段 相关概念:
补全引擎
补全引擎就是为 Neovim 提供代码补全核心功能的插件,比如 nvim-cmp
补全源
补全源就是补全引擎需要的数据来源,最常见的来源是来自 Language Server 提供的数据,它会知道某个类有哪些属性和方法等。
snippet 引擎
snippet 引擎就是自定义代码段的引擎,常见的有 vsnip
、luasnip
等
添加插件 -- 补全引擎 use("hrsh7th/nvim-cmp" ) -- snippet 引擎 use("hrsh7th/vim-vsnip" ) -- 补全源 use("hrsh7th/cmp-vsnip" ) use("hrsh7th/cmp-nvim-lsp" ) -- { name = nvim_lsp } use("hrsh7th/cmp-buffer" ) -- { name = 'buffer' }, use("hrsh7th/cmp-path" ) -- { name = 'path' } use("hrsh7th/cmp-cmdline" ) -- { name = 'cmdline' } -- 常见编程语言代码段 use("rafamadriz/friendly-snippets" )
注意:只有 hrsh7th/nvim-cmp
是补全引擎插件本身,其他 cmp-xxx
基本都是插件补全来源,也就是说当你输入一个变量的时候,可以从多个来源显示补全的内容。
像 hrsh7th/cmp-nvim-lsp 就是 Neovim 内置 LSP 提供的补全内容,hrsh7th/cmp-buffer 补全当前 buffer 的内容, hrsh7th/cmp-cmdline 是命令行的补全,hrsh7th/cmp-path 则是用来补全路径,如果配置了这个,当输入一个路径的时候会补全路径
创建lua/lsp/cmp.lua
local cmp = require("cmp" )cmp.setup({ -- 指定 snippet 引擎 snippet = { expand = function (args) -- For `vsnip` users . vim.fn["vsnip#anonymous" ](args.body) -- For `luasnip` users . -- require('luasnip' ).lsp_expand(args.body) -- For `ultisnips` users . -- vim.fn["UltiSnips#Anon" ](args.body) -- For `snippy` users . -- require'snippy' .expand_snippet(args.body) end, }, -- 补全源 sources = cmp.config.sources({ { name = "nvim_lsp" }, -- For vsnip users . { name = "vsnip" }, -- For luasnip users . -- { name = 'luasnip' }, --For ultisnips users . -- { name = 'ultisnips' }, -- -- For snippy users . -- { name = 'snippy' }, }, { { name = "buffer" }, { name = "path" } }), -- 快捷键设置 mapping = require("keybindings" ).cmp(cmp), }) -- / 查找模式使用 buffer 源 cmp.setup.cmdline("/" , { mapping = cmp.mapping.preset.cmdline(), sources = { { name = "buffer" }, }, }) -- : 命令行模式中使用 path 和 cmdline 源. cmp.setup.cmdline(":" , { mapping = cmp.mapping.preset.cmdline(), sources = cmp.config.sources({ { name = "path" }, }, { { name = "cmdline" }, }), })
lua/keybindings.lua
添加
-- 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
init.lua
require("lsp.cmp" ) -- (新增)
LSP 功能增强 UI 插件,同时 lspage还可以自定义快捷键,对于我自己而言我认为有些地方文字更加直观,如果有些朋友需要美化可自行查找
弹窗显示错误
当一行代码很长的时候,右侧的提示文字就会显示不全
之前配置过 gp 快捷键使用弹窗显示错误,可以有效解决
功能增强 添加插件 use("tami5/lspsaga.nvim" )
创建lua/lsp/ui.lua
local lspsaga = require 'lspsaga' lspsaga.setup { -- defaults ... debug = false , use_saga_diagnostic_sign = true , -- diagnostic sign error_sign = "" , warn_sign = "" , hint_sign = "" , infor_sign = "" , diagnostic_header_icon = " " , -- code action title icon code_action_icon = " " , code_action_prompt = { enable = true , sign = true , sign_priority = 40, virtual_text = true , }, finder_definition_icon = " " , finder_reference_icon = " " , max_preview_lines = 10, finder_action_keys = { -- open = "o" , open = "<CR>" , vsplit = "s" , split = "i" , -- quit = "q" , quit = "<ESC>" , scroll_down = "<C-f>" , scroll_up = "<C-b>" , }, code_action_keys = { -- quit = "q" , quit = "<ESC>" , exec = "<CR>" , }, rename_action_keys = { -- quit = "<C-c>" , quit = "<ESC>" , exec = "<CR>" , }, definition_preview_icon = " " , border_style = "single" , rename_prompt_prefix = "➤" , rename_output_qflist = { enable = false , auto_open_qflist = false , }, server_filetype_map = {}, diagnostic_prefix_format = "%d. " , diagnostic_message_format = "%m %c" , highlight_prefix = false , }
lua/keybindings.lua
中的mapLSP 函数替换为
-- lsp 回调函数快捷键设置 pluginKeys.mapLSP = function (mapbuf) -- rename --[[ Lspsaga 替换 rn mapbuf("n" , "<leader>rn" , "<cmd>lua vim.lsp.buf.rename()<CR>" , opt) --]] mapbuf("n" , "<leader>rn" , "<cmd>Lspsaga rename<CR>" , opt) -- code action --[[ Lspsaga 替换 ca mapbuf("n" , "<leader>ca" , "<cmd>lua vim.lsp.buf.code_action()<CR>" , opt) --]] mapbuf("n" , "<leader>ca" , "<cmd>Lspsaga code_action<CR>" , opt) -- go xx --[[ mapbuf('n' , 'gd' , '<cmd>Lspsaga preview_definition<CR>' , opt) --]] mapbuf("n" , "gd" , "<cmd>lua vim.lsp.buf.definition()<CR>" , opt) --[[ Lspsaga 替换 gh mapbuf("n" , "gh" , "<cmd>lua vim.lsp.buf.hover()<CR>" , opt) --]] mapbuf("n" , "gh" , "<cmd>Lspsaga hover_doc<cr>" , opt) --[[ Lspsaga 替换 gr mapbuf("n" , "gr" , "<cmd>lua vim.lsp.buf.references()<CR>" , opt) --]] mapbuf("n" , "gr" , "<cmd>Lspsaga lsp_finder<CR>" , opt) --[[ Lspsaga 替换 gp, gj, gk mapbuf("n" , "gp" , "<cmd>lua vim.diagnostic.open_float()<CR>" , opt) mapbuf("n" , "gj" , "<cmd>lua vim.diagnostic.goto_next()<CR>" , opt) mapbuf("n" , "gk" , "<cmd>lua vim.diagnostic.goto_prev()<CR>" , opt) --]] -- diagnostic mapbuf("n" , "gp" , "<cmd>Lspsaga show_line_diagnostics<CR>" , opt) mapbuf("n" , "gj" , "<cmd>Lspsaga diagnostic_jump_next<cr>" , opt) mapbuf("n" , "gk" , "<cmd>Lspsaga diagnostic_jump_prev<cr>" , opt) mapbuf("n" , "<leader>f" , "<cmd>lua vim.lsp.buf.format({ bufnr = bufnr })<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' , '<leader>q' , '<cmd>lua vim.diagnostic.setloclist()<CR>' , opt) -- mapbuf("n" , "<C-k>" , "<cmd>lua vim.lsp.buf.signature_help()<CR>" , opt) -- mapbuf('n' , '<space>wa' , '<cmd>lua vim.lsp.buf.add_workspace_folder()<CR>' , opt) -- mapbuf('n' , '<space>wr' , '<cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>' , opt) -- mapbuf('n' , '<space>wl' , '<cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>' , opt) -- mapbuf('n' , '<space>D' , '<cmd>lua vim.lsp.buf.type_definition()<CR>' , opt) end
init.lua
代码格式化 之前lua/keybindings.lua
定义过格式化快捷键
mapbuf("n" , "<leader>f" , "<cmd>lua vim.lsp.buf.format({ bufnr = bufnr })<CR>" , opt)
但是你会发现格式化不起作用,这是因为使用的 Lua Language Server 并没有实现格式化功能。
格式化方案有如下两种:
第一种是使用专门的格式化插件;
第二种是给 Language Server 注入格式化功能。
这里只讲解第二种
添加插件 -- 代码格式化 use({ "jose-elias-alvarez/null-ls.nvim" , requires = "nvim-lua/plenary.nvim" })
添加配置文件 lua/lsp/null-ls.lua
local status, null_ls = pcall(require, "null-ls" )if not status then vim.notify("没有找到 null-ls" ) return end local formatting = null_ls.builtins.formatting null_ls.setup({ debug = false , sources = { -- Formatting --------------------- -- brew install shfmt formatting.shfmt, -- StyLua formatting.stylua, -- frontend formatting.prettier.with({ -- 只比默认配置少了 markdown filetypes = { "javascript" , "javascriptreact" , "typescript" , "typescriptreact" , "vue" , "css" , "scss" , "less" , "html" , "json" , "yaml" , "graphql" , }, prefer_local = "node_modules/.bin" , }), -- formatting.fixjson, -- formatting.black.with({ extra_args = { "--fast" } }), }, -- 保存自动格式化 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 () -- on 0.8, you should use vim.lsp.buf.format({ bufnr = bufnr }) instead vim.lsp.buf.format({ bufnr = bufnr }) -- vim.lsp.buf.formatting_sync() end, }) end end, })
init.lua
安装之后可以运行:LspInfo
查看绑定的Language Server
然后我们可以看到有两个 LSP 了,null-ls 作为通用 LSP,可以在任何 filetypes 中运行。
然后执行:NullLsInfo
查看源的激活情况
之后的话即可使用:lua vim.lsp.buf.format()
命令或是直接使用快捷键<leader>f
进行格式化
但是我们会发现包如下错误,这是因为相应语言应该配置相应的Code Formatter,显示报错就是缺少Lua语言的StyLua,其它语言可以自行配置相应的Formatter
下面展示如何配置stylua
配置环境 stylua 需要配置 rust 环境 ,rust官网:安装 Rust - Rust 程序设计语言 (rust-lang.org)
因为我是使用WSL,因此直接执行如下命令即可
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
执行source "$HOME/.cargo/env"
执行rustc --version
看是否成功,失败则看是否添加~/.cargo/bin
路径到环境变量中
安装步骤 执行
输出stylua -V
看是否成功
出现问题
格式化之后里面的空格就都变成了”^I”, 原本应该是“·”的
直接将basic.lua
文件中设置vim.o.listchars = "space:·,tab:··"
或是设置vim.o.list = false
前端开发必要配置 配置语法高亮 执行:TSInstall css scss json html vue javascript typescript
执行:TSInstallInfo
查看安装情况
配置 LSP lua/lsp/setup.lua
mason_config.setup({ ensure_installed = {"lua_ls" , "html" , "cssls" } }) ... local servers = { lua_ls = require("lsp.config.lua" ), -- 新增 html = require("lsp.config.html" ), cssls = require("lsp.config.css" ) }
创建lsp/common-config.lua
local M = {}M.keyAttach = function (bufnr) local function buf_set_keymap(mode, lhs, rhs) vim.keymap.set(mode, lhs, rhs, { noremap = true , silent = true , buffer = bufnr }) end -- 绑定快捷键 require("keybindings" ).mapLSP(buf_set_keymap) end -- 禁用格式化功能,交给专门插件插件处理 M.disableFormat = function (client) if vim.fn.has("nvim-0.8" ) == 1 then client.server_capabilities.documentFormattingProvider = false client.server_capabilities.documentRangeFormattingProvider = false else client.resolved_capabilities.document_formatting = false client.resolved_capabilities.document_range_formatting = false end end -- M.capabilities = require("cmp_nvim_lsp" ).update_capabilities(vim.lsp.protocol.make_client_capabilities()) M.capabilities = require("cmp_nvim_lsp" ).default_capabilities() M.flags = { debounce_text_changes = 150, } return M
创建lsp/config/html.lua
local common = require("lsp.common-config" )local opts = { capabilities = common.capabilities, flags = common.flags, on_attach = function (client, bufnr) -- 禁用本身语言格式化 common.disableFormat(client) common.keyAttach(bufnr) end, } return { on_setup = function (server) server.setup(opts) end, }
创建lsp/config/css.lua
local common = require("lsp.common-config" )local opts = { capabilities = common.capabilities, flags = common.flags, on_attach = function (client, bufnr) common.disableFormat(client) common.keyAttach(bufnr) end, settings = { css = { validate = true , -- tailwindcss lint = { unknownAtRules = "ignore" , }, }, less = { validate = true , lint = { unknownAtRules = "ignore" , }, }, scss = { validate = true , lint = { unknownAtRules = "ignore" , }, }, }, } return { on_setup = function (server) server.setup(opts) end, }
上面不禁用的话也是可以的,只不过会和默认的null-ls中的 prettier 格式化冲突,每次格式化都需要选择
注意html、css文件均是需要项目根目录有package.json
文件和prettier依赖
npm init -y && npm i -D prettier eslint
Emmet LSP 配置 使用 emmet 可是使用简单的语法可以快速打出 HTML 结构标签
创建lua/lsp/config/emmet.lua
local opts = { filetypes = { "html" , "typescriptreact" , "javascriptreact" , "css" , "sass" , "scss" , "less" }, } return { on_setup = function (server) server.setup(opts) end, }
lua/lsp/setup.lua
修改
mason_config.setup({ ensure_installed = {"lua_ls" , "html" , "cssls" , "emmet_ls" } }) ... local servers = { lua_ls = require("lsp.config.lua" ), html = require("lsp.config.html" ), cssls = require("lsp.config.css" ), -- 新增 emmet_ls = require("lsp.config.emmet" ) }
配置 jsonls JSON Schema Store 插件,即JSON增强包
plugins.lua
添加插件
-- JSON 增强 use("b0o/schemastore.nvim" )
新建lua/lsp/config/json.lua
local common = require("lsp.common-config" )local opts = { capabilities = common.capabilities, flags = common.flags, on_attach = function (client, bufnr) -- use fixjson to format -- https://github.com/rhysd/fixjson common.disableFormat(client) common.keyAttach(bufnr) end, settings = { json = { schemas = require("schemastore" ).json.schemas(), }, }, } return { on_setup = function (server) server.setup(opts) end, }
lsp/config/setup.lua
修改
mason_config.setup({ ensure_installed = {"lua_ls" , "html" , "cssls" , "emmet_ls" ,"jsonls" } }) local servers = { lua_ls = require("lsp.config.lua" ), html = require("lsp.config.html" ), cssls = require("lsp.config.css" ), emmet_ls = require("lsp.config.emmet" ), -- 新增 jsonls = require("lsp.config.json" ) }
配置 tssserver 添加TS增强包插件
use({ "jose-elias-alvarez/nvim-lsp-ts-utils" , requires = "nvim-lua/plenary.nvim" })
创建lsp/config/ts.lua
local common = require("lsp.common-config" )local keybindings = require("keybindings" )local ts_utils = require("nvim-lsp-ts-utils" )local opts = { flags = common.flags, capabilities = common.capabilities, -- https://github.com/jose-elias-alvarez/nvim-lsp-ts-utils/blob/main/lua/nvim-lsp-ts-utils/utils.lua -- 传入 tsserver 初始化参数 -- make inlay hints work init_options = { hostInfo = "neovim" , preferences = { includeInlayParameterNameHints = "all" , includeInlayParameterNameHintsWhenArgumentMatchesName = true , includeInlayFunctionParameterTypeHints = true , includeInlayVariableTypeHints = true , includeInlayPropertyDeclarationTypeHints = true , includeInlayFunctionLikeReturnTypeHints = true , includeInlayEnumMemberValueHints = true , }, }, on_attach = function (client, bufnr) common.disableFormat(client) common.keyAttach(bufnr) -- defaults ts_utils.setup({ debug = false , disable_commands = false , enable_import_on_completion = false , -- import all import_all_timeout = 5000, -- ms -- lower numbers = higher priority import_all_priorities = { same_file = 1, -- add to existing import statement local_files = 2, -- git files or files with relative path markers buffer_content = 3, -- loaded buffer content buffers = 4, -- loaded buffer names }, import_all_scan_buffers = 100, import_all_select_source = false , -- if false will avoid organizing imports always_organize_imports = true , -- filter diagnostics filter_out_diagnostics_by_severity = {}, -- https://github.com/microsoft/TypeScript/blob/main/src/compiler/diagnosticMessages.json filter_out_diagnostics_by_code = { 80001, }, -- inlay hints auto_inlay_hints = true , inlay_hints_highlight = "Comment" , inlay_hints_priority = 200, -- priority of the hint extmarks inlay_hints_throttle = 150, -- throttle the inlay hint request inlay_hints_format = { -- format options for individual hint kind Type = {}, Parameter = {}, Enum = {}, -- Example format customization for `Type` kind: -- Type = { -- highlight = "Comment" , -- text = function (text) -- return "->" .. text:sub(2) -- end, -- }, }, -- update imports on file move update_imports_on_move = false , require_confirmation_on_move = false , watch_dir = nil, }) -- required to fix code action ranges and filter diagnostics ts_utils.setup_client(client) -- no default maps, so you may want to define some here keybindings.mapTsLSP(bufnr) end, } return { on_setup = function (server) server.setup(opts) end, }
同样setup.lua
修改
mason_config.setup({ ensure_installed = {"lua_ls" , "html" , "cssls" , "emmet_ls" , "jsonls" , "tsserver" } }) -- 安装列表 -- { key: 服务器名, value: 配置文件 } -- key 必须为下列网址列出的 server name,不可以随便写 -- https://github.com/williamboman/nvim-lsp-installer local servers = { lua_ls = require("lsp.config.lua" ), html = require("lsp.config.html" ), cssls = require("lsp.config.css" ), emmet_ls = require("lsp.config.emmet" ), jsonls = require("lsp.config.json" ), -- 新增 tsserver = require("lsp.config.ts" ) }
添加快捷键lua/keybindings.lua
-- typescript 快捷键 pluginKeys.mapTsLSP = function (mapbuf) mapbuf("n" , "gs" , ":TSLspOrganize<CR>" , opt) mapbuf("n" , "gr" , ":TSLspRenameFile<CR>" , opt) mapbuf("n" , "gi" , ":TSLspImportAll<CR>" , opt) end
gs 删除不用的 import 语句并重新排序。
gr 用于改变文件名,同时其他文件中引用该文件的文件名也会被修改。
gi 导入当前文件的所有依赖,并且会自动排序
ESLint 和 Prettier 配置 null-ls 中除了格式化 Formatting,还有Diagnostics(红字错误提示) 和 Code Actions(代码触发的行为)
lsp/null-ls.lua
修改为
local status, null_ls = pcall(require, "null-ls" )if not status then vim.notify("没有找到 null-ls" ) return end local formatting = null_ls.builtins.formattinglocal diagnostics = null_ls.builtins.diagnosticslocal code_actions = null_ls.builtins.code_actionsnull_ls.setup({ debug = false , sources = { -- Formatting --------------------- -- brew install shfmt formatting.shfmt, -- StyLua formatting.stylua, -- frontend formatting.prettier.with({ -- 只比默认配置少了 markdown filetypes = {"javascript" , "javascriptreact" , "typescript" , "typescriptreact" , "vue" , "css" , "scss" , "less" , "html" , "json" , "yaml" , "graphql" }, prefer_local = "node_modules/.bin" }), -- Diagnostics --------------------- diagnostics.eslint.with({ prefer_local = "node_modules/.bin" }), -- code actions --------------------- code_actions.gitsigns, code_actions.eslint.with({ prefer_local = "node_modules/.bin" })}, -- -- -- diagnostics_format = "[#{s}] #{m}" , -- 保存自动格式化 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 () -- on 0.8, you should use vim.lsp.buf.format({ bufnr = bufnr }) instead vim.lsp.buf.format({ bufnr = bufnr }) -- vim.lsp.buf.formatting_sync() end }) end end })
code_actions.gitsigns 没有安装可以注释掉
使用<leader>ca
调用 Code Action 自动修复快捷键
Rust 开发配置 前文已安装rust环境,此处不赘述
语法高亮 plugin-config/nvim-treesitter.lua
中的ensure_installed中添加”rust”
或是执行:TSInstall rust
代码提示 lsp/setup.lua
mason_config.setup({ ensure_installed = {"lua_ls" , "html" , "cssls" , "emmet_ls" , "jsonls" , "rust_analyzer" } }) ... local servers = { -- 新增 rust_analyzer = require("lsp.config.rust" ), }
lsp/config/rust.lua
local common = require("lsp.common-config" )local opts = { capabilities = common.capabilities, flags = common.flags, on_attach = function (client, bufnr) common.disableFormat(client) common.keyAttach(bufnr) end, settings = { -- to enable rust-analyzer settings visit: -- https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/generated_config.adoc ["rust-analyzer" ] = { -- enable clippy on save checkOnSave = { command = "clippy" , }, }, }, } return { on_setup = function (server) local ok_rt, rust_tools = pcall(require, "rust-tools" ) if not ok_rt then print ("Failed to load rust tools, will set up `rust_analyzer` without `rust-tools`." ) server.setup(opts) else -- We don't want to call lspconfig.rust_analyzer.setup() when using rust-tools rust_tools.setup({ server = opts, -- dap = require("dap.nvim-dap.config.rust"), }) end end, }
添加 Rust 增强包插件
-- Rust 增强 use("simrat39/rust-tools.nvim" )
代码格式化 安装相应 Formtter
rustup component add rustfmt
null-ls.lua
source中添加
-- rustfmt formatting.rustfmt,
filetypes中添加"rust"
配置C/C++开发环境 熟悉上面的流程,这个就简单讲了
相关插件:p00f/clangd_extensions.nvim: Clangd’s off-spec features for neovim’s LSP client. Use https://sr.ht/~p00f/clangd_extensions.nvim instead (github.com)
setup.lua
servers 添加 “c”和“cpp”
安装格式化插件:apt-get install -y clang-format
plugin-config/nvim-treesitter.lua
ensure_installed 添加 “cpp” "c"
null-ls.lua
formatting.prettier.with({ filetypes = { "c" , "cpp" } }), -- clang-format formatting.clang_format,
lsp/config/clangd.lua
local common = require("lsp.common-config" )local opts = { capabilities = common.capabilities, flags = common.flags, on_attach = function (client, bufnr) common.disableFormat(client) common.keyAttach(bufnr) end, } return { on_setup = function (server) local ok_rt, clangd_extensions = pcall(require, "clangd_extensions" ) if not ok_rt then server.setup(opts) else clangd_extensions.setup({ server = opts, extensions = { -- defaults: -- Automatically set inlay hints (type hints) autoSetHints = true , -- These apply to the default ClangdSetInlayHints command inlay_hints = { -- Only show inlay hints for the current line only_current_line = false , -- Event which triggers a refersh of the inlay hints. -- You can make this "CursorMoved" or "CursorMoved,CursorMovedI" but -- not that this may cause higher CPU usage. -- This option is only respected when only_current_line and -- autoSetHints both are true . only_current_line_autocmd = "CursorHold" , -- whether to show parameter hints with the inlay hints or not show_parameter_hints = true , -- prefix for parameter hints parameter_hints_prefix = "<- " , -- prefix for all the other hints (type , chaining) other_hints_prefix = "=> " , -- whether to align to the length of the longest line in the file max_len_align = false , -- padding from the left if max_len_align is true max_len_align_padding = 1, -- whether to align to the extreme right or not right_align = false , -- padding from the right if right_align is true right_align_padding = 7, -- The color of the hints highlight = "Comment" , -- The highlight group priority for extmark priority = 100, }, ast = { -- These are unicode, should be available in any font role_icons = { type = "🄣" , declaration = "🄓" , expression = "🄔" , statement = ";" , specifier = "🄢" , ["template argument" ] = "🆃" , }, kind_icons = { Compound = "🄲" , Recovery = "🅁" , TranslationUnit = "🅄" , PackExpansion = "🄿" , TemplateTypeParm = "🅃" , TemplateTemplateParm = "🅃" , TemplateParamObject = "🅃" , }, --[[ These require codicons (https://github.com/microsoft/vscode-codicons) role_icons = { type = "" , declaration = "" , expression = "" , specifier = "" , statement = "" , ["template argument" ] = "" , }, kind_icons = { Compound = "" , Recovery = "" , TranslationUnit = "" , PackExpansion = "" , TemplateTypeParm = "" , TemplateTemplateParm = "" , TemplateParamObject = "" , }, ]] highlights = { detail = "Comment" , }, }, memory_usage = { border = "none" , }, symbol_info = { border = "none" , }, }, }) end end, }
代码运行器 相关插件:CRAG666/code_runner.nvim: Neovim plugin.The best code runner you could have, it is like the one in vscode but with super powers, it manages projects like in intellij but without being slow (github.com)
添加插件:
use 'CRAG666/code_runner.nvim'
配置插件plugin-config/code-runner.lua
local status, code_runner = pcall(require, "code_runner" )if not status then vim.notify("没有找到 code_runner" ) return end code_runner.setup({ -- choose default mode (valid term, tab, float , toggle) mode = "term" , -- Focus on runner window(only works on toggle, term and tab mode) focus = true , -- startinsert (see ':h inserting-ex' ) startinsert = false , term = { -- Position to open the terminal, this option is ignored if mode is tab position = "bot" , -- window size, this option is ignored if tab is true size = 8, }, float = { -- Key that close the code_runner floating window close_key = "<ESC>" , -- Window border (see ':h nvim_open_win' ) border = "none" , -- Num from `0 - 1` for measurements height = 0.8, width = 0.8, x = 0.5, y = 0.5, -- Highlight group for floating window/border (see ':h winhl' ) border_hl = "FloatBorder" , float_hl = "Normal" , -- Transparency (see ':h winblend' ) blend = 0, }, filetype_path = "" , -- No default path defined before_run_filetype = function () vim.cmd(":w" ) end, filetype = { javascript = "node" , java = { "cd $dir &&" , "javac $fileName &&" , "java $fileNameWithoutExt " , }, c = { "cd $dir &&" , "gcc $fileName " , "-o $fileNameWithoutExt &&" , "$dir /$fileNameWithoutExt " , }, cpp = { "cd $dir &&" , "g++ $fileName " , "-o $fileNameWithoutExt &&" , "$dir /$fileNameWithoutExt " , }, python = "python -u" , sh = "bash" , rust = { "cd $dir &&" , "rustc $fileName &&" , "$dir /$fileNameWithoutExt " , }, }, project_path = "" , -- No default path defined project = {}, })
之后init.lua中添加插件,最后添加快捷键,使用空格加r
即可运行代码
-- 代码运行器 map('n' , '<leader>r' , ':RunCode<CR>' , { noremap = true , silent = false })
其他配置 自动补全括号 插件仓库:windwp/nvim-autopairs: autopairs for neovim written by lua (github.com)
引入插件
use("windwp/nvim-autopairs" )
创建lua/plugin-config/nvim-autopairs.lua
-- https://github.com/windwp/nvim-autopairs local status, autopairs = pcall(require, "nvim-autopairs" )if not status then vim.notify("没有找到 nvim-autopairs" ) return end autopairs.setup({ check_ts = true , ts_config = { lua = { "string" }, -- it will not add a pair on that treesitter node javascript = { "template_string" }, java = false , -- don't check treesitter on java }, }) -- If you want insert `(` after select function or method item local cmp_autopairs = require("nvim-autopairs.completion.cmp") local cmp = require("cmp") cmp.event:on("confirm_done", cmp_autopairs.on_confirm_done({ map_char = { tex = "" } }))
init.lua
快速注释插件 插件仓库:numToStr/Comment.nvim: // Smart and powerful comment plugin for neovim. Supports treesitter, dot repeat, left-right/up-down motions, hooks, and more (github.com)
添加插件
use("numToStr/Comment.nvim" )
创建lua/plugin-config/comment.lua
local status, comment = pcall(require, "Comment" )if not status then vim.notify("没有找到 Comment" ) return end local default_opts = { ---Add a space b/w comment and the line ---@type boolean|fun():boolean padding = true , ---Whether the cursor should stay at its position ---NOTE: This only affects NORMAL mode mappings and doesn't work with dot-repeat ---@type boolean sticky = true, ---Lines to be ignored while comment/uncomment. ---Could be a regex string or a function that returns a regex string. ---Example: Use ' ^$' to ignore empty lines ---@type string|fun():string ignore = nil, ---LHS of toggle mappings in NORMAL + VISUAL mode ---@type table toggler = { ---Line-comment toggle keymap line = "gcc", ---Block-comment toggle keymap block = "gbc", }, ---LHS of operator-pending mappings in NORMAL + VISUAL mode ---@type table opleader = { ---Line-comment keymap line = "gc", ---Block-comment keymap block = "gb", }, ---LHS of extra mappings ---@type table extra = { ---Add comment on the line above above = "gcO", ---Add comment on the line below below = "gco", ---Add comment at the end of line eol = "gcA", }, ---Create basic (operator-pending) and extended mappings for NORMAL + VISUAL mode ---NOTE: If `mappings = false` then the plugin won' t create any mappings ---@type boolean|table mappings = { ---Operator-pending mapping ---Includes `gcc`, `gbc`, `gc[count]{motion}` and `gb[count]{motion}` ---NOTE: These mappings can be changed individually by `opleader` and `toggler` config basic = true , ---Extra mapping ---Includes `gco`, `gcO`, `gcA` extra = false , ---Extended mapping ---Includes `g>`, `g<`, `g>[count]{motion}` and `g<[count]{motion}` extended = false , }, ---Pre-hook, called before commenting the line ---@type fun(ctx: Ctx):string pre_hook = nil, ---Post-hook, called after commenting is done ---@type fun(ctx: Ctx) post_hook = nil, } -- 关闭了extra快捷键,只用keybindings里定义的基础快捷键 comment.setup(vim.tbl_deep_extend("force" , default_opts, require("keybindings" ).comment))
定义快捷键 keybindings.lua
gcc 快捷键作为行注释,gbc 快捷键作为块注释
-- 代码注释插件 -- see ./lua/plugin-config/comment.lua pluginKeys.comment = { -- Normal 模式快捷键 toggler = { line = "gcc" , -- 行注释 block = "gbc" , -- 块注释 }, -- Visual 模式 opleader = { line = "gc" , bock = "gb" , }, }
init.lua
中引入
require("plugin-config.comment" )
Surround 配置 引入插件
-- surround use("ur4ltz/surround.nvim" )
lua/plugin-config/surround.lua
local status, surround = pcall(require, "surround" )if not status then vim.notify("没有找到 surround" ) return end surround.setup({ mappings_style = "surround" , })
init.lua
surround 官方快捷键如下
Normal Mode - Sandwich Mode Provides key mapping to add surrounding characters.( visually select then press s<char> or press sa{motion}{char}) Provides key mapping to replace surrounding characters.( sr<from><to> ) Provides key mapping to delete surrounding characters.( sd<char> ) ss repeats last surround command . Normal Mode - Surround Mode Provides key mapping to add surrounding characters.( visually select then press s<char> or press ys{motion}{char}) Provides key mapping to replace surrounding characters.( cs<from><to> ) Provides key mapping to delete surrounding characters.( ds<char> ) Insert Mode <c-s><char> will insert both pairs in insert mode. <c-s><char><space> will insert both pairs in insert mode with surrounding whitespace. <c-s><char><c-s> will insert both pairs on newlines insert mode.
常用快捷键
ds<char>
cs<from><to>
ys{motion}{char}
其他问题 我在推送仓库时频繁遇到GnuTLS recv error (-110)
解决链接:git - GnuTLS recv error (-110)
#!/bin/bash set -esudo apt-get install build-essential fakeroot dpkg-dev -y sudo apt-get build-dep git -y sudo apt-get install libcurl4-openssl-dev -y cd ~mkdir source-gitcd source-git/apt-get source git cd git-2.*.*/sed -i -- 's/libcurl4-gnutls-dev/libcurl4-openssl-dev/' ./debian/control sed -i -- '/TEST\s*=\s*test/d' ./debian/rules dpkg-buildpackage -rfakeroot -b -uc -us sudo dpkg -i ../git_*ubuntu*.deb
直接运行这个脚本即可
之后推送遇到remote: Support for password authentication was removed on August 13, 2021
问题,虽然我不太清楚我配了SSH key,为什么还是走的密码验证,但是我还是按照他的提示来做获取token
,直接github首页中Setting->Developer settings->Personal access tokens 生成token
执行
git remote set-url origin https://<your_token>@github.com/<USERNAME>/<REPO>.git
最后即可免密推送
备忘记录 学习vim快捷键网站:Vim Cheat Sheet (rtorr.com)
使用:h clipboard
查看复制粘贴命令
文档推荐使用set clipboard=unnamedplus
,或是执行set clipboard^=unnamed,unnamedplus
就可以连通vim剪切板和系统剪切板,但是这个命令只是暂时性的,只能当前页面生效,为了永久生效,可以在basic.lua
中添加
-- 复制粘贴联通系统粘贴板 vim.o.clipboard = "unnamedplus"
全选复制文件:%y
或是ggyG
只是删除而不将删除的部分复制到剪切板中"_dd
(本质上是将剪切的内容放到_寄存器中,以便于和+寄存器区分)
结语 对我自己而言,配置的这些功能虽然少,但是暂时够用,如果追求更多的功能,可以直接用小册作者的仓库,或是其他优秀仓库
其实我觉得其实作为个人使用来说,没有特殊需求的情况下不是很推荐自己配置,只需要看懂本篇博客,然后可以做到更改别人写好的配置即可
还有就是切忌盲目复制粘贴,一定要看官方文档
本篇博客示例配置仓库:ReturnTmp/rettmp-nvim
掘金小册作者仓库:https://github.com/nshen/learn-neovim-lua
注意:v2分支是neovim 8.0+适用
或是直接使用作者的新仓库:nshen/InsisVim: 🗃 An out-of-the-box Neovim IDE solution that setup development environment in an incredibly simple way. (github.com)
除了小册作者的配置仓库,这里再推荐一个优秀Neovim配置仓库:ayamir/nvimdots: A well configured and structured Neovim. (github.com)
至此Neovim配置学习就结束了:smile: