对目前配置使用的Neovim做一个小结回顾梳理。
NeoVim
使用的是v0.9版本。
|
|
插件管理器
插件管理器多种选择,如:packer.nvim、wbthomason/packer.nvim、savq/paq-nvim、NTBBloodbath/cheovim,我选用的是vim-plug(原因是他很常见,刚接触插件安装时遇到的就是它,后面也没有再做调整)。
后续进行启动时间优化时,将插件管理改为了 packer.nvim,并改用纯 lua 进行配置 neovim。 使用纯lua进行一些优化后,成功将启动时间从214.900ms 降低到了121.062ms。 优化方向:
- 使用效率更好的插件进行替代;
- 延迟加载插件;
- 使用基于Lua编写的插件;
- 使用纯lua替代VimScript进行配置NeoVim;
- 缓存lua模块编译 lewis6991/impatient ;
- 设置Lazyredraw;
- 删除或者禁用不用的插件;
- 禁用一些内置插件
最后,我将iterm2 更换成alacritty + tmux + Hammerspoon , Neovim 平均启动时间进一步降低到了98ms。 在使用 Packer 延迟加载功能,对相关插件进行延迟加载;使用filetype.nvim 优化
filetype.vim
在启动过程中的行为,将平均启时间降低到42ms
插件
- 插件管理器 -
vim-plugpacker.nvimlazy.nvim - 目录管理 -
NERDTreenvim-tree - 文件模糊搜索 - telescope-fzf-native
- 代码缩略图 -
tagbarsymbols-outline.nvim - 主题配色 -
molokai.nvimMofiqul/dracula.nvimMofiqul/vscode.nvim - 状态栏 -
lualinefeline.nvim - 启动屏 -
vim-stratifyalpha-nvim - LSP - lspconfig
- 可移植包管理器(LSP、DAP、linter、formatter)- williamboman/mason.nvim
- LSP函数签名提示 ray-x/lsp_signature.nvim
- Linter mfussenegger/nvim-lint
- 控制台 - toggleterm
- GIT -
fugitive、lazygit(注:lazygit 通过toggleterm进行集成) - 注释 - numToStr/Comment.nvim
- 自动括号补全 - jiangmiao/auto-pairs
- Blame text -
gitsignsf-person/git-blame.nvim - 代码自动补全 -
coq_nvimhrsh7th/nvim-cmp - 代码片段引擎 -
nvim-snippyLuaSnip - 代码片段源 -
vim-snippetsFriendly Snippets - 图标 - kyazdani42/nvim-web-devicons
- 代码高亮着色 - nvim-treesitter
- 空白缩进高亮 - lukas-reineke/indent-blankline.nvim
- 十六进制文本颜色高亮 - norcalli/nvim-colorizer.lua
- 高亮光标下文本 - rRethy/vim-illuminate
yamatsum/nvim-cursorline - 标题栏 - bufferline.nvim
- trouble窗口 - trouble.vim
- 当前工作目录 -
vim-rooterahmedkhalf/project.nvim - 改善启动时间 - impatient.nvim
- 调试器 - puremourning/vimspector nvim-dap
- Go IDE - go.nvim
- 测试 - vim-test
vim-ultestnvim-neotest - 预览Markdown - markdown-preview.nvim
- TODO注释- folke/todo-comments.nvim
- 通知管理器- rcarriga/nvim-notify
- 快速更改分隔符 - kylechui/nvim-surround
字体
使用的是Nerd Fonts中的Blex*(详见:https://github.com/ryanoasis/nerd-fonts#option-4-homebrew-fonts) 。macOS平台上 Nerd Fonts所有字体在Homebrew Cask Fonts 都是可用的:
|
|
Awesome
- Awesome Neovim - Collections of awesome Neovim plugins. Mostly targeting Neovim specific features.
- Vimawesome - Showcases various plugins for vim and has a neovim tag for other plugins targeting Neovim.
- awesome-vim - Short list of vim plugins and helpful guides.
小技巧
文档操作
:e
- 重新加载当前文档:w
- 保存修改:wq
- 保存并退出ZZ
- 保存并退出:x
- 保存并退出:q[uit]
- 退出当前窗口(CTRL-W q或CTRL-W CTRL-Q):gf
- 跳转到当前光标下文字对应的文件vim -o file1.txt file2.txt
- 同时打开文件,水平分割窗口vim -O file1.txt file2.txt
- 同时打开文件,垂直分割窗口
切分窗口
<CTRL+w>+s
- 水平切分当前窗口,新窗口仍显示当前缓冲区<CTRL+w>+v
- 垂直切分当前窗口,新窗口仍显示当前缓冲区:sp[lit] {file}
- 水平切分当前窗口, 并在新窗口中载入{file}:vsp[lit] {file}
- 垂直切分当前窗口, 并在新窗口中载入{file}
更改分割窗口大小
<CTRL-w>-
- 将分割窗口高度减少一行<CTRL-w>+
- 高度增加一行<CTRL-w>>
- 将分割窗口宽度增加一行<CTRL-w><
- 宽度减少一行
关闭窗口
Ex 命令 | 普通模式命令 | 用途 |
---|---|---|
:clo[se] |
<CTRL+w>+c |
关闭活动窗口 |
:on[ly] |
<CTRL+w>+o |
只保留活动窗口,关闭其他所有窗口 |
切换窗口
<CTRL+w>w
- 在窗口见循环切换<CTRL+w>h
- 切换到左边窗口<CTRL+w>l
- 切换到右边窗口<CTRL+w>k
- 切换到上边窗口<CTRL+w>j
- 切换到下边窗口
缓存区
:buffers
、:files
、:ls
- 列出当前缓冲区,默认情况下列出可以显示的缓存区:ls!
- 列出所有缓存区,包括不显示的:bNext
- 切换到下一个缓冲区:bprevious
- 切换到上一个缓冲区:blast
- 切换到最后一个缓冲区:bfirst
- 切换到第一个缓冲区:bdelete
- 删除缓冲区:bunload
- 卸载缓冲区,为缓冲区打开的窗口也会关闭,但文件名仍保留在缓冲区列表中:badd
- 添加缓冲区
移动
H
- 把光标移到屏幕最顶端一行M
- 把光标移到屏幕中间一行L
- 把光标移到屏幕最底端一行gg
- 到文件头部G
- 到文件尾部
基本插入
i
- 在光标前插入;一个小技巧:按8,再按i,进入插入模式,输入=, 按esc进入命令模式,就会出现8个=。 这在插入分割线时非常有用,如30i+就插入了36个+组成的分割线 I
- 在当前行第一个非空字符前插入I
- 在当前行第一列插入a
- 在光标后插入A
- 在当前行最后插入o
- 在下面新建一行插入O
- 在上面新建一行插入:r filename
- 在当前位置插入另一个文件的内容:[n]r filename
- 在第n行插入另一个文件的内容:r !date
- 在光标处插入当前日期与时间。同理,:r !command可以将其它shell命令的输出插入当前文档
搜索
*
- 向下搜索光标所在词g*
- 同上,但部分符合即可#
- 向上搜索光标所在词g#
- 同上,但部分符合即可g CTRL-g
- 统计全文或统计部分的字数
改写
c[n]w
- 改写光标后1(n)个词c[n]l
- 改写光标后n个字母c[n]h
- 改写光标前n个字母[n]cc
- 修改当前[n]行C
- C可更改光标所在位置到此行结尾间的文本。它的功用和c加上特殊行末指示符$的效果―样(c$)[n]s
- 以输入的文本替代光标之后1(n)个字符,相当于c[n]l[n]S
- 删除指定数目的行,并以所输入文本代替之r
- 把一个字符替换成宁外一个,结束后不需要按ESC
回到命令模式
全局替换
s/old/new
- 将当前行第一个出现的模式 old 改为 news/old/new/g
- 将当前行每一个 old 改为 new50,100s/old/new/g
- 将第50行到第100行之间每一个 old 修改为 new%s/old/new/g
- 全局替换每一个出现的 old 为 new%s/bacsdfws/&,HELLO/
- 将每一行的第一个 bacsdfws 替换为 bacsdfws,HELLO 。&
在模式匹配替换字符串时,会被替换成搜索模式匹配出的完整文本,这可以避免重复输入要被替换的文本%s/\(foo\)hello\(world\)/\1 \2/g
将每一行的foohelloworld
替换为fooworld
.\(foo\)
对foo
反向引用\&
插入搜索模式匹配的文本\0
插入与整个模式匹配的文本\1
插入第一个反向引用的文本\2
插入第二个反向引用,\3
,\4
依次类推
s#/usr/local/bin#/usr/sbin#g
- 将行中所有的usr/local/bin
替换成/usr/sbin
,vim/neovim 允许在在模式替换中使用任何正则表达式 分隔符,根本不需要使用/
,使用#
进行替代,这样就可以避免转义斜线
删除
dw
- dw会删除光标所在位置的单词,请注意单词后的空白也会被删除dd
或D
- 删除当前光标所在行d0
- 从光标位置删除当前光标所在行的开头d$
- 从光标位置删除当前光标所在行的结尾x
- 它只会删除光标所在位置的字符:g/re/d
- 删除所有的匹配行,global 命令允许在某个指定模式的所有匹配行上运行Ex命令::[range] global[!] /{pattern}/ [cmd]
:v/re/d
- 只保留匹配行:g/^$/d
- 删除掉所有的空白行:g/^#/d
- 删除所有#
开头的行
重复上一个命令
.
- “想要重复相同的编辑命令时,可以使用重复命令——句号(.),以节省时间”
撤销
u
- “以撤销上一个命令只要按下u即可,光标不需要在原来下命令时所在的位置”
缩进操作
>>
- 向右缩进<<
- 向左缩进
翻屏
ctrl+f
- 下翻一屏ctrl+b
- 上翻一屏ctrl+d
- 下翻半屏ctrl+u
- 上翻半屏ctrl+e
- 向下滚动一行ctrl+y
- 向上滚动一行n%
- 到文件n%的位置zz
- 将当前行移动到屏幕中央zt
- 将当前行移动到屏幕顶端zb
- 将当前行移动到屏幕底端
编程辅助
[[
- 跳转到上一个函数块开始,需要有单独一行的{]]
- 跳转到下一个函数块开始,需要有单独一行的{[]
- 跳转到上一个函数块结束,需要有单独一行的}][
- 跳转到下一个函数块结束,需要有单独一行的}ci’, di’, yi’
- 修改、剪切或复制’之间的内容ca’, da’, ya’
- 修改、剪切或复制’之间的内容,包含’ci”, di”, yi”
- 修改、剪切或复制”之间的内容ca”, da”, ya”
- 修改、剪切或复制”之间的内容,包含”ci(, di(, yi(
- 修改、剪切或复制()之间的内容ca(, da(, ya(
- 修改、剪切或复制()之间的内容,包含()ci[, di[, yi[
- 修改、剪切或复制[]之间的内容ca[, da[, ya[
- 修改、剪切或复制[]之间的内容,包含[]ci{, di{, yi{
- 修改、剪切或复制{}之间的内容ca{, da{, ya{
- 修改、剪切或复制{}之间的内容,包含{}ci<, di<, yi<
- 修改、剪切或复制<>之间的内容ca<, da<, ya<
- 修改、剪切或复制<>之间的内容,包含<>va'
- 选中’之间的内容,包括`vi'
- 选中’之间的内容xp
- 交换两个字母位置:23t.
- 复制第23行且将其粘贴到当前行的下一行:t23
- 复制当前行到第23行的下一行
修改大小写
Vu
- 整行小写guu
- 整行小写VU
- 整行大写gUU
- 整行大写~
- 反转光标所在字符的大小写
忽略大小写
If the ignorecase option is on, the case of normal letters is ignored. smartcase can be set to ignore case when the pattern contains lowercase letters only. When \c appears anywhere in the pattern, the whole pattern is handled like ignorecase is on. The actual value of ignorecase and smartcase is ignored. \C does the opposite: Force matching case for the whole pattern. {only Vim supports \c and \C} Note that ignorecase, \c and \C are not used for the character classes.
1 2 3 4 5 6 7 8 |
Examples: pattern 'ignorecase' 'smartcase' matches foo off - foo foo on - foo Foo FOO Foo on off foo Foo FOO Foo on on Foo \cfoo - - foo Foo FOO foo\C - - foo |
拼写
[s
: 跳转到上一处拼写错误]s
: 跳转到下一处拼写错误z=
: 对光标下或之后的单词,显示建议正确的拼写单词zg
: 将光标下的单词作为正确拼写单词加入到‘spellfile’zw
: 和zg类似,只是zw是将这个单词标记为拼写错误的单词
执行外部命令
:!
- cmd 执行外部命令:!!
- 执行上一次的外部命令:sh
- 调用shell,用exit返回vim:r !cmd
- 将命令的返回结果插入文件当前位置:m,nw !cmd
- 将文件的m行到n行之间的内容做为命令输入执行命令
宏
qa
- 开始录制宏a(键盘操作记录)q
- 停止录制@a
- 播放宏a
命令行
:6t.
- 把第六行复制到当前行下方,:t
是命令:copy
简写形式 [range]copy {address}:6m.
- 把第六行移动到当前行下方,:move
是命令:move
简写形式,格式:[range] {address}:'<,'>m$
- 把选中的文本移动到文件结尾,其中:'<,'>
代表高亮选区,’<是代表高亮选区行首的位置标记,’>是代表高亮选区的最后一行:'<,>'p
- 回显上一次高亮选区选中的内容
排序
:sort
- 升序重排所有行:sort!
- 降序重排所有行:sort u
- 升序号所有行,并去除重复行:sort n
- 按数值升序重拍所有行(此时100排在20后面)1,100sort
- 第1行到第100行,进行升序重排:g/start/.,/end/-1 sort n
- 使用正则表达式进行匹配开始和结束行,对从start
开始到end
结束的所有文本行,进行数值升序重排1,$!sort -t',' -k2 -r
- 使用外部命令sort,将第一行到最后一行,-t参数指定以,
为分隔,-k参数指定按第二个字段进行降序排列
可视模式
v
- 进入可视化模式, 移动光标高亮选择, 然后可以对选择的文本执行命令(比如y-复制)V
- 进入可视化模式(行粒度选择)o
- 切换光标到选择区开头/结尾Ctrl + v
- 进入可视化模式(矩阵选择)O
- 切换光标到选择区的角aw
- 选择当前单词ab
- 选择被 () 包裹的区域(含括号)aB
- 选择被 {} 包裹的区域(含花括号)at
- 选择被 <> 标签包裹的区域(含<>标签)ib
- 选择被 () 包裹的区域(不含括号)iB
- 选择被 {} 包裹的区域(不含花括号)it
- 选择被 <> 标签包裹的区域(不含<>标签)Esc
- 退出可视化模式- 使用可视模式选择和
normal
命令来注释和取消注释代码:- 可视模式下选择目标
normal i#
或norm i#
: 添加#
normal x
或norm x
: 取消每行开头的第一个字符,这里也就是开头的#
可视化模式命令
>
- 向右缩进<
- 向左缩进y
- 复制d
- 剪切~
- 大小写切换u
- 将选中文本转换为小写U
- 将选中文本转换为大写
代码折叠
zo
- 在光标下打开折叠zc
- 在光标下关闭折叠za
- 切换光标下打开、关闭状态zr
- 将所有折叠打开一层zm
- 将所有折叠关闭一层zR
- 打开全部折叠zM
- 关闭全部折叠
其他
1、启动耗时检查
检查Neovim启动耗时情况,可以使用参数--startuptime <file>
,将启动时间消耗信息写入的文件<file>
中。
2、格式化JSON文件
可以通过执行外部命令:
1
|
:%!python -m json.tool |
也可以通过NeoVim支持的LSP功能进行格式化,先通过安装JSON LSP server:
1
|
npm i -g vscode-langservers-extracted |
再在配置文件中激活LSP server:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
local on_attach = function(client, bufnr) buf_set_keymap('n', '<space>f', '<cmd>lua vim.lsp.buf.formatting()<CR>', opts) end local servers = { 'jsonls' } for _, lsp in ipairs(servers) do nvim_lsp[lsp].setup { on_attach = on_attach, flags = { debounce_text_changes = 150, } } end |
配置成功后,在使用<sapce>f
快捷键就可以完成格式化JSON文件
3、快速计算一列数字的平均值
如有下面的一段文本,要计算第一列表数字的平均值:
1 2 3 4 5 6 7 8 9 10 11 |
106.826 000.003- --- NVIM STARTED --- 107.274 000.003- --- NVIM STARTED --- 107.374 000.003- --- NVIM STARTED --- 107.436 000.003- --- NVIM STARTED --- 108.689 000.004- --- NVIM STARTED --- 108.824 000.004- --- NVIM STARTED --- 109.007 000.003- --- NVIM STARTED --- 109.509 000.003- --- NVIM STARTED --- 109.715 000.003- --- NVIM STARTED --- 110.581 000.003- --- NVIM STARTED --- 111.096 000.004- --- NVIM STARTED --- |
可以使用Ex命令 :r!awk '{total+=$1} END {print "Average- " total/NR}' %
, 文件末尾添加:
1
|
Average- 111.244 |
命令详解:
:r
- 读取文件内容插入到光标所在位置的下一行!
- 执行外部命令awk
- 外部命令awk{total+=$1}
- 累加第一列数值求和赋值给total{print "Average- " total/NR}
:计算并输出平均值(NR
:已读取的记录数)%
- 当前文件
4、给文件的每一行开头添加序号
1 2 3 |
:%!nl -ba :%!cat -n :%!awk '{print NR $0}' |
5、tabstop, softtabstop, expandtab, shiftwidth 和 空格
tabstop
:tabstop
选项设置tab字符显示宽度为多少个空格expandtab
:set expandtab
后,在插入模式,按TAB键所插入的tab字符会被替换成合适数目的空格softtabstop
:softtabstop
选项影响Vim在插入模式下按TAB键所得到的实际字符,可能是特定数目的空格,也可能是一个tab字符。具体受到tabstop
和expandtab
配置项影响shiftwidth
: 配置进行缩进时插列数
使用下面的配置:
1 2 3 4 |
: set tabstop=2 : set shiftwidth=2 : set softtabstop=2 : set expandtab |
或者
1
|
: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab |
将tab字符显示为2个空格,缩进时使用2个空格,插入模式下键入TA替换成两个空格 在上述配置下,如果文件中已经有tab字符,此时要将替换成2个空格,可以使用命令:
1
|
: retab |
6、Listing autocommands
auto[cmd] [group]
: 显示所有autocommands,如果提供[group]参数,Vim 仅会列出[group]相关的autocommandsverbose autocmd BufEnter
: 显示最后定义的BufEnter
autocommandsauto[cmd] [group] {event}
: 显示{event}事件相关的所有 autocommands
7、查看highlight group
verbose highlight
: 显示所有hightlight
组样式定义verbose highlight GroupName
: 显示指定的highlight
组的样式定义
8、设置透明背景
系统环境:
1 2 3 4 5 6 7 |
❯ kitty --version kitty 0.31.0 created by Kovid Goyal ❯ nvim --version NVIM v0.10.0-dev-1873+g8f08b1efb Build type: RelWithDebInfo LuaJIT 2.1.1702233742 Run "nvim -V1 -v" for more info |
- 修改 kitty 背景透明度为0.9, macos_traditional_fullscreen 从 no 设置为 yes
- 修改 neovim 配置,设置:
|
|
有趣的历史
Vim(及其家族)的词源
ed 是最初的 UNIX 文本编辑器,它编写于图形显示器很稀有的年代,那时源代码通常是打印在纸带上,并在电传终端机 上编辑。 在终端上输入的命令被送到大型机上进行处理,每条命令的输出会被打印出来。在那个年代,从终端到大型机之间的连接很慢,以至于一个快速打字员比网络还快,他们输入命令的速度要比命令被发出去处理更快。 在这种情况下,ed 能够提供一个简洁的语法变得异常重要。p 被用来打印当前行,而 %p 被用来打印整个文件,皆缘于此。
ed 历经了几代的改进,包括em (意为“editor for mortals”,即“人类的编辑器”)、en ,最终到ex。此时图形显示器已经比较普及了,ex 增加了一个把终端屏幕设置成交互窗口的功能,并在窗口内显示文件的内容。这样,在做修改时实时看到变化成为了可能。此屏幕编辑模式由 :visual 命令激活,其简写为 :vi,这即是 vi 这个名字的由来。
Vim 代表改进版的 vi (vi improved),更多信息通过查阅 :h vi-differences,我们可以看到Vim支持而 vi 不支持的功能列表。 Vim 对功能的增强是必要的,但另一方面它却仍继承了大量的遗产。这些指导 Vim 先祖们设计的约束,提供了一个非常高效的命令集,这在今天依然很有价值。
学习资料
引用参考
- Neovim
- Awesome Neovim
- VIM 命令操作教程
- 简明 VIM 练级攻略
- 130+vim基本命令
- vi / vim 初学者入门(系列文章)
- nanotee/nvim-lua-guide
- getting-started-with-vim
- 《学习vi和vim编辑器》
- 《Vim实用技巧》
- nvim lua guide
- The 300 line init.lua challenge
- configuring neovim using lua
- Speedup neovim
- reduce startup and improve lua config
- turning neovim into a full fledged code editor with lua
- Vim Tips Wiki
- Neovim Tips
- Vim Cheat Sheet
- Easier buffer switching
- Variables Reference in Debugging configuration files
- Motion
- Using marks