vim入门--(7)基本编程支持

作者 by adtxl / 2022-01-12 / 暂无评论 / 572 个足迹

1. 文件类型和关联设定

程序源代码由文件组成,不同的文件都有一个文件类型。Vim根据文件类型的不同对不同语言采取一定的处理,可能包括

  • 如何对文件进行高亮
  • 制表符(tab)的宽度(空格数)
  • 是否在键入 <Tab> 时扩展为空格字符
  • 每次缩进的空格数(是的,可以和制表符宽度不同)
  • 采用何种自动缩进方法
  • 其他可适用的选项

文件高亮暂不讨论,其它各项都是以选项的形式出现。这些选项都是文件本地(local)选项,即可以在一个文件里修改其数值而不影响其他文件。对于这样的选项,可以用 :setlocal:setglobal 命令分别访问本地值和全局值。一般的 :set 命令在读取数值时(如 :set tabstop?)返回本地值,在写入数值时(如 :set tabstop=4)同时设置本地值和全局值。

制表符宽度对应的选项是tabstop,默认值是8,但在不同的文件类型中该值可能会不同。

是否扩展<Tab>为空格由expandtab选项控制,该选项是布尔类型选项,如果打开了expandtab选项,那输入的tab就会转变为空格;如果关闭的话,则tab字符会被保留。

还有个softtabstop选项,软制表符宽度,设置了这个值后,再按tab键和Backsoace键会让你觉得是按设置的宽度来缩进,有相应数量的缩进或取消缩进,但实际插入的字符仍然受 expandtab 和 tabstop 两个选项控制。

1.1 文件类型判断

Vim的文件类型判断在filetype.vim中执行,用户可以在该文件中自定义一些配置。

1.2 文件类型选项

确定了文件类型,Vim 会从运行支持文件目录下载入同名的文件。以 Python 为例:

syntax/python.vim 包含了如何对 Python 进行语法加亮的设置
indent/python.vim 包含了如何对 Python 代码进行缩进的设置(如在用户输入 if 时进行缩进等)
ftplugin/python.vim 是文件类型插件,包含了其他跟文件类型相关的设置

在vimrc中推荐像下面这样配置:


au FileType c,cpp,objc  setlocal expandtab shiftwidth=4 softtabstop=4 tabstop=4 cinoptions=:0,g0,(0,w1
au FileType json        setlocal expandtab shiftwidth=2 softtabstop=2
au FileType vim         setlocal expandtab shiftwidth=2 softtabstop=2

上面的含义如下,如对c类型语言,将tab扩展为空格,shiftwidth表示当你输入{和回车键时,自动产生的缩进,缩进和制表符宽度设为4

cinoptions 可以精调 C 风格缩进的方式;上面 :0 表示 switch 下面的 case 语句不进行额外缩进,g0 代表作用域声明(public:、private: 等)不额外缩进,(0w1 配合代表没结束的圆括号里的内容折行时不额外缩进。

2. Tags支持

Vim 对一种叫 tags 的文本索引格式有特殊支持。

2.1 生成tags文件的工具

推荐使用Universal Ctags工具。


# 下载
git clone https://github.com/universal-ctags/ctags.git

# 如果没安装autoconf
sudo apt-get install autoconf

./autogen.sh
./configure --prefix=/where/you/want # defaults to /usr/local
make
sudo make install # may require extra privileges depending on where to install

2.2 生成tags文件的命令

要生成 tags 文件时,你可以简单地进入到一个目录下,然后执行下面的语句对该目录及子目录下的程序源文件生成一个 tags 文件:

ctags -R .

但根据场景和语言不同,你可能需要使用更多的选项。比如,对于 C++,我一般使用:

ctags --fields=+iaS --extras=+q -R .

如果是对系统的头文件生成 tags 文件——可以用来查找函数的原型信息——那我们一般还需要加上 --c-kinds=+p 选项。

2.3 使用tags文件

如果当前目录下或当前文件所在目录下存在 tags 文件,Vim 会自动使用这个文件,不需要你做额外的设定。你所需要做的就是在待搜索的关键字上(也可以在可视模式下选中需要的关键字)使用正常模式命令 <C-]>,或者按 g(g 可理解成 go)键加鼠标单击。你愿意的话,也可以手工输入命令 :tag 后面跟空格和待搜索的符号加回车键。这样 Vim 即会跳转到该符号的定义或声明位置。

如果待搜索的符号找不到,Vim 会报错“E426: tag not found”。如果存在一个或多个匹配项,Vim 会跳转到第一个匹配的位置。下面我列举一下其他相关的常用命令:

:tnext(缩写 :tn)跳转到下一个标签匹配位置
:tNext(缩写 :tN)或 :tprevious(缩写 :tp)跳转到上一个标签匹配位置
:tfirst:trewind 跳转到第一个标签匹配位置
:tlast 跳转到最后一个标签匹配位置
:tselect 名称(:tselect 可缩写为 :ts)跟 :tag 类似,但会列举可能的匹配项,让你自己选择(而非跳转到第一个匹配位置)
g]<C-]> 类似,但跟 :tselect 一样会给出一个列表而非直接跳转
:tjump 名称(:tjump 可缩写为 :tj)跟 :tselect 类似,但在只有一个匹配项的时候会直接跳转到匹配位置
g<C-]>g] 类似,但跟 :tjump 一样在只有一个匹配项时会直接跳转到匹配位置
:stselect 名称(:stselect 可缩写为 :sts)跟 :tselect 类似,但结果会打开到一个新分割的窗口中
:stjump 名称(:stjump 可缩写为 :stj)跟 :tjump 类似,但结果会打开到一个新分割的窗口中

我们的标签跳转分为 :tag:tselect:tjump 三种不同方法,正常模式和可视模式的命令 <C-] 也同样有后两种方法的变体,对应的命令分别是 g]g<C-]>。这三个命令前面也都可以额外加上 <C-W>,表示结果打开到新窗口中而非当前窗口。

Vim 默认只在当前目录下和文件所在目录下寻找 tags 文件。对于含多层目录的项目,这个设定就不合适了。解决方法是使用 Vim 的选项 tags。一个小技巧是根据项目的可能深度,检查上层存在的 tags 文件:

" 加入记录系统头文件的标签文件和上层的 tags 文件
set tags=./tags;,tags,/usr/local/etc/systags

3. Tagbar插件

根据上面的描述,我们可以看到 Ctags 是一个可以从源代码中提取符号的工具。事实上,这个工具在我们不生成 tags 文件也都是有用的。Vim 的插件 tagbar 就可以利用 Ctags 来提取符号,生成源代码的结构图。只要 Ctags 能支持这种语言,插件就能“识别” 这种语言,来生成结构图;识别的好坏程度也视 Ctags 对其的支持程度而定。下面是一个示例:

安装,使用minipac,在vimrc中Other plugins”那行下面加入下面的语句,并运行 :PackUpdate 来安装一下:

call minpac#add('majutsushi/tagbar')

我给它映射了快捷键 <F9>,可以快速打开和关闭 Tagbar 的窗口:

" 开关 Tagbar 插件的键映射
nnoremap <F9>      :TagbarToggle<CR>
inoremap <F9> <C-O>:TagbarToggle<CR>

4. Quickfix窗口

Vim 里有一种特殊类型的窗口,被称作 quickfix(快速修复)。这个窗口中会展示外部命令的结果,并可以通过这个窗口中的内容直接跳转到特定文件的特定位置。这个设计最初是用来加速“编辑 - 编译 - 编辑”这个循环的,但它的实际用处并不只是用来编译程序。

可以使用命令:copen,打开quickfix窗口。另外,我们在 quickfix 窗口中也有跟之前类似的“next”类命令:

  • :cnext(缩写 :cn)跳转到下一个出错位置
  • :cNext(缩写 :cN)或 :cprevious(缩写 :cp)跳转到上一个出错位置
  • :cfirst:crewind 跳转到第一个出错位置
  • :clast 跳转到最后一个出错位置

事实上,在这些下一个、上一个的命令中,我用得最多的就是这个快速修复里的跳转了。为了方便记忆,我对它们都映射了相似的快捷键。


" 用于 quickfix、标签和文件跳转的键映射
nmap <F11>   :cn<CR>
nmap <F12>   :cp<CR>
nmap <M-F11> :copen<CR>
nmap <M-F12> :cclose<CR>
nmap <C-F11> :tn<CR>
nmap <C-F12> :tp<CR>
nmap <S-F11> :n<CR>
nmap <S-F12> :prev<CR>

这是我的映射,你可以根据自己的需要进行调整。另外要留意的一点是,取决于环境,不是所有的快捷键都能被 Vim 接收到,尤其在使用终端和远程连接的时候。

4.1 :make命令的其他细节

Vim 里的 :make 命令缺省会执行 make 命令,并且这是可以通过选项 makeprg 来进行配置的。比如,如果你希望启用四路并发编译,你就可以设置 :set makeprg=make\ -j4。你也可以使用 GNU Make 之外的构建工具,但需要注意的是,如果发现 Vim 不能识别你使用的构建工具产生的错误信息,你可能需要利用 errorformat(:help errorformat)选项来告诉 Vim 如何处理错误信息。

4.2 :grep命令

可以使用 Vim 的 :grep 命令根据关键字找到相关的源代码。跟 :make 命令相似,Vim 会调用一个合适的外部程序(可通过 grepprg 选项来进行配置)来进行搜索,并从结果中找到文件名、行号等信息。

小提示:在查看搜索结果时,适时使用 zz(或 zt、zb)重定位当前行在屏幕上的位置,可能可以更清晰地查看前后的相关代码。

4.3 异步支持

上面这些命令,都有一个缺点:在执行过程中你干不了其他事情。对于执行过程可能较慢的 make,这个问题尤其严重。幸好,在 Vim 8 支持异步任务之后,这个问题也得到了解决。我们利用一个插件,就可以获得类似在一些集成开发环境中的体验,在构建过程中仍然可以继续做其他事情。

可以安装asyncrun.vim插件,使用minpac安装,在vimrc中的合适位置加入下面这行,

call minpac#add('skywind3000/asyncrun.vim')

我们还需要一个跟 :make 相似的命令。我使用下面的命令定义

" 和 asyncrun 一起用的异步 make 命令
command! -bang -nargs=* -complete=file Make AsyncRun -program=make @ <args>

这个命令同样会使用 makeprg 选项。不过,还有个问题是默认情况下屏幕上看不到执行过程的信息。我们可以让 asyncrun 在执行命令时立即打开 quickfix 窗口:

" 异步运行命令时打开 quickfix 窗口,高度为 10 行
let g:asyncrun_open = 10

对于 C/C++ 程序员来讲,启动和停止构建应该是一个很频繁的操作吧。所以,我也给它分配了一个快捷键:

" 映射按键来快速启停构建
nnoremap <F5>  :if g:asyncrun_status != 'running'<bar>
                 \if &modifiable<bar>
                   \update<bar>
                 \endif<bar>
                 \exec 'Make'<bar>
               \else<bar>
                 \AsyncStop<bar>
               \endif<CR>

上面的代码通过判断异步任务状态和窗口是否可修改,还会自动执行保存文件和终止构建等操作。

5. 查看文档

Vim 里快捷键 K 可以用来查看光标下关键字的相关文档。它的行为是由选项 keywordprg(:help 'keywordprg')控制的。这个选项的缺省值是 man,表示查看 Unix 的 man 手册,很多文件类型插件会对当前缓冲区设置一个更合适的值,如 Vim 脚本就会直接把行为改成调用 :help 命令。查看 man 手册的默认行为通常只在终端工作良好,而在图形界面 Vim 里会出现显示问题。我推荐使用 Vim 内置的 man 插件,并把全局的 keywordprg 设成 :Man:

" 启用 man 插件source $VIMRUNTIME/ftplugin/man.vim
set keywordprg=:Man

这样,我们在使用 K 命令时,将在 Vim 里直接打开 man 手册

独特见解