大爱vim,虽然用的很多,但是没有系统总结过,属于初中级阶段,而且最近因为vim-go的原因,很想学习下vim的脚本和插件开发。刚好最近发现一本好书,《Mastering Vim》,这本书有Bram Moolenaar(vim creator和maintainer)背书,应该不错的!
顺便扯一下蛋,个人觉得今后editor和IDE的发展应该是朝着轻量级和插件化发展,从vim的长盛不衰和最近很火的vs code就能看出这个趋势。jet brains虽然很重,但是用起来感觉比较轻量级,也没有跳出这个范畴。但是从本源来说,个人倾向于vim,有了这个神器,对几乎所有源码的编辑任务都能以不变应万变,而且能享受到命令行本身的乐趣。Keep It Simple and Stupid!
读的是《Mastering Vim》第一版,好像有第二版了
此篇笔记的组织方式是按照原书的目录结构组织的,这样方便去找原书的对应章节内容。但是与常用插件相关的部分因为实用性太强,自己就按照自己的方式组织了:)
此篇笔记是使用Vnote-一个更懂程序员和Markdown的笔记的vim模式编辑的。
Getting Started
常用配置
syntax on " Enable syntax highlighting.
filetype plugin indent on " Enable file type based indentation.
set autoindent " Respect indentation when starting a new line.
set expandtab " Expand tabs to spaces. Essential in Python.
set tabstop=4 " Number of spaces tab is counted for.
set shiftwidth=4 " Number of spaces to use for autoindent.
set backspace=2 " Fix backspace behavior on most terminals.
colorscheme murphy " Change a colorscheme.
命令模式下查看当前某个选项的值
#注意问号
:set xxx?
vim8自带的colorscheme
心情不好的时候可以换个主题,哈哈
- blue
- darkblue
- default
- delek
- desert
- elflord
- evening
- industry
- koehler
- moring
- merhpy
- pablo
- peachpuff
- ron
- shine
- slate
- torte
- zellner
各种移动/导航操作
vim中,word表示字母,数字和下划线的组合,WORD表示非空格字符序列。一般主要使用word。比如
that's a person! ok?, word就是that-'-s-a-person-!-ok-?,WORD就是that's-a-person!-ok?.
vim中,paragraph(段落)的含义是至少两个新行组成的块,段落之间是有换行的。
**向前(forward)**指从左到右,或者,从上到下。 **向后(backward)**指从右到左,或者,从下到上
| 快捷键 | 行为 |
|---|---|
| w | 向前移动一个word |
| e | 向前移动到word的末尾(end) |
| W | 向前移动一个WORD |
| E | 向前移动到WORD的末尾(end) |
| b | 向后移动一个word |
| B | 向后移动一个WORD |
| { | 向后移动一个段落 |
| } | 向前移动一个段落 |
| ctrl+b | 向后移动一页 |
| ctrl+f | 向前移动一页 |
vim中,除了i进入insert模式,c也常用
操作一般组合起来使用,vim中的组合模式为 。比如 4w,就是向前移动4个word,dd4就是删除4行。

一些编辑指令
| 快捷键 | 行为 |
|---|---|
| c | 进入编辑模式, 一般使用cw |
| cc | 清除整行并进入编辑模式 |
| dd | 删除整行 |


undo和redo
u是undo,ctrol+r是redo
帮助
vim帮助文档中的表示换行符的意思
| 命令 | 说明 |
|---|---|
| :help | 进入帮助 |
| :h cc | 查看cc指令的帮助 |
| :h search-command | 命令行下搜索的帮助 |
| :h search<Ctrl+D> | 包含search字符串的tag |
Advanced Editing and Navigation
vim中,打开的一个文件就是一个window,多个window构成一个tag, +--开始的行表明是折叠
文件在vim中,称之为buffer。每一个打开的文件都有一个关联的buffer。
命令模式下使用:ls可以看到目前已存在的buffer。:files和:buffers有同样的效果。
buffer的操作可以通过
:bX来操作。
:b 1切换到id为1的buffer上:b cat切换到文件名有cat的buffer上,同名会报错:bd删除当前buffer,即关闭文件。如果文件没有保存,会报错。
Navigating Text
tX当前行下搜索字符X,并将光标移动到X之前的字符fX与tX类似,不过光标移动到目标字符上$将光标移动到行的末尾,^将光标移动到行的开头 (原书说的是_到行的开头,但是不行,我的使用的iterm2)ge将光标移动到上一个word的末尾,GEfor WORDgg文件开头G文件结尾
书中的这张图有个很不错的总结:

:set nu与:set nonu是打开和关闭行号显示:NN为行号,跳转到某行:+N和:-N为向下和向上N行
进入插入模式
a当前光标之后插入A当前行最后插入,等价于$ai当前光标之前插入I当前行最前(不包括最前的连续空白符)插入,等价于^io当前行之后新增一行O当前行之前新增一行gi上一次插入模式的位置C删除当前光标以及之后的所有行内字符并进入插入模式ccS同C但是是向前s删除一个字符并进入插入模式
使用/和?搜索
Utilizing text objects
text object是vim中的一种对象类型。很有用!这块我也没怎么用过,学习!di)di(删除括号中的数据di"删除双引号中的数据di{'di}删除大括号中的数据di<'di>删除大括号中的数据- `字符也可以的 - markdown编辑器无法转义。。。
拷贝与拷贝
yy复制一行ye复制到word结尾yXx是一些导航字符,比如w, b, e等等p拷贝
register模式
:reg列出当前register:reg <名字>"ayw注册一个叫做a的register,并且复制到word结尾"ap拷贝叫做a的register中的内容
Follow the Leader - Plugin Management
vim-plug是一个易用的插件管理器,当然还有其他的。
Profiling slow plugins
vim --startuptime startuptime.log
Profiling specific actions
这里原书讲了CtrlP的例子,也是我遇到到的问题。
:profile start profile.log
:profile func *
:profile file *
Deeper dive into modes
Visual and select mode
v进入visual modeV进入visual mode,按行Ctrl + v进入visual mode,按列
Replace mode
R进入替换模式,替换模式与insert模式的区别就是insert下是插入,replace下是覆盖
Terminal mode - vim8.1新增
:terminal或者:term进入终端模式,会开一个新窗口。
说实话,这个模式有点鸡肋,使用iterm2拆分窗口,或者使用tmux就可以达到同样的效果了。
Remapping commands - 自定义热键/映射
vim中可以设置自定义热键
:map:noremap可以查看当前映射的map和noremapnoremap ; :将;映射为:noremap <c-u> :w<cr>将ctrl+s定义为保存文件
map是递归的映射的意思,noremap是不会递归映射的意思。
递归映射的含义是,如果映射了a为b指令,c为a指令,那么执行c指令的时候,实际上执行b指令,有一种传递/递归关系。详见这里
请不要尝试映射
<c-s>,即ctrl+s,这样会锁住vim,让人以为是vim死机了,其实不是,ctrl-q会恢复。
vim各个模式下的映射命令如下,请注意,都是在map和remap前加前缀:
:nmap and :nnoremap: Normal mode
:vmap and :vnoremap: Visual and select modes :xmap and :xnoremap: Visual mode
:smap and :snoremap: Select mode
:omap and :onoremap: Operator-pending mode
:map! and :noremap!: Insert and command-line modes :imap and :inoremap: Insert mode
:cmap and :cnoremap: Command-line mode
自定义热键/映射非常有用,请看下面的配置,这个配置已经收录在自己的vimrc项目中了
" 输入'"<{[`自动补全另外一半,并将光标移动到合适的位置,提升输入效率杠杠的!
inoremap ' ''<esc>i
inoremap " ""<esc>i
inoremap ( ()<esc>i
inoremap { {}<esc>i
inoremap [ []<esc>i
inoremap ` ``<esc>i
有一个leader key的概念,和tmux的leader key概念一致。可以简单理解为一个按键前缀。
在自定义热键和映射时,可以使用noremap <leader>s :w<cr>
vim的默认Leader key是/,按照上面的定义,在非insert模式下,先按下/再按下s就等价于
可以通过 let mapleader = "\<c-b>"来修改默认的leader key,这里修改为ctrl+b就和tmux的leader key一样了。注意如果leader key中含有特殊字符,比如 等等,需要加入\
因为使用vim-go习惯了,所以自己设置的逗号作为leader key。很好用。
Understanding the Text
Navigating the code base with tags
//建立tags文件
ctags -R .
-
set tags=tags向当前目录找tags文件,如果没有找到,递归的向父目录寻找。 -
ctrl+]跳转到定义,下面定义的命令/快捷键需要在此命令调用后再调用 -
ctrl+t调回来 -
:tn如果有同名的tag,这个调到找到的下一个tag -
:tp调到上一个tag -
:ts(助记:tag select)如果有同名的tag,这个命令会将所有的tag列出来,这个功能我竟然没有用过!!! -
g]打开tag的文件列表,这个不需要调用ctrl+] -
vim -t some_tag打开vim的时候,直接调到某个tag上
可以在.vimrc中配置以下指令来自动更新tags,会每次保存一个py文件后,执行ctags -R
autocmd BufWritePost *.py silent! !ctags -R &
对于c++可以设置为:
autocmd BufWritePost *.cpp *.h silent! !ctags -R &
TODO:这个功能挺好,因为现在vim-go扩展的自动提示功能缺乏自动编译功能,看能否加入。