VIM 笔记
阙荣文 - Que's C++ Studio / 2017.9.14
0. 学会的是解决 VIM 相关问题的方法论而不仅仅是一些知识点. 1. 善用 VIM 自带的用户手册 :h 关键在于平时就一直使用 VIM,命令用得多了自然就记牢熟练了. 2. 基本常识 - 掌握之后可以代替Windows下一般的文本编辑器 2.1.1 VIM 的模式 VIM 的几种模式: 常规模式/可视模式/命令行模式/插入模式. 常规模式下可以使用快捷命令浏览,复制粘贴,查找替换文本. 可视模式下通常用来选中文本后使用编辑命令. 命令行模式可以使用 VIM Ex命令(以:开头),或者 !{cmd} 执行 shell 命令. 插入模式等同于普通的文本编辑器. 2.1.2 VIM 中的 {range} 见帮助 [:h [range]] - 主要指行范围,[.]代表当前行,[$]代表最后一行,所以 [1,$]=[%] 代表整个文件. 很多命令前使用 range 修饰作用于不同范围. 2.1.3 VIM 中的 {motion} 比如 [d/c/y] 命令都需要指定作用范围,{motion}据 VIM 文档指的是光标移动命令,即 [d0] 可以删除光标到行首的内容,[d$] 可以删除光标到行尾的内容.同理 [db] [dw] 都很好理解. 同理,[dl] 和 [x] 效果相同. 文本对象也属于一种 {motion}. VIM 的文本对象以 [i](inner) 或者 [a](around / an) 开头,后续 [w] 表示一个单词,[(){}[]""'<>] 表示成对符号内的内容. [t]表示一对 XML 标签. 2.2 普通模式(这是VIM的最常状态,保持在普通模式是一个好习惯) 2.2.1 普通模式命令 [y]:复制 [d]:剪切需要后续动作,比如[dd]删除一行,[dw]删除一个单词 [x]:剪切光标位置或者选中的字符 [p]:粘贴 [u]:undo [<C-r>] redo g开头的命令,有些表示 "GUI",有些表示 "goto",如 [gk/gj] 光标在屏幕行内移动,[gf] goto file with name eq word under cursor. [gd] 跳转至变量定义处. 2.2.2 寄存器 [:reg] 列出所有寄存器的内容,如果上述复制粘贴命令前添加寄存器的名字就可以复制/粘贴指定寄存器的内容,否则使用默认寄存器[""]. 可以看到系统剪切板的寄存器名称是["*],如果不做快捷键映射的话在 Windows 下使用会很不习惯. 2.2.3 移动光标 除了 [hjkl]之外,快速移动光标的常用技巧: 对于每个窗口,VIM会维护一个"跳转列表",[:jumps]查看,[<C-O>] 和 [C-i]/<TAB> 浏览跳转列表,这是非常重要的技巧大大加快编码速度(理解什么是"跳转"很微妙,总体而言"大"的移动才是跳转,hjkl<C-d><C-f> 等"小"移动/翻页不算跳转). [<C-]>] 可以访问 VIM 里的链接,在查看 :h 时很方便.(这个功能其实是通过查找tags实现的,<C-]> 触发VIM查找光标下的单词为关键字定位 tags) [`.] 光标跳转到上次修改的位置(这是一个自动设置的书签) [:mark]/[m{a-zA-Z}]设置书签,[`{a-zA-Z}]访问书签 [w][b][e]]可以以单词为单位移动光标,[^][$]移动光标至行首行尾也比较常用. [f{x}][;][,]配合飞快行内定位. [3G]/[3gg] 跳转到第3行.[G]跳转到最后一行,[gg]跳转到第一行.[:3]也一样可以定位到第三行. [zz] [zt]/[z<CR>] [zb] [<C-f>](forward 下翻页)/[<C-b>](backward 上翻页)/[<C-u>][<C-d>] 向上/下翻半屏,[<C-y>][<C-e>]上下滚动一行,把光标所在行滚动到屏幕中间/顶部/底部,可以代替鼠标滚轮让阅读更舒适.(我就是在熟悉了这些操作之后感觉终于可以舍去鼠标了) [m{x}] [`{x}] :marks :delmarks 一组书签命令和 [``] [`.] 等自动书签组合使用也可以迅速定位光标 2.3 命令行模式(普通模式下按[:]进入命令行模式,<ESC>返回普通模式) 2.3.1 常用的 Ex 命令: e,w,q !{cmd} 和 {cmd}! 的区别 [:set] 设置参数,选项. [@:][@@] 直接重复执行上一个 Ex 命令 [:r !cmd] / [:r <filename>] 读取命令输出或者文件插入到文档中. [:!] 执行一个空命令相当于从 VIM 切换到 shell 窗口. [:shell] 返回 shell,在 shell 中 [exit] 返回 VIM [K] 相当于执行 man <光标下单词> 2.3.2 查找 (1). [/] 向后查找,[?] 向前查找. [n][N] 跳转到下一个或者前一个匹配项. (2). 进入查找命令行模式后,按 [<C-r>]可以粘贴寄存器的内容,[<C-u>]清空命令行. (3). [:set hlsearch] [:set nohls][:noh] 可以高亮/关闭高亮匹配项 (4). [\C][\c]可以大小写或者忽略大小写查找. 2.3.3 替换 [:substitute]简写[:s]格式见 :h :s/{pattern 需要查找的正则表达式}/替换的文本/标志位 标志位: c 依次确认,n 只报告不执行真正替换,g 替换当前行的全部匹配项,用 [:%s] 配合 g 标志位就可以在整个文件范围内替换全部,& 重复上次 [:s] 命令的标志位. 在替换文本中可以用 \1 ~ \9 使用 pattern 中的捕获. 统计查找匹配个数 [:%s///gn] - :%s 表示在整个文件的范围内运行 :substitute 命令,pattern项留空表示使用上一次查找的 pattern,替换项留空因为不需要替换,标志位 g 表示处理行内的所有匹配项而不仅仅每行的第一项,n 表示不执行真正的替换.所以整个命令就是统计匹配项的个数. 可以用 [map] 映射一个快捷键. [:s] 命令使用 :s 后的第一个字符作为分隔符,所以有时候会看到这样的写法: [:s|pattern|replace|opt] 2.3.4 正则表达式查找替换 [\v] 开启简洁模式,VIM认为输入的文本就是正则文本,否则需要输入很多很多的\ [()] 可以捕获,[\1] 则可以访问对应的捕获,而[\0]总是值匹配整个正则串的文本. [:h usr_27.txt] 有关于正则表达式很详细的说明. 2.3.5 [:global] 命令 [:g/{pattern}/ex cmd] 意思是在整个文件范围内(这是 global 命令的默认 range,和其他命令通常默认行内 range 不同)对匹配 {pattern} 的文本执行后续的 ex 命令.可以想象这是一个强大的命令. 2.4 插入模式(普通模式下按 i,I,a,A,o,O 进入插入模式) 除了快捷键,其他都很普通,<C-r> 可以在插入模式下粘贴寄存器的内容. <o>直接插入新行并进入插入模式,这是非常好用的. 2.5 可视模式([v] / [V] 行选择) 进入可视模式后用光标移动命令选择文本 [w,b,e,%,gg,G] 等都比较常用 一个有用的技巧是 [vi({["] 选中{}[]""()等成对符合内的内容 [va..] 则连同符号一起选中,比如普通模式下在括号内 [vi(] 会选中括号内的所有内容,不含括号. [va(] 则会选中包含括号的文本. ([i]表示 inside,[a]表示 around) 2.6 块编辑模式(<C-v>) [d] 删除所选内容 [I] 在列前插入,[A] 在列后插入.处于块编辑模式时,选中的块是作为一个"文本对象"处理 [i/a] 分别表示 "inside" 和 "around" [<ESC>] 退出块编辑模式后会把插入内容应用到选中的所有行. 3. 实用技巧 - 掌握之后可以应付多文件编辑 3.1 多buffer 理解 buffer 和 file 的区别和联系. [:ls] 列出 buffer [:b {x}] 切换 buffer,可以用序号或者 buffer名(也就是文件名) 中的部分字符然后用 <tab> 补齐 [:bn] 切换到下一个 buffer 如果buffer修改后未保存,则需要使用 [!] 强制切换 [:bn!],[:set autowrite] 之后会自动保存,就不需要 [!] 了. 3.2 多窗口 - [:h window] 除非特别修饰,命令行只对当前窗口有效. 窗口命令以 [<C-w>] 开头 [<C-w>]hjkl/w 循环激活窗口 [:close] 关闭窗口. [:sp]/[<C-w>s] 水平分割当前窗口 [:vsp]/[<C-w>v] 垂直分割当前窗口 [:res +/-N] 改变窗口高度 [:vertical res +/-N] 改变窗口宽度 3.3 多标签 标签是对窗口的分组 [:tabnew] 理解了 buffer - window - tabpage 的关系之后就知道 buffer 才是核心. 4. 程序员的VIM - 掌握之后可以应付中大规模程序开发 4.1 列出所有函数,结构体,类等 tag 定义 -> ctags,[<C-[>] -> taglist 插件 4.2 关键字,变量上下文自动补齐 intelligent sense,[<C-l>]/[<C-p>]/[<C-n>],理解实现这个功能的插件的原理. 4.3 会话保存和恢复[:mks] 4.3.1 [:mks] 保存当前工作区至会话文件. 4.3.2 "vim -S mysession_file" 直接恢复工作区,包括工作路径. 4.4 在 VIM 内部运行 [:make] 并利用 QuickFix 信息迅速定位编译错误的位置. [:copen] 参考 [:h quickfix] 4.5 使用 grep 在多个文件中搜索 4.6 对 C++ 函数名和变量着色 4.6.1 基本原理是定一个变量用一个正则表达式捕获所有函数名,然后对这个变量着色: "highlight Functions,这可以高亮函数 syn match cFunction /\<\w\+\%(\s*(\)\@=/ hi default link cFunction Include 4.6.2 写出合适的正则表达式捕获类名,函数名比较容易.捕获变量名则很麻烦. 5. 编写VIM脚本 - 掌握之后达到需要定制VIM的时候知道大概方向,通过查找资料就可以编写脚本 参考[:h] usr_41.txt Write a Vim script 所谓"VIM脚本"和 shell 脚本是一样的,包括命令和语言函数 .vimrc 就是一个"VIM脚本",一个脚本不仅仅只有语言函数,C程序员往往会忽略着一点.事实上脚本中的大多数功能都是由命令(如 echo,set,exec,norm,Syntax 等)实现的,较少情况下才需要用到语言通过各种控制语句,函数等实现.所以可以在执行Ex命令的 "exec" 和可以执行普通模式下命令的 "norm" 两个命令可以完成大多数功能(完整列表在[:h index.txt]) 基本语法和PHP基本一致,实际使用的时候再 Google 一下细节. 变量声明: let var = 123; exists("var") 可以检查变量var是否被定义,注意是以变量名字符串为参数,而不是 exists(var). 每次赋值都需要使用 let,而不能直接 var += 1 应该写为 let var += 1 字符串连接: '.',字符串是否相等 =~,是否不相等 !~,!~? !~# 忽略/匹配大小写. 调用函数: 需要使用关键字 call,eg. call MyFunc(),如果指定返回值则又不需要 call 关键字,eg. let ret = MyFunc(); 变量的作用域: a:name - 函数参数 / s:name - 当前脚本内有效 / b:name - buffer 作用域 / w:name - 窗口作用域 / g:name - 全局变量 / v:name - vim 内部变量 / l:name - local 函数内部的局部变量 环境变量: $VIM,$VIMRUNTIME 等,[:version] 可以查看 VIM option: &开头,直接给这些变量赋值可以起到和 :set 命令同样的效果,eg. &tabstop = 4 等效于 set &tabstop = 4. 区别是 set 命令可以用vim选项的简写形式,而赋值语句不能. register 变量: @开头 引用 vim 寄存器内的内容. eg. @/ 引用搜索内容,@" 引用无名寄存器 可以用 a:1 来引用 ... 中的第一个参数,a:0 参数个数,查看 [:h a:0] 在脚本中调用普通模式命令的写法 [norm gg] 在脚本中调用 Ex 命令的写法 [exec "colorscheme default"] script 和 当前 buffer 的交互: getline()/setline() 获取/设置行内容 "." 表示当前行 5.2 常用库函数 5.2.1 再次强调库函数都可以用 [:h 函数名] 查看帮助 5.2.2 库函数基本上就是操作各种 VIM 类型的变量,比如 string,list,dictionary 各自有一组工具函数,以及一些数学函数,printf 等 5.2.3 :h 调出 vim 帮助,在 [index] 目录可以找到内置函数索引,以及 option,vim-variables的索引,需要的时候结合 google 再调试一下问题应该不大. 5.3 在脚本中使用正则表达式 5.3.1 正则表达式的选项是通过把 \opt 插入 pattern 文本实现的 \v \i \c 5.3.2 由于字符串会对 \ 做一次转义,所以要注意 \\ 才能把反斜杠传递给解释器,如 "\\v^[0-9]{5}$" 或者 "\\^\\[0-9]\\{5}\\$" 表述相同意思. 6. 杂项 6.1 :set guifont=Consolas:h10 和 :set guifontwide=新宋体:h10 在 Windows 下就可以把字体设置为和 Visual studio 相同,注意 _vimrc 要保存为 utf8 编码,否则中文字符"新宋体"识别不了. 6.2 其它在 Windows 下便利的设置 baidu 都能找到. 6.3 添加帮组文件之后,[:helptags ALL] 命令更新帮助索引. 6.4 在 Ubuntu 16.04 中,如果 vim 不支持系统剪贴板,可以利用输入法的一个特性:在中文输入状态下按 <C-;> 可以输入剪贴板中的内容. 7. 重点插件 7.1 netrw(内置) [:E] 或者 [:e <path>]/[:e.] 打开目录窗口,浏览后 <CR> 用选中文件的内容替换目录窗口. 7.2 ctags(必装) 这是另外一个程序,`apt install exuberant-ctags`.运行 ctags 生成一个 tags 文件(这是一个文本文件),vim 通过选项 tags [:echo &tags] 指定搜索 tag 文件的路径,根据 tag 文件中记录的关键字对应的 PATTERN 通过查找定位到对应位置. <C-]> 或者 :tag {keyword} 跳转到定义. <C-t> / <C-o> 跳转回编辑处. 源文件变化之后需要重新运行 ctags 以生成新的 tag 文件. [!ctags -R] VIM之所以是"程序员的编辑器"很大程度上是因为 ctags 提供的跳转,必装,毫无疑问. 7.3 taglist(选装,依赖 ctags) github.com 搜索 taglist 下载 doc/taglist.txt 和 plugin/taglist.vim 复制到 vim 对应目录 按照 taglist.txt 的说明映射 F8 为打开/关闭 tag 窗口的快捷键 tag 窗口对应条目 <CR> 跳转. tag窗口列出的是当前文件内的函数,结构体等的定义,阅读代码时相当方便,推荐安装. 7.4 MiniBufExplorer(选装) <C-w> 切换到顶部的MiniBufExplorer窗口后 <TAB> 或者其他光标移动命令选择对应的 buffer,<CR>/o | s | v 在不同的窗口内打开指定 buffer set mouse=a 用鼠标操作. 个人不是很喜欢用此类插件,[:b]命令就足够了.开发时工作目录下大概有哪些文件其实是很清楚的,没必要一直显示在屏幕上.一方面可以节省屏幕空间,另外开启 MiniBufExplorer 会导致窗口布局比较混乱. 7.5 nerdtree - 目录树窗口(选装) 因为有 netrw 可以很方便的用 [:e.] 或者 [:E] 打开工作目录下的文件,并且在 Linux 下开发一般都会打开多个控制台窗口可以随时了解工作目录的状况,所以没有太大必要让 nerdtree 占用宝贵的屏幕空间. 7.6 Quickfix - 定位编译错误(内置) [:make] 编译工程(哪怕只有一个源文件的小工程,也使用Makefile,这是个好习惯) [:copen/cclose/cw] 打开/关闭 Quickfix 窗口,[:cnext/cprev] 浏览编译错误,或者在 Quickfix 窗口内 <CR> 定位到源文件. [:h Quickfix] 查看更多命令. ctags 和 Quickfix 让VIM真正成为一个 "IDE". 7.7 各种颜色主题(必装) 颜色主题文件都是 vim 脚本,存放在 $VIMRUNTIME/colors 目录下,把从网上下载的主题文件放入该目录,[:color <主题文件名>] 即可切换至该主题. 之所以是"必装"插件,是因为多尝试不同颜色后会让你爱上 VIM.推荐 http://bytefluent.com/vivify/ 有很多主题可选,并可在线编辑后下载.