为了能像 Gilles Castel 那样记笔记(用 Vim + snippets 居然可以在课堂上同步记数学笔记😱),我又又又又一次捡起了 Vim 。感觉 Vim 之所以这么难学,是因为没一篇教程写对路了的😒。
我的最初想法是重现一个新手可能会遇到的问题,寻找原生的解决方案 & 更优雅的方案。不过回头看,更加深切地感受到,这么多年过去,Vim & Emacs 的对手(各种 IDE )换了一波又一波,它俩却还屹立在那,不得不说其设计中有很多先进那个时代、即使在今天仍不过时的思想(当然,也必然存在很多缺陷。当我们认识了这些缺陷,并知道原生如何解决、以及如何更好地解决,使用插件🧩就更加得当)。就算从程序设计的角度,Vim 仍然值得深入学习。
Unix 上最早的文本编辑器是 ed ,而后经过几代的改进,包括 em (editor for mortals) ,en ,最终到 ex 。再之后才是 vi ,Vim (vi improved) 。这段有趣而没用的历史,我一定要告诉你。
1. 打开 & 退出
- 用 Vim 打开某份文件:
vim a.txt
; - MD,怎么卡死了!
<ctrl-w>
也退不出去🤯! - 哦,要按
i
(insert) 进入「编辑模式🖋[1]」才可以写字😒; - MD,
ctrl-s
怎么保存不了🤯! - 哦,要按
<esc>
退出「编辑模式🖋」,进入到「思考模式🤔[2]」才能执行命令; - 保存 & 退出:
:wq
(其实是进入了「命令行模式」);
这么会有这么反人性的设计😒。改下配置 vim ~/.vimrc
(即,告诉 Vim 你的使用习惯)。当然,你可以改成熟知的 ctrl + s
( Vim 习惯写成 c-s
),但为了符合 Vim 的用键传统,还是用单字母吧:
" Save & quit
map Q :q<CR>
map S :w<CR>
现在你可以用 S
保存,按 Q
退出了。
但是,要是在新文件里写了点什么,怎么保存呢?:w <new_filename>
。
map S :write %<CR> " % :活动缓冲区的完整文件路径。
其实,这样写也不准确。改变原生键位最好准确指定起效的模式,要是能给出原生键位的替代方案就更好了。比如,幸运的是,不会和 surround.vim 的 S 命令冲突。
" Save & quit
nnoremap S :w %<CR> " ⚠️ 幸运的是,不会和 surround.vim 的 S 命令(只在 visual mode 下起效)冲突。
nnoremap Q :q<CR>
听人说,Vim 的强大来自「多模式」?呵呵,你也可以将「多模式」理解为「反人性」(例如:同一键位在不同的模式下作用可能天差地别。详见下表)。∴别再说什么「「多模式」是 Vim 之髓」了😓。我认为,Vim 的强大源于它既提供了粗粒度的操作,又提供了细粒度的操作。强大的工具其实莫不如是,比如 PS 。后面再详细解释。
no. | 操作 | 思考模式🤔 | 可视模式🌈 | 编辑模式🖋 |
---|---|---|---|---|
::no.:: |
i & a
|
进入「编辑模式🖋」插入 (insert / append) | (或「思考模式🤔」下 <operator> 后)i<text-object> (inner / inside) & a<text-object> (arround / all) |
|
::no.:: | o |
进入「编辑模式🖋」并插入新行 | 在高亮选区两端来回切换 | |
::no.:: | c-r |
重做 (redo) |
<c-r><register> 意为插入寄存器 register 的内容 |
2. 怎么对调 caps lock
和 esc
?
你听到的第一个建议,大概率是改键:将 <caps lock>
改为 <esc>
。这件事在 MacOS 上原生自带:
Windows 上也不难,下一个 PowerToys ,用里面的 Keyboard Manager 就搞定。
最麻烦的是 Linux ,折腾了很久都没有成功😓。
3.界面太丑怎么办🥴🤢🤮?
Vim 最为人诟病的就是「记忆负担太重」。比如:在不同的模式之间切换,完全凭脑力。再比如什么 marks 啦、buffers 啦、tabs 啦……原厂的统统不显示。
最简单的改造方式是高亮显示当前行 / 列:
set cursorline
set cursorcolumn
可以装个叫 vim-airline
的美化插件😍,至少不同模式的颜色不同。但你得先装个管理插件的插件 vim-plug
。
装好以后在 ~/.vimrc
里写:
call plug#begin()
" ...
call plug#end()
现在你终于可以装 vim-airline
了:
call plug#begin()
Plug 'vim-airline/vim-airline'
Plug 'vim-airline/vim-airline-themes'
call plug#end()
let g:airline_theme = 'simple'
airline
除了好看,一定要把 Smarter tab line 打开:
let g:airline#extensions#tabline#enabled = 1
现在,当你打开多个文件 vim *.md
,就可以用 :bprevious
& :bnext
来回切换缓冲区 (buffers) 了。太蠢了是不🤢🤮?还是改个快捷键吧。这是从 Tim Pope 的 unimpaired.vim
那抄来的:
" 快速遍历 Vim 列表
" ⚠️ 加 ! 后可以强制切换缓冲区。
nnoremap <silent> [b :bprevious!<CR>
nnoremap <silent> ]b :bnext!<CR>
nnoremap <silent> [B :bfirst!<CR>
nnoremap <silent> ]B :blast!<CR>
你最好再装个 Powerline
。
但还是没有解决 marks、buffers、tabs 的提示问题啊?那还得靠插件:
4. 怎么在窗口间切换啊?
当你装插件上瘾以后,比如 NERDTree
、Undo Tree
,会遇到一个问题,怎么切换到新开的窗口上呢?
原生命令是:
命令 | 效果 |
---|---|
<C-w>w |
在窗口间循环切换 🔁 |
<C-w>h |
切换到左边的窗口 ⬅️ |
<C-w>j |
切换到下边的窗口 ⬇️ |
<C-w>k |
切换到上边的窗口 ⬆️ |
<C-w>l |
切换到下边的窗口 ⬇️ |
<C-w>c |
关闭活动窗口 ❌ |
但还是喜欢 @TheCW 的改法:
" ===
" === Window management
" ===
" Set <LEADER> as <SPACE>
let mapleader="\<space>"
" Use <space> + new arrow keys for moving the cursor around windows
map <LEADER>w <C-w>w
map <LEADER>k <C-w>k
map <LEADER>j <C-w>j
map <LEADER>h <C-w>h
map <LEADER>l <C-w>l
5. 最纠结的改键😵
排名 | 原生键位 | 想改成 | 改键原因 | 得失 |
---|---|---|---|---|
::no.:: | : |
; |
需要频繁进入 Ex mode 。 |
; & , 是一对操作。 |
::no.:: |
<Leader> [3] 用哪个?缺省是 \ 。 |
6. 常用连招
从按键效率来说,当然是「单键」>「双击单键」>「易记的键组合」>……:
no. | 快捷键 | 效果 | 初始模式 | 切入模式 |
---|---|---|---|---|
::no.:: | A |
(等效于 $a )在行尾添加文本。 |
normal mode🤔 | insert mode🖋 |
::no.:: | I |
(等效于 ^i )在行首添加文本。 |
normal mode🤔 | insert mode🖋 |
::no.:: | o |
(等效于 A<CR> )在当前行下方新起一行。 |
normal mode🤔 | insert mode🖋 |
::no.:: | O |
(等效于 ko )在当前行上方插入一行。 |
normal mode🤔 | insert mode🖋 |
::no.:: | C |
(等效于 c$ ) |
normal mode🤔 | insert mode🖋 |
::no.:: | s |
(等效于 cl )删掉光标下的字符,然后进入插入模式。即,替换光标下的字符。 |
normal mode🤔 | insert mode🖋 |
::no.:: | S |
(等效于 ^c ) |
normal mode🤔 | insert mode🖋 |
::no.:: | dd |
删除当前行。 | normal mode🤔 | normal mode🤔 |
::no.:: | >> |
增加当前行缩进。 | normal mode🤔 | normal mode🤔 |
::no.:: | << |
减少当前行缩进。 | normal mode🤔 | normal mode🤔 |
::no.:: | ea |
在单词结尾 (end) 添加文本。 | normal mode🤔 | insert mode🖋 |
::no.:: | gf |
go to file. | normal mode🤔 | normal mode🤔 |
::no.:: | xp |
调换光标之后的两个字符。 | normal mode🤔 | normal mode🤔 |
::no.:: | gUU |
将当前行整行转换为大写。 | normal mode🤔 | normal mode🤔 |
::no.:: | yyp |
复制当前行,并粘贴在下方。 | normal mode🤔 | normal mode🤔 |
::no.:: | daw |
delete a word. | normal mode🤔 | normal mode🤔 |
::no.:: | vit |
visully select inside the tag. | ||
::no.:: | gv |
重选上次的高亮选区。 | ||
::no.:: | f,dt. |
删除句子的后半段。 | normal mode🤔 | normal mode🤔 |
::no.:: | qaq |
清空寄存器 a 。 |
||
::no.:: | gUaw |
将当前单词转换为大写。 | normal mode🤔 | normal mode🤔 |
7. 成对操作🎎
我觉得,学 Vim 完全可以比照学二外的方式——比较 & 联系。
no. | 快捷键 | 效果 | 初始模式 | 切入模式 |
---|---|---|---|---|
::no.:: |
a v.s. A
|
从后方 (append) 插入。 | normal mode🤔 | insert mode🖋 |
::no.:: |
i v.s. I
|
从前方插入 (insert) 。 | normal mode🤔 | insert mode🖋 |
::no.:: |
n v.s. N
|
下一处 (next) 。 | ||
::no.:: |
o v.s. O
|
插入新行。 | normal mode🤔 | insert mode🖋 |
::no.:: |
v v.s. V
|
进入 Visual mode 。 | ||
::no.:: |
f<char> / F<char> v.s. t<char> / T<char>
|
正向 / 反向行内查找 <char> ,区别在于包不包括 char ( till 不包括)。 |
normal mode🤔 | normal mode🤔 |
::no.:: |
; v.s. ,
|
继续寻找与调转方向搜寻前次搜寻的位置。 | normal mode🤔 | normal mode🤔 |
::no.:: |
/<pattern><CR> v.s. ?<pattern><CR>
|
文内查找。 | normal mode🤔 | comand mode🎛 |
::no.:: |
<C-a> v.s. <C-x>
|
对当前行其后最近的数字执行加 / 减操作。 | normal mode🤔 | normal mode🤔 |
::no.:: |
. v.s. u
|
重复与撤回。 | normal mode🤔 | normal mode🤔 |
::no.:: | normal mode🤔下的命令 v.s. Ex 命令 | ⚠️:normal mode🤔下的命令适合在本地操作,而 Ex 命令则可远距离操作。比如: p v.s. :t (copy To) 。 |
||
::no.:: |
<c-]> v.s. :tag <keyword>
|
跳转到 tag 到定义处。 | ||
::no.:: |
mm v.s. ``m` |
标记位置📌 m / 跳转到标记📌 m 。 |
||
::no.:: | (in visual mode)o v.s. (in normal mode)%
|
在高亮选区两端来回切换 v.s. 在括号(([{ v.s. }]) )间跳转 。 |
||
::no.:: |
<c-o> v.s. <c-i>
|
在跳转列表 (:jumps ) 中后退 / 前进(📢注意:低头看一下,在 qwerty 键盘⌨️中,i o 是紧挨在一起的)。 |
||
::no.:: |
g, v.s. g;
|
|||
::no.:: |
/someWord v.s. ?someWord
|
重复类型 | 重复操作符 | 回退操作符 |
---|---|---|
文本编辑重复 | . |
u |
行内查找重复 | ; |
, |
全文查找重复 | n |
N |
文本替换重复 | & |
u |
宏重复 | @<register> |
u |
8. 插件🧩
no. | 插件名 | 用途 | 原生解决方案 |
---|---|---|---|
::no.:: | NERDTree |
文件管理 |
netrw ,标配插件,across NETwork Read and Write files 的意思。 |
::no.:: | coc-yank |
可视复制 | |
::no.:: | vim-peekaboo |
寄存器增强。(Peekaboo extends " and @ in normal mode and <CTRL-R> in insert mode so you can see the contents of the registers.) |
|
::no.:: | Undo Tree |
||
::no.:: | vim-devicons |
美化插件:添加图标 | |
::no.:: | vim-table-mode |
美化插件:增强表格 | |
::no.:: | vim-signature |
增强书签📌 | marks |
::no.:: | vim-gitgutter |
在行号栏显示修改状态(必须置于 Git 看管之下)。 | (changs list):changes & g; , g,
|
::no.:: | vim-surround |
增强 text object ,实现快速环绕编辑。请和 wildfire.vim 配合食用。 |
|
::no.:: | wildfire.vim |
按下 <enter> 就能智能选中最近的文本块。请和 vim-surround 配合食用。 |
|
::no.:: | vim-expand-region |
快速扩大 / 缩小选中区域 | |
::no.:: | vim-commentary |
注释 | |
::no.:: | NERD Commenter |
注释 | |
::no.:: | coc.nvim |
补全🤖️ | |
::no.:: | ctrlp.vim |
||
::no.:: | Tagbar |
tag 导航 | |
::no.:: | vim-markdown |
MarkDown 增强 | |
::no.:: | markdown-preview.nvim |
MarkDown 预览 |
从 @theCW 那抄来的超有用的 MarkDown 速记技巧:
动作 | 用途 |
---|---|
,b |
加粗 |
,d |
code |
,c |
代码块 |
,p |
图片🎨 |
,a |
链接🔗 |
,f |
跳转到下一定位点 <++>
|
autocmd Filetype markdown inoremap ,f <Esc>/<++><CR>:nohlsearch<CR>c4l
autocmd Filetype markdown inoremap ,n ---<Enter><Enter>
autocmd Filetype markdown inoremap ,b **** <++><Esc>F*hi
autocmd Filetype markdown inoremap ,s ~~~~ <++><Esc>F~hi
autocmd Filetype markdown inoremap ,i ** <++><Esc>F*I
autocmd Filetype markdown inoremap ,d `` <++><Esc>F`i
autocmd Filetype markdown inoremap ,c ~~~<Enter><++><Enter>~~~<Enter><Enter><++><Esc>4kA
autocmd Filetype markdown inoremap ,h ====<Space><++><Esc>F=hi
autocmd Filetype markdown inoremap ,p [图片上传失败...(image-302a4-1669910210820)] <++><Esc>F[a
autocmd Filetype markdown inoremap ,a [](<++>) <++><Esc>F[a
autocmd Filetype markdown inoremap ,1 #<Space><Enter><++><Esc>kA
autocmd Filetype markdown inoremap ,2 ##<Space><Enter><++><Esc>kA
autocmd Filetype markdown inoremap ,3 ###<Space><Enter><++><Esc>kA
autocmd Filetype markdown inoremap ,4 ####<Space><Enter><++><Esc>kA
autocmd Filetype markdown inoremap ,l --------<Enter>
Ref📚
- 很喜欢 B 站 Up 主 @TheCW 的配置,誊抄了不少;