第六部分 工具
第16章 通过ctags建立索引,并用其浏览源代码(实测不好用)
-
ctags
是一个外部程序(要自己安装),它通过扫描代码库,生成关键字的索引。ctags
使得我们可以快速地跳到函数及类的定义之处,从而实现浏览整个代码库的目的。ctags
的输出结果也可以用于建立自动补全的单词列表
-
ctags
的使用- 在系统命令行中调用 ctags,以要建立索引的文件路径作为它的参数,既可以是一个文件,也可以是多个文件
- ctags会创建了一个名为
tags
的纯文本标签文件- 标签文件前几行由元数据组成。
- 此后的每一行文本均由关键字、文件名以及关键字在源代码中的位置这3项内容构成。
- 关键字是按照字母顺序排列的,因此Vim可以采用折半查找法快速地定位到某个关键字。
-
配置
vim
使用ctags
tags
选项指定了 Vim 应该到哪里去找标签文件,当tags
选项为./
时,Vim将把它替换成当前文件所在的路径
- 通过
:set tags?
查看tags选项的默认配置为tags=./tags,tags
,即Vim会在当前文件所在目录以及工作目录中查找标签文件- 如果Vim已在第一个标签文件中找到了匹配项,就不会在第二个标签文件中继续查找了
- 我们可以在工程的每一个子目录中都建立一个标签文件,或者为了简单起见,只在工程的根目录中维护一个全局的标签文件即可。
- 之后按
<C-]>
可以到达定义,按<C-t>
可以回来
- 之后按
-
ctags
的更新- ① 手动更新:
:!ctags -R
将从Vim当前的工作目录开始,遍历其所有的子目录,并为其中的每一个文件建立索引。最终,再把这个标签文件保存到当前工作目录中。每次改变文件都需要更新一下标签文件,可以使用:nnoremap <f5> :!ctags -R<CR>
映射,这样按<F5>就能快捷激活这一命令
- ① 手动更新:
- ② 每次保存缓冲区时自动更新:使用Vim的自动命令功能,它允许我们在某个事件发生时调用一条命令,这些事件包括缓冲区的创建、打开或者保存等,
:autocmd BufWritePost * call system("ctags -R")
会在每次保存文件时自动调用ctags,这样每当我们保存文件的改动时,都会对整个代码库进行更新索引操作。- ③ 提交代码改动时,自动更新代码库的索引:可参见《Effortless Ctags with Git》一文
- 但是编辑器之外的改动还是没办法及时更新,维护这个标签文件实在麻烦, 所以这个ctags我感觉还是比较鸡肋
第 17 章 编译代码,并通过Quickfix列表浏览错误信息
-
Quickfix 列表会维护一组由文件名、行号、列号(可选)与消息组成的注释定位信息。
-
⭐️ 不用离开Vim也能编译代码
-
使用
:make
编译代码 -
Vim会对输入结果进行某些智能的处理,除了会显示make命令的输出结果外,还会解析结果中的每一行内容,并把文件名、行号以及错误信息提取出来。对于每一条出错信息,Vim都会在quickfix列表中为其创建一项记录。我们可以上下浏览这些记录项,让Vim跳到错误信息所在的行上。
-
运行完
:make
后,Vim 会跳转到quickfix 列表的第一项记录 -
:make!
位于结尾处的符号!
将指示Vim只更新quickfix列表,而不跳到第一处错误。 -
使用
<C-o>
命令将返回跳转列表(jumplist)的上一处位置
-
-
Quickfix列表
-
quickfix列表会保存一组针对单个或多个文件内容的位置信息。
-
每一项记录可以是在执行
:make
时由编译器产生的出错信息,也可以是在执行:grep
时找到的查找匹配。 -
浏览Quickfix列表
命令 用途 :cnext
跳转到下一项 :cprev
跳转到上一项 :cfirst
跳转到第一项 :clast
跳转到最后一项 :cnfile
跳转到下一个文件中的第一项 :cpfile
跳转到上一个文件中的最后一项 :cc N
跳转到第n项 :copen
打开quickfix窗口 :cclose
关闭quickfix窗口 :cnext
和:cprev
都可以在其前面附加执行次数- quickfix 窗口也有其特别之处。如果我们将光标置于某条列表项并按
<CR>
键的话,Vim 将会打开相应的文件,并将光标置于包含匹配结果的那一行上。文件会显示在 quickfix 窗口上方的窗口中,但如果该文件已经在当前标签页的某个窗口中打开了,那么就会复用该缓冲区。 - 通过运行
:colder
命令,我们可以回溯quickfix 列表之前的某个版本(Vim 会保存最近的 10 个列表)。为了从旧的quickfix 列表回到比较新的列表,我们可以运行:cnewer
。请注意,:colder
与:cnewer
命令也都支持次数,这意味着可以分别让这两个命令运行指定的次数 - 在运行
:colder
或者:cnewer
之后打开quickfix窗口,状态栏将会显示刚刚用于创建此列表的命令。 - 可以将 :colder与 :cnewer命令想象成针对quickfix列表的撤销(undo)与重做(redo)命令。就是说,我们可以试着执行其他重新生成 quickfix 列表的命令,而不必担心后果
-
-
位置列表
-
对于每一条用于填充 quickfix 列表的命令,都有一条对应的命令(加了个l前缀),把结果保存到位置列表,如
:make
、:grep
以及:vimgrep
会使用 quickfix 列表,类似地,:lmake
、:lgrep
以及:lvimgrep
将使用位置列表 -
区别在于:quickfix列表是全局的,但是每个窗口都对应一个位置列表。
-
-
定制外部编译器
-
makeprg
选项允许我们指定运行:make
时所调用的程序。通过以下命令,我们可以指示Vim运行nodelint:➾:setlocal makeprg=NODE_DISABLE_COLORS=1\ nodelint\ %
这相当于在shell中执行了
export NODE_DISABLE_COLORS=1 nodelint ~/quickfix/fizzbuzz.js
- 在缺省情况下,nodelint采用ANSI色标编码把错误信息高亮为红色。而配置NODE_DISABLE_COLORS=1将会禁用颜色高亮,这样一来,就可以更容易地解析出错信息。
-
让Vim解析不同外部编译器的报错信息,使得它可以填充quickfix列表
errorformat
选项允许我们指导 Vim 如何解析由:make
产生的输出结果,配起来比较麻烦,详见:h errorformat
-
⭐️ 可以将
makeprg
和errorformat
保存到某个文件里,然后利用:compiler
命令来激活它:compiler nodelint
就会激活nodelint编译器插件,它会自动配置makeprg
和errorformat
:args $VIMRUNTIME/compiler/ *.vim
可以查看vim自带的多个编译器插件
-
在Vim的术语中,编译器是指任何可以针对我们的文档进行处理,并生成错误或警告列表的外部程序。而
:make
命令只负责调用外部编译器,并对其输出进行解析,以此构建一个可供浏览的quickfix列表,因此编译器也可以是代码检查或者是什么转换器之类的
-
第18章 通过grep,vimgrep以及其他工具对整个工程进行查找
-
Vim的
:grep
命令给外部 grep(或类似 grep的)程序提供了一层封装 -
在命令行执行
grep
$ grep -n Waldo *
表示在当前目录的所有文件中查找单词“Waldo”-n
指示grep在显示结果时加入行号信息。
- 需要自己根据结果去往对应文件的对应行
-
在Vim内部调用grep
:grep Waldo *
- Vim会在后台为我们在 shell 中执行
grep -n Waldo *
,并且对结果进行处理,将它们加载进quickfix列表中。 -n
参数默认添加,其他参数,如-i
表示不区分大小写,需要手动添加
-
定制
:grep
-
grepprg
选项负责指定所调用的shell程序- 缺省设置为
grepprg="grep -n $* /dev/null"
$*
表示占位符,将被替换成:grep
命令的参数
- 缺省设置为
-
grepformat
选项则指示Vim如何解析来自:grep命令的输出结果-
缺省设置为
grepformat="%f:%l:%m,%f:%l%m,%f %l%m"
-
%f
表示文件名,%l
表示行号,而%m
则表示匹配行的文本。 -
逗号分隔了多组格式,缺省设置也能匹配
%f:%l%m
或%f %l%m
-
-
用
ack
替换grep
-
缺省情况下,ack会用单独的一行列出文件名,然后再从下一行起,列出此文件匹配行的行号及内容
-
ack -nogroup
=grep -n
-
因此可以设置
:set grepprg=ack\ --nogroup\ $*
-
--columm
参数会给出每一处匹配的行号与列号,下面的配置使得之后可以定位行+列:set grepprg=ack\ --nogroup\ --column\ $*
:set grepformat=%f:%l:%c:%m
(%c表示列号)
-
-
Ack.vim
插件可以完全模拟ack的行为grep
采用的是POSIX
风格的正则表达式,而ack
则采用的是Perl
风格的正则表达式。如果:grep
命令在后台调用ack
,可能会引起误导。
-
-
使用
:vimgrep
- 允许我们使用Vim自带的正则表达式引擎,实现跨文件的查找功能。
- 语法:
:vim[grep][!] /{pattern}/[g][j] {file} ...
- 一行中有多处匹配时,默认为整行文本创建一项记录,
g
标志位可以为一行中的每处匹配创建一条记录 - 设置
j
标志位使得只更新quickfix列表,但不跳到第一处匹配 {file}
可接受的参数与:args
命令的相同(文件名、通配符、反引号表达式以及这些内容的组合),还可以使用##
,它将被扩展成参数列表中的所有文件- ⚠️
:vimgrep
命令与Vim查找历史之间的关联程度并不高。因此,如果我们想重用最近一次的查找模式,不能空着,必须通过<C-r>/
将其直接粘贴至查找域才行。
第 19 章 自动补全
-
Vim的自动补全可以在插入模式下被触发,Vim首先会根据当前编辑会话内所有缓冲区的内容建立一份补全列表,然后再检测光标左侧的字符,看能否找到单词的一部分。如果找到的话,会用这个未完成的单词对补全列表进行过滤,所有不是以它开头的内容都将被过滤掉。最终的补全列表将以菜单形式出现,供我们选择。
-
大小写敏感问题:
ignorecase
选项开启后,除了查找忽略大小写,自动补全也会忽略大小写infercase
选项可以修正这一副作用,只不过Vim只会按照你输入的格式进行插入,如文档中有One
,你输了一个o
,那么虽然可以匹配one
,但第一个字母是小写
-
触发Vim自动补全的方式
命令 补全类型 <C-n>
普通关键字 <C-x><C-n>
当前缓冲区关键字 <C-x><C-i>
包含文件关键字 <C-x><C-]>
标签文件关键字 <C-x><C-k>
字典查找 <C-x><C-l>
整行补全 <C-x><C-f>
文件名补全 <C-x><C-o>
全能(Omni)补全 -
与自动补全的弹出式菜单进行交互
- 详见
:h popumenu-completion
按键操作 作用 <C-n>
使用来自补全列表的下一个匹配项(next匹配项) <C-p>
使用来自补全列表的上一个匹配项(previous匹配项) <Down>
选择来自补全列表的下一个匹配项 <Up>
选择来自补全列表的上一个匹配项 <C-y>
确认使用当前选中的匹配项(yes) <C-e>
还原最早输入的文本(从自动补全中exit) <C-h>
/<BS>
从当前匹配项中删除一个字符 <C-l>
从当前匹配项中增加一个字符 {char}
中止自动补全并插入字符 {char}
- 最好用的方式:直接按
<C-n><C-p>
或者<C-p><C-n>
是比较好用,其中第一项用来触发自动补全功能,第二项用来回到输入的文本,这样可以继续输入,Vim将实时过滤 - 类似的,其他的触发命令都可以通过加一个
<C-p>
来达到通过输入逐步过滤列表的方式
- 详见
-
当前缓冲区补全:根据当前缓冲区的内容生成补全列表 (
<C-x><C-n>
) -
包含文件补全(
<C-x><C-i>
)-
对通过包含的方式加载进来的文件内容生成补全列表,如
import
,#include
等 -
Vim本身就理解C语言包含文件的方式
-
通过设置
include
选项,我们也可以让它了解其他语言的对应提示符 -
通常,文件类型(file-type)插件会对该选项进行设置,因此基本不需要手动设置,可以通过
:set include
查看
-
-
标签文件补全(
<C-x><C-]>
)ctags
生成的标签文件中的关键字也可以作为补全列表的内容
-
普通关键字补全(
<C-n>
)- 综合缓冲区列表、包含文件以及标签文件的单词列表,生成一个补全列表
- 通过
complete
选项可以定制普通关键字补全时所扫描的位置,缺省为complete=.,w,b,u,t,i
- 例如
:set complete-=i
禁止扫描包含文件,:set complete+=k
激活拼写字典自动补全功能 - 详见
:h complete
- 例如
-
字典单词自动补全(
<C-x><C-k>
)- 在字典中进行查找并插入匹配的单词
- 需要为Vim提供一份合适的单词列表。最简单的方法就是通过运行
:set spell
来激活 Vim 的拼写检查功能 - 如果不想激活拼写检查功能,也可以通过
dictionary
选项来指定一个或多个含有单词列表的文件
-
整行文本补全
<C-x><C-l>
根据当前缓冲区的行进行行补充
-
文件名 自动补全
- 在插入模式下,可以通过
<C-x><C-f>
自动补全文件名 - 插入的文件名是相对于当前工作目录的
:pwd
命令获取当前工作目录- 通过
:cd {path}
命令可以切换工作目录:cd-
将切换到之前的工作目录
- 在插入模式下,可以通过
-
全能补全
-
Vim实现的intellsense 功能,它将提供一份为光标所处的上下文量身定做的补全建议列表
-
通过
<C-x><C-o>
命令进行触发 -
该功能由专用的文件类型插件实现,因此,必须先加载以下配置行
essential.vim set nocompatible filetype plugin on
-
必须安装一个为所用语言实现了全能补全功能的插件。Vim的发行版本身就支持十几种语言,包括HTML、CSS、JavaScript、PHP以及SQL。可以通过
:h compl-omni-filetypes
找到完整的列表。 -
如果你对基于某个特定语言的全能补全功能不满意,就到官网上淘个新插件,或者干脆自己写一个。要了解如何撰写全能补全插件,你可以查阅
:h complete-functions
-
第 20 章 利用Vim的拼写检查器,查找并更正拼写错误
-
Vim的拼写检查器可以帮助人们更容易地查找并更正拼写错误
-
Vim的发行版通常只为英语提供了拼写文件,但是安装其他语言的拼写文件也绝非难事。
-
:set spell
启用Vim内置的拼写检查器- 缺省情况下,Vim 将用包含英文单词的字典进行拼写检查,拼写有误的单词下方会显示一条红色虚线
-
我们可以用
[s
与]s
命令在拼写错误间相应地进行反向及正向跳转 -
光标位于某个拼错单词之上时,可以通过
z=
命令来获取Vim提供的更正建议列表- 按
行号+<CR>
可以更正为建议的项目 - 按
<Esc>
可以退出 1z=
可以直接改正为第一个纠正项
- 按
-
常用命令
命令 用途 ]s
跳到下一处拼写错误 [s
跳到上一处拼写错误 z=
为当前单词提供更正建议 zg
把当前单词添加到拼写文件中 zw
把当前单词从拼写文件中删除 zug
撤销对当前单词的 zg
或zw
命令 -
配置
spelllang
选项,可以我们更改缺省的拼写字典- ⭐️
spelllang
选项只在本地缓冲区生效。在编辑两个或两个以上的文档时,可以分别采用不同的拼写文件。如果你是用双语进行写作的话,这样做的确很方便 - 缺省设置
en
同时接受美式和英式英语,可以通过修改成en_us
使得只支持美式拼法 - 可以到
http://ftp.vim.org/vim/runtime/spell/
下载对其他语言的拼写检查文件 - 使用
spellfile.vim
插件,当试图加载系统中没有的拼写文件时,Vim会自动提醒并下载对应文件- 需要
set nocompatible
和filetype plugin on
- 需要
- ⭐️
-
维护专用词典
-
任何通过
zg
命令添加的单词都将被保存到某个拼写文件中 -
spellfile
选项可以指定一个文件路径,用于保存由zg
和zw
命令所添加、删除的单词 -
可以指定多个拼写文件,这意味着可以维护多份单词列表,例如
setlocal spelllang=en_us
setlocal spellfile=~/.vim/spell/en.utf-8.add
setlocal spellfile+=~/books/practical_vim/jargon.utf-8.add
就加载了两个拼写字典,之后可以归类保存,如
1zg
时保存到第一个词典里,2zg
是保存到第二个词典里,依次类推
-
-
更正拼写错误
- ① 普通模式下按
[s
跳转到上一处出错的位置,用z=
进行改正 - ② 插入模式下按
<C-x>s
或<C-x><C-s>
- 优点是会自动向前搜索错误的单词,并且生成更正列表,不用手动跳转
- 只限一行内,不能更正上一行的错误
- ① 普通模式下按
第 21 章 接下来干什么
-
定制自己的
vimrc
- 可以参考的资源
- 热心网友的分享
- 随本书代码发布的essential.vim文件可以作为你的vimrc的基础
- 作者的vim设置:
http://github.com/nelstrom/dotfiles
- 可以参考的资源
-
⭐️ Vim的选项
:h option-list
-
可以使用
:set
命令来改变:set {option}
是打开某一选项:set no{option}
是关闭:set {option}!
是反转选项的行为:set {option}?
是获取该选项当前的状态:set {optino}&
会将选项重置为默认值
-
有些Vim设置项的参数要用到字符串或者数字
- 例如
:set tabstop=2
- 例如
-
可以用一条 set 语句设置多组选项
- 例如
:set ts=2 sts=2 sw=2 et
- 例如
-
大多数Vim选项都有其简写形式
- 例如
ignorecase
=ic
- 动态定制Vim时,适合用简写名称;在配置vimrc文件时,出于可读性的考虑,适合用全称。
- 例如
-
由
:setlocal
命令所触发的改动,只会影响当前窗口或者缓冲区(除非该选项只能被设置为全局性的)
-
-
我们可以将定制化的选项写入文件,加以保存。此后,我们可以通过
:source{file}
命令,将指定{file}
中的设置项应用于当前的编辑会话- 保存在文件里的命令前面可以不加冒号,因为
:source
命令会把文件的每一行都当成Ex命令,让Vim执行。
- 保存在文件里的命令前面可以不加冒号,因为
-
为特定类型的文件应用个性化设置
-
少量的设置可以在vimrc中通过
autocmd
实现,例如if has("autocmd") filetype on autocmd FileType ruby setlocal ts=2 sts=2 sw=2 et autocmd FileType javascript setlocal ts=4 sts=4 sw=4 noet autocmd FileType javascript compiler nodelint endif
autocmd
语句的检测机制将指示Vim监听某一类事件,一旦该事件发生,Vim将执行指定的命令。- 在本例中,我们将监听FileType事件,它会在Vim检测出当前文件类型时被触发。
-
大量的设置最好使用**文件类型插件(ftplugin)**来为不同文件类型进行定制。
-
例如
customizations/ftplugin/javascript.vim setlocal ts=4 sts=4 sw=4 noet compiler nodelint
-
详见
:h ftplugin-name
-
要保证
filetype plugin on
是开启的
-
-