惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

TaoSecurity Blog
TaoSecurity Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
F
Fortinet All Blogs
Cisco Talos Blog
Cisco Talos Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
Secure Thoughts
美团技术团队
雷峰网
雷峰网
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
C
CXSECURITY Database RSS Feed - CXSecurity.com
Engineering at Meta
Engineering at Meta
人人都是产品经理
人人都是产品经理
月光博客
月光博客
T
Tor Project blog
P
Privacy & Cybersecurity Law Blog
Recorded Future
Recorded Future
I
Intezer
博客园 - 【当耐特】
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
GbyAI
GbyAI
罗磊的独立博客
V
V2EX
Google DeepMind News
Google DeepMind News
D
DataBreaches.Net
Last Week in AI
Last Week in AI
T
Tailwind CSS Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
A
About on SuperTechFans
Scott Helme
Scott Helme
Vercel News
Vercel News
Spread Privacy
Spread Privacy
T
Threat Research - Cisco Blogs
Recent Announcements
Recent Announcements
Hacker News: Ask HN
Hacker News: Ask HN
C
CERT Recently Published Vulnerability Notes
G
Google Developers Blog
B
Blog
博客园 - 叶小钗
WordPress大学
WordPress大学
博客园 - 聂微东
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Jina AI
Jina AI
IT之家
IT之家
C
Cybersecurity and Infrastructure Security Agency CISA
P
Palo Alto Networks Blog
小众软件
小众软件
博客园 - Franky
Microsoft Azure Blog
Microsoft Azure Blog
AWS News Blog
AWS News Blog

少数派

派早报:Google 发布 Fitbit Air 等 - 少数派 「新人报到」確認需求,再開始 - 少数派 从 SOLO 独立开发者社区,我看到了越来越多开发者开始做自己的产品 - 少数派 我怎么管理那些"不常做,但总会忘"的生活事项 - 少数派 人形机器人量产元年,数据才是具身智能的“生死线” - 少数派 BuhoLaunchpad 高度还原 Mac 启动台:开发历程与思考 - 少数派 五年陪伴依然不舍,DIY 换壳后让罗技 MX Master 3 继续服役 - 少数派 新玩意 240|少数派的编辑们最近买了啥? - 少数派 一日一技|为什么你应该关闭 iOS 的键盘声音 - 少数派 我做了个插件和 Skills,一键提取任何网站的设计规范 Design.md - 少数派 住在三四线城市的你,该开始录播客了 - 少数派 甘南秘境,大白高国 - 少数派 AI的审美:谁让把我变成川内倫子 - 少数派 返工怎能不烦恼,打工人片单总有一部是你的「嘴替」 - 少数派 为了让「上厕所」更健康,我做了一个小工具 - 少数派 AI + Skill,能够让生成的文章去除 AI 味吗? - 少数派 新玩意|韶音OpenDots ONE 耳夹式耳机 - 少数派 《美满》| 在每一个春天的晚上相爱(362) - 少数派 新玩意|优篮子 PS01 MagSnap 磁吸支架 - 少数派 自我整合手记 | 我开始早睡了:用稳定规则,为自由托底 - 少数派 用龙虾(OpenClaw)两个多月,我最深的12个体会 - 少数派 听歌时间到,12 张你可能错过的 2025 华语乐坛好专辑 - 少数派 承诺能追吗 - 少数派 macOS 26启动台没了? 我做了个不一样的App启动器 - Keboard - 少数派 《四海为家的人》| INTJ对话INTJ(361) - 少数派 你发过的那些黑历史,是时候一次清干净了 - 少数派 新玩意:安安静静玩,越玩越专注:计客密码机 - 少数派 iPad 用户首次体验 Android 平板:vivo Pad6 Pro - 少数派 数据逻辑强 - 少数派 极北行+ | 一路向北,探访日本至北之地 | 001 - 少数派 万字剖析:千问App深度体验报告(2026) - 少数派 在2026年,如何真正防止别人抄袭你的作品 - 少数派 怎么用 50 块搭个 AI 语音助手?我踩了 3 天坑 - 少数派 YeeroAI:让 AI 对话真正成为知识管理的一部分 - 少数派 爬泰山 - 少数派 「旅图显影」 App 更新:这次,我们补上了一点「手感」 - 少数派 假期出门太折磨?我的 23 条经验帮你规划惬意旅行 - 少数派 工作流会变吗 - 少数派 Claude Opus 4.6 怎么用最省钱?我测了 5 种方案 - 少数派 GPT Image 2 让图文并茂不再稀罕 - 少数派 用户侧出发——什么是AI,我要不要学习? - 少数派 找片、转存、整理、播放一条龙!让你的付费网盘值回票价 - 少数派 欢迎试用!日课一问2.0插件 - 少数派 自己做的MDeditor,原本想购买 Typora 试了两次支付不成功,干脆自己做一个 - 少数派 vibe coding了一个 3MB 的小工具,让 ~/Downloads 彻底告别混乱 - 少数派 因为受不了 Mac 的风扇策略,我做了一个风扇控制工具 - 少数派 别只怪模型 - 少数派 Warp 终端的 AI 功能怎么用?我测了一周的体验 - 少数派 AI 写代码老是出 bug?这 5 个配置我后悔没早知道 - 少数派 「新玩意」苹果出相机可能就这样:Sigma BF + 45mm F2.8 DG Contemporary - 少数派 一个面向2030年的AI操作系统是什么样子的:浅谈cola这款有灵魂的Agent - 少数派 别只看写代码 - 少数派 每天解决10个问题,还是一口气攻坚解决400个? - 少数派 AI 交易机器人怎么搭?我用 Claude 跑了一周实盘 - 少数派 Maptoposter Online:把你爱的城市画成艺术海报 - 少数派 Function Calling 怎么用?我测了 3 个模型发现差距真大 - 少数派 Legend Talk:我做了个 AI 圆桌,让 160 位思想家围着你的问题转 - 少数派 如何找到自己的蓝方?在小县城寻找压力测试 - 少数派 语音输入与软件接口|2026年聊AI时,我们都聊些什么(上) - 少数派 混动已经卖爆,纯电又来补刀——钛7闪充版简直“不讲武德” - 少数派 本月玩什么|朋友收藏、识质存在、沙罗周期 - 少数派 为什么要每天坚持输出? - 少数派 Claude API 挂了好几个小时,你的项目有备用方案吗? - 少数派 Function Calling 没你想的复杂——我用它做了个有点用的工具 - 少数派 登录系统立即播放视频或者图片音乐的软件 - 少数派 我为什么创建 FlipHTML5 下载工具 - 少数派 残局没电?多品牌外设电量统一管理软件EasyBluetooth已支持RTSS游戏内显示以及AIDA64 - 少数派 前往通义路的路 - 少数派 太好看了,媲美Sun的个人导航页,NAS部署星云门户 - 少数派 乌黑嘴唇“一键检测”上线了 - 少数派 派早报:Claude AI 接入多个创意软件生态、FILCO 生产方接手品牌等 - 少数派 【更新】BearCLI、Claude 连接器与 MCP 服务器 - 少数派 记了上千条流水,还是看不懂财务?我做了一个让 AI 读懂账本的工作台 - 少数派 MINI R56 升级原厂 Sport 模式 - 少数派 新玩意 | 一棵柠檬树(仿真版) - 少数派 Momenta的“物理AI”野望,需迈过“含摩量”这道关 - 少数派 网页直接投屏控制手机!NAS一键部署PandaScrcpy,流畅丝滑可远程。 - 少数派 众测|邀你一同探索随身 AI 硬件入口 YoooClaw C·ONE - 少数派 2050大会:分享时间是真诚 参会记 - 少数派 iPad 赋能电影创作:国内首部宣纸手绘长片《燃比娃》的幕后故事 - 少数派 AI的审美:我用 8 个大模型给 100 张旅行照片打分 - 少数派 普通人如何破圈?去参加一个本地协会 - 少数派 把极空间的图标全换了,主题DIY全攻略打造你的专属NAS桌面 - 少数派 电子便签墙,帮你实现便签自由 - 少数派 我如何用三个 CLI 工具取代文档创建需求 - 少数派 原来真的有人可以玩一辈子 - 少数派 社区速递 139 | 派友热议三月买了啥、复古单反尼康 Df 体验 - 少数派 06 作品的赏析与评价 - 少数派 TDS REVIEW|索尼 WF-1000XM6 降噪真无线耳机体验 - 少数派 35.98万起售的第二代腾势D9,我看重的不是堆料,而是不凑合 - 少数派 鼠须管 Squirrel 皮肤配置指北 - 少数派 从watch ultra2换到redmi watch6 - 少数派 派早报:阿里巴巴发布视频生成模型 HappyHorse 1.0 等 - 少数派 别迷信1M - 少数派 家人们天塌了!网盘“大封杀”,多个渠道多条路,NAS部署PanHub - 少数派 AI与人勾心斗角!NAS一键部署AI狼人杀,假日休闲必备。 - 少数派 电商必备!Comfyui工作流批量生图插件,一次生成12张!支持Nano banana pro模型 - 少数派 Comfyui工作流配置Gpt-image-2模型教程,0.03/张 - 少数派 OpenClaw第三方APi怎么配置?可使用Gpt-image-2模型 - 少数派 会员社区话题精选 Ep. 103 - 少数派
「临门一脚」助你入门 Vim:Kickstart 配置笔记 - 少数派
2024-07-03 · via 少数派

Matrix 首页推荐 

Matrix 是少数派的写作社区,我们主张分享真实的产品体验,有实用价值的经验与思考。我们会不定期挑选 Matrix 最优质的文章,展示来自用户的最真实的体验和观点。

文章代表作者个人观点,少数派仅对标题和排版略作修改。


前言

Vim 绝对是一个让很多人又爱又恨的软件,爱它的速度快与功能强大,恨它的学习曲线与复杂的配置。我接触 Vim 的这些年来,从原版 Vim 折腾到 Neovim,也慢慢构建了一套属于自己的 Neovim 配置,主题、快捷键、插件配置等等都比较顺手了,也逐渐习惯用 Neovim 编辑一切,事实上,我在此网站发布的文章,包括目前正在写的这篇,都是我用 Neovim 以 Markdown 格式编辑完成的。但是随着时间的流逝,我也遇到一些问题,主要的问题是我的配置大多都是东拼西凑从互联网各处抄来的,配置文件写得比较杂乱且没有很好地作注释,时间一长搞得我自己也看不懂我的配置文件了,遇到一些问题要排查就要废挺多脑筋。

网上也存在一些现成的 Neovim 配置,或者叫「Neovim 发行版」,比如 LazyVimLunarVimNvChad 等等,这些所谓的发行版都提供了开即用的使用体验,不过我个人倒是觉得这些发行版有些「重」了,根据我自己的使用情况,可能根本用不到里面的很多高级功能,而且后期自定义与添加插件也多少有点不方便。

就在最近,我了解到了一个项目 kickstart.nvim,按照项目的介绍,这并不是一个 Neovim 发行版,而是一套 Neovim 配置的「出发点」,追求用最少的配置达到可以开箱即用的功能,同时对配置内容做了很详尽的注释,用户可以在这一套配置之上进一步自定义,打造属于自己的 Neovim 配置。

在详细看了一遍项目的内容后,发现我常用的绝大多数功能,在这个项目中都已经配置好了,而且没有太多多余的功能。所以我打算,在这个项目的基础上,重新自定义一套我自己的配置,并对配置内容做好整理与注释,方便未来的自己进一步管理。

开始安装

前置要求

首先,要学会如何退出 Vim,也就是说了解一些 Vim 的基础操作,比如一些基础的移动、编辑、保存与退出之类的。

之后,要了解一些 Lua 的基本用法,因为这个项目的配置文件是用 Lua 编写的,不了解也没关系,我就是在自己折腾过程中慢慢了解 Lua 的语法的。

接着,需要安装最新稳定版或 nightly 版本的 Neovim,旧版本的 Neovim 可能有些特性不兼容,原版的 Vim 也不行,因为不支持 lua 配置文件。

最后,安装项目推荐的 外部依赖,如 git、make、unzip、gcc 等等,以及可选安装 Nerd Font 字体用来在终端显示图标。

安装

如果你已经有一套 Neovim 配置了,需要将位于 ~/.config/nvim/ 的配置文件备份到别处,并将配置文件目录 ~/.config/nvim/ 与数据目录 ~/.local/share/nvim/ 删除。

或者也可以让新旧配置文件共存,添加环境变量 NVIM_APPNAME 并运行 nvim,比如 NVIM_APPNAME="nvim-kickstart" nvim 就可以让 Neovim 以 ~/.config/nvim-kickstart 作为配置文件目录启动,数据目录也会相应的变为 ~/.local/share/nvim-kickstart/,我个人是在 .zshrc 中添加了一行 alias:

alias k='NVIM_APPNAME="nvim-kickstart" nvim'

这样就可以用命令 k 来启动新的 Neovim 配置文件,而常规的 nvim 命令则是启动旧的配置文件。

之后将 kickstart 项目克隆到本地,我个人并没有用原版的 kickstart,而是用的它的一个 Fork,kickstart-modular,相比于原版的 kickstart,这个项目将配置文件拆成了多份,在管理上更方便一些,实际内容是一样的。

git clone https://github.com/dam9000/kickstart-modular.nvim.git "${XDG_CONFIG_HOME:-$HOME/.config}"/nvim

当然如果选择了新旧配置文件共存,需要相应修改路径:

git clone https://github.com/dam9000/kickstart-modular.nvim.git "${XDG_CONFIG_HOME:-$HOME/.config}"/nvim-kickstart

另外文档中也比较推荐自己将仓库 Fork 下来,并克隆自己的 Fork 仓库,这样在后期比较方便版本管理与维护。

之后重新打开 Neovim,插件管理器 Lazy 会自动下载安装插件,之后呈现在你面前的就是一个完全配置好开箱即用的 Neovim 配置了。

之后你就可以探索这套配置可以用来做什么了。它用了 Which Key 插件用来管理快捷键,只要按下 leader 键(在这套配置里是空格键),并停顿半秒钟,屏幕上面就会显现目前所有可用的快捷键了;


也可以仔细阅读配置文件中的注释内容,并了解其中每个插件的作用;或者也可以观看作者录制的介绍视频,以对这个项目有更直观的了解。

自定义配置

在了解了这个项目的大致内容后,就需要根据自己的需要对配置进行自定义修改了,在这一节内容中我会 kickstart 的配置进行修改,以满足我自己的使用习惯。

内置选项

kickstart 在配置文件中有些选项是默认禁用的没有启用,我们可以根据自己的喜好将其启用,也可以禁用其中一些选项。

首先编辑 init.lua 文件,找到 vim.g.have_nerd_font = false,这个选项默认禁用了 Nerd Font 图标字体,使用 Unicode emoji 作为图标字体,如果你的终端已经配置好了 Nerd Font 的话,可以将这个选项改为 true,这样界面中的图标就会以 Nerd Font 图标字体显示了。

之后编辑 lua/options.lua 文件。首先找到 vim.opt.relativenumber = true,这个选项控制显示相对于当前光标所在行的相对行号,打开这个选项后就可以比较方便地跳转多行。这一行是被注释掉的,删掉前面的 -- 就可以取消注释,另外因为安装了 Comment.nvim 插件,可以直接在 Visual 模式中选中这一行,按快捷键 gc 来快速取消注释。

接着找到 vim.opt.clipboard = 'unnamedplus',这个选项可以让 Neovim 与系统共享剪贴板,不过我个人倒不太喜欢这个功能,因为 Vim 每一次剪切或删除操作都会把内容保存进剪贴板,如果与系统共享剪贴板,会导致系统剪贴板被一大堆无意义的内容占据,之后我会定义一个快捷键,专门用来将内容复制到系统剪贴板。同样选中这一行按 gc 就可以将内容注释掉。

然后找到 vim.opt.undofile = true,这个选项会将文件的修改历史保存到磁盘里,这样即使退出重新打开文件也可以找到之前的修改历史,不过我个人不太喜欢这个功能,所以将其注释掉了,如果觉得这个功能有用也可以保留。

快捷键

我个人主要添加了两组快捷键,一个是快速切换与关闭 buffer,另一个是将当前选中内容复制进系统剪贴板,编辑 lua/keymaps.lua,在最后插入下面几行:

-- Changeing buffer
vim.keymap.set('n', '<leader>l', ':bnext<CR>', { desc = 'Switch to Next buffer' })
vim.keymap.set('n', '<leader>h', ':bprevious<CR>', { desc = 'Switch to Previous buffer' })
vim.keymap.set('n', '<leader>bd', ':bd<CR>', { desc = 'Delete Current Buffer' })
vim.keymap.set('n', '<leader>b!', ':bd!<CR>', { desc = 'Force Delete Current Buffer' })

-- Yanking into clipboard
vim.keymap.set('v', '<leader>y', [["+y]], { desc = 'Yanking into Clipboard' })

kickstart 配置文件中已经定义了 leader 键是空格键,所以上面的快捷键就是:在打开了多个 buffer 的情况下,空格+l空格+h 用来在 Buffer 间快速切换,空格+bd 快速关闭当前 buffer,空格+b! 强制关闭当前 buffer 不询问是否保存。还有最后一个 空格+y 将当前选中内容复制进系统剪贴板。

不过 空格+h 的快捷键与 gitsigns 插件定义的快捷键冲突了,所以还需要改一下 gitsigns 的快捷键,编辑 lua/kickstart/plugins/gitsigns.lua 文件,找到下面的内容:

        -- visual mode
        map('v', '<leader>hs', function()
          gitsigns.stage_hunk { vim.fn.line '.', vim.fn.line 'v' }
        end, { desc = 'stage git hunk' })
        map('v', '<leader>hr', function()
          gitsigns.reset_hunk { vim.fn.line '.', vim.fn.line 'v' }
        end, { desc = 'reset git hunk' })
        -- normal mode
        map('n', '<leader>hs', gitsigns.stage_hunk, { desc = 'git [s]tage hunk' })
        map('n', '<leader>hr', gitsigns.reset_hunk, { desc = 'git [r]eset hunk' })
        map('n', '<leader>hS', gitsigns.stage_buffer, { desc = 'git [S]tage buffer' })
        map('n', '<leader>hu', gitsigns.undo_stage_hunk, { desc = 'git [u]ndo stage hunk' })
        map('n', '<leader>hR', gitsigns.reset_buffer, { desc = 'git [R]eset buffer' })
        map('n', '<leader>hp', gitsigns.preview_hunk, { desc = 'git [p]review hunk' })
        map('n', '<leader>hb', gitsigns.blame_line, { desc = 'git [b]lame line' })
        map('n', '<leader>hd', gitsigns.diffthis, { desc = 'git [d]iff against index' })
        map('n', '<leader>hD', function()
          gitsigns.diffthis '@'
        end, { desc = 'git [D]iff against last commit' })

改成:

        -- visual mode
        map('v', '<leader>gs', function()
          gitsigns.stage_hunk { vim.fn.line '.', vim.fn.line 'v' }
        end, { desc = 'stage git hunk' })
        map('v', '<leader>gr', function()
          gitsigns.reset_hunk { vim.fn.line '.', vim.fn.line 'v' }
        end, { desc = 'reset git hunk' })
        -- normal mode
        map('n', '<leader>gs', gitsigns.stage_hunk, { desc = 'git [s]tage hunk' })
        map('n', '<leader>gr', gitsigns.reset_hunk, { desc = 'git [r]eset hunk' })
        map('n', '<leader>gS', gitsigns.stage_buffer, { desc = 'git [S]tage buffer' })
        map('n', '<leader>gu', gitsigns.undo_stage_hunk, { desc = 'git [u]ndo stage hunk' })
        map('n', '<leader>gR', gitsigns.reset_buffer, { desc = 'git [R]eset buffer' })
        map('n', '<leader>gp', gitsigns.preview_hunk, { desc = 'git [p]review hunk' })
        map('n', '<leader>gb', gitsigns.blame_line, { desc = 'git [b]lame line' })
        map('n', '<leader>gd', gitsigns.diffthis, { desc = 'git [d]iff against index' })
        map('n', '<leader>gD', function()
          gitsigns.diffthis '@'
        end, { desc = 'git [D]iff against last commit' })

基本就是把 h 开头的快捷键改成了 g 开头。

之后还需要改一下 which-key 的快捷键提示,编辑 lua/kickstart/plugins/which-key.lua,找到如下内容:

      require('which-key').register {
        ['<leader>c'] = { name = '[C]ode', _ = 'which_key_ignore' },
        ['<leader>d'] = { name = '[D]ocument', _ = 'which_key_ignore' },
        ['<leader>r'] = { name = '[R]ename', _ = 'which_key_ignore' },
        ['<leader>s'] = { name = '[S]earch', _ = 'which_key_ignore' },
        ['<leader>w'] = { name = '[W]orkspace', _ = 'which_key_ignore' },
        ['<leader>t'] = { name = '[T]oggle', _ = 'which_key_ignore' },
        ['<leader>h'] = { name = 'Git [H]unk', _ = 'which_key_ignore' },
      }
      -- visual mode
      require('which-key').register({
        ['<leader>h'] = { 'Git [H]unk' },
      }, { mode = 'v' })

改成:

      require('which-key').register {
        ['<leader>c'] = { name = '[C]ode', _ = 'which_key_ignore' },
        ['<leader>d'] = { name = '[D]ocument', _ = 'which_key_ignore' },
        ['<leader>r'] = { name = '[R]ename', _ = 'which_key_ignore' },
        ['<leader>s'] = { name = '[S]earch', _ = 'which_key_ignore' },
        ['<leader>w'] = { name = '[W]orkspace', _ = 'which_key_ignore' },
        ['<leader>t'] = { name = '[T]oggle', _ = 'which_key_ignore' },
        ['<leader>g'] = { name = '{G]it Hunk', _ = 'which_key_ignore' },
        ['<leader>b'] = { name = '[B]uffer', _ = 'which_key_ignore' },
      }
      -- visual mode
      require('which-key').register({
        ['<leader>g'] = { '[G]it Hunk' },
      }, { mode = 'v' })

启用内置插件

kickstart 还有一些内置的插件默认没有启用,我想要启用 autopairs 和 neo-tree 这两个插件,autopairs 可以自动补全括号、双引号这些成对的符号,而 neo-tree 则可以在侧边栏显示一个文件树用来管理文件,按下反斜杠 \ 就可以显示或隐藏文件树。


要启用这两个插件也很简单,编辑 lua/lazy-plugins.lua,找到以下两行并取消注释:

  -- require 'kickstart.plugins.autopairs',
  -- require 'kickstart.plugins.neo-tree',

另外我还想要禁用一个插件 mini.nvim,这是个很多功能简单的小插件的集合,目前下面的状态栏就是用它实现的,不过我后面打算用其他插件来代替它,所以先将它禁用了,找到 require 'kickstart/plugins/mini', 并将其注释掉。

之后重新打开 Neovim,就会自动开始下载缺失的插件,之后输入 :Lazy 回车打开 Lazy 的主界面,会看到 mini.nvim 已经被禁用了,按大写 X 就可以将禁用的插件卸载。

此外,kickstart 还有其他插件默认没有启用,比如 nvim-dap 用来 debug,indent-blankline 用来显示缩进,nvim-lint 用来语法检查。

自动补全

kickstart 使用 nvim-cmp 插件进行自动补全,这个插件的配置项十分丰富,kickstart 默认只配置了 lsp 与文件系统路径作为补全来源,虽然配置了 Luasnip 来补全代码片段,但没有启用代码片段的来源,而且 kickstart 对 nvim-cmp 的快捷键设定我也不太习惯。所以对这个插件的配置修改会比较复杂。

编辑 lua/kickstart/plugins/cmp.lua,首先添加更多补全来源,找到下面的内容并取消注释,这启用了 friendly-snippets 插件,包含了很多常用的适用于各种文件格式的代码补全片段:

          -- {
          --   'rafamadriz/friendly-snippets',
          --   config = function()
          --     require('luasnip.loaders.from_vscode').lazy_load()
          --   end,
          -- },

另外我还添加了一些我自己写的代码片段,位于 ~/.config/nvim/snippets,主要是在 Markdown 文档里方便地输入直角引号,具体的内容可以看这里。所以修改完之后应该是这样的:

          {
            'rafamadriz/friendly-snippets',
            config = function()
              require('luasnip.loaders.from_vscode').lazy_load()
              require('luasnip.loaders.from_vscode').lazy_load { paths = { '~/.config/nvim/snippets' } }
            end,
          },

之后再在插件列表中添加两个插件分别用来补全来自 buffer 中的内容和 vim 命令行的内容:

      'hrsh7th/cmp-buffer',
      'hrsh7th/cmp-cmdline',

再在文件最后添加刚刚的补全源:

        sources = {
          { name = 'nvim_lsp' },
          { name = 'luasnip' },
          { name = 'path' },
          { name = 'buffer' },
        },
        -- 前面三个是之前就有的,最后的 buffer 是要添加的

如果你想在 vim 命令行中或使用 / 搜索内容时显示补全,则需要添加以下几行:

      -- Use buffer source for `/` and `?` (if you enabled `native_menu`, this won't work anymore).
      cmp.setup.cmdline({ '/', '?' }, {
        mapping = cmp.mapping.preset.cmdline(),
        sources = {
          { name = 'buffer' },
        },
      })

      -- Use cmdline & path source for ':' (if you enabled `native_menu`, this won't work anymore).
      cmp.setup.cmdline(':', {
        mapping = cmp.mapping.preset.cmdline(),
        sources = cmp.config.sources({
          { name = 'path' },
        }, {
          { name = 'cmdline' },
        }),
        matching = { disallow_symbol_nonprefix_matching = false },
      })

默认 nvim-cmp 只会在进入 Insert 模式后才会启用,要想让它在 vim 命令行下也可以自动启用,需要添加相关的 event,找到 event = 'InsertEnter', 这一行,改成 event = { 'InsertEnter', 'CmdlineEnter' },,这样 nvim-cmp 就会在进入 vim 命令行模式时自动启用了。

接着是自定义快捷键,kickstart 使用 ctrl+nctrl+p 来上下选择不同的补全项,用 ctrl+y 来选中补全项,对于代码片段,使用 ctrl+lctrl+h 来跳转上一个和下一个占位符。如果你可以接受这个快捷键设定,则无需折腾了,不过我更习惯使用 TabShift+Tab 来选择补全项,回车来选中补全项。好在这种快捷键在 nvim-cmp 的文档里面有教程。首先禁用先前定义的快捷键,找到下面这些行,将其注释掉:

          ['<C-n>'] = cmp.mapping.select_next_item(),

          ['<C-p>'] = cmp.mapping.select_prev_item(),

          ['<C-y>'] = cmp.mapping.confirm { select = true },

          ['<C-l>'] = cmp.mapping(function()
            if luasnip.expand_or_locally_jumpable() then
              luasnip.expand_or_jump()
            end
          end, { 'i', 's' }),
          ['<C-h>'] = cmp.mapping(function()
            if luasnip.locally_jumpable(-1) then
              luasnip.jump(-1)
            end
          end, { 'i', 's' }),
          -- 这些行在配置文件中应该是分散开的

然后添加新的快捷键:

          ['<CR>'] = cmp.mapping(function(fallback)
            if cmp.visible() then
              if luasnip.expandable() then
                luasnip.expand()
              else
                cmp.confirm {
                  select = true,
                }
              end
            else
              fallback()
            end
          end),

          ['<Tab>'] = cmp.mapping(function(fallback)
            if cmp.visible() then
              cmp.select_next_item()
            elseif luasnip.locally_jumpable(1) then
              luasnip.jump(1)
            else
              fallback()
            end
          end, { 'i', 's' }),

          ['<S-Tab>'] = cmp.mapping(function(fallback)
            if cmp.visible() then
              cmp.select_prev_item()
            elseif luasnip.locally_jumpable(-1) then
              luasnip.jump(-1)
            else
              fallback()
            end
          end, { 'i', 's' }),

再之后还可以自定义补全菜单的外观,比如说我想在补全菜单中显示图标,首先在插件列表中添加 onsails/lspkind.nvim 这个插件,之后在 config = function() 这个函数里面添加一行 local lspkind = require('lspkind'),并在 cmp.setup {} 这个 lua table 里面添加如下内容:

        formatting = {
          format = lspkind.cmp_format {
            mode = 'symbol_text',
            menu = {
              buffer = '[Buffer]',
              nvim_lsp = '[LSP]',
              path = '[Path]',
              luasnip = '[Snippet]',
              cmdline = '[Cmdline]',
            },
          },
        },

其中的 mode 可以选择 texttext_symbolsymbol_text 以及 symbol,分别对应只显示文字、先文字后图标、先图标后文字以及只显示图标,menu 里面是自定义各个补全来源显示的名字。

另外我还配置了显示 Ghost Text,即在光标后面显示预补全内容:

        experimental = {
          ghost_text = true,
        },

如果觉得这部分的配置太过复杂,这里是我完整的 nvim-cmp 的配置文件,直接抄作业就行了。

通过 nvim-cmp,我可以在编辑 Markdown 文档时快速地输入一些内容,比如各级标题、图片、链接、表格等等

LSP 配置

LSP 的全称是「Language Server Protocol」,是微软开发并开源的一个协议,简单来说就是可以让 Language Server 与编辑器相互沟通,从而为代码提供补全、诊断、Code Action 等等接近完整 IDE 的功能。Neovim 原生支持 LSP,kickstart 使用 nvim-lspconfig 插件来配置 Language Server,用 Mason.nvim 插件来自动安装 Language Server。

kickstart 默认为 lua 文件配置了 Lua_ls 和 stylua 作为 Language Server。在上一节配置完 nvim-cmp 后,你会发现在配置文件中出现了一些 Missing fileds 警告:


但实际上这个警告是不影响功能的,如果觉得这个警告比较恼人,可以选择禁用它,编辑 lua/kickstart/plugins/lspconfig.lua,找到下面一行取消注释即可:

              -- diagnostics = { disable = { 'missing-fields' } },

如果为其他语言配置 Language Server,只需运行 :Mason 回车打开 Mason 插件的界面,选择自己想要安装的 Language Server 安装即可,或者也可以在配置文件中指定自动安装:

      vim.list_extend(ensure_installed, {
        'stylua', -- Used to format Lua code
        'marksman', -- Used for markdown file
      })

另外也可以自定义代码诊断的图标 ,在 config = function() 后面添加下面这几行:

      vim.diagnostic.config {
        virtual_text = false,
        signs = true,
        underline = true,
        update_in_insert = false,
        severity_sort = false,
      }

      -- Diagnostics icons
      local signs = { Error = '󰅚 ', Warn = '󰀪 ', Hint = '󰌶 ', Info = ' ' }
      for type, icon in pairs(signs) do
        local hl = 'DiagnosticSign' .. type
        vim.fn.sign_define(hl, { text = icon, texthl = hl, numhl = hl })
      end

更多 LSP 相关的自定义可以查看 nvim-lspconfig 的 Wiki

因为我并不是程序员,LSP 对我来说用处并不大,所以我只打算在一些特定的文件中启用它,在配置文件中添加一行 ft = { 'markdown', 'lua' },,会让插件只在 Markdown 文件和 Lua 文件中启用,在其他情况下禁用,可以提升一点启动速度。在 Lua 中启用可以让我更好地编辑 Neovim 配置文件,而在 Markdown 中则可以用 marksman 提供的 Code Action 来方便地添加标题目录。

文档格式化

在编辑代码或是文档时,虽然一些格式上的不标准可能并不会影响最终的效果,但对于其他人来说可读性就要差很多,但是大部分时候我们可能没有精力去关心格式是否标准,或者出现了格式错误也很难及时发现,这时就需要借助一些工具来格式化文档了。

kickstart 使用 conform.nvim 插件来格式化文档,并通过 Mason 插件来自动安装格式化工具,conform 支持的工具列表可以看这里 。默认已经配置了使用 stylua 来为 lua 文件格式化,在编辑完 lua 文件后,保存时便可自动格式化,也可以使用快捷键 空格+f 来手动格式化。

如果不太喜欢在每次保存时自动格式化,可以选择禁用这个功能,编辑 lua/kickstart/plugins/conform.lua 文件,找到下面的内容注释掉:

      format_on_save = function(bufnr)
        -- Disable "format_on_save lsp_fallback" for languages that don't
        -- have a well standardized coding style. You can add additional
        -- languages here or re-enable it for the disabled ones.
        local disable_filetypes = { c = true, cpp = true }
        return {
          timeout_ms = 500,
          lsp_fallback = not disable_filetypes[vim.bo[bufnr].filetype],
        }
      end,

并替换为:

      format_on_save = false,

我另外还为 Markdown 文档配置了使用 autocorrect 来进行格式化,可以实现在中英文混输的文档里面为中英文之间自动添加空格,更正标点符号等操作:

      formatters_by_ft = {
        lua = { 'stylua' },
        markdown = { 'autocorrect' },
        -- Conform can also run multiple formatters sequentially
        -- python = { "isort", "black" },
        --
        -- You can use a sub-list to tell conform to run *until* a formatter
        -- is found.
        -- javascript = { { "prettierd", "prettier" } },
      },

不过 autocorrect 目前不支持使用 Mason 来自动安装,需要手动将其安装到当前系统的 $PATH 里面,具体可以参考项目 README 的安装部分。另外我还发现使用 autocorrect 最新的 2.11.1 版本,在每次格式化后文件的最后会多出一个空行,而上一个版本 2.10.0 就没这个问题,如果介意的话,可以选择安装旧版本。

为了更快地启动 Neovim,我还配置了让 conform 只在 Lua 和 Markdown 文件中启用,找到 lazy = false, 一行,将其注释掉,并在其后面添加一行:ft = { 'lua', 'markdown' },

懒加载更多插件

kickstart 使用 Lazy.nvim 来管理插件,从其名字也可以推测出来,这个插件管理器一大特色功能就是懒加载,Lazy 对插件懒加载的配置可谓十分灵活,可以配置让插件在不同的情况下懒加载。让插件只在必须的时候进行加载,可以很大程度上提升 Neovim 的启动速度。

kickstart 默认已经为一些插件配置了懒加载,还有些插件我前文的配置过程中也按照我自己的使用情况配置了懒加载,不过还有一些插件默认没有配置懒加载,虽然其中大部分插件对启动速度影响并不大,但积少成多还是能省出一点启动时间的。

首先是 vim-sleuth 和 Comment.nvim,这两个插件大概是作为演示作用,没有配置懒加载,也没有放进单独的配置文件,而是直接写在 lua/lazy-plugins.lua 里面。

vim-sleuth 插件可以自动检测并调整当前文本的 tab 缩进,所以实际上只有在加载 buffer 或是 新建 buffer 的时候才真正有用,所以找到 'tpope/vim-sleuth', 这一行,改成:

  {
    'tpope/vim-sleuth', -- Detect tabstop and shiftwidth automatically
    event = { 'BufReadPre', 'BufNewFile' },
  },

Comment.nvim 插件可以快捷地对文档进行注释,这个功能在前文已经介绍过了,我自己实际上也只用到了它的 gc 快捷键,所以我打算让它只在按下这个快捷键的时候再启用,找到 { 'numToStr/Comment.nvim', opts = {} }, 一行,改成:

  {
    'numToStr/Comment.nvim',
    keys = { 'gc' },
    opts = {},
  },

todo-comments.nvim 这个插件可以自动高亮代码中包含 TODONOTE 等关键词的注释段落,让注释更有可读性,这个插件也只有在加载 buffer 的时候才有用,编辑 lua/kickstart/plugins/todo-comments.lua,找到 event = 'VimEnter', 改成 event = { 'BufRead', 'BufNewFile' },

gitsigns.nvim 插件提供了 git 的集成,也只有在加载 buffer 的时候才有用,编辑 lua/kickstart/plugins/gitsigns.lua 添加一行:event = { 'BufReadPre', 'BufNewFile' },

which-key.nvim 插件可以帮助显示快捷键,所以在不使用快捷键的时候自然也用不到,编辑 lua/kickstart/plugins/which-key.lua,将 event = 'VimEnter', 改成 event = 'VeryLazy',。这个的意思大概是在其他东西都加载完之后再加载它,我也是从其他人的配置里抄来的。

nvim-treesitter 插件主要用来显示代码高亮,我为它配置了只在加载 buffer 的时候启用,同样加一行 event = { 'BufReadPre', 'BufNewFile' }, 即可。

telescope.nvim 是一个类似于 Fzf 的模糊查找器,但是功能更强大,telescope 与 kickstart 是同一个作者写的(同时这个作者也是 Neovim 项目的主要维护者之一),自然 kickstart 中有很多功能都用到了 telescope,打开 Lazy 的主界面也可以看到 telescope 的启动占用了挺长的时间。为了让 telescope 实现懒加载而不影响到现有的功能,需要将 telescope 的快捷键重新注册一遍,编辑 lua/kickstart/plugins/telescope.lua,找到 event = 'VimEnter', 并将其注释掉,并在后面添加如下内容:

    keys = {
      { '<leader>sh', desc = '[S]earch [H]elp' },
      { '<leader>sk', desc = '[S]earch [K]eymaps' },
      { '<leader>sf', desc = '[S]earch [F]iles' },
      { '<leader>ss', desc = '[S]earch [S]elect Telescope' },
      { '<leader>sw', desc = '[S]earch current [W]ord' },
      { '<leader>sg', desc = '[S]earch by [G]rep' },
      { '<leader>sd', desc = '[S]earch [D]iagnostics' },
      { '<leader>sr', desc = '[S]earch [R]esume' },
      { '<leader>s.', desc = '[S]earch Recent Files ("." for repeat)' },
      { '<leader><leader>', desc = '[ ] Find existing buffers' },
      { '<leader>/', desc = '[/] Fuzzily search in current buffer' },
      { '<leader>s/', desc = '[S]earch [/] in Open Files' },
      { '<leader>sn', desc = '[S]earch [N]eovim files' },
    },

意思是 telescope 只会在按下上面这些快捷键的时候才会启用,并添加了功能描述方便 which-key 识别。

经过上面这些配置,我这个性能相当弱鸡的笔记本,Neovim 的启动速度也可以控制在 50ms 以下了。

添加更多插件

为了满足我的使用习惯,我还需要添加更多插件,好在 kickstart 安装额外插件也比较简单,只要编辑 lua/lazy-plugins.lua 文件,找到 -- { import = 'custom.plugins' }, 一行取消注释,之后将想要添加的插件配置放到 lua/custom/plugins 文件夹里,重新启动 Neovim,就可以自动安装了。

主题配色

使用什么样的主题配色这个完全是看个人喜好了,kickstart 默认使用了 tokyonight 主题配色,不过我个人更喜欢 Gruvbox 配色,要使用新的主题配色,首先禁用原来的的 tokyonight 主题,编辑 lua/lazy-plugins.lua 文件,找到 require 'kickstart/plugins/tokyonight', 并注释掉。然后添加新的主题插件,新建 lua/custom/plugins/gruvbox.lua 文件,然后重启 Neovim 即可:

return {
  {
    'ellisonleao/gruvbox.nvim',
    priority = 1000, -- Make sure to load this before all the other start plugins.
    init = function()
      vim.o.background = 'dark' -- or "light" for light mode
      vim.cmd [[colorscheme gruvbox]]
    end,
  },
}

但是这样你会发现虽然大部分时候 Neovim 都是用的 Gruvbox 主题,在 Lazy 在安装新插件的时候还是用的之前的主题配色,这是因为 Lazy 默认在安装插件时使用了不同的主题配色,这可能是为了在主题插件还没准备好时防止 Neovim 的默认配色闪瞎眼。你也可以配置 Lazy 在安装插件时使用的主题配色,编辑 lua/lazy-plugins.lua 文件,在最后一个括号前插入一行:

  install = { colorscheme = { 'gruvbox' } },

当然如果 gruvbox 插件没装的话这个设置是不起作用的。

状态栏

在前面的内容里面我禁用了 mini.nvim 插件,其中就包括状态栏插件,我要用另一个状态栏插件来代替它,就是 lualine.nvim。新建 lua/custom/plugins/lualine.lua

return {
  'nvim-lualine/lualine.nvim',
  dependencies = { 'nvim-tree/nvim-web-devicons' },
  event = "VeryLazy",
  config = function()
    require('lualine').setup {
      options = {
        theme = 'gruvbox',
      },
      sections = {
        lualine_a = { 'mode' },
        lualine_b = { 'branch', 'diff', 'diagnostics' },
        lualine_c = { 'filename', 'lsp_progress' },
        lualine_x = {
          {
            require('lazy.status').updates,
            cond = require('lazy.status').has_updates,
            --color = { fg = "#a89984" },
          },
          'encoding',
          'fileformat',
          'filetype',
        },
        lualine_y = { 'progress' },
        lualine_z = { 'location' },
      },
      tabline = {
        lualine_a = { 'tabs' },
        lualine_b = { 'buffers' },
        lualine_c = {},
        lualine_x = {},
        lualine_y = {},
        lualine_z = {},
      },
    }
  end,
}

除了常规的底部状态栏,我还在顶部添加了标签栏和 buffer 栏,方便结合前面设定的快捷键快速跳转 buffer。

另外我还添加了一个功能用来在状态栏上显示当前有多少插件可以更新,我也忘了是在哪里抄来的了,不过要想这个功能可用,还需要为 Lazy 启用自动检查更新,编辑 lua/lazy-plugins.lua,在最后一个括号前插入一行:

  checker = { enabled = true, frequency = 600, notify = false },

这个的意思是启用自动检查更新,10 分钟检查一次,并禁用更新通知(因为有更新的话会在状态栏上显示)。

更多的配置项可以查看 lualine 的文档

输入法切换

使用 Vim 编辑中文时,最恼人的一点就是输入法问题,在 Insert 模式输入完中文并退出到 normal 模式后,必须将输入法手动切换到英文模式才可以正常使用,不过还好这个问题也有很多插件可以解决,我目前正在用的插件是 vim-barbaric,可以支持在退出 Insert 模式时自动切换到英文输入法,进入 Insert 模式又切换回中文,我用的 Fcitx5 输入法,可以完美支持,其他输入法请自行测试。新建 lua/custom/plugins/barbaric.lua

return {
  'rlue/vim-barbaric',
  event = 'InsertEnter',
}

我配置了懒加载,让它在进入 Insert 模式时再启用。

快速跳转

使用 Vim 编辑中文另一个让人头疼的问题是几乎完全不支持中文分词,Vim 快捷键用来跳转上一个与下一个单词的快捷键 wb,在中文文档中会直接跳转一整句,要想在中文中实现指哪打哪的快速跳转,就需要借助 vim-easymotionvim-easymotion-zh 这两个插件了。vim-easymotion 实现了在 Vim 中搜索并快速跳转,而 vim-easymotion-zh 则在其基础上实现了使用双拼搜索中文。新建 lua/custom/plugins/easymotion.lua

return {
  'zzhirong/vim-easymotion-zh',
  dependencies = { 'easymotion/vim-easymotion' },
  config = function()
    vim.cmd [[
    let g:EasyMotion_leader_key=";"
    let g:EasyMotion_skipfoldedline=0
    let g:EasyMotion_space_jump_first=1
    let g:EasyMotion_move_highlight = 0
    let g:EasyMotion_use_migemo = 1
    ]]
  end,
  keys = {
    {
      '<leader>sp',
      '<Plug>(easymotion-overwin-f2)',
      mode = 'n',
      desc = 'Search Current Page',
    },
  },
}

我定义了一个快捷键 空格+sp,按下快捷键后,输入要跳转内容的前两个英文字母,或是中文的小鹤双拼码,比如「键」这个字就是「jm」,输入后会显示当前页面所有符合搜索结果的位置,按下对应的按键就可快速跳转。当然这个功能只有比较熟悉小鹤双拼的人才能用的比较顺手吧,惯用其他双拼方案的人可以在原作者基础上进行修改,如果根本不熟悉双拼,则可以看看 vim-easymotion-chs

记住光标位置

Neovim 默认不会记住上次关闭文件时光标所在的位置,所以每次打开文件时光标都会在第一行,这有时挺烦人的,为了让 Neovim 记住上一次光标的位置,可以添加 vim-lastplace 这个插件,新建 lua/custom/plugins/lastplace.lua

return {
  'farmergreg/vim-lastplace',
  event = { 'BufReadPre', 'BufNewFile' },
}

处理大文件

在打开一些比较大的文档时,一些插件,比如 lspconfig 和 treesitter,可能会拖慢 Neovim 的加载速度,严重时会导致 Neovim 卡死,bigfile.nvim 这个插件会在打开大文件时自动禁用一些会拖慢 Neovim 的功能,从而加快 Neovim 的加载,新建 lua/custom/plugins/bigfile.lua

return {
  'LunarVim/bigfile.nvim',
  event = { "FileReadPre", "BufReadPre", "User FileOpened" },
}

这个配置是我从 LunarVim 项目里抄来的,触发文件的大小与禁用哪些功能都可以单独配置,具体可以看项目的文档。

Markdown 预览

markdown-preview.nvim 插件可以在编辑 Markdown 文档时打开一个浏览器页面实时显示文档渲染效果,实现所见即所得。新建 lua/custom/plugins/markdown-preview.lua

return {
  'iamcco/markdown-preview.nvim',
  build = function()
    vim.fn['mkdp#util#install']()
  end,
  ft = 'markdown',
  config = function()
    vim.g.mkdp_filetypes = { 'markdown' }
    vim.cmd [[let g:mkdp_browser = 'firefox']]
  end,
  keys = {
    {
      '<leader>m',
      ':MarkdownPreviewToggle<CR>',
      mode = '',
      desc = '[M]arkdown Preview',
    },
  },
}


这里我配置了使用 firefox 来预览 Markdown 文档,定义了一个快捷键 空格+m 来快速打开预览。不过需要注意的是,插件第一次安装好后还需要下载额外文件,如果下载失败,会导致功能不可用。

包裹符号

vim-surround 插件可以方便地删除、添加以及修改包裹符号,包括括号、引号等等,比如我常用的一个功能,选中一个词,按大写 S,再按下想要将其包裹的符号,比如引号,就可以快速将其包裹上引号。添加 lua/custom/plugins/surround.lua

return {
  'tpope/vim-surround',
  event = { 'CursorMoved', 'CursorMovedI' },
}

更多这个插件的高级用法,可以查看项目的文档。

更好的修改历史预览

在 Vim 中在 Normal 模式下按下 u 就可以撤销之前的修改,而若是想要更直观地查看自己的修改历史并快速撤销修改,就需要 undotree 这个插件了,这个插件在侧边显示一个菜单用来显示之前的修改历史,并可以在不同的修改历史之间快速跳转。添加 lua/custom/plugins/undotree.lua

return {
  'mbbill/undotree',
  keys = {
    {
      '<leader>u',
      ':UndotreeToggle<CR>',
      mode = 'n',
      desc = 'Toggle Undo Tree',
    },
  },
}

我定义了一个快捷键 空格+u 用来显示或隐藏侧边菜单。

打开终端

在编辑文本时,有时需要临时打开终端执行一些命令,这时就需要退出编辑器,执行完命令后再重新打开编辑器,这就有些麻烦了,而 toggleterm.nvim 这个插件就解决了这个问题,这个插件允许在 Neovim 中临时打开一个终端。新建 lua/custom/plugins/toggleterm.lua

return {
  'akinsho/toggleterm.nvim',
  config = function()
    require('toggleterm').setup {
      size = 20,
      open_mapping = [[<c-\>]],
      direction = 'float',
      float_opts = {
        border = 'curved',
      },
    }

    function _G.set_terminal_keymaps()
      local opts = { buffer = 0 }
      vim.keymap.set('t', 'jk', [[<C-\><C-n>]], opts)
    end

    -- if you only want these mappings for toggle term use term://*toggleterm#* instead
    vim.cmd 'autocmd! TermOpen term://* lua set_terminal_keymaps()'
  end,
  keys = [[<c-\>]],
  cmd = 'ToggleTerm',
}

这个插件默认使用 ctrl+\ 来打开终端,或者也可以用命令 ToggleTerm 来打开终端,使用快捷键 jk 来进入「vim 模式」,从而用 vim 快捷键复制当前终端中的任意内容,快捷键 i 回到终端模式。而且在打开的终端中,还可以再套娃打开一个 Neovim,只要屏幕够大,就可以无限套娃下去,虽然这样并没有什么用就是了。

结尾

我目前已经将我的 Neovim 配置完全迁移到了基于 kickstart 的配置,相比于旧的配置文件,新的配置不但有更丰富的功能,还有更清晰的结构,方便我后期维护。

这里是我完整的配置文件,想要抄作业的朋友可以直接来看,不过还是推荐在 kickstart 的基础上进行自定义,这样更符合自己的使用习惯,而且能对 Neovim 配置文件的结构有更好的理解。