Vim documentation: usr_30
*usr_30.txt* For Vim version 8.0. 最近更新: 2017年8月
VIM 用户手册 - by Bram Moolenaar
译者: wandys http://vimcdoc.sf.net
编辑程序
Vim 有很多帮助编写程序代码的命令。例如: 直接在 Vim 内编译程序并跳转到出错位
置;根据语言种类自动设定缩进,还有对注释进行排版。
|30.1| 编译
|30.2| C 文件缩进
|30.3| 自动缩进
|30.4| 其它缩进
|30.5| 制表符和空格
|30.6| 排版注释格式
下一章: |usr_31.txt| 使用 GUI
前一章: |usr_29.txt| 在代码间移动
目录: |usr_toc.txt|
==============================================================================
*30.1* 编译
Vim 有个 "quickfix" 命令集。通过这些命令,你可在 Vim 内编译程序并能直接跳转到
出错位置进行修正。你可以接着重新编译并做修正,直到不再出错为止。
下面的命令运行 "make" (包括你所给出的参数) 程序并捕捉其运行结果: >
:make {arguments}
如果有错误产生那么它们将被捕获到。而且光标会停留在出现第一个错误的地方。
让我们看一个 "make" 的例子。当输入 ":make" 后,屏幕出现如下的结果:
:!make | &tee /tmp/vim215953.err ~
gcc -g -Wall -o prog main.c sub.c ~
main.c: In function 'main': ~
main.c:6: too many arguments to function 'do_sub' ~
main.c: At top level: ~
main.c:10: parse error before '}' ~
make: *** [prog] Error 1 ~
2 returned ~
"main.c" 11L, 111C ~
(3 of 6): too many arguments to function 'do_sub' ~
Press ENTER or type command to continue ~
你会看到编译 "main.c" 文件 (见下) 出现的错误。按一下 <Enter>,Vim 会显示文件
"main.c" 并将光标移到第一个出现错误行。你无需指定文件名或具体的行号,Vim 会根
据出错信息定位相关位置。
+---------------------------------------------------+
|int main() |
|{ |
| int i=3; |
光标 -> | do_sub("foo"); |
| ++i; |
| return (0); |
|} |
|} |
| ~ |
|(3 of 12): too many arguments to function 'do_sub' |
+---------------------------------------------------+
这个命令将光标移到下一个错误所在的行: >
:cnext
光标跳转到文件的最后一行 (第十行),那里有一个多余的 '}'。
有时由于空间不够,Vim 会缩短出错信息。如果你想查看详细信息,可执行: >
:cc
你可以通过 ":clist" 命令得到一个有关所有出错信息的概览。输出的内容如下所示: >
:clist
< 3 main.c: 6:too many arguments to function 'do_sub' ~
5 main.c: 10:parse error before '}' ~
只有那些含有文件名或行数的错误信息会被显示。Vim 假定你对其它的信息不敢兴趣。有
时你也许需要查看那些并不含文件名或行数的信息 (比如连接器输出的有关未定义函数的
错误信息)。要查看所有信息,在命令后加上 "!" 就可以:
>
:clist!
< 1 gcc -g -Wall -o prog main.c sub.c ~
2 main.c: In function 'main': ~
3 main.c:6: too many arguments to function 'do_sub' ~
4 main.c: At top level: ~
5 main.c:10: parse error before '}' ~
6 make: *** [prog] Error 1 ~
Vim 会高亮显示当前错误。如果你想回到上一个错误,执行:
>
:cprevious
以下是一些其它的跳转命令:
:cfirst 到第一处错误
:clast 到最后一处错误
:cc 3 到第三处错误
指 定 编 译 器
通过 'makeprg' 选项可以指定 ":make" 命令启动的程序名。通常它被设定为 "make"。
但 Visual C++ 的用户需要将它设定为 "nmake":
>
:set makeprg=nmake
你可以对这个选项指定参数。如有特殊字符,请用 '\' 来转义。比如:
>
:set makeprg=nmake\ -f\ project.mak
你还可以使用特殊的 Vim 内部关键字。比如用 '%' 来代替当前文件名: >
:set makeprg=make\ %:S
这样,当你编辑文件 "main.c" 时,运行 ":make" 命令会运行下面的程序:
>
make main.c
这不太有用,所以你需要用 :r (root) 修饰符来稍稍修改一下:
>
:set makeprg=make\ %:r:S.o
现在运行的程序就成了:
>
make main.o
有关修饰符的更多信息,请查看这里: |filename-modifiers|。
旧 的 出 错 信 息 列 表
假设你用 ":make" 编译了一个程序。其中的一个文件里有个警告 (warning) 信息而另一
个文件中有一个出错 (error) 信息。你修改了这个错误,并再次运行 ":make" 以参看它
是否已被纠正。现在你想查看刚才的那个警告信息。但由于含有警告的那个文件并没有被
重新编译,你无法在当前出错信息列表中看到原来的警告信息。在这种情况下,你可以用
下面的命令来查看上一个出错信息列表:
>
:colder
然后你可以通过 ":clist" 和 ":cc {nr}" 命令来跳转到出现警告的地方。
要查看下一个出错列表:
>
:cnewer
Vim 可以记住十个出错信息列表。
更 换 编 译 器
要做到这一点,你需要告诉 Vim 所使用编译器产生的错误信息格式。这可以通过
'errorformat' 来设定。这个选项几乎可以配合任意一个编译器的使用,但它的具体配置
却很复杂。请在这里查看详细解释: |errorformat|。
你可能会用到多种不同的编译器。每次都设定 'makeprg' 选项,尤其是 'errorformat'
选项是很繁杂的。为此,Vim 提供一个简便的方法。比如: 要使用微软的 Visual C++ 编
译器:
>
:compiler msvc
这个命令会找到适合 "msvc" 编译器的 Vim 脚本文件并设定相关选项。
你可以为编译器编写自己的脚本文件。请参考 |write-compiler-plugin|。
输 出 重 定 向
"make" 命令会将运行结果重定向到一个错误文件中。具体的工作方式取决于很多方面,
比如 'shell' 选项。如果你的 ":make" 命令不能捕获输出,请检查 'makeef' 和
'shellpipe' 选项。选项 'shellquote' 和 'shellxquote' 可能也会起作用。
如果你无法利用 ":make" 命令重定向输出,一种变通的方法是在另一个窗口编译程序并
将输出重定向到一个文件中。然后你可在 Vim 中查看此文件:
>
:cfile {filename}
这样,你就可以像运行 ":make" 命令那样跳转到出错的地方。
==============================================================================
*30.2* C 风格文件缩进
合理的缩进会使程序更容易理解。Vim 提供了多种方法来简化这项工作。要对 C 或 C 风
格如 Java 或 C++ 的程序缩进,请设定 'cindent' 选项。Vim 相当地了解 C 程序,它
会尽可能地为你自动缩进。通过 'shiftwidth' 选项,你可以指定下一级的缩进空格数。
4 个空格的效果很好。用一个 ":set" 命令就可做到:
>
:set cindent shiftwidth=4
设定了这一选项之后,当你输入了一个语句,比如 "if (x)" 后,下一行会自动向下一级
缩进。
if (flag)
自动缩进 ---> do_the_work();
自动取消缩进 <-- if (other_flag) {
自动缩进 ---> do_file();
保持缩进 do_some_more();
自动取消缩进 <-- }
当你在大括号 ({}) 内输入时,语句会在开始处缩进,而在结束处取消缩进。每次输入
'}' 后都会取消缩进,因为 Vim 不知道你下一步将要输入什么。
自动缩进还能帮助你提前发现代码中的错误。比如当你输入了一个 '}' 后,如果发现比
预想中的缩进多,那可能缺少了一个 '}'。请用 "%" 命令查找与你输入的 '}' 相匹配的
'{'。
缺少 ')' 和 ';' 同样会引起额外的缩进。当你发现比预料中多空白时,最好检查一
下之前的代码。
当你的代码没有被正确地排版,或者你插入或删除了某些行时,你需要重新进行代码缩
进。操作符 "=" 能完成这项功能,最简单的格式是:
>
==
这会缩进当前行。像其它的操作符一样,有三种方式使用它。可视模式下,"=" 缩进选中
的行。一个有用的文本对象是 "a{"。它会选中当前 {} 区。这样,要重新缩进光标所在
代码块:
>
=a{
你还可以重新缩进整个文件:
>
gg=G
但是,不要对已经手工缩进的文件使用此命令。自动缩进的确做得很好,但在某些情况下
你也许确实需要手工缩进。
设 定 缩 进 风 格
不同的人有不同的缩进风格。在默认情况下,Vim 采用了 90% 的程序员都使用的一种方
式并能很好地完成工作。但是,如果你想使用其它的风格,你可以通过 'cinoptions' 选
项来设定。
'cinoptions' 默认为空,Vim 会使用默认风格。你可以在你希望改变的地方添加相应
的项目。例如,要使大括号的缩进如下所示:
if (flag) ~
{ ~
i = 8; ~
j = 0; ~
} ~
请使用这个命令:
>
:set cinoptions+={2
还有很多其它的项目可供使用。请参考 |cinoptions-values|。
==============================================================================
*30.3* 自动缩进
你无需每次编辑 C 文件时都手工设定 'cindent' 选项。通过下面的命令你可以使它自动
化:
>
:filetype indent on
实际上,它不仅为 C 文件打开了 'cindent' 选项。首先,它会使 Vim 自动检查文件类
型。语法高亮同样需要此功能。
一旦文件类型被识别,Vim 会为此类型的文件查找相关的缩进文件。(Vim 的发行中包
含了适合多种不同编程语言的缩进文件。) 该缩进文件将会被用来缩进当前文件。
如果你不喜欢这项功能,可以将它关闭:
>
:filetype indent off
如果你不想为某种特定类型的文件进行缩进,你可以这样做:
首先建一个只包括下行的文件:
>
:let b:did_indent = 1
然后将其重命名为:
{directory}/indent/{filetype}.vim
{filetype} 是文件类型的名字,比如 "cpp" 或 "java"。你可以用下面的命令来得到
Vim 识别到的文件类型名:
>
:set filetype
对本文件,输出会是:
filetype=help ~
这样你就可以用 "help" 来表示 {filetype}。
对 {directory} 部分,你需要根据你的运行时目录来设定。请查看下面命令的输
出:
>
set runtimepath
请使用第一项 (也就是第一个逗号前的名字)。如果上面命令的输出是:
runtimepath=~/.vim,/usr/local/share/vim/vim60/runtime,~/.vim/after ~
你需要使用 "~/.vim" 来表示 {directory}。这样最后的文件名就是:
~/.vim/indent/help.vim ~
除了关闭缩进选项,你还可以编写自己的缩进文件。请参考 |indent-expression|。
==============================================================================
*30.4* 其它缩进
最简单的自动缩进通过 'autoindent' 选项来完成,它会延续上一行的缩进。稍微聪明点
的是 'smartindent',这个选项对那些没有缩进文件可用的编程语言很有用。
'smartindent' 选项没有 'cindent' 选项聪明,但要比 'autoindent' 聪明些。
如果 'smartindent' 被设定,会在每个 '{' 处新增一级缩进,并在每个 '}' 处消
减。另外,对于 'cinwords' 选项所设定的所有单词也会添加新一级的缩进。所有以 '#'
开始的行都会被特殊处理: 所有缩进都被清除。这样做是为了保持所有的预处理命令都在
第一列开始。缩进会在下一行中恢复。
修 正 缩 进
当你利用 'autoindent' 和 'smartindent' 选项延续上一行的缩进时,有很多时候你都
需要添加或删除一个 'shiftwidth' 宽度的缩进。一个快速的方法是在插入模式下利用
CTRL-D 和 CTRL-T 命令。
比如,当你需要输入以下的 shell 脚本时:
if test -n a; then ~
echo a ~
echo "-------" ~
fi ~
设定了这样的选项:
>
:set autoindent shiftwidth=3
你先输入了第一行,按下回车后又输入了第二行的开头:
if test -n a; then ~
echo ~
这时你会发现你需要一个额外的缩进。输入 CTRL-T,结果变为:
if test -n a; then ~
echo ~
在插入模式下,CTRL-T 命令会加入一个 'shiftwidth' 宽度的缩进,无论光标在当前行
的什么位置。
你继续输入第二行,按下回车后又输入了第三行。现在的缩进一切正常。然后你按下
回车输入最后一行,现在的情况如下所示:
if test -n a; then ~
echo a ~
echo "-------" ~
fi ~
要删除这个多余的缩进,可以在最后一行输入 CTRL-D。这会删除一个 'shiftwidth' 宽
度的缩进,无论光标在行中的什么位置。
在普通模式下,你可以用 ">>" 和 "<<" 命令来完成缩进的修正。'>' 和 '<' 是操作
符,因此你可以使用通常的那三种方式来指定你要缩进的行。一个有用的组合是:
>
>i{
这个命令会缩进当前 {} 区内的行,'{' 和 '}' 本身并不被缩进。">a{" 会包括它们。
在下面的例子中,光标停留在 "printf" 上:
原文 ">i{" 之后 ">a{" 之后
if (flag) if (flag) if (flag) ~
{ { { ~
printf("yes"); printf("yes"); printf("yes"); ~
flag = 0; flag = 0; flag = 0; ~
} } } ~
==============================================================================
*30.5* 制表符和空格
'tabstop' 在缺省状态下被设定为 8。尽管你可以改变它,但很快你就会遇到麻烦。其它
的程序不知道你用的制表符间隔值是多少,你的文件看起来会一下子改变许多。另外,很
多打印机都将制表符间隔值固定为 8。所以最好还是保留 'tabstop' 值不变。(如果你编
辑使用其它制表符间隔值的文件,请参考 |25.3| 来修正。)
如果使用 8 个空格来缩进程序,你很快就会走到窗口的最右端;而用 1 个空格又看
不出足够的差别。因此很多人喜欢用 4 个空格。这的确是个很好的折衷。
由于一个制表符 (<Tab>) 是 8 个空格,而你又想使用 4 个空格来缩进,这样你就无
法使用制表符来完成缩进。这里有两种解决办法:
1. 混合使用制表符和空格。由于一个制表符占用 8 个空格的位置,你的文件会含有更少
的字节数。插入或删除一个制表符也要比 8 个空格快很多。
2. 只用空格。这就避免了那些使用不同制表符间隔值的文件所带来的麻烦。
幸运的是,Vim 能够同时很好地支持这两种方式。
混 合 使 用 空 格 和 制 表 符
如果你使用制表符和空格的组合,你直接按正常情况编辑就行。Vim 缺省状态下,能够很
好地处理这些情况。
通过设定 'softtabstop' 可以使工作变得更简便。这个选项能使 <Tab> 看起来像是
被设定为 'softtabstop' 所指定的值,但实际上使用的确是制表符和空格的组合。
当你执行下面的命令后,你每次按下 <Tab> 键,光标都会移动到下一个 "4 列" 边
界:
>
:set softtabstop=4
<
当你在第一列按下 <Tab> 键后,4 个空格会插入到文本中;再次按下 <Tab> 键,Vim 会
先删除那 4 个空格,然后再插入一个制表符。Vim 会尽可能地使用制表符,并辅以空格
填补。
删除会以相反的方式进行。<BS> 键总是删除 'softtabstop' 指定的数量。Vim 尽可
能地使用制表符,而用空格来填补空隙。
下面的例子显示了多次输入制表符然后使用 <BS> 的情况。"." 代表一个空格而
"------->" 代表制表符。
输入 结果 ~
<Tab> ....
<Tab><Tab> ------->
<Tab><Tab><Tab> ------->....
<Tab><Tab><Tab><BS> ------->
<Tab><Tab><Tab><BS><BS> ....
另一种方法是使用 'smarttab' 选项。当它被设定,Vim 对每个在缩进行中的制表符使用
'shiftwidth',而对在第一个非空字符后输入的 <Tab> 使用真的制表符。但 <BS> 键不
会像在 'softtabstop' 选项下那样工作。
只 用 空 格
如果你不想在文件中出现制表符,可以设定 'expandtab' 选项:
>
:set expandtab
<
当这个选项被设定,<Tab>键会插入一系列的空格。这样你可以获得如同插入一个制表符
一样数量的空格。但你的文件中并不包含真正的制表符。
退格键 (<BS>) 每次只能删除一个空格。这样如果你键入了一个 <Tab>,你需要键入
8 次 <BS> 才能恢复。如果你在调整缩进中,输入 CTRL-D 会更快些。
制 表 符 与 空 格 的 相 互 转 换
设定 'expandtab' 选项并不会影响已有的制表符。如果你想将制表符转换为空格,可以
用 ":retab" 命令。使用下面的命令:
>
:set expandtab
:%retab
Vim 会在所有缩进中使用空格而非制表符。但是,所有非空字符后的制表符不会受到影
响。如果你想要转化这些制表符,需要在命令中加入 !:
>
:%retab!
这不大安全。因为它也许会修改字符串内的制表符。要检查这种情况是否存在,可以执
行:
>
/"[^"\t]*\t[^"]*"
这里建议你不要在字符串中直接使用制表符。请用 "\t" 来替代,麻烦会少些。
将空格转化为制表符的命令则恰好相反:
>
:set noexpandtab
:%retab!
==============================================================================
*30.6* 排版注释格式
Vim 最了不起的地方之一就是它理解注释。你可以要求 Vim 排版一段注释。它会做得很
出色。
比如,你有下面的一段注释:
/* ~
* This is a test ~
* of the text formatting. ~
*/ ~
你可以要求 Vim 排版这段注释。将光标定位到注释开头,然后输入:
>
gq]/
"gq" 是用来排版文本的操作符。"]/" 是移动到注释尾的动作。命令的结果是:
/* ~
* This is a test of the text formatting. ~
*/ ~
注意 Vim 可以正确处理每行的开头。
另外一种方法是在可视模式下用 "gq" 排版选中的文本。
要在注释中加入新的一行,先将光标移到中间一行,然后按 "o"。结果会如下所示:
/* ~
* This is a test of the text formatting. ~
* ~
*/ ~
Vim 会为你自动添加一个星号和空格,现在你可以输入新的注释。如果一行注释长于
'textwidth',Vim 会将其自动分开。同样,星号和空格会被自动添加进来:
/* ~
* This is a test of the text formatting. ~
* Typing a lot of text here will make Vim ~
* break ~
*/ ~
要使用这些功能,你必须在 'formatoptions' 选项中指定一些标志位:
r 在插入模式下,输入回车时插入星号。
o 在普通模式下,使用 "o" 或 "O" 时插入星号。
c 根据 'textwidth' 将注释分行。
更多标志位请参考 |fo-table|。
定 义 注 释
'comments' 选项可以定义注释的样式。Vim 可以分辨单行注释和那些包含开头,中间,
结尾三部分的注释。
很多单行注释都是以一个特殊的字符开头。在 C++ 中是 //,在 Makefile 中是 #,
在 Vim 脚本中是 "。比如,要使 Vim 理解 C++ 注释:
>
:set comments=://
<
冒号将条目的标志位和用来识别注释的字符分开。'comments' 的一般格式是:
{flags}:{text}
{flags} 部分可以为空 (就像本例)。
不同的条目可以连接在一起,用逗号隔开。这样可以在同时识别多种不同的注释。比
如,让我们编辑一个 email 信息。当回复时,别人写的内容会以 ">" 和 "!" 字符开
头:
>
:set comments=n:>,n:!
这里有两个条目。一个识别以 ">" 开头的注释,一个识别以 "!" 开头的注释。两个都设
定了 "n" 标志位,这意味着注释可以嵌套。也就是说,一个以 ">" 开始的行可以在 ">"
之后包括其它的注释符号。这样就可以用来排版下面的文本了:
> ! Did you see that site? ~
> ! It looks really great. ~
> I don't like it. The ~
> colors are terrible. ~
What is the URL of that ~
site? ~
试着将 'textwidth' 设定为其它的值,例如 80。在可视模式下选中注释,然后输入
"gq"。结果为:
> ! Did you see that site? It looks really great. ~
> I don't like it. The colors are terrible. ~
What is the URL of that site? ~
你会注意到 Vim 并没有将文本从一种注释移动到另一种注释。因为第一行以 ">!" 开
头,而第二行以 ">" 开头,Vim 知道它们是不同的注释,所以第二行的 "I" 并没有移到
上一行。
包 括 三 部 分 的 注 释
C 语言注释的样式是: 以 "/*" 开头,中间含有 "*",以 "*/" 结尾。我们可以通过
'comments' 选项来这样进行设定:
>
:set comments=s1:/*,mb:*,ex:*/
开始部分用 "s1:/*" 定义。"s" 表示三部分注释的开始。冒号将标志位与代表注释的特
殊字符 "/*" 分开。这里有一个标志位 "1"。它指明注释的中间部分有一个空格位置的偏
移。
"mb:*" 的 "m" 表示这是注释的中间部分。"b" 标志位表示星号后要有空格。否则
Vim 会将形如 "*pointer" 的语句视为某个注释的中间部分。
"ex:*/" 中的 "e" 表示注释的结尾。"x" 标志位表示在 Vim 自动插入星号后,输入
"/" 会删除多余的空格。
要了解更多细节,请参考 |format-comments|。
==============================================================================
下一章: |usr_31.txt| 使用 GUI