大爱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的末尾,GE
for WORDgg
文件开头G
文件结尾
书中的这张图有个很不错的总结:
:set nu
与:set nonu
是打开和关闭行号显示:N
N为行号,跳转到某行:+N
和:-N
为向下和向上N行
进入插入模式
a
当前光标之后插入A
当前行最后插入,等价于$a
i
当前光标之前插入I
当前行最前(不包括最前的连续空白符)插入,等价于^i
o
当前行之后新增一行O
当前行之前新增一行gi
上一次插入模式的位置C
删除当前光标以及之后的所有行内字符并进入插入模式cc
S
同C
但是是向前s
删除一个字符并进入插入模式
使用/
和?
搜索
Utilizing text objects
text object
是vim中的一种对象类型。很有用!这块我也没怎么用过,学习!di)
di(
删除括号中的数据di"
删除双引号中的数据di{'
di}
删除大括号中的数据di<'
di>
删除大括号中的数据- `字符也可以的 - markdown编辑器无法转义。。。
拷贝与拷贝
yy
复制一行ye
复制到word结尾yX
x是一些导航字符,比如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扩展的自动提示功能缺乏自动编译功能,看能否加入。