技巧
跳至选择的区域另一端
在使用 v
或者 V
选择某段文字后,可以用 o
或者 O
按键跳至选择区域的开头或者结尾。
:h v_o
:h v_O
聪明地使用 n 和 N
<kbd>n</kbd> 与 <kbd>N</kbd> 的实际跳转方向取决于使用 /
还是 ?
来执行搜索,其中 /
是向后搜索,?
是向前搜索。一开始我(原作者)觉得这里很难理解。
如果你希望 <kbd>n</kbd> 始终为向后搜索,<kbd>N</kbd> 始终为向前搜索,那么只需要这样设置:
nnoremap <expr> n 'Nn'[v:searchforward]
nnoremap <expr> N 'nN'[v:searchforward]
聪明地使用命令行历史
我(原作者)习惯用 <kbd>Ctrl</kbd> + <kbd>p</kbd> 和 <kbd>Ctrl</kbd> + <kbd>n</kbd> 来跳转到上一个/下一个条目。其实这个操作也可以用在命令行中,快速调出之前执行过的命令。
不仅如此,你会发现 <kbd>上</kbd> 和 <kbd>下</kbd> 其实更智能。如果命令行中已经存在了一些文字,我们可以通过按方向键来匹配已经存在的内容。比如,命令行中现在是 :echo
,这时候我们按 <kbd>上</kbd>,就会帮我们补全成 :echo "Vim rocks!"
(前提是,之前输入过这段命令)。
当然,Vim 用户都不愿意去按方向键,事实上我们也不需要去按,只需要设置这样的映射:
cnoremap <c-n> <down>
cnoremap <c-p> <up>
这个功能,我(原作者)每天都要用很多次。
智能 Ctrl-l
<kbd>Ctrl</kbd> + <kbd>l</kbd> 的默认功能是清空并「重新绘制」当前的屏幕,就和 :redraw!
的功能一样。下面的这个映射就是执行重新绘制,并且取消通过 /
和 ?
匹配字符的高亮,而且还可以修复代码高亮问题(有时候,由于多个代码高亮的脚本重叠,或者规则过于复杂,Vim 的代码高亮显示会出现问题)。不仅如此,还可以刷新「比较模式」(请参阅 :help diff-mode
)的代码高亮:
nnoremap <leader>l :nohlsearch<cr>:diffupdate<cr>:syntax sync fromstart<cr><c-l>
禁用错误报警声音和图标
set noerrorbells
set novisualbell
set t_vb=
请参阅 Vim Wiki: Disable beeping。
快速移动当前行
有时,我(原作者)想要快速把当前行上移或下移一行,只需要这样设置映射:
nnoremap [e :<c-u>execute 'move -1-'. v:count1<cr>
nnoremap ]e :<c-u>execute 'move +'. v:count1<cr>
这个映射,同样可以搭配数字使用,比如连续按下 <kbd>2</kbd> <kbd>]</kbd> <kbd>e</kbd> 就可以把当前行向下移动两行。
快速添加空行
nnoremap [<space> :<c-u>put! =repeat(nr2char(10), v:count1)<cr>'[
nnoremap ]<space> :<c-u>put =repeat(nr2char(10), v:count1)<cr>
设置之后,连续按下 <kbd>5</kbd> <kbd>[</kbd> <kbd>空格</kbd> 在当前行上方插入 5 个空行。
运行时检测
需要的特性:+profile
Vim 提供了一个内置的运行时检查功能,能够找出运行慢的代码。
:profile
命令后面跟着子命令来确定要查看什么。
如果你想查看所有的:
:profile start /tmp/profile.log
:profile file *
:profile func *
<do something in Vim>
<quit Vim>
Vim 不断地在内存中检查信息,只在退出的时候输出出来。(Neovim 已经解决了这个问题用 :profile dump
命令)
看一下 /tmp/profile.log
文件,检查时运行的所有代码都会被显示出来,包括每一行代码运行的频率和时间。
大多数代码都是用户不熟悉的插件代码,如果你是在解决一个确切的问题, 直接跳到这个日志文件的末尾,那里有 FUNCTIONS SORTED ON TOTAL TIME
和 FUNCTIONS SORTED ON SELF TIME
两个部分,如果某个 function 运行时间过长一眼就可以看到。
查看启动时间
感觉 Vim 启动的慢?到了研究几个数字的时候了:
vim --startuptime /tmp/startup.log +q && vim /tmp/startup.log
第一栏是最重要的因为它显示了绝对运行时间,如果在前后两行之间时间差有很大的跳跃,那么是第二个文件太大或者含有需要检查的错误的 VimL 代码。
NUL 符用新行表示
文件中的 NUL 符 (\0
),在内存中被以新行(\n
)保存,在缓存空间中显示为 ^@
。
更多信息请参看 man 7 ascii
和 :h NL-used-for-Nul
。
快速编辑自定义宏
这个功能真的很实用!下面的映射,就是在一个新的命令行窗口中读取某一个寄存器(默认为 *
)。当你设置完成后,只需要按下 <kbd>回车</kbd> 即可让它生效。
在录制宏的时候,我经常用这个来更改拼写错误。
nnoremap <leader>m :<c-u><c-r><c-r>='let @'. v:register .' = '. string(getreg(v:register))<cr><c-f><left>
只需要连续按下 <kbd>leader</kbd> <kbd>m</kbd> 或者 <kbd>"</kbd> <kbd>leader</kbd> <kbd>m</kbd> 就可以调用了。
请注意,这里之所以要写成 <c-r><c-r>
是为了确保 <c-r>
执行了。请参阅 :h c_^R^R
快速跳转到源(头)文件
这个技巧可以用在多种文件类型中。当你从源文件或者头文件中切换到其他文件的时候,这个技巧可以设置「文件标记」(请参阅 :h marks
),然后你就可以通过连续按下 <kbd>'</kbd> <kbd>C</kbd> 或者 <kbd>'</kbd> <kbd>H</kbd> 快速跳转回去(请参阅 :h 'A
)。
autocmd BufLeave *.{c,cpp} mark C
autocmd BufLeave *.h mark H
注意:由于这个标记是设置在 viminfo 文件中,因此请先确认 :set viminfo?
中包含了 :h viminfo-'
。
在 GUI 中快速改变字体大小
印象中,我(原作者)记得一下代码是来自 tpope's 的配置文件:
command! Bigger :let &guifont = substitute(&guifont, '\d\+$', '\=submatch(0)+1', '')
command! Smaller :let &guifont = substitute(&guifont, '\d\+$', '\=submatch(0)-1', '')
根据模式改变光标类型
我(原作者)习惯在普通模式下用块状光标,在插入模式下用条状光标(形状类似英文 "I" 的样子),然后在替换模式中使用下划线形状的光标。
if empty($TMUX)
let &t_SI = "\<Esc>]50;CursorShape=1\x7"
let &t_EI = "\<Esc>]50;CursorShape=0\x7"
let &t_SR = "\<Esc>]50;CursorShape=2\x7"
else
let &t_SI = "\<Esc>Ptmux;\<Esc>\<Esc>]50;CursorShape=1\x7\<Esc>\\"
let &t_EI = "\<Esc>Ptmux;\<Esc>\<Esc>]50;CursorShape=0\x7\<Esc>\\"
let &t_SR = "\<Esc>Ptmux;\<Esc>\<Esc>]50;CursorShape=2\x7\<Esc>\\"
endif
原理很简单,就是让 Vim 在进入和离开插入模式的时候,输出一些序列,请参考 escape sequence。Vim 与终端之间的中间层,比如 tmux 会处理并执行上面的代码。
但上面这个还是有一个缺点的。终端环境的内部原理不尽相同,对于序列的处理方式也稍有不同。因此,上面的代码可能无法在你的环境中运行。甚至,你的运行环境也有可能不支持其他光标形状,请参阅你的 Vim 运行环境的文档。
好消息是,上面这个代码,可以在 iTerm2 中完美运行。
防止水平滑动的时候失去选择
如果你选中了一行或多行,那么你可以用 <kbd><</kbd> 或 <kbd>></kbd> 来调整他们的缩进。但在调整之后就不会保持选中状态了。
你可以连续按下 <kbd>g</kbd> <kbd>v</kbd> 来重新选中他们,请参考 :h gv
。因此,你可以这样来配置映射:
xnoremap < <gv
xnoremap > >gv
设置好之后,在可视模式中使用 >>>>>
就不会再出现上面提到的问题了。
重新载入保存文件
通过自动命令,你可以在保存文件的同时触发一些其他功能。比如,如果这个文件是一个配置文件,那么就重新载入;或者你还可以对这个文件进行代码风格检查。
autocmd BufWritePost $MYVIMRC source $MYVIMRC
autocmd BufWritePost ~/.Xdefaults call system('xrdb ~/.Xdefaults')
更加智能的当前行高亮
我(原作者)很喜欢「当前行高亮」(请参阅 :h cursorline
)这个功能,但我只想让这个效果出现在当前窗口,而且在插入模式中关闭这个效果:
autocmd InsertLeave,WinEnter * set cursorline
autocmd InsertEnter,WinLeave * set nocursorline
更快的关键字补全
关键字补全(<c-n>
或 <c-p>
)功能的工作方式是,无论 'complete'
设置中有什么,它都会尝试着去补全。这样,一些我们用不到的标签也会出现在补全列表中。而且,它会扫描很多文件,有时候运行起来非常慢。如果你不需要这些,那么完全可以像这样把它们禁用掉:
set complete-=i " disable scanning included files
set complete-=t " disable searching tags
改变颜色主题的默认外观
如果你想让状态栏在颜色主题更改后依然保持灰色,那么只需要这样设置:
autocmd ColorScheme * highlight StatusLine ctermbg=darkgray cterm=NONE guibg=darkgray gui=NONE
同理,如果你想让某一个颜色主题(比如 "lucius")的状态栏为灰色(请使用 :echo color_name
来查看当前可用的所有颜色主题):
autocmd ColorScheme lucius highlight StatusLine ctermbg=darkgray cterm=NONE guibg=darkgray gui=NONE
命令
下面的命令都比较有用,最好了解一下。用 :h :<command name>
来了解更多关于它们的信息,如::h :global
。
:global 和 :vglobal - 在所有匹配行执行命令
在所有符合条件的行上执行某个命令。如: :global /regexp/ print
会在所有包含 "regexp" 的行上执行 print
命令(译者注:regexp 有正则表达式的意思,该命令同样支持正则表达式,在所有符合正则表达式的行上执行指定的命令)。
趣闻:你们可能都知道老牌的 grep 命令,一个由 Ken Thompson 编写的过滤程序。它是干什么用的呢?它会输出所有匹配指定正则表达式的行!现在猜一下 :global /regexp/ print
的简写形式是什么?没错!就是 :g/re/p
。 Ken Thompsom 在编写 grep 程序的时候是受了 vi :global
的启发。(译者注: https://robots.thoughtbot.com/how-grep-got-its-name)
既然它的名字是 :global
,理应仅作用在所有行上,但是它也是可以带范围限制的。假设你想使用 :delete
命令删除从当前行到下一个空行(由正则表达式 ^$
匹配)范围内所有包含 "foo" 的行:
:,/^$/g/foo/d
如果要在所有 不 匹配的行上执行命令的话,可以使用 :global!
或是它的别名 :vglobal
( V 代表的是 inVerse )。
:normal 和 :execute - 脚本梦之队
这两个命令经常在 Vim 的脚本里使用。
借助于 :normal
可以在命令行里进行普通模式的映射。如::normal! 4j
会令光标下移 4 行(由于加了"!",所以不会使用自定义的映射 "j")。
需要注意的是 :normal
同样可以使用范围数(译者注:参考 :h range
和 :h :normal-range
了解更多),故 :%norm! Iabc
会在所有行前加上 "abc"。
借助于 :execute
可以将命令和表达式混合在一起使用。假设你正在编辑一个 C 语言的文件,想切换到它的头文件:
:execute 'edit' fnamemodify(expand('%'), ':r') . '.h'
(译者注:头文件为与与源文件同名但是扩展名为 .h
的文件。上面的命令中 expand 获得当前文件的名称,fnamemodify 获取不带扩展名的文件名,再连上 '.h' 就是头文件的文件名了,最后在使用 edit 命令打开这个头文件。)
这两个命令经常一起使用。假设你想让光标下移 n 行:
:let n = 4
:execute 'normal!' n . 'j'
重定向消息
许多命令都会输出消息,:redir
用来重定向这些消息。它可以将消息输出到文件、寄存器或是某个变量中。
" 将消息重定向到变量 `neatvar` 中
:redir => neatvar
" 打印所有寄存器的内容
:reg
" 结束重定向
:redir END
" 输出变量
:echo neatvar
" 恶搞一下,我们把它输出到当前缓冲区
:put =neatvar
再 Vim 8 中,可以更简单的方式即位:
:put =execute('reg')
(译者注:原文最后一条命令是 :put =nicevar
但是实际会报变量未定义的错误) (实测 neovim/vim8 下没问题)
帮助文档::h :redir