《Mastering Vim》读书笔记 - 第一部分
vim 工具 vim
Lastmod: 2020-09-20

大爱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,即关闭文件。如果文件没有保存,会报错。
  • tX 当前行下搜索字符X,并将光标移动到X之前的字符
  • fXtX类似,不过光标移动到目标字符上
  • $ 将光标移动到行的末尾,^将光标移动到行的开头 (原书说的是_到行的开头,但是不行,我的使用的iterm2)
  • ge 将光标移动到上一个word的末尾,GE for WORD
  • gg 文件开头
  • G 文件结尾

书中的这张图有个很不错的总结: cheat sheet

  • :set nu:set nonu是打开和关闭行号显示
  • :N N为行号,跳转到某行
  • :+N:-N为向下和向上N行

进入插入模式

  • a 当前光标之后插入
  • A 当前行最后插入,等价于$a
  • i 当前光标之前插入
  • I 当前行最前(不包括最前的连续空白符)插入,等价于^i
  • o 当前行之后新增一行
  • O 当前行之前新增一行
  • gi 上一次插入模式的位置
  • C 删除当前光标以及之后的所有行内字符并进入插入模式
  • cc SC但是是向前
  • 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 mode
  • V 进入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和noremap
  • noremap ; : 将;映射为:
  • 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

//建立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扩展的自动提示功能缺乏自动编译功能,看能否加入。