Vim 实用技术,第 3 部分: 定制 Vim

Vim脚本基础与配置
本文介绍Vim脚本的基础知识,包括变量、表达式、条件与循环语句及函数等内容。并通过一个具体的.vimrc文件配置示例,展示了如何定制Vim以提高编辑效率。

3.1. Vim 脚本基础

在 .vimrc 文件中,和在第二章提到的插件和语法文件中,使用的语言就是 Vim 脚本语言。这种脚本语言语法有点像 BASIC,表达式有点像 C,还是比较容易理解的。本章中并不打算对其作很系统的介绍(要完整了解的话,请参见“:help usr_41.txt”),而只是介绍一些基本知识,特别是,了解定制 .vimrc 所需要的基本知识。

Vim 脚本相当于可直接在命令模式下执行的命令,只是不需要输入前面的冒号(如果用了冒号也不会出错)。因此,像设置选项、创建键盘映射这样的命令是直接可用的。当然,作为一种脚本语言,除了普通键盘上会输入的命令外,我们还需要一些更复杂的功能,特别是:变量,表达式,条件和循环语句,函数。

3.1.1. 变量

Vim 中使用如下的语法对变量进行赋值(创建变量):

let 变量名 = 数值

变量类型有两种,整数和字符串,在第一次赋值之前都不能使用。变量名除了可使用常规的字母、下划线和数字外,还可以使用几种特殊的前缀:

  • “b:”——只对当前缓冲区(buffer)有效的变量;
  • “w:”——只对当前编辑窗口(window)有效的变量。
  • “g:”——全局变量(在函数中访问全局变量必须使用该前缀,不加前缀的话则认为是函数内的局部变量);
  • “s:”——变量名只在当前脚本中有效;
  • “a:”——函数的参数;
  • “v:”——Vim 内部预定义的特殊变量(参见“:help vim-variable”)。

下面三个前缀用来访问特殊的数值,由于行为和变量较为相似(可以读取和修改),也放在这儿一起讲:

  • “$”——访问环境变量;
  • “&”——访问 Vim 选项;
  • “@”——访问寄存器。

当变量不再使用时,可以使用“unlet 变量名”删除变量。

3.1.2. 表达式

和 C 非常类似,可以使用变量和常量,可以使用括号,可以调用函数(“函数名(...)”),支持加法(“+”)、减法(“-”)、乘法(“*”)、除法(“/”)和取模(“%”),支持逻辑操作(“&&”、“||”和“!”),支持三元条件表达式(“a ? b : c”)。字符串操作方面当然比 C 要强,可以使用“.”进行字符串拼接;可使用“==”、“<=”等进行字符串大小比较,可使用“=~”和“!~”进行正则表达式匹配,而且可以在比较操作符后面添加“#”或“?”来强制进行大小写敏感或不敏感的比较(缺省受 Vim 选项 ignorecase 影响)。显示一个表达式的结果,可以使用“:echo 表达式”显示到状态栏上,或者在插入模式下使用“Ctrl-R=表达式”插入到缓冲区的文本中。

和其它很多在 Unix 下成长起来的语言一样,Vim 的字符串常量有双引号和单引号两种方式。使用单引号的话,单引号间的任何字符都是字符串的一部分,其中不能再包含单引号。使用双引号的话,则可以使用“/”产生换码序列(具体可参考“:help expr-quote”),如“/n”代表换行符,“/"”代表双引号,“//”代表反斜杠本身,等等。

需要注意的话,双引号除了可以表示字符串常量外,还可以表示注释。行首的“"”,以及表达式中出现的成单的“"”,都表示“"”后面的部分全部是注释。

3.1.3. 条件和循环语句

条件语句形式如下:


if 表达式
  语句
endif


if 表达式
  语句
else
  语句
endif


if 表达式
  语句
elseif 表达式
  语句
endif

循环语句形式如下:


while 表达式
  语句
endwhile

条件和循环语句都可以嵌套。这些比较简单,就不多加说明了。

3.1.4. 函数

在表达式中使用函数时,就跟 C 里面的方式类似,直接使用函数名加括号,括号里写上参数(可选)。在不需要返回值的情况下调用函数时,稍稍有些不同,要使用“call”命令,后面跟函数名和括号(括号里面写上可能有的参数)。

定义函数使用下面的语法:


function 函数名称(参数列表)
  语句
endfunction

如果已有同名函数存在,Vim 会报错,除非在“function”后面加上一个“!”。

如果参数中不包含“...”,那么参数的数量是固定的,函数的调用者必须提供跟定义同样多的参数(在函数定义中使用参数名之前加上“a:”进行访问)。如果参数中包含“...”,那么参数的数量不固定,除了可以使用参数名称访问传递过来的参数外,还可以使用“a:0”知道额外传递的参数数量,使用“a:1”、“a:2”等访问这些额外传递的参数。

要在函数的中间返回,或者要返回数值的话,可以使用“return”语句。

Vim 内部定义了一百多个函数,详细列表请参见“:help function-list”。

3.2. 我的 .vimrc

作为一个 Vim 脚本的一个具体示例,我将讲解一下最实用的情况,我的 .vimrc 文件。文件 .vimrc.html (请下载到本地打开) 是我的 .vimrc 文件通过以下步骤生成的 HTML 文件:

1. 在 Vim 中打开 .vimrc 文件;

2. 执行命令“:colorscheme koehler”(缺省配色可能在浏览器中效果不佳)

3. 执行命令“:%!nl -w4 -s' '”(1.11 节)

4. 执行命令“:TOhtml”(1.13 节)

5. 执行命令“:w”

可以把浏览器中的文本内容粘贴到 Vim 中,然后使用下面这个替换命令“:%s/^ /+[0-9]/+ //”删除前面的行号,来恢复出最初的 .vimrc 文件。

下面逐行进行讲解,并包含理解其内容所需的资料的链接。建议大家直接阅读 .vimrc 文件的内容,并在有疑问时查阅下面的解释。

第 1 行:注释(3.1.2 节末段),其中包含一个模式行(1.4 节和 1.5 节)。

第 2 行:首先判断系统是否具有“自动命令”(autocmd)的支持,有的话才执行第3到第六行的内容(1.1 节、“:help has”和“:help feature-list”)。

第 3 行:纯注释(后面我将跳过注释行不再说明)。

第 4 行:清除所有的自动命令(“:help autocmd-remove”),以方便调试,可以使用“source ~/.vimrc”查看一些修改后的效果(“:help source”)。

第 6 行:对于后缀为“.asm”的文件,认为其是微软的 Macro Assembler 格式(“:help masm-syntax”)。

第 7 行:与第 2 行的 if 语句配对。

第 8-10 行:当使用了图形界面时(“:help feature-list”),确保所有的文件类型会在菜单“语法”(“Syntax”)下出现,而不是出现一个菜单项“Show filetypes in menu”。缺省行为可以让 Vim 启动得更快一点点。

第 11-13 行:当使用了图形界面,并且环境变量 LANG 中不含“.”(即没有规定编码)时,把 Vim 的内部编码设为 UTF-8。

第 14 行:不需要保持和 vi 非常兼容(“:help 'compatible'”)。

第 15 行:执行 Vim 缺省提供的 .vimrc 文件的示例,包含了打开语法加亮显示等最常用的功能。

第 16 行:打开自动缩进(1.4 节)。

第 17 行:缺省不产生备份文件(“:help 'backup'”)。

第 18 行:在输入括号时光标会短暂地跳到与之相匹配的括号处,不影响输入(“:help 'showmatch'”)。

第 19 行:正确地处理中文字符的折行和拼接(1.12 节)。

第 20 行:可自动识别的文件类型为带 BOM 字符的 Unicode 文件、UTF-8 编码的文件和 GBK 编码的文件。

第 21 行:设置状态行,使其能额外显示文件的编码信息,如图 2 中的“gbk”和“big5”(“:help 'statusline'”)。


图 2
图2  

第 22-24 行:如果该 Vim 支持鼠标,则启用鼠标支持(1.3 节)。

第 25-29 行:判断 Vim 是否包含多字节语言支持(multi_byte 特性),并且版本号(“:help v:version”)大于 6.1(包含 ambiwidth 选项)。

第 26-28 行:如果 Vim 的语言(“:help v:lang”;受环境变量 LANG 影响)是中文(zh)、日文(ja)或韩文(ko)的话,将模糊宽度的 Unicode 字符的宽度(ambiwidth选项,1.2 节)设为双宽度(double)。

第 31-36 行:改变上、下方向键行为方式:通常情况下这些键的作用范围是逻辑行,所以如果行很长的话光标的移动可能会不太方便;这些键盘映射把这些键的作用范围改成屏幕行(“help gk”),还为习惯使用“j”、“k”的人增加了映射“Ctrl-j”和“Ctrl-k”作用于屏幕行。前面四个映射使用的命令是“noremap”,作用于正常模式、可视模式和命令执行时;后面两个映射使用的命令是“inoremap”,仅作用于插入模式,其中使用“Ctrl-O”临时执行一个普通模式的命令(“:help i_CTRL-O”)。

第 38-41 行:在 Vim 中的插入模式中可以使用“Ctrl-R =”计算整数表达式的数值,但 Vim 本身没有计算浮点表达式的能力。这四个映射提供了浮点表达式的计算能力:使用“/ma”(假设 Leader 字符为缺省的“/”,参见“:help <Leader>”)可将计算的结果放到下一行上(待计算的表达式为当前行或在可视模式选中的内容),使用“/mr”则用计算的结果替换待计算的表达式(同样为当前行或在可视模式选中的内容)。这些映射假设有一个命令“calcu”可用来计算一个表达式的内容。该命令可用下面的 shell 脚本简单实现:


#! /bin/sh
echo "$*" | sed -e $'s//r$//' -e 's/sin *(/s(/g' -e 's/cos *(/c(/g' -e 's/atan * 
        (/a(/g' -e 's/log *(/l(/g' 
-e 's/exp *(/e(/g' | bc -l

该脚本把表达式转换成 bc [1] 能接受的形式(把“sin(x)”转换成“s(x)”,等等),并通过标准输出送到 bc 的标准输入。

该映射较为复杂,此处不详加解释了——其中心思想都是选取待计算的表达式,放到无名寄存器中,然后使用“Ctrl-R"”粘贴到命令行上,使用 calcu 进行计算,再把结果粘贴回正在编辑的缓冲区中;最后一个最复杂,因为为了替换原先的表达式,还需要记住原先被选中的内容的起始和结束位置,你可能希望看一下“:help gv”、“:help v_o”、“:help m”、“:help `”,并复习节 1.11。可以注意一下,在映射中使用了“<silent>”(“:help map-<silent>”),这会防止命令行上回显执行的内容。

第 43-44 行:允许用户使用 F2 来取消搜索/替换的加亮显示。此处一个映射用于正常模式(nmap),一个用于插入模式(imap)。上面已经提过一次,“Ctrl-O”可以在插入模式中执行一个正常模式的命令。

第 46-47 行:这两个映射用于 taglist 插件,使用 F9 直接打开(或关闭)taglist 的窗口。

第 49-50 行:方便快速修订窗口(1.10 节)的使用,可使用 F11(和 F12)查看下一个(上一个)错误(或 grep 项等)。

第 52-65 行:一些适用于文本模式运行的 Vim 的设定;详见下面的具体说明。

第 54-56 行:将变量 Tlist_Inc_Winwidth 的值设为 0,防止 taglist 插件改变终端窗口的大小(有些情况下会引起系统不稳定)。使用“has('eval')”是让该语句仅在功能较为完整、至少支持表达式的 Vim 版本中运行。

第 58-64 行:在系统支持 wildmenu 特性(“:help 'wildmenu'”)启用文本模式的菜单。

第 59 行:打开 wildmenu 选项,启动具有菜单项提示的命令行自动完成。

第 60 行:确保字符序列“<C-Z>”被理解为 Ctrl-Z 而不是分开的五个字符(“:help 'cpoptions'”)。

第 61 行:设置使用 Ctrl-Z 激活自动完成提示。

第 62-63 行:把正常模式和插入模式下的 F10 映射成执行菜单项,并自动提示菜单内容。注意缺省菜单仍不会自动载入,我使用该特性的主要目的是在文本模式的 Vim 中使用 CVS 菜单。图 16 是按 F10 键后再按 Tab 键的结果。


图 16
图16  

第 66-161 行:使用自动命令(autocmd)特性的设置。使用“has”来防止该部分内容在不支持自动命令的 Vim 版本中运行。

第 67-129 行:定义了若干个下面的自动命令会用到的函数,具体在下面的自动命令中讲。请注意在每个“function”之后都用了一个“!”(“:help E122”):这也是为了方便调试,让“source ~/.vimrc”能正确运行而不会报告函数已定义的错误。

第 131-133 行:只要没有将环境变量 VIM_HATE_SPACE_ERRORS 的值设为零,则把变量 c_space_errors 的值设为 1——效果是在 C/C++ 代码中“不正确”的空白字符(行尾的空白字符和紧接在制表符之前的空格字符)将会被高亮显示。图 17所示的代码中,第 3 行的行尾多了两个空格,第 5 行的第一个制表符之前多了个空格。Vim 提示#935 里有一些额外的说明。同时请参看对第 160 行的说明。


图 17
图 17  

第 135 行:使用的英文拼写变体为加拿大风格,即:使用拼写“abridgement”(而不是“abridgment”)、“colour”(而不是“color”)、“realize”(而不是“realise”)、“theatre”(而不是“theater”)等,比较符合中国人一般的英语教科书中的拼写方式,也比较适合于写“国际”英语。

第 138 行:使用键盘映射“/a”来查看光标下字符的属性,主要用于调试 Vim 的语法文件。图 18显示了光标下的字符所属的语法“组”为 vimOption,使用配色方案中的 PreProc(预处理符号)项,前景色为紫色(RGB:#a020f0)。有兴趣可查看 Vim 脚本#383 的具体内容。


图 18
图18  

第 140 行:在函数找不到时(“:help FuncUndefined”),自动在运行环境(Linux 下一般为 ~/.vim)的 autoload 目录下读入与函数名同名的 .vim 文件。这是脚本#383 的建议安装方式(SyntaxAttr.vim 文件放在 autoload 目录下,仅在执行时载入)。

第 142 行:设置适用于 C/C++ 文件的选项(1.4 节)。

第 143 行:把补丁文件的缩进和制表符宽度设定设成和 C/C++ 文件相同(1.4 节)。

第 144 行:取消 Vim 对 HTML 标记自动产生的缩进,但打开自动缩进选项(1.4 节)。

第 145 行:对于变更日志类型的文件,设置行宽为 76 个字符(1.12 节)。

第 147 行:当文件后缀为“.gb”时,认为这是一个 GBK 编码的文件,在读入文件之前(“:help BufReadPre”)调用函数 SetFileEncodings 把原先的 fileencodings 选项的内容保存在本缓冲区的一个变量中(3.1.1 节),然后把 fileencodings 设成 gbk,即只尝试对文件内容作为 GBK 字符序列来解释。

第 148 行:类似于上面把“.big5”后缀的文件当作 Big5 编码的文件,在读入文件之前把 fileencodings 设成 big5,只尝试对文件内容作为 Big5 字符序列来解释。

第 149 行:类似于上面把“.nfo”后缀的文件当作 CP437 编码(即英文 DOS 的 OEM 字符集编码)的文件。效果可参看图 19。


图 19
图19  

第 150 行:在读入 .gb、.big5 或 .nfo 文件之后(“:help BufReadPost”),调用函数 RestoreFileEncodings 恢复保存起来的 fileencodings 原数值。

第 151 行:对于 .txt 后缀的文件,在显示文件时(“:help BufWinEnter”,确保在模式行被执行之后)调用函数 CheckFileEncoding 检查文件是否已修改并且 fileencoding 设有数值。条件满足的话说明该文件在模式行中修改了 fileencoding,因而使用该编码(“:help ++enc”)重新强制(“!”)读入该文件以保证文件被正确解码。Vim 提示#911 里有一些额外的说明。

第 153 行:在遇到 HTML 文件时,如果 Vim 判断出的编码类型和 HTML 代码中使用“<meta http-equiv="Content-Type" content="text/html; charset=编码">”规定的编码不一致,将使用网页中规定的编码重新读入该文件。函数 ConvertHtmlEncoding 会把一些网页中使用的编码名称转换成 Vim 能够正确处理的编码名称;函数 DetectHtmlEncoding 在判断文件类型确实是 HTML 之后,会记下当前的光标位置,并搜索上面这样的 HTML 代码行,找出字符集编码后,在编码不等于当前文件编码(fileencoding)时且当前文件编码为空或等于系统判断出的文件编码时,使用该编码强制重新读入文件,忽略任何错误(“silent!”)。该自动命令写成是可嵌套执行的(“:help autocmd-nested”),目的是保证语法高亮显示有效,且上次打开文件的光标位置能够正确保持。Vim 提示#1074 里有一些额外的说明。

第 155-156 行:确保把 /usr/include/c++ 和 /usr/include/g++-3 目录下的所有文件都当成 C++ 类型的文件,不管 Vim 原先认定这些文件类型是什么(“:help BufEnter”)。C++ 的很多标准头文件(如“algorithm”)没有文件后缀,缺省情况下不会被 Vim 当作 C++ 文件。

第 158 行:第 142 行把 C/C++ 文件的制表符宽度设成了 4(个人设置),但系统的源代码一般使用 GNU 编码规范,制表符宽度为 8。该行设置所有 /usr 目录下的文件都使用 GNU 编码规范(1.4 节)。

第 160 行:在写文件之前(“:help BufWritePre”),调用函数 RemoveTrailingSpace:只要没有将环境变量 VIM_HATE_SPACE_ERRORS 的值设为零,则对于文件类型为 C、C++、Vim 脚本类型的文件,自动悄悄清除所有的行尾空白字符;“normal m`”记忆当前的光标位置,“normal ``”恢复记忆下来的光标位置。

至此为此,我已经介绍了 Vim 的基本知识、很多实用技巧和一些最常用的 Vim 插件,并通过定制 .vimrc 文件介绍了脚本的基本知识。如果有需要进一步深入学习 Vim 或是想提什么关于 Vim 的特定问题的话,不妨参加从 Vim 的网站上参加 Vim 的邮件讨论列表,应该会获益良多。而作者也希望本文至此也已经完成了引导读者学习、了解 Vim 的高级特性的任务。


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值