EVAL

*eval.txt*      For Vim version 6.4.  最近更新: 2006年3月


                  VIM 参考手册    by Bram Moolenaar
                                译者: Willis
                                http://vimcdoc.sf.net


表达式求值                              *expression* *expr* *E15* *eval*

用户手册第 41 章 |usr_41.txt| 有使用表达式的介绍。

注意: 表达式求值可以在编译时关闭。如果你这么做,本文档介绍的特性就不复存在。见
|+eval| 和下面最后一节 (译者注: 现应为第 10 节)。

1.  变量                |variables|
2.  表达式语法          |expression-syntax|
3.  内部变量            |internal-variables|
4.  内建函数            |functions|
5.  定义函数            |user-functions|
6.  花括号名字          |curly-braces-names|
7.  命令                |expression-commands|
8.  例外处理            |exception-handling|
9.  示例                |eval-examples|
10. 不包含 +eval 特性   |no-eval-feature|
11. 沙盘 (sandbox)      |eval-sandbox|

{Vi 无此功能}


1. 变量 *variables*

有两种类型的变量: 数值 32 位带符号整数。 字符串 NUL 结尾的 8 位无符号字符串。 它们之间会根据使用的情况自动转换。 数值到字符串的转换使用数值的 ASCII 表示。例如: 数值 123 --> 字符串 "123" 数值 0 --> 字符串 "0" 数值 -1 --> 字符串 "-1" 字符串到数值的转换则把字符串开头的一系列数字位转换成数值。十六进制 "0xf9" 和八 进制 "017" 形式的数值可以识别。如果字符串不以数字开始,则结果为零。例如: 字符串 "456" --> 数值 456 字符串 "6bar" --> 数值 6 字符串 "foo" --> 数值 0 字符串 "0xf1" --> 数值 241 字符串 "0100" --> 数值 64 字符串 "-8" --> 数值 -8 字符串 "+8" --> 数值 0 要强制从字符串转换到数值,给它加零: :echo "0100" + 0 布尔型的操作使用数值类型。零代表假值 (FALSE),非零代表真值 (TRUE)。 注意 在命令 :if "foo" 里,"foo" 被转换成 0,也就是假值。要测试字符串非空,应该使用 strlen(): :if strlen("foo") 如果你需要知道变量或表达式的类型,使用 |type()| 函数。 如果 'viminfo' 选项包含 '!' 标志位,大写开头且不包含小写字母的全局变量被保存在 viminfo 文件里 |viminfo-file|。 如果 'sessionoptions' 选项包含 "global",大写开头且包含至少一个小写字母的全局 变量被保存在会话文件里 |session-file|。 变量名 可以保存的位置 my_var_6 无 My_Var_6 会话文件 MY_VAR_6 viminfo 文件 可以使用花括号来构造变量名,见 |curly-braces-names|。

2. 表达式语法 *expression-syntax*

表达式语法小结,优先级从低到高排列: |expr1| expr2 ? expr1 : expr1 if-then-else |expr2| expr3 || expr3 .. 逻辑或 |expr3| expr4 && expr4 .. 逻辑与 |expr4| expr5 == expr5 等于 expr5 != expr5 不等于 expr5 > expr5 大于 expr5 >= expr5 大于等于 expr5 < expr5 小于 expr5 <= expr5 小于等于 expr5 =~ expr5 匹配正规表达式 expr5 !~ expr5 不匹配正规表达式 expr5 ==? expr5 等于,忽略大小写 expr5 ==# expr5 等于,匹配大小写 等等 如上,? 忽略大小写,# 则匹配之 |expr5| expr6 + expr6 .. 数值加法 expr6 - expr6 .. 数值减法 expr6 . expr6 .. 字符串连接 |expr6| expr7 * expr7 .. 数值乘法 expr7 / expr7 .. 数值除法 expr7 % expr7 .. 数值求余 |expr7| ! expr7 逻辑非 - expr7 一元减法: 取反 + expr7 一元加法: 原值 expr8 |expr8| expr9[expr1] 字符串索引 |expr9| number 数值常数 "string" 字符串常数 'string' 按本义出现的字符串常数 &option 选项值 (expr1) 嵌套表达式 variable 内部变量 va{ria}ble 带花括号的内部变量 $VAR 环境变量 @r 寄存器 'r' 的值 function(expr1, ...) 函数调用 func{ti}on(expr1, ...) 带花括号的函数调用 ".." 标明这一层上的操作可以连接。比如: &nu || &list && &shell == "csh" 同一层的表达式从左到右进行分析。 expr1 *expr1* *E109*


expr2 ? expr1 : expr1

'?' 之前的表达式作为数值求值。如果结果非零,最终的结果是 '?' 和 ':' 之间的表达
式的值,不然最终的结果是 ':' 之后的表达式的值。例如:
        :echo lnum == 1 ? "top" : lnum

因为第一个表达式是 "expr2",它不能包含另一个 ?:。另外两个表达式则没有这个限
制,从而使得递归使用 ?: 成为可能。例如:
        :echo lnum == 1 ? "top" : lnum == 1000 ? "last" : lnum

要使之可读,建议使用行继续符 |line-continuation|:
        :echo lnum == 1
        :\      ? "top"
        :\      : lnum == 1000
        :\              ? "last"
        :\              : lnum


expr2 和 expr3                                          *expr2* *expr3*


                                        *expr-barbar* *expr-&&*
"||" 和 "&&" 操作符左右两边各接受一个参数。参数是 (或转化为) 数值。运算结果是:

         输入                            输出 
n1              n2              n1 || n2        n1 && n2 
零              零              零              零
零              非零            非零            零
非零            零              非零            零
非零            非零            非零            非零

操作符可以连接。比如:

        &nu || &list && &shell == "csh"

注意 "&&" 比 "||" 优先级高,所以这等价于:

        &nu || (&list && &shell == "csh")

一旦结果可以确定,表达式使用 "短路" 计算,也就是,不再计算后面的参数,这和 C
的情形类似。比如:

        let a = 1
        echo a || b

这是合法的,即使没有叫 "b" 的变量也是如此。因为 "a" 已经是非零值,结果必然是非
零。下面的情形类似:

        echo exists("b") && b == "yes"

无论 "b" 定义与否,这是合法的。第二个子句只有在 "b" 定义的时候才会被计算。


expr4                                                   *expr4*


expr5 {cmp} expr5

比较两个 expr 表达式,如果结果为假,返回 0,如果结果为真,返回 1。

                        *expr-==*  *expr-!=*  *expr->*   *expr->=*
                        *expr-<*   *expr-<=*  *expr-=~*  *expr-!~*
                        *expr-==#* *expr-!=#* *expr->#*  *expr->=#*
                        *expr-<#*  *expr-<=#* *expr-=~#* *expr-!~#*
                        *expr-==?* *expr-!=?* *expr->?*  *expr->=?*
                        *expr-<?*  *expr-<=?* *expr-=~?* *expr-!~?*
                使用 'ignorecase'    匹配大小写     忽略大小写 
等于                    ==              ==#             ==?
不等于                  !=              !=#             !=?
大于                    >               >#              >?
大于等于                >=              >=#             >=?
小于                    <               <#              <?
小于等于                <=              <=#             <=?
匹配正规表达式          =~              =~#             =~?
不匹配正规表达式        !~              !~#             !~?

示例:
"abc" ==# "Abc"   结果为 0
"abc" ==? "Abc"   结果为 1
"abc" == "Abc"    如果置位了 'ignorecase',结果为 1,不然结果为 0

如果比较字符串和数值,字符串被转化成数值,而比较是在数值之间进行的。这意味着
"0 == 'x'" 为真,因为 'x' 被转化成数值 0。

如果比较两个字符串,使用 strcmp() 或 stricmp()。因而,比较的是数学上的差异 (比
较字节码),而不必然是本地语言的字母的差异。

如果操作符后带上 '#',或者 'ignorecase' 关闭时使用无 '#' 的版本时,比较使用
strcmp().

如果操作符后带上 '?',或者 'ignorecase' 打开时使用无 '?' 的版本时,比较使用
stricmp().

"=~" 和 "!~" 操作符使用右边的参数作为模式来匹配左边的参数。模式的定义见
|pattern|。匹配进行时,总是假设置位了 'magic' 并且 'cpoptions' 为空,无论
'magic' 或 'cpoptions' 实际的值为何。这使得脚本可移植。要避免在正规表达式里使
用的反斜杠需要加倍的问题,可以使用单引号的字符串,见 |literal-string|。
既然字符串假定为单行,多行的模式 (包含 \n,即反斜杠-n) 不会被匹配。不过,按本
义出现的单个 NL 字符可以像普通字符一样匹配。比如:
        "foo\nbar" =~ "\n"      结果为 1
        "foo\nbar" =~ "\\n"     结果为 0


expr5 和 expr6                                          *expr5* *expr6*

expr6 +  expr6 ..       数值加法                *expr-+*
expr6 -  expr6 ..       数值减法                *expr--*
expr6 .  expr6 ..       字符串连接              *expr-.*

expr7 *  expr7 ..       数值乘法                *expr-star*
expr7 /  expr7 ..       数值除法                *expr-/*
expr7 %  expr7 ..       数值求余                *expr-%*

除了 "." 以外,这里所有的操作都把字符串转化成数值。

注意 "+" 和 "." 的差异:
        "123" + "456" = 579
        "123" . "456" = "123456"

如果 '/' 的右边为零,结果为 0x7fffffff。
如果 '%' 的右边为零,结果为 0。


expr7                                                   *expr7*

! expr7                 逻辑非                  *expr-!*
- expr7                 一元减法: 取反          *expr-unary--*
+ expr7                 一元加法: 原值          *expr-unary-+*

'!' 把非零变为零,零变为 1。
'-' 改变数值的符号。
'+' 保持原值。

字符串会先转化为数值。

可以重复和混合这三种运算。例如:
        !-1         == 0
        !!8         == 1
        --9         == 9


expr8                                                   *expr8*

expr9[expr1]            字符串索引              *expr-[]* *E111*

结果是字符串,包含 expr9 里第 expr1 个字节。expr9 视作字符串,expr1 视作数值。
注意 这不适用于多字节编码。

注意 索引 0 给出第一个字符。这和 C 类同。
要小心: 文本列号可是从 1 开始的!例如,要得到光标所在的字符:
        :let c = getline(line("."))[col(".") - 1]

如果字符串的长度小于索引值,结果为空字符串。

                                                *expr9*
number

number                  数值常数                *expr-number*

十进制、十六进制 (0x 或 0X 开始)、或八进制 (0 开始)。


string                                                  *expr-string* *E114*

"string"                字符串常数              *expr-quote*

注意 使用的是双引号。

字符串常数接受以下特殊字符:
\...    三位八进制数 (例如,"\316")
\..     两位八进制数 (必须后跟非数字)
\.      一位八进制数 (必须后跟非数字)
\x..    两位十六进制数指定的字节 (例如,"\x1f")
\x.     一位十六进制数指定的字节 (必须后跟非十六进制数字)
\X..    等同于 \x..
\X.     等同于 \x.
\u....  四位十六进制指定的字符。根据 'encoding' 的当前值决定的编码进行存贮 (例
        如,"\u02a4")
\U....  等同于 \u.....
\b      退格 <BS>
\e      escape <Esc>
\f      换页 <FF>
\n      换行 <NL>
\r      回车 <CR>
\t      制表 <Tab>
\\      反斜杠
\"      双引号
\<xxx>  "xxx" 命名的特殊字符,例如 "\<C-W>" 代表 CTRL-W注意 "\000" 和 "\x00" 强制字符串结束。


literal-string                                          *literal-string* *E115*

'string'                按本义出现的字符串常数          *expr-'*

注意 使用的是单引号。

字符串这里按本义出现。不去掉反斜杠,它也没有特殊含义。按本义出现的字符串不能包
含单引号。如果需要,使用普通的字符串。


option                                          *expr-option* *E112* *E113*

&option                 选项值,如有存在,使用局部值
&g:option               全局选项值
&l:option               局部选项值

例如:
        echo "tabstop is " . &tabstop
        if &insertmode

这里可以使用任何选项值。见 |options|。如果指定要使用局部值,但不存在局部于缓冲
区或局部于窗口的选项,则还是使用全局值。


register                                                *expr-register*

@r                      寄存器 'r' 的值

结果是命名寄存器的内容,以单个字符串表达。换行符在需要时会被插入。要得到无名寄
存器的内容,使用 @" 或 @@。这里不能使用 '=' 寄存器。可用寄存器的相关解释可见
|registers|。


nesting                                                 *expr-nesting* *E110*

(expr1)                 嵌套表达式


environment variable                                    *expr-env*

$VAR                    环境变量

任何环境变量的字符串值。如果该环境变量没有定义,结果为空字符串。
                                                *expr-env-expand*
注意 直接使用 $VAR 和使用 expand("$VAR") 有区别。直接使用的形式只能扩展当前
Vim 会话所知的环境变量。使用 expand() 会先尝试当前 Vim 会话所知的环境变量,如
果不成功,则使用外壳扩展该变量。这会变慢,但可以用来扩展只有外壳知道的变量。
例如:
        :echo $version
        :echo expand("$version")
前者可能不会回显任何内容,后者会回显 $version 变量 (如果你的外壳支持的话)。


internal variable                                       *expr-variable*

variable                内部变量
见下面的 |internal-variables|。


function call           *expr-function* *E116* *E117* *E118* *E119* *E120*

function(expr1, ...)    函数调用
见下面的 |functions|。



3. 内部变量 *internal-variables* *E121*

*E461* 内部变量的名字由字母、数字和 '_' 组成。但不能由数字开始。可以使用花括号,见 |curly-braces-names|。 内部变量通过 ":let" 命令建立 |:let|。 内部变量通过 ":unlet" 命令删除 |:unlet|。 使用非内部变量或已经删除的内部变量的名字会产生错误。 变量有不同的命名空间,根据附加的前缀决定: (无) 函数内: 局部于函数;否则: 全局 |buffer-variable| b: 局部于当前缓冲区。 |window-variable| w: 局部于当前窗口。 |global-variable| g: 全局。 |local-variable| l: 局部于函数。 |script-variable| s: 局部于 |:source| 的 Vim 脚本。 |function-argument| a: 函数参数 (只限于函数内使用)。 |vim-variable| v: Vim 预定义的全局变量。 *buffer-variable* *b:var* "b:" 开头的变量名局部于当前缓冲区。这样,你可以为每个缓冲区定义不同的 "b:foo" 变量。这种变量在缓冲区被删除时 (:bwipeout 或 :bdelete |:bdelete|) 同时被删除。 预定义了如下的缓冲区局部变量: *b:changedtick-variable* *changetick* b:changedtick 当前缓冲区的改变次数。每次改变都会递增。撤销命令在此情形下也被 视作一次改变。这可用来在缓冲区发生改变时执行一些动作。比如: :if my_changedtick != b:changedtick : let my_changedtick = b:changedtick : call My_Update() :endif *window-variable* *w:var* "w:" 开头的变量名局部于当前窗口。窗口关闭时被删除。 *global-variable* *g:var* 函数内部,全局变量可以通过 "g:" 访问。如果不提供前缀,会使用函数的局部变量。在 其他地方,如果你想的话。也可以使用 "g:"。 *local-variable* *l:var* 访问函数的局部变量无需任何前缀。但如果你想要,可以使用 "l:"。 *script-variable* *s:var* Vim 脚本里,可以使用 "s:" 开头的变量。它们不能在脚本之外访问,因而可以称为局部 于脚本的变量。 它们可以用于: - 载入脚本时执行的命令 - 脚本定义的函数 - 脚本定义的自动命令 - 脚本定义的函数和自动命令里定义的函数和自动命令 (递归) - 脚本里定义的用户定义命令 但不能用在: - 该脚本载入的其它脚本 - 映射 - 等等 脚本变量可以用来防止和全局变量名的冲突。看看这个例子: let s:counter = 0 function MyCounter() let s:counter = s:counter + 1 echo s:counter endfunction command Tick call MyCounter() 你可以从任何脚本里启动 "Tick",但那个脚本里的 "s:counter" 变量不会被改变,只有 在 "Tick" 定义所在脚本的 "s:counter" 才会。 另一个完成相同功能的例子: let s:counter = 0 command Tick let s:counter = s:counter + 1 | echo s:counter 如果调用函数或者启动用户定义命令,脚本变量的上下文设置为函数和命令定义所在的脚 本。 脚本变量也可用于脚本里定义的函数里定义的函数。例如: let s:counter = 0 function StartCounting(incr) if a:incr function MyCounter() let s:counter = s:counter + 1 endfunction else function MyCounter() let s:counter = s:counter - 1 endfunction endif endfunction 调用 StartCounting() 时,定义 MyCounter() 函数或者递增或者递减计数器。不管 StartCounting() 在哪里调用,s:counter 变量总可以在 MyCounter() 里访问。 如果相同的脚本多次执行,使用的是同一个脚本变量。只要 Vim 还在运行,就保持有 效。这可以用于维护计数: if !exists("s:counter") let s:counter = 1 echo "脚本首次执行" else let s:counter = s:counter + 1 echo "脚本现在执行了 " . s:counter . " 次" endif 注意 这意味着 filetype 插件不能为每个缓冲区提供不同的脚本变量。这时应使用缓冲 区的局部变量 |b:var|。 预定义的 Vim 变量: *vim-variable* *v:var* *v:charconvert_from* *charconvert_from-variable* v:charconvert_from 要转换的文件字符编码名。只在计算 'charconvert' 选项时有效。 *v:charconvert_to* *charconvert_to-variable* v:charconvert_to 转换后的文件字符编码名。只在计算 'charconvert' 选项时有效。 *v:cmdarg* *cmdarg-variable* v:cmdarg 该变量有两个目的: 1. 文件读写命令的额外参数。目前,它们包括 "++enc=" 和 "++ff="。该变量在文件读写命令的自动命令事件激活之前设置。开 头有一个空格,以便直接把该变量附加到读写命令之后。注意: 这 里不包括 "+cmd" 参数,因为它总要被执行的。 2. 使用 ":hardcopy" 打印 PostScript 文件时,":hardcopy" 命令的 参数。在 'printexpr' 里用得到。 *v:cmdbang* *cmdbang-variable* v:cmdbang 文件读写命令时,和 v:cmdarg 设置的时间类似。如果使用了 "!",其 值为 1,不然为 0。注意 它只能用于自动命令。用户命令里可以用 |<bang>|。 *v:count* *count-variable* v:count 最近的普通模式命令使用的计数。在映射前可用于得到计数。只读。 例如: :map _x :<C-U>echo "计数为 " . v:count<CR> 注意: <C-U> 是必要的,它删除紧跟在计数之后 ':' 所给出的行范 围。 为了后向兼容,这里也可以用 "count"。 *v:count1* *count1-variable* v:count1 类似于 "v:count",但没有给出计数时,缺省为 1。 *v:ctype* *ctype-variable* v:ctype 运行环境当前的字符 locale 设置。它使得 Vim 脚本能得到当前的 locale 编码。技术细节: 这就是 LC_CTYPE 的值。如果没有使用 locale,其值为 "C"。 该变量不能直接设置,请使用 |:language| 命令。 见 |multi-lang|。 *v:dying* *dying-variable* v:dying 通常为零。如果捕获到某个 "致命" 的 signal,设为 1。如果同时捕 获到多个 signal,其值相应增加。在自动命令里可以用来检查 Vim 是否被异常终止。{only 仅限于 Unix} 例如: :au VimLeave * if v:dying | echo "\nAAAAaaaarrrggghhhh!!!\n" | endif *v:errmsg* *errmsg-variable* v:errmsg 最近给出的错误信息。该变量可以设置。 例如: :let v:errmsg = "" :silent! next :if v:errmsg != "" : ... handle error 为了后向兼容,这里也可以用 "errmsg"。 *v:exception* *exception-variable* v:exception 最近捕获且没有完成的例外的值。见 |v:throwpoint| 和 |throw-variables|。 例如: :try : throw "oops" :catch /.*/ : echo "caught" v:exception :endtry 输出: "caught oops"。 *v:fname_in* *fname_in-variable* v:fname_in 输入文件名。只有在计算以下选项时才合法: 选项 用于 'charconvert' 要转换的文件 'diffexpr' 原始文件 'patchexpr' 原始文件 'printexpr' 要打印的文件 *v:fname_out* *fname_out-variable* v:fname_out 输出文件名。只有在计算以下选项时才合法: 选项 用于 'charconvert' 生成的转换完成的文件 (*) 'diffexpr' diff 的结果 'patchexpr' 产生的补丁文件 (*) 如果用于为写入命令进行转换 (比如,":w file"),等价于 v:fname_in。如果用于为读入命令进行转换 (比如,":e file"),它是 一个临时文件名,和 v:fname_in 不同。 *v:fname_new* *fname_new-variable* v:fname_new 文件新版本的名字。只有在计算 'diffexpr' 的时候才有效。 *v:fname_diff* *fname_diff-variable* v:fname_diff 比较结果 (或补丁) 的文件名。只有在计算 'patchexpr' 的时候才有 效。 *v:folddashes* *folddashes-variable* v:folddashes 用于 'foldtext': 反映关闭的折叠的折叠级别的连字符。 只读。|fold-foldtext| *v:foldlevel* *foldlevel-variable* v:foldlevel 用于 'foldtext': 关闭的折叠的折叠级别。 只读。|fold-foldtext| *v:foldend* *foldend-variable* v:foldend 用于 'foldtext': 关闭的折叠的最后一行。 只读。|fold-foldtext| *v:foldstart* *foldstart-variable* v:foldstart 用于 'foldtext': 关闭的折叠的第一行。 只读。|fold-foldtext| *v:lang* *lang-variable* v:lang 运行环境当前的消息 locale 设置。它使得 Vim 脚本能得到当前使用 的语言。技术细节: 这就是 LC_MESSAGES 的值。该值和系统有关。 该变量不能直接设置,请使用 |:language| 命令。 它和 |v:ctype| 不同,因为消息可能使用不同于字符编码的语言。见 |multi-lang|。 *v:lc_time* *lc_time-variable* v:lc_time 运行环境当前的时间消息 locale 设置。它使得 Vim 脚本能得到当前使用的语言。技术细节: 这就是 LC_TIME 的值。 该变量不能直接设置,请使用 |:language| 命令。见 |multi-lang|。 *v:lnum* *lnum-variable* v:lnum 'foldexpr' 和 'indentexpr' 表达式里的行号。只有在计算这些表达 式的时候才合法。 只读。|fold-expr| 'indentexpr' *v:prevcount* *prevcount-variable* v:prevcount 倒数第二次的普通模式命令使用的计数,也就是再上一个命令用的 v:count 的值。可以用来先中止可视模式,然后使用计数。 :vmap % <Esc>:call MyFilter(v:prevcount)<CR> 只读。 *v:progname* *progname-variable* v:progname 包含 Vim 启动时使用的名字 (路径已被去掉)。可以用来为 "view"、 "evim" 等符号链接到 Vim 的名字提供特殊的设置。 只读。 *v:register* *register-variable* v:register 最近的普通模式命令使用的寄存器名字。如果没有使用过,为空。 |getreg()| |setreg()| *v:servername* *servername-variable* v:servername 如果有的话,注册过的 |x11-clientserver| 名字。 只读。 *v:shell_error* *shell_error-variable* v:shell_error 最近一次外壳命令的返回值。如果非零,最近一次外壳命令有错。如果 为零,则该命令成功返回。这只有在外壳把错误代码返回给 Vim 的时 候才工作。-1 通常用来告知该命令无法执行。只读。 例如: :!mv foo bar :if v:shell_error : echo '不能把 "foo" 换名为 "bar"!' :endif 为了后向兼容,这里也可以用 "shell_error"。 *v:statusmsg* *statusmsg-variable* v:statusmsg 最近给出的状态消息。可以设置该变量。 *v:termresponse* *termresponse-variable* v:termresponse 使用 |t_RV| termcap 项目返回的终端的转义序列。Vim 收到 ESC [ 或者 CSI 开始,以一个 'c' 结束,并且其间只包含数字,';' 和 '.' 的转义序列的时候,会设置该值。 如果设置该选项,会激活 TermResponse 自动命令事件,这样你就可以 对终端的应答做出反应。 新的 xterm 的应答是: "<Esc>[ Pp ; Pv ; Pc c"。 Pp 是终端类型: 0 代表 vt100,而 1 代表 vt220。 Pv 是补丁号 (因为这是 patch 95 引入的,补丁号应该总是 95 会更高)。Pc 总是零。 {only 只有在编译时加入 |+termresponse| 特性才有效} *v:this_session* *this_session-variable* v:this_session 最近载入或者保存的会话文件的文件名 |:mksession|。可以设置该变 量。如果没有保存过会话文件,该变量为空。 为了后向兼容,这里也可以用 "this_session"。 *v:throwpoint* *throwpoint-variable* v:throwpoint 最近捕获且未完成的例外的抛出位置。输入的命令不会设置此变量。另 见 |v:exception| 和 |throw-variables|。 例如: :try : throw "oops" :catch /.*/ : echo "Exception from" v:throwpoint :endtry 输出: "Exception from test.vim, line 2" *v:version* *version-variable* v:version Vim 的版本号: 主版本号乘以 100 加上副版本号。5.0 版本对应的是 500。5.1 版本 (5.01) 则是 501。只读。为了后向兼容,这里也可以 用 "version"。 用 |has()| 可以检查是否包含某补丁,例如: if has("patch123") 注意 补丁号和版本有关,5.0 和 5.1 版本都有补丁号 123,但完全不 同。 *v:warningmsg* *warningmsg-variable* v:warningmsg 最近给出的警告消息。该变量可以设置。

4. 内建函数 *functions*

|function-list| 提供了按功能分组的一个函数列表。 (在函数名上使用 CTRL-] 跳转到完整的功能说明) 用法 结果 描述 append( {lnum}, {string}) 数值 在第 {lnum} 行下附加字符串 {string} argc() 数值 参数列表的文件数目 argidx() 数值 参数列表的当前索引 argv( {nr}) 字符串 参数列表第 {nr} 个参数 browse( {save}, {title}, {initdir}, {default}) 字符串 启动文件请求窗口 bufexists( {expr}) 数值 如果缓冲区 {expr} 存在则为真 buflisted( {expr}) 数值 如果缓冲区 {expr} 被列出则为真 bufloaded( {expr}) 数值 如果缓冲区 {expr} 被载入则为真 bufname( {expr}) 字符串 缓冲区 {expr} 的名字 bufnr( {expr}) 数值 缓冲区 {expr} 的数目 bufwinnr( {expr}) 数值 缓冲区 {expr} 的窗口号 byte2line( {byte}) 数值 第 {byte} 个字节所在的行号 char2nr( {expr}) 数值 {expr} 里第一个字符串的 ASCII 值 cindent( {lnum}) 数值 第 {lnum} 行的 C 缩进 col( {expr}) 数值 光标或位置标记的列号 confirm( {msg} [, {choices} [, {default} [, {type}]]]) 数值 用户选择的序号 cscope_connection( [{num} , {dbpath} [, {prepend}]]) 数值 检查 cscope 连接是否存在 cursor( {lnum}, {col}) 数值 移动光标到 {lnum}{col} delete( {fname}) 数值 删除文件 {fname} did_filetype() 数值 如果使用过 FileType 自动命令事件则为真 escape( {string}, {chars}) 字符串 在 {string} 里用 '\' 转义 {chars} eventhandler( ) 数值 如果在事件处理中则为真 executable( {expr}) 数值 如果可执行文件 {expr} 存在则为 1 exists( {expr}) 数值 如果 {expr} 存在则为真 expand( {expr}) 字符串 扩展 {expr} 里的特殊关键字 filereadable( {file}) 数值 如果 {file} 是个可读文件则为真 filewritable( {file}) 数值 如果 {file} 是个可写文件则为真 fnamemodify( {fname}, {mods}) 字符串 修改文件名 foldclosed( {lnum}) 数值 {lnum} 所在折叠的首行,如果是关闭的话 foldclosedend( {lnum}) 数值 {lnum} 所在折叠的末行,如果是关闭的话 foldlevel( {lnum}) 数值 {lnum} 的折叠级别 foldtext( ) 字符串 关闭的折叠显示的行 foreground( ) 数值 把 Vim 窗口带到前台 getchar( [expr]) 数值 让用户输入一个字符 getcharmod( ) 数值 最近输入字符的修饰符 getbufvar( {expr}, {varname}) 缓冲区 {expr} 的变量 {varname} getcmdline() 字符串 返回当前命令行 getcmdpos() 数值 返回命令行的光标位置 getcwd() 字符串 当前工作目录 getfsize( {fname}) 数值 字节计算的文件大小 getftime( {fname}) 数值 文件的最新修改时间 getline( {lnum}) 字符串 当前缓冲区的第 {lnum} 行 getreg( [{regname}]) 字符串 寄存器内容 getregtype( [{regname}]) 字符串 寄存器类型 getwinposx() 数值 GUI Vim 窗口以像素计的 X 坐标 getwinposy() 数值 GUI Vim 窗口以像素计的 Y 坐标 getwinvar( {nr}, {varname}) 窗口 {expr} 的变量 {varname} glob( {expr}) 字符串 扩展 {expr} 里的文件通配符 globpath( {path}, {expr}) 字符串 在 {path} 所有目录下执行 glob({expr}) has( {feature}) 数值 如果支持特性 {feature} 则为真 hasmapto( {what} [, {mode}]) 数值 如果 {what} 的映射存在则为真 histadd( {history},{item}) 字符串 在历史里增加项目 histdel( {history} [, {item}]) 字符串 从历史里删除项目 histget( {history} [, {index}]) 字符串 得到历史的第 {index} 项 histnr( {history}) 数值 历史里最高的项目号 hlexists( {name}) 数值 如果高亮组 {name} 存在则为真 hlID( {name}) 数值 高亮组 {name} 的语法 ID hostname() 字符串 Vim 运行的机器名字 iconv( {expr}, {from}, {to}) 字符串 转换 {expr} 的编码 indent( {lnum}) 数值 第 {lnum} 行的缩进 input( {prompt} [, {text}]) 字符串 从用户得到输入 inputdialog( {p} [, {t} [, {c}]]) 字符串 类似于 input(),但使用 GUI 对话框 inputrestore() 数值 恢复预输入 inputsave() 数值 保存和清除预输入 inputsecret( {prompt} [, {text}]) 字符串 类似于 input(),但隐藏文本 isdirectory( {directory}) 数值 如果 {directory} 是目录则为真 libcall( {lib}, {func}, {arg}) 字符串 调用库 {lib} 的函数 {func},使用参数 {arg} libcallnr( {lib}, {func}, {arg}) 数值 同上,但返回数值 line( {expr}) 数值 光标所在、末行或者位置标记所在的行号 line2byte( {lnum}) 数值 行 {lnum} 的字节位置 lispindent( {lnum}) 数值 行 {lnum} 的 Lisp 缩进 localtime() 数值 当前时间 maparg( {name}[, {mode}]) 字符串 模式 {mode} 的映射 {name} 的右边 mapcheck( {name}[, {mode}]) 字符串 检查匹配 {name} 的映射 match( {expr}, {pat}[, {start}]) 数值 {expr}{pat} 的匹配位置 matchend( {expr}, {pat}[, {start}]) 数值 {expr}{pat} 的结束位置 matchstr( {expr}, {pat}[, {start}]) 字符串 {expr}{pat} 的匹配文本 mode() 字符串 当前编辑模式 nextnonblank( {lnum}) 数值 第一个 >= {lnum} 的非空白行的行号 nr2char( {expr}) 字符串 ASCII 值为 {expr} 的单个字符 prevnonblank( {lnum}) 数值 最后一个 <= {lnum} 的非空白行的行号 remote_expr( {server}, {string} [, {idvar}]) 字符串 发送表达式 remote_foreground( {server}) 数值 把 Vim 服务器带到前台 remote_peek( {serverid} [, {retvar}]) 数值 检查应答字符串 remote_read( {serverid}) 字符串 读入应答字符串 remote_send( {server}, {string} [, {idvar}]) 字符串 发送键序列 rename( {from}, {to}) 数值 换名 (移动) 文件,从 {from}{to} resolve( {filename}) 字符串 解析快捷方式对应的文件名 search( {pattern} [, {flags}]) 数值 搜索 {pattern} searchpair( {start}, {middle}, {end} [, {flags} [, {skip}]]) 数值 搜索 start/end 对的另一侧 server2client( {clientid}, {string}) 数值 发送应答字符串 serverlist() 字符串 得到可用的服务器列表 setbufvar( {expr}, {varname}, {val}) 设置缓冲区 {expr}{varname}{val} setcmdpos( {pos}) 数值 设置命令行的光标位置 setline( {lnum}, {line}) 数值 设置第 {lnum} 行的内容为 {line} setreg( {n}, {v}[, {opt}]) 数值 设置寄存器的值和类型 setwinvar( {nr}, {varname}, {val}) 设置窗口 {expr}{varname}{val} simplify( {filename}) 字符串 尽可能简化文件名 strftime( {format}[, {time}]) 字符串 指定格式的时间 stridx( {haystack}, {needle}) 数值 {haystack} 里第一个 {needle} 的位置 strlen( {expr}) 数值 字符串 {expr} 的长度 strpart( {src}, {start}[, {len}]) 字符串 {src}{start} 开始的 {len} 个字节 strridx( {haystack}, {needle}) 数值 {haystack} 里最后一个 {needle} 的位置 strtrans( {expr}) 字符串 翻译字符串,使之可以显示 submatch( {nr}) 字符串 ":substitute" 的特定匹配 substitute( {expr}, {pat}, {sub}, {flags}) 字符串 {expr} 里的所有 {pat}{sub} 替代 synID( {line}, {col}, {trans}) 数值 {line}{col} 列所在的语法 ID synIDattr( {synID}, {what} [, {mode}]) 字符串 syntax ID {synID}{what} 属性 synIDtrans( {synID}) 数值 {synID} 经过翻译的语法 ID system( {expr}) 字符串 外壳命令 {expr} 的输出 tempname() 字符串 临时文件的文件名 tolower( {expr}) 字符串 字符串 {expr} 变为小写 toupper( {expr}) 字符串 字符串 {expr} 变为大写 type( {name}) 数值 变量 {name} 的类型 virtcol( {expr}) 数值 光标或位置标记的屏幕列 visualmode( [expr]) 字符串 最近使用的可视模式 winbufnr( {nr}) 数值 窗口 {nr} 的缓冲区号 wincol() 数值 光标所在的窗口列 winheight( {nr}) 数值 窗口 {nr} 的高度 winline() 数值 光标所在的窗口行 winnr() 数值 当前窗口的编号 winrestcmd() 字符串 返回恢复窗口大小的命令 winwidth( {nr}) 数值 窗口 {nr} 的宽度 append({lnum}, {string}) *append()* 在当前缓冲区第 {lnum} 行后附加字符串 {string}{lnum} 可以为 零,用于在第一行前插入一行。如果失败 ({lnum} 越界),返回 1,成 功则返回 0。 *argc()* argc() 返回当前窗口参数列表的文件数目。见 |arglist|。 *argidx()* argidx() 返回参数列表的当前索引。0 是第一个文件。argc() - 1 是最后一 个。见 |arglist|。 *argv()* argv({nr}) 返回当前窗口参数列表第 {nr} 个参数。见 |arglist|。"argv(0)" 是 第一个。 例如: :let i = 0 :while i < argc() : let f = escape(argv(i), '. ') : exe 'amenu Arg.' . f . ' :e ' . f . '<CR>' : let i = i + 1 :endwhile *browse()* browse({save}, {title}, {initdir}, {default}) 启动文件请求窗口。只有在 "has("browse")" 返回非零时 (只有在一 些 GUI 版本里) 才可以。 输入的字段包括: {save} 非零时,选择要写入的文件 {title} 请求窗口的标题 {initdir} 开始浏览的目录 {default} 缺省文件名 如果按了 "Cancel" 按钮、出错、或者无法浏览,返回空字符串。 bufexists({expr}) *bufexists()* 返回数值,如果名为 {expr} 的缓冲区存在的话,返回非零。 如果 {expr} 参数是字符串,缓冲区的名字必须与其完全匹配。 如果 {expr} 参数是数值,指定缓冲区号。 未列出的缓冲区也会被找到。 注意 帮助文件在 |:buffers| 里列出的是它们的短名字。但 bufexists() 需要它们的长名字才能找到它们。 使用 "bufexists(0)" 可以测试是否存在轮换文件名。 *buffer_exists()* 已废弃的名字: buffer_exists()。 buflisted({expr}) *buflisted()* 返回数值,如果名为 {expr} 的缓冲区列出的话 (置位了 'buflist' 选项),返回非零。 {expr} 参数用法同 bufexists()。 bufloaded({expr}) *bufloaded()* 返回数值,如果名为 {expr} 的缓冲区存在且已载入的话 (在窗口显 示,或者至少被隐藏),返回非零。 {expr} 参数用法同 bufexists()。 bufname({expr}) *bufname()* 返回缓冲区的名字,如同 ":ls" 命令显示的那样。 如果 {expr} 参数是数值,指定缓冲区号。数值零代表当前窗口的轮换 缓冲区。 如果 {expr} 参数是字符串,它用作 |file-pattern| 来匹配缓冲区名 字。这里总假设置位 'magic' 而 'cpoptions' 为空。如果有超过一个 匹配,返回空字符串。 "" 或 "%" 可用来指定当前缓冲区,"#" 指定轮换缓冲区。 完整的匹配优先,如果没有,也接受在缓冲区名的开始,结束和中间的 匹配。 先查找列出的缓冲区。如果列出缓冲区有唯一的匹配,返回之。不然, 再查找未列出的缓冲区。 如果 {expr} 是字符串,但你想用作缓冲区号,给它加零可以强制转化 为数值型: :echo bufname("3" + 0) 如果缓冲区不存在,或者没有名字,返回空字符串。 bufname("#") 轮换缓冲区名 bufname(3) 缓冲区 3 的名字 bufname("%") 当前缓冲区名 bufname("file2") 匹配 "file2" 的缓冲区名。 *buffer_name()* 已废弃的名字: buffer_name()。 *bufnr()* bufnr({expr}) 返回缓冲区的编号,如同 ":ls" 命令显示的那样。 关于 {expr} 的使用,见上 |bufname()|。 如果不存在符合的缓冲区,返回 -1。 bufnr("$") 是最后的缓冲区: :let last_buffer = bufnr("$") 返回数值,即最大的已有的缓冲区的编号。注意 较小的编号不一定都 对应存在的缓冲区,因为 ":bwipeout" 可能永久地删除了部分的缓冲 区。用 bufexists() 可以测试缓冲区是否存在。 *buffer_number()* 已废弃的名字: buffer_number()。 *last_buffer_nr()* bufnr("$") 已废弃的名字: last_buffer_nr()。 bufwinnr({expr}) *bufwinnr()* 返回数值,即缓冲区 {expr} 对应的第一个窗口的编号。{expr} 的使 用方式见上 |bufname()|。如果缓冲区 {expr} 不存在或者没有对应的 窗口,返回 -1。例如: echo "包含缓冲区 1 的窗口是 " . (bufwinnr(1)) 该编号可用于 |CTRL-W_w| 和 ":wincmd w" |:wincmd|。 byte2line({byte}) *byte2line()* 返回当前缓冲区第 {byte} 个字节所在的行号。取决于当前缓冲区的 'fileformat' 选项,这可以包括不同的换行符。第一个字符的字节编 号为 1。 另见 |line2byte()|、|go| 和 |:goto|。 {only 仅当编译时加入 |+byte_offset| 特性才存在} char2nr({expr}) *char2nr()* 返回 {expr} 第一个字符的数值结果。例如: char2nr(" ") 返回 32 char2nr("ABC") 返回 65 使用当前的 'encoding'。比如对 "utf-8" 来说: char2nr("á") 返回 225 char2nr("á"[0]) 返回 195 cindent({lnum}) *cindent()* 得到第 {lnum} 行根据 C 缩进规则应有的缩进距离,见 'cindent'。 缩进的计算以空格计,因而和 'tabstop' 的值是有关系的。{lnum} 的 使用方式和 |getline()| 相同。 如果 {lnum} 非法或者 Vim 编译时不带 |+cindent| 特性,返回 -1。 *col()* col({expr}) 返回数值,即 {expr} 给定的列位置的字节索引。可接受的位置是: . 光标位置 $ 光标行的行尾 (返回光标行的字符数加 1) 'x 位置标记 x 的位置 (如果该位置标记没有设置,返回 0) 要得到屏幕列的位置,用 |virtcol()|。 注意 只能使用当前文件的位置标记。 例如: col(".") 光标所在列 col("$") 光标行的长度加 1 col("'t") 位置标记 t 的列号 col("'" . markname) 等于 markname 的位置标记的列号 第一列为 1。0 用来返回错误。如果激活了 'virtualedit' 并且如果 光标在行尾之后的话,计算光标位置得到的列号比实际的多 1。可用来 在插入模式得到列数: :imap <F2> <C-O>:let save_ve = &ve<CR> \<C-O>:set ve=all<CR> \<C-O>:echo col(".") . "\n" <Bar> \let &ve = save_ve<CR> *confirm()* confirm({msg} [, {choices} [, {default} [, {type}]]]) confirm() 提供用户一个对话框,从中可以作出选择。返回选择的序 号。第一个选择为 1。 注意: confirm() 只有在编译时加入对话框支持才存在,见 |+dialog_con| 和 |+dialog_gui|。 在 |dialog| 里显示 {msg} 消息,并提供可能的选择 {choices}。如 果 {choices} 不存在或者为空,使用 "&OK" (经过翻译)。 {msg} 是字符串,'\n' 用来包含换行符。在有些系统上该字符串在放 不下时被回绕,但并非所有系统都如此。 {choices} 是一个字符串,用 '\n' 分隔各个选择,例如 confirm("Save changes?", "&Yes\n&No\n&Cancel") '&' 之后的字符提供该选择的快捷键。这样,你可以输入 'c' 来选择 "Cancel"。快捷键不一定是第一个字符: confirm("file has been modified", "&Save\nSave &All") 控制台里,每个选择的第一个字符用作缺省的快捷键。 可选的 {default} 参数是用户按 <CR> 使用的选择号。设定 1 使得第 一个选项成为缺省,如果是 0,则不设定任何缺省。如果不提供 {default},假设为 1。 可选的 {type} 参数指定对话框的类型。只有在 Win32 GUI 上才用得 上,它用以指定图标。可以取的值是: "Error"、"Question"、 "Info"、 "Warning" 或 "Generic"。只有第一个字符是重要的。如果 忽略 {type},使用 "Generic"。 如果用户用 <Esc>CTRL-C 或者别的合法的中断键中止对话框, confirm() 返回 0。 一个例子: :let choice = confirm("你要吃什么?", "&苹果\n&桔子\n&香蕉", 2) :if choice == 0 : echo "快下定决心!" :elseif choice == 3 : echo "好吃" :else : echo "我本人喜欢香蕉。" :endif GUI 的对话框使用按钮。按钮的排放方式取决于 'guioptions' 里的 'v' 标志位。如果包含该标志位,按钮总是竖排的。不然,confirm() 试图把按钮放在一行里。如果放不下,那么还是使用竖排的方式。在有 的系统上,无论如何总是使用横排。 *cscope_connection()* cscope_connection([{num} , {dbpath} [, {prepend}]]) 检查 |cscope| 连接是否存在。如果没有参数,则函数返回: 0,如果 cscope 不存在 (编译没有带该特性),或者不存在 cscope 连接; 1,如果至少有一个 cscope 连接。 如果指定了参数,那么 {num} 的值指定如何检查 cscope 连接存在与 否的方式: {num} 存在检查的方式描述 ----- ------------------------------ 0 等同于无参数 (例如,"cscope_connection()")。 1 忽略 {prepend},使用 {dbpath} 的字符串部分匹配。 2 忽略 {prepend},使用 {dbpath} 的字符串完整匹配。 3 使用 {prepend},使用 {dbpath}{prepend} 的字符串部 分匹配。 4 使用 {prepend},使用 {dbpath}{prepend} 的字符串完 整匹配。 注意: 所有的字符串比较都对大小写敏感! 示例。假定我们有如下设置 (":cs show" 的输出): # pid database name prepend path 0 27664 cscope.out /usr/local 启动方式 返回值 ---------- ---------- cscope_connection() 1 cscope_connection(1, "out") 1 cscope_connection(2, "out") 0 cscope_connection(3, "out") 0 cscope_connection(3, "out", "local") 1 cscope_connection(4, "out") 0 cscope_connection(4, "out", "local") 0 cscope_connection(4, "cscope.out", "/usr/local") 1 cursor({lnum}, {col}) *cursor()* 把光标定位在第 {lnum} 行的第 {col} 列。不改变跳转表。 如果 {lnum} 超过缓冲区的行数,光标定位在缓冲区的末行。 如果 {lnum} 为零,光标留在当前行。 如果 {col} 超过该行的字符数,光标定位在该行的最后一个字符上。 如果 {col} 为零,光标留在当前列。 *delete()* delete({fname}) 删除名为 {fname} 的文件。返回类型为数值。如果成功删除文件,返 回 0,如果删除失败,返回非零。 *did_filetype()* did_filetype() 如果执行自动命令时,激活 FileType 事件至少一次,则返回非零。可 以用于防止在检测文件类型的脚本里再次激活 FileType 事件。 |FileType| 如果编辑另外一个文件,该计数被复位,因而这只能检查 FileType 事 件是否在当前缓冲区里激活过。它允许开始编辑另一个缓冲区的自动命 令设置 'filetype' 并载入语法文件。 escape({string}, {chars}) *escape()*{string} 里用反斜杠转义 {chars} 里的字符。例如: :echo escape('c:\program files\vim', ' \') 返回: c:\\program\ files\\vim eventhandler() *eventhandler()* 如果在事件处理中则返回 1。此时,Vim 在等待用户输入一个字符的时 候被中断,比如,在 Vim 上拖放了一个文件。这也意味着此时不能使 用交互的命令。如果不是,返回零。 executable({expr}) *executable()* 本函数检查名字由 {expr} 指定的可执行文件存在与否。{expr} 必须 是程序不带任何参数的名字。executable() 使用普通的 $PATH。返回 数值: 1 存在 0 不存在 -1 此系统中没有实现 *exists()* exists({expr}) 返回数值,如果 {expr} 被定义,返回非零,不然返回零。{expr} 参 数是字符串,可以使用以下选择之一: &option-name Vim 选项 (只检查是否存在,而不是是否工 作) +option-name 能工作的 Vim 选项。 $ENVNAME 环境变量 (也可以通过和空字符串比较完 成) *funcname 内建函数 (见 |functions|) 或者用户定义 的函数 (见 |user-functions|)。 varname 内部变量 (见 |internal-variables|)。不 适用于 |curly-braces-names|。 :cmdname Ex 命令: 内建命令、用户命令或者命令修 饰符 |:command|。 返回: 1 匹配命令的开始 2 完整匹配命令 3 匹配多个用户命令 要检查命令是否支持,检查返回值是否为 2。 #event 符合此事件的自动命令 #event#pattern 符合此事件和此模式的自动命令 (模式按本 义出现,和自动命令的模式逐字符比较) 要检查某特性是否支持,用 |has()|。 例如: exists("&shortname") exists("$HOSTNAME") exists("*strftime") exists("*s:MyFunc") exists("bufcount") exists(":Make") exists("#CursorHold"); exists("#BufReadPre#*.gz") 符号 (&/$/*/#) 和名字之间不能有空格。 注意 参数必须是字符串,不是变量的名字本身。例如: exists(bufcount) 不检查 "bufcount" 变量是否存在,而是提取 "bufcount" 内容,并检 查其 (根据此处的语法) 是否存在。 expand({expr} [, {flag}]) *expand()* 扩展 {expr} 里的通配符和下列特殊关键字。返回的是字符串。 如果返回多个匹配,以 <NL> 字符分隔 [备注: 5.0 版本使用空格。但 是文件名如果也包含空格就会有问题]。 如果扩展失败,返回空字符串。这不包括不存在文件的名字。 如果 {expr} 以 '%'、'#' 或 '<' 开始,以类似于 |cmdline-special| 变量的方式扩展,包括相关的修饰符。这里是一个 简短的小结: % 当前文件名 # 轮换文件名 #n 轮换文件名 n <cfile> 光标所在的文件名 <afile> 自动命令文件名 <abuf> 自动命令缓冲区号 (以字符串形式出现!) <amatch> 自动命令匹配的名字 <sfile> 载入的脚本文件名 <cword> 光标所在的单词 <cWORD> 光标所在的字串 (WORD) <client> 最近收到的消息的 {clientid} |server2client()| 修饰符: :p 扩展为完整的路径 :h 头部 (去掉最后一个部分) :t 尾部 (只保留最后一个部分) :r 根部 (去掉一个扩展名) :e 只有扩展名 例如: :let &tags = expand("%:p:h") . "/tags" 注意 扩展 '%'、'#' 或者 '<' 开头的字符串的时候,其后的文本被忽 略。这样_不_行: :let doesntwork = expand("%:h.bak") 应该这样: :let doeswork = expand("%:h") . ".bak" 还要 注意 扩展 "<cfile>" 和其它形式只能返回被引用的文件名,而 不会进一步扩展。如果 "<cfile>" 是 "~/.cshrc",你需要执行另一个 expand() 把 "~/" 扩展为主目录的路径: :echo expand(expand("<cfile>")) 变量名和其后的修饰符之间不能有空白。|fnamemodify()| 函数可以用 来修改普通的文件名。 使用 '%' 或 '#' 但当前或轮换文件名没有定义的时候,使用空字符 串。在无名缓冲区使用 "%:p" 生成当前目录,后加一个 '/'。 如果 {expr} 不以 '%'、'#' 或 '<' 开始,它以命令行上的文件名那 样被扩展。使用 'suffixes' 和 'wildignore',除非给出可选的 {flag} 参数而且非零。这里可以有不存在的文件的名字。 expand() 也可用来扩展变量和只有外壳知道的环境变量。但这会很 慢,因为需要启动外壳。见 |expr-env-expand|。扩展后的变量还是被 当作文件名的列表处理。如果不能扩展环境变量,保留其不变。这样, ":echo expand('$FOOBAR')" 返回的还是 "$FOOBAR"。 |glob()| 说明如何找到存在的文件。|system()| 说明如何得到外部命 令的原始输出。 filereadable({file}) *filereadable()* 返回数值,如果名为 {file} 的文件存在且可读,则为真。如果 {file} 不存在,或者是一个目录,返回假。{file} 可以是任何返回字 符串的表达式。 *file_readable()* 已废弃的名字: file_readable()。 filewritable({file}) *filewritable()* 返回数值,如果名为 {file} 的文件存在且可写,则为 1。如果 {file} 不存在,或者不可写,返回 0。如果 {file} 是一个目录但是 可写,返回 2。 fnamemodify({fname}, {mods}) *fnamemodify()* 根据 {mods} 修改文件名 {fname}{mods} 是一个字符序列组成的字 符串,就像命令行上使用的文件名那样。见 |filename-modifiers|。 例如: :echo fnamemodify("main.c", ":p:h") 返回: /home/mool/vim/vim/src 注意: 环境变量和 "~" 不能用于 {fname},需要先用 |expand()| 扩 展。 foldclosed({lnum}) *foldclosed()* 返回数值,如果行 {lnum} 在关闭的折叠中,返回该折叠开始的行号。 如果行 {lnum} 不在关闭的折叠中,返回 -1。 foldclosedend({lnum}) *foldclosedend()* 返回数值,如果行 {lnum} 在关闭的折叠中,返回该折叠结束的行号。 如果行 {lnum} 不在关闭的折叠中,返回 -1。 foldlevel({lnum}) *foldlevel()* 返回数值,当前缓冲区第 {lnum} 行的折叠级别。如果在嵌套的折叠 里,返回最深的那层。如果行 {lnum} 没有折叠,返回零。这和折叠是 打开还是关闭无关。在更新折叠时 (在 'foldexpr' 里),如果折叠还 在更新而相应的折叠级别未知,返回 -1。一个特例是前一行的级别通 常总是知道的。 *foldtext()* foldtext() 返回关闭的折叠所显示的行。这是 'foldtext' 选项使用的缺省函数, 而且也只应该在计算 'foldtext' 时使用。它使用 |v:foldstart|、 |v:foldend| 和 |v:folddashes| 变量。 返回的字符串看起来像: +-- 45 lines: abcdef 连字符的数目取决于折叠级别。"45" 是折叠的行数。"abcdef" 是折叠 第一个非空白行的文本。开头的空白、"//" 和 "/*" 还有 'foldmarker' 和 'commentstring' 选项的文本都被去除。 {only 只有在编译时加入 |+folding| 特性才有效} *foreground()* foreground() 把 Vim 窗口带到前台。用于从客户发送到 Vim 服务器的时候。 |remote_send()| 在 Win32 系统上,可能不行,操作系统并不总能允许窗口把自己带到 前台。这时应使用 |remote_foreground()|。 {only 只有在 Win32、Athena、Motif 和 GTK GUI 版本和 Win32 控 制台版本才有} getchar([expr]) *getchar()* 让用户输入一个字符。如果是 8 位字符,返回数值。不然返回经过编 码的字符构成的字符串。比如,特殊键返回一串字节,以 0x80 (十进 制: 128) 开始。 如果忽略 [expr],等待直到有字符输入为止。 如果 [expr] 为 0,只有在有字符可用时才取得字符。 如果 [expr] 为 1,只检查是否有字符可用,并不消耗该字符。如果可 用的是普通字符,返回之。不然返回非零值。 如果有可用的普通字符,以数值形式返回。用 nr2char() 把它转化成 字符串。 如果没有字符可用,返回零。 特殊键或者使用了修饰符 (Shift,Control,Alt) 的时候,返回字符 构成的字符串。 这里没有提示,你需要想办法告诉用户,需要输入一个字符。 字符不通过映射。 键码被替换。因而,用户输入 <Del> 键时,你得到 <Del> 的键码,而 不是原始的字符序列。比如: getchar() == "\<Del>" getchar() == "\<S-Left>" 下例重新定义 "f",使它忽略大小写: :nmap f :call FindChar()<CR> :function FindChar() : let c = nr2char(getchar()) : while col('.') < col('$') - 1 : normal l : if getline('.')[col('.') - 1] ==? c : break : endif : endwhile :endfunction getcharmod() *getcharmod()* 返回数值,反映最近用 getchar() 或其它方式输入字符的修饰符状 态。这些值可以相加: 2 Shift 4 Control 8 Alt (Meta) 16 鼠标双击 32 鼠标三击 64 鼠标四击 128 仅限于 Macintosh: command 只有没有包含字符本身的修饰符被返回。因而,Shift-a 产生没有修饰 符的 "A"。 getbufvar({expr}, {varname}) *getbufvar()* 返回缓冲区 {expr} 里的选项或者局部变量 {varname} 的值。注意 必 须使用不带 "b:" 的名字。 也可用于全局或者局部于窗口的选项,但不能用于全局或者局部于窗口 的变量。 关于 {expr} 的使用方式,见上 |bufname()|。 如果缓冲区或者变量不存在,返回空字符串。不会有错误消息。 示例: :let bufmodified = getbufvar(1, "&mod") :echo "todo myvar = " . getbufvar("todo", "myvar") getcmdline() *getcmdline()* 返回当前命令行。只有在编辑命令行时有效,所以必须在 |c_CTRL-\_e| 或|c_CTRL-R_=| 里使用。 例如: :cmap <F7> <C-\>eescape(getcmdline(), ' \')<CR> 另见 |getcmdpos()| 和 |setcmdpos()|。 getcmdpos() *getcmdpos()* 返回命令行的字节计算的光标位置。第一列为 1。 只有在编辑命令行时有效,所以必须在 |c_CTRL-\_e| 或 |c_CTRL-R_=| 里使用。不然,返回 0。 另见 |setcmdpos()| 和 |getcmdline()|。 *getcwd()* getcwd() 返回字符串,当前工作目录的名字。 getfsize({fname}) *getfsize()* 返回数值,文件 {fname} 以字节数计算的大小。 如果 {fname} 是目录,返回 0。 如果找不到文件 {fname},返回 -1。 getftime({fname}) *getftime()* 返回数值,给定文件 {fname} 的最新修改时间。该时间为 1970 年 1 月 1 日开始计算的秒数,可以传给 strftime()。 另见 |localtime()| 和 |strftime()|。 如果找不到文件 {fname},返回 -1。 *getline()* getline({lnum}) 返回字符串,即当前缓冲区第 {lnum} 行文本。例如: getline(1) 如果 {lnum} 是不以数字开始的字符串,调用 line() 来把该字符串转 化成数值。要得到光标所在的行: getline(".") 如果 {lnum} 小于 1 或者大于缓冲区的总行数,返回空字符串。 getreg([{regname}]) *getreg()* 返回字符串,寄存器 {regname} 的内容。例如: :let cliptext = getreg('*') getreg('=') 返回最近一次表达式寄存器计算的返回值 (用于映射)。 如果没有指定 {regname},使用 |v:register|。 getregtype([{regname}]) *getregtype()* 返回字符串,寄存器 {regname} 的类型。 该值会是以下可能之一: "v" |characterwise| (面向字符) 的文本 "V" |linewise| (面向行) 的文本 "<CTRL-V>{width}" |blockwise-visual| (面向列块) 的文本 0 空或者未知的寄存器 <CTRL-V> 是一个字符,其值为 0x16。 如果没有指定 {regname},使用 |v:register|。 *getwinposx()* getwinposx() 返回数值,即 GUI Vim 窗口以像素计从左起算的 X 坐标。如果该信息 得不到,返回 -1。 *getwinposy()* getwinposy() 返回数值,即 GUI Vim 窗口以像素计从顶部起算的 Y 坐标。如果该信 息得不到,返回 -1。 getwinvar({nr}, {varname}) *getwinvar()* 返回窗口 {nr} 里的选项或者局部变量 {varname} 的值。 也可用于全局或者局部于缓冲区的选项,但不能用于全局或者局部于 缓冲区的变量。 注意 必须使用不带 "w:" 的名字。 例如: :let list_is_on = getwinvar(2, '&list') :echo "myvar = " . getwinvar(1, 'myvar') *glob()* glob({expr}) 扩展 {expr} 里的文件通配符。结果是字符串。如果返回多个匹配,以 <NL> 字符分隔。 如果扩展失败,返回空字符串。 扩展结果不包含不存在文件的名字。 多数系统上,可以用反引号从外部命令得到文件名。例如: :let tagfiles = glob("`find . -name tags -print`") :let &tags = substitute(tagfiles, "\n", ",", "g") 反引号包围的程序的输出结果必须每个项目一行。项目内部可以使用空 格。 特殊 Vim 变量的扩展见 |expand()|。|system()| 说明如何得到外部 命令的原始输出。 globpath({path}, {expr}) *globpath()*{path} 的所有目录下执行 glob() 并连接所有的返回结果。例如: :echo globpath(&rtp, "syntax/c.vim") {path} 是逗号分隔的目录名的列表。每个目录名都附加在 {expr} 之 前,然后如同 glob() 那样被扩展。必要的话,插入路径分隔符。 要在目录名字里加上逗号,可以使用反斜杠转义。注意 在 MS-Windows 上目录的最后可能有一个反斜杠。如果你要在后面加上逗号进行分隔, 先把反斜杠去掉。 如果某个目录下的扩展失败,不会有错误信息。 这里应用 'wildignore' 选项: 忽略匹配 'wildignore' 里任何一个模 式的名字。 *has()* has({feature}) 返回数值,如果支持特性 {feature} 则为 1,不然为零。 {feature} 参数是字符串。见下面的 |feature-list|。 另见 |exists()|。 hasmapto({what} [, {mode}]) *hasmapto()* 返回数值,如果存在某映射,其右边的表达式 (被映射到的部分) 的某 处包含 {what},并且该映射在 {mode} 指定的模式下存在,返回 1。 同时检查全局映射和局部于当前缓冲区的映射以寻找匹配。 如果没有匹配的映射,返回 0。 {mode} 识别下列字符: n 普通模式 v 可视模式 o 操作符等待模式 i 插入模式 l Language-Argument ("r"、 "f"、"t" 等等) 模式 c 命令行模式 如果没有提供 {mode},使用 "nvo"。 该函数可用于检查是否存在映射到 Vim 脚本的某个函数的映射。例如: :if !hasmapto('\ABCdoit') : map <Leader>d \ABCdoit :endif 这样,到 "\ABCdoit" 的映射只有在到 "\ABCdoit" 的映射还不存在的 时候才会进行。 histadd({history}, {item}) *histadd()* 把字符串 {item} 加到历史 {history} 里。后者可以是: *hist-names* "cmd" 或 ":" 命令行历史 "search" 或 "/" 搜索模式历史 "expr" 或 "=" 输入表达式历史 "input" 或 "@" 输入行历史 如果 {item} 已经在历史里存在,它会被调整位置,从而成为最新的一 项。 返回结果为数值: 如果操作成功则为 1,不然返回 0。 例如: :call histadd("input", strftime("%Y %b %d")) :let date=input("Enter date: ") 该函数在沙盘里不可用 |sandbox|。 histdel({history} [, {item}]) *histdel()* 清除 {history},换而言之,删除它所有的项目。|hist-names| 解释 {history} 的所有可能值。 如果给出字符串参数 {item},它被看作正规表达式。从历史里删除所 有匹配该模式的项目 (如果有的话)。 必须匹配大小写,除非使用 "\c" |/\c|。 如果 {item} 是数值,它被解释为索引值,见 |:history-indexing|。如果该索引存在,删除相应的项目。 返回结果为数值: 如果操作成功则为 1,不然返回 0。 例如: 清除表达式寄存器历史: :call histdel("expr") 删除所有 "*" 开始的搜索历史: :call histdel("/", '^\*') 下面三者是等价的: :call histdel("search", histnr("search")) :call histdel("search", -1) :call histdel("search", '^'.histget("search", -1).'$') 要删除最后的搜索模式,并在 "n" 命令和 'hlsearch' 里使用倒数第 二个模式: :call histdel("search", -1) :let @/ = histget("search", -1) histget({history} [, {index}]) *histget()* 返回字符串,即 {history} 历史的第 {index} 项。|hist-names| 解 释 {history} 的所有可能值,而 |:history-indexing| 解释 {index}。如果没有这个项目,返回空字符串。如果忽略 {index},返 回历史里最近使用的项目。 例如: 重做历史里的倒数第二个搜索 :execute '/' . histget("search", -2) 定义 Ex 命令 ":H {num}",以重新执行 |:history| 输出的第 {num} 项。 :command -nargs=1 H execute histget("cmd", 0+<args>) histnr({history}) *histnr()* 返回当前项目在 {history} 里的编号。|hist-names| 解释 {history} 的所有可能值。 如果有错,返回 -1。 例如: :let inp_index = histnr("expr") hlexists({name}) *hlexists()* 返回数值。只要名为 {name} 的高亮组用某种方法定义过,返回非零。 不一定要为该组定义过高亮属性。一些语法项目可能已经使用该组。 *highlight_exists()* 已废弃的名字: highlight_exists()。 *hlID()* hlID({name}) 返回数值,即名为 {name} 的高亮组的 ID。如果该高亮组不存在,返 回零。 可用于提取高亮组的信息。比如,要得到 "Comment" 组的背景颜色: :echo synIDattr(synIDtrans(hlID("Comment")), "bg") *highlightID()* 已废弃的名字: highlightID()。 hostname() *hostname()* 返回字符串,即 Vim 运行的机器名字。超过 256 字符串长度的机器名 被截短。 iconv({expr}, {from}, {to}) *iconv()* 返回字符串,即文本 {expr}{from} 编码转到 {to} 编码以后的文 本。 如果转换失败,返回空字符串。 编码名字可以是任何 iconv() 库函数接受的名字,见 ":!man 3 iconv"。 大多数转换需要 Vim 编译时加入 |+iconv| 特性。不然,只支持 UTF-8 和 latin1 的相互转换。 这可以用来显示包含特殊字符的消息。不管 'encoding' 设为何值,总 可以用 UTF-8 书写消息,然后使用: echo iconv(utf8_str, "utf-8", &enc) 注意 Vim 使用 UTF-8 进行所有的 Unicode 编码,从/到 UCS-2 的转 换都自动转为 UTF-8。你不能在字符串里使用 UCS-2,因为那里有 NUL 字节。 {only 只有在编译时加入 +multi_byte 特性才有效} *indent()* indent({lnum}) 返回数值,第 {lnum} 行的缩进距离。缩进的计算以空格计,因而它和 'tabstop' 的值是有关系的。{lnum} 的使用方式和 |getline()| 相 同。 如果 {lnum} 非法,返回 -1。 input({prompt} [, {text}]) *input()* 返回字符串,即用户在命令行上的输入内容,可以为任何值。参数或者 是一个提示字符串,或者是一个空白字符串 (没有提示)。'\n' 可以在 提示里使用,以开始新行。该提示使用 |:echohl| 设置的高亮。 输入方法和命令行相似,也使用相同的编辑命令和映射。但 input() 输入的行使用另外的历史。 如果给出可选的 {text},它被用作缺省的回答,就像是用户输入的那 样。 注意: 在只能运行于 GUI 模式的版本里 (比如 Win32 GUI),此函数不 能在启动文件里使用。 注意: input() 在映射里调用时,它会消耗该映射余下的字符,因为映 射的处理就像那些字符被键盘输入一样。在 input() 前使用 |inputsave()| 然后在 input() 输入之后 |inputrestore()| 可以避 免这一点。另一个方法是避免在映射的后面提供任何字符,比如,使用 |:execute| 或 |:normal|。 示例: :if input("Coffee or beer? ") == "beer" : echo "Cheers!" :endif 提供缺省文本的例子: :let color = input("Color? ", "white") 使用映射的例子: :nmap \x :call GetFoo()<CR>:exe "/" . Foo<CR> :function GetFoo() : call inputsave() : let g:Foo = input("enter search pattern: ") : call inputrestore() :endfunction inputdialog({prompt} [, {text} [, {cancelreturn}]]) *inputdialog()* 类似于 input(),但如果运行 GUI 且支持文本对话框,弹出一个对话 框窗口来输入文本。 例如: :let n = inputdialog("value for shiftwidth", &sw) :if n != "" : let &sw = n :endif 如果对话框被取消,返回 {cancelreturn}。如果忽略,返回空字符 串。 输入 <Enter> 和按 OK 按钮相同。按 <Esc> 和按 Cancel 按钮相同。 inputrestore() *inputrestore()* 恢复前一个 inputsave() 保存的预输入。应该和 inputsave() 调用的 次数相同,不过调用更多次也无妨。 如果没有可以恢复的,返回 1,不然返回 0。 inputsave() *inputsave()* 保存预输入 (也包括映射的) 并清除之,使得下一个提示能从用户得到 输入。在提示之后应该跟上配套的 inputrestore()。可以多次使用, 此时应该有同样多次的 inputrestore() 调用。 如果内存不足,返回 1,不然返回 0。 inputsecret({prompt} [, {text}]) *inputsecret()* 该函数和 |input()| 函数类似,但有两个例外: a) 用户的应答显示为一串星号 ("*"),从而输入可以保密,还有 b) 用户的应答不会记录在输入 |history| 栈中。 返回字符串,即用户在命令行上根据提示输入的应答。 isdirectory({directory}) *isdirectory()* 返回数值,如果名为 {directory} 的目录存在,返回非零。如果 {directory} 不存在或者不是目录,返回假值。{directory} 可以是任 何表达式,最终用作字符串。 *libcall()* *E364* *E368* libcall({libname}, {funcname}, {argument}) 在运行库 {libname} 里调用函数 {funcname} 并给出单个参数 {argument}。 这可以用于调用库里的函数,尤其是 Vim 里用到的那些。因为只能使 用单个参数,所以可以调用的标准库函数相当有限。 结果是函数返回的字符串。如果函数返回 NULL,在 Vim 里会以空字符 串 "" 出现。 如果函数返回数值,请使用 libcallnr()! 如果 {argument} 是数值,它以 int 类型传给函数;如果 {argument} 是字符串,它以 null 结尾的字符串类型传入。 在 |restricted-mode| 里,该函数不能运行。 libcall() 允许你写自己的 Vim '插件' 扩展,而无须重新编译程序。 它并_不_是用来调用系统函数的一个方法!如果你试图这么做,Vim 很 有可能会崩溃。 Win32 上,你写的函数必须在 DLL 里提供,而且必须使用普通的 C 调 用惯例 (_不是_ Windows 系统 DLL 使用的 Pascal 惯例)。函数必须 只能接受单个参数,或者是字符指针,或者是长整数,而且必须返回字 符指针或者 NULL。返回的字符指针必须指向在函数返回之后仍然指向 合法的内存 (比如 DLL 的静态区域)。如果指向分配的区域,那么内存 会发生泄漏。在函数里使用静态缓冲区应该可以,在 DLL 卸载时会被 释放。 警 告: 如果函数返回不合法的指针,Vim 会崩溃!如果函数返回数值 也会发生同样的问题,因为 Vim 把它当作指针看待。 Win32 系统上,{libname} 必须是不带 ".DLL" 后缀的 DLL 文件名。 只有 DLL 不在常见的位置的时候,才需要指定完整的路径名。 Unix 上: 如果编译你自己的插件,记住目标代码必须生成位置无关代 码 ('PIC')。 {only 只有在 Win32 和一些 Unix 版本里且带有 |+libcall| 特性时 才有效} 例如: :echo libcall("libc.so", "getenv", "HOME") :echo libcallnr("/usr/lib/libc.so", "getpid", "") *libcallnr()* libcallnr({libname}, {funcname}, {argument}) 和 libcall() 类似,但函数返回 int,而不是字符串。 {only 只有在 Win32 和一些 Unix 版本里且带有 |+libcall| 特性时 才有效} 例如 (不是很有用...): :call libcallnr("libc.so", "printf", "Hello World!\n") :call libcallnr("libc.so", "sleep", 10) *line()* line({expr}) 返回数值,即 {expr} 给定的文件位置的行号。可接受的位置是: . 光标位置 $ 缓冲区的最后一行 'x 位置标记 x 的位置 (如果该位置标记没有设置,返回 0) 注意 只能使用当前文件的位置标记。 例如: line(".") 光标所在的行号 line("'t") 位置标记 t 的行号 line("'" . marker) 名为 marker 的位置标记的行号 *last-position-jump* 如果设置了 '" 位置标记的话,下面的自动命令在打开文件后跳转到最 后已知的文件位置: :au BufReadPost * if line("'\"") > 0 && line("'\"") <= line("$") | exe "normal g'\"" | endif line2byte({lnum}) *line2byte()* 返回当前缓冲区第 {lnum} 行从缓冲区开始计算的字节数。这里包括换 行符,但它具体的值取决于当前缓冲区的 'fileformat' 选项,第一行 返回 1。 这也可以用来得到最后一行之后的 "那行" 的字节计数: line2byte(line("$") + 1) 这就等于文件大小加 1。 如果 {lnum} 非法或者编译时关闭了 |+byte_offset| 特性,返回 -1。另见 |byte2line()|、|go| 和 |:goto|。 lispindent({lnum}) *lispindent()* 得到第 {lnum} 行根据 lisp 缩进规则应有的缩进距离,见 'lisp'。 缩进的计算以空格计,因而和 'tabstop' 的值是有关系的。 {lnum} 的使用方式和 |getline()| 相同。 如果 {lnum} 非法或者 Vim 编译时不带 |+lispindent| 特性,返回 -1。 localtime() *localtime()* 返回当前时间,以 1970 年 1 月 1 日开始的秒数计算。另见 |strftime()| 和 |getftime()|。 maparg({name}[, {mode}]) *maparg()* 返回模式 {mode} 名为 {name} 的映射的右边。如果没有名为 {name} 的映射,返回空字符串。 {mode} 可以使用下列字符: n 普通模式 v 可视模式 o 操作符等待模式 i 插入模式 c 命令行模式 l Language-Argument ("r"、 "f"、"t" 等等) 模式 "" 普通、可视和操作符等待模式。 如果没有提供 {mode},使用 "" 指定的模式。 {name} 可以使用特殊键名,如同 ":map" 命令那样。返回的字符串会 把特殊的字符翻译成和 ":map" 命令所列出输出结果一样的格式。 先检查局部于当前缓冲区的映射,然后再检查全局映射。 mapcheck({name}[, {mode}]) *mapcheck()* 检查是否有模式 {mode} 下匹配 {name} 的映射。|maparg()| 说明 {mode}{name} 里的特殊键名。 匹配在映射名以 {name} 开始或者映射名等于 {name} 的开始部分时候 发生。 匹配映射 "a" "ab" "abc" mapcheck("a") 是 是 是 mapcheck("abc") 是 是 是 mapcheck("ax") 是 否 否 mapcheck("b") 否 否 否 和 maparg() 的差别是,mapcheck() 查找匹配 {name} 的映射,而 maparg() 只查找名字完全符合 {name} 的映射。 如果没有 {name} 开始的映射,返回空字符串。如果有一个,返回该映 射的右边。如果有多个,返回其中某一个的右边。 先检查局部于当前缓冲区的映射,然后再检查全局映射。 该函数用于检查是否可以无二义性地添加映射。例如: :if mapcheck("_vv") == "" : map _vv :set guifont=7x13<CR> :endif 就避免了在已有 "_v" 或者 "_vvv" 映射的时候添加 "_vv" 映射。 match({expr}, {pat}[, {start}]) *match()* 返回数值,给出 {expr}{pat} 匹配的 (字节计算的偏移量) 位 置。在第一个字符上的匹配返回零。如果没有匹配,返回 -1。例如: :echo match("testing", "ing") 返回 "4"。 |string-match| 说明如何使用 {pat}。 如果给出 {start},搜索从位置 {start} 开始。 不过,结果仍然从第一个字符开始算起。比如: :echo match("testing", "ing", 2) 返回结果是 "4"。 :echo match("testing", "ing", 4) 返回结果还是 "4"。 :echo match("testing", "t", 2) 返回 "3"。 如果 {start} < 0,它被置为 0。 如果 {start} > strlen({expr}) 返回 -1。 |pattern| 说明可以接受的模式。 'ignorecase' 选项用来设定模式是否忽略大小写。_不_使用 'smartcase'。匹配总是假定置位了 'magic' 而 'cpoptions' 为空。 matchend({expr}, {pat}[, {start}]) *matchend()* 和 match() 相同,但返回匹配之后的第一个字符的位置。比如: :echo matchend("testing", "ing") 返回 "7"。 如果给出 {start},和 match() 里的用法相同。 :echo matchend("testing", "ing", 2) 返回 "7"。 :echo matchend("testing", "ing", 5) 返回 "-1"。 matchstr({expr}, {pat}[, {start}]) *matchstr()* 和 match() 相同,但返回匹配的字符串。例如: :echo matchstr("testing", "ing") 返回 "ing"。 如果没有匹配,返回 ""。 如果给出 {start},它和 match() 里的用法相同。 :echo matchstr("testing", "ing", 2) 返回 "ing"。 :echo matchstr("testing", "ing", 5) 返回 ""。 *mode()* mode() 返回指示当前模式的字符串: n 普通模式 v 面向字符的可视模式 V 面向行的可视模式 CTRL-V 面向列块的可视模式 s 面向字符的选择模式 S 面向行的选择模式 CTRL-S 面向列块的选择模式 i 插入模式 R 替换模式 c 命令行模式 r 输入回车的提示 可用于 'statusline' 选项。在其它的多数地方,它总是返回 "c" 或 "n"。 nextnonblank({lnum}) *nextnonblank()* 返回第一个从 {lnum} 开始的非空白行的行号。例如: if getline(nextnonblank(1)) =~ "Java" 如果 {lnum} 非法或者在从该行开始都没有非空白行,返回零。 另见 |prevnonblank()|。 nr2char({expr}) *nr2char()* 返回单个字符组成的字符串,该字符的数值为 {expr}。例如: nr2char(64) 返回 "@" nr2char(32) 返回 " " 使用当前的 'encoding'。比如对 "utf-8" 来说: nr2char(300) 返回带有弓形的 I 注意 文件里的 NUL 字符须用 nr2char(10) 指定。因为 Vim 用换行符 来表示 NUL。真正的 NUL 是 nr2char(0),而它会终结字符串,不一定 合用。 prevnonblank({lnum}) *prevnonblank()* 返回第一个 {lnum} 所在或之上的非空白行的行号。例如: let ind = indent(prevnonblank(v:lnum - 1)) 如果 {lnum} 非法或者在该行和它之前都没有非空白行,返回零。 另见 |nextnonblank()|。 *remote_expr()* *E449* remote_expr({server}, {string} [, {idvar}]) 发送 {string}{server}。该发送的字符串是一个表达式,而返回 的是远端执行的结果。 如果给出 {idvar},将 {serverid} 保存在以它命令的变量里,此后的 remote_read() 需要使用此值。 另见 |clientserver| |RemoteReply|。 该函数在沙盘里不可用 |sandbox|。 {only 只有在编译时加入 |+clientserver| 特性才可用} 注意: 任何错误会在本地产生错误信息,但返回的结果只是一个空字符 串。 例如: :echo remote_expr("gvim", "2+2") :echo remote_expr("gvim1", "b:current_syntax") remote_foreground({server}) *remote_foreground()* 把名为 {server} 的 Vim 服务器带到前台。 这类似于: remote_expr({server}, "foreground()") Win32 系统除外。那里,客户端完成实际的工作。因为操作系统不 总能允许服务器把自己带到前台。 该函数在沙盘里不可用 |sandbox|。 {only 仅可用在 Win32、Athena、Motif 和 GTK 的 GUI 版本和 Win32 的控制台版本} remote_peek({serverid} [, {retvar}]) *remote_peek()* 如果 {serverid} 有可用的字符串,返回正数。如果指定了 {retvar},复制任何应答字符串到 {retvar} 指定的变量。{retvar} 必须是一个用来指定变量名的字符串。 如果没有可用的应答,返回 0。 如果出错,返回 -1。 另见 |clientserver|。 该函数在沙盘里不可用 |sandbox|。 {only 只有在编译时加入 |+clientserver| 特性才可用} 示例: :let repl = "" :echo "PEEK: ".remote_peek(id, "repl").": ".repl remote_read({serverid}) *remote_read()* 返回从 {serverid} 发送的存在时间最长的应答,并删除之。该调用会 等待直到有应答为止。 另见 |clientserver|。 该函数在沙盘里不可用 |sandbox|。 {only 只有在编译时加入 |+clientserver| 特性才可用} 例如: :echo remote_read(id) *remote_send()* *E241* remote_send({server}, {string} [, {idvar}]) 发送 {string}{server}。发送的字符串是输入键的序列。函数立 即返回。 如果给出 {idvar},将 {serverid} 保存在以它命令的变量里,此后的 remote_read() 需要使用此值。 另见 |clientserver| |RemoteReply|。 该函数在沙盘里不可用 |sandbox|。 {only 只有在编译时加入 |+clientserver| 特性才可用} 注意: 任何错误会在服务器端报告,从而影响那里的显示。 例如: :echo remote_send("gvim", ":DropAndReply ".file, "serverid"). \ remote_read(serverid) :autocmd NONE RemoteReply * \ echo remote_read(expand("<amatch>")) :echo remote_send("gvim", ":sleep 10 | echo ". \ 'server2client(expand("<client>"), "HELLO")<CR>') rename({from}, {to}) *rename()* 把文件名 {from} 换成 {to}。这也可用来在文件系统间移动文件。返 回数值,如果文件成功换名,返回零,如果换名失败,返回非零。该函 数在沙盘里不可用 |sandbox|。 resolve({filename}) *resolve()* *E655* 在 MS-Windows 上,如果 {filename} 是一个快捷方式 (.lnk 文件), 返回简化的快捷方式指向的路径。 在 Unix 上,反复分析 {filename} 的所有路径部分的符号链接的真正 路径,直到返回最简化的结果为止。为了处理循环链接的问题,符号链 接的分析在 100 次叠代之后停止。 在其它系统上,返回简化了的 {filename}。 简化的工作通过 |simplify()| 完成。 resolve() 保留指向当前目录的首个路径部分 (保证结果仍然是相对路 径名),也保留出现在尾部的路径分隔符。 search({pattern} [, {flags}]) *search()* 搜索正规表达式模式 {pattern}。搜索从光标位置开始。 {flags} 是字符串,可以包含以下字符标志位: 'b' 反向搜索,而不是正向搜索 'w' 在文件尾部处回绕到文件开始处 'W' 不在文件尾部处回绕 如果 'w' 和 'W' 都没有给出,根据 'wrapscan' 选项决定。 如果找到了匹配,返回其所在的行号,并且光标定位于匹配所在的位置 上。如果找不到匹配,返回 0 并且光标位置不改变。不会给出错误信 息。 示例 (遍历参数列表里的所有文件): :let n = 1 :while n <= argc() " 循环遍历参数列表的每个文件 : exe "argument " . n : " 从文件最后一个字符开始并回绕,这样第一个搜索可以找到 : " 文件开始的匹配 : normal G$ : let flags = "w" : while search("foo", flags) > 0 : s/foo/bar/g : let flags = "W" : endwhile : update " 如果修改过,写入文件 : let n = n + 1 :endwhile *searchpair()* searchpair({start}, {middle}, {end} [, {flags} [, {skip}]]) 搜索嵌套的 start-end 组对的匹配。这可以用来查找匹配 "if" 的 "endif"。在这里面的其它的 if/endif 组对被忽略。搜索从光标开 始。如果找到一个匹配,光标移动到那里并返回行号。如果没有匹配, 返回 0 或者 -1,光标不移动。不会给出错误信息。 {start}{middle}{end} 都是模式,见 |pattern|。它们不能包 含 \( \) 对,但可以使用 \%( \)。如果 {middle} 非空,在相应的方 向试图寻找它 (如果找到,停留在哪里),但在嵌套的 start-end 组对 里面的不算。一个典型的应用是: searchpair('\<if\>', '\<else\>', '\<endif\>') 如果 {middle} 为空,跳过 "else"。 {flags} 的使用方式和 |search()| 类似。此外,还可用: 'n' 不 (Not) 移动光标 'r' 重复 (Repeat) 直到没有更多匹配位置;会找到最外层的组对 'm' 返回匹配 (Match) 的数目而不是匹配的行号;只有在使用 'r' 时才可能 > 1。 如果找到 {start}{middle}{end} 的匹配,计算 {skip} 表达 式,此时假定光标定位在匹配的开始处。如果返回零,该匹配被跳过。 比如,可能是出现在注释里的匹配。 如果 {skip} 不提供或者为空,接受每一个匹配。如果计算 {skip} 时 出现错误,搜索被中止,并返回 -1。 使用 'ignorecase' 的值。忽略 'magic',使用模式时假设它总是置位 的。 搜索从准确的光标处开始。根据搜索方向,寻找从下一个字符开始的 {start}{middle}{end}。比如: if 1 if 2 endif 2 endif 1 如果从 "if 2" 开始且光标在 "i" 上并正向搜索,找到的是 "endif 2"。如果刚好在 "if 2" 之前开始,找到的是 "endif 1"。因 为先找到的了 "if 2",而它被认为是嵌套的 if/endif,以 "if 2" 开始,以 "endif 2" 结束。 如果反向搜索且 {end} 多于一个字符,在模式的最后加上 "\zs" 可能 有用,这样光标在 end 匹配的中间某位置的时候,仍然可以找到匹配 的 start 匹配。 例如,要找到 Vim 脚本里的 "endif" 命令: :echo searchpair('\<if\>', '\<el\%[seif]\>', '\<en\%[dif]\>', 'W', \ 'getline(".") =~ "^\\s*\""') 光标必须在要寻找匹配的 "if" 之上或之后。注意 单引号字符串的使 用,它避免了反斜杠的麻烦。skip 表达式只用来发现行首的注释,命 令之后的不行。另外,一行中间的单词 "en" 或 "if" 也被认为是匹 配。 另一个例子,搜索匹配 "}" 的 "{": :echo searchpair('{', '', '}', 'bW') 只需要光标在需要匹配的 "}" 之上或之前就可以了。要拒绝语法高亮 识别为字符串的匹配: :echo searchpair('{', '', '}', 'bW', \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string"') server2client( {clientid}, {string}) *server2client()* 发送应答字符串到 {clientid}。最近刚发送过字符串的 {clientid} 可以通过 expand("<client>") 得到。 {only 只有在编译时加入 |+clientserver| 特性才能得到} 备注: 该 id 应在接受下一个命令前保存。也就是,在接收命令返回之前 (译 者注: 似应为 "之后") 和任何等待输入的命令之前。 另见 |clientserver|。 示例: :echo server2client(expand("<client>"), "HELLO") serverlist() *serverlist()* 返回可用的服务器名字列表,每行一个。如果没有服务器或者该信息 无法得到,返回空字符串。另见 |clientserver|。 {only 只有在编译时加入 |+clientserver| 特性才能得到} 示例: :echo serverlist() setbufvar({expr}, {varname}, {val}) *setbufvar()* 设置缓冲区 {expr} 的选项或局部变量 {varname} 的值为 {val}。 也可用于全局或者局部于窗口的选项,但不能用于全局或者局部于窗口 的变量。 如果设置局部于窗口的选项,全局值不会改变。 {expr} 的使用方式见上 |bufname()|。 注意必须使用不带 "b:" 的变量名。 示例: :call setbufvar(1, "&mod", 1) :call setbufvar("todo", "myvar", "foobar") 该命令在沙盘里不可用 |sandbox|。 setcmdpos({pos}) *setcmdpos()* 设置命令行的光标位置到字节位置 {pos}。第一个位置为 1。 用 |getcmdpos()| 得到当前的位置。 只有在编辑命令行时有效,所以必须在 |c_CTRL-\_e| 或 |c_CTRL-R_=| 里使用。在命令行设为表达式的内容之后才设置位置。 如果数值太大,光标放在行尾。如果小于 1,结果没有定义。 如果成功,返回 0,如果不在编辑命令行,返回 1。 setline({lnum}, {line}) *setline()* 设置当前缓冲区第 {lnum} 行的内容为 {line}。如果成功,返回 0。 如果失败 (多数是因为 {lnum} 不合法) 返回 1。例如: :call setline(5, strftime("%c")) 注意: 这里不会设置 '[ 和 '] 位置标记。 *setreg()* setreg({regname}, {value} [,{options}]) 设置寄存器 {regname} 的值为 {value}。 如果 {options} 包含 "a" 或者 {regname} 为大写,该值被附加于现 有值之后。 {options} 还可以指定寄存器新类型的规格: "c" 或 "v" |characterwise| (面向字符) 模式 "l" 或 "V" |linewise| (面向行) 模式 "b" 或 "<CTRL-V>" |blockwise-visual| (面向列块) 模式 如果 "b" 或 "<CTRL-V>" 之后紧跟数值,那么该数值用作选择的宽度 - 如果没有指定,那么列块的宽度设为最长的行字符数 (把 <TAB> 看作一个字符)。 如果 {options} 没有寄存器的设置,那么缺省使用面向字符模式,除 非 {value}<NL> 结尾。 不能设置 '=' 寄存器。 返回零代表成功,非零代表失败。 示例: :call setreg(v:register, @*) :call setreg('*', @%, 'ac') :call setreg('a', "1\n2\n3", 'b5') 本例说明如何使用函数来保存和恢复寄存器 :let var_a = getreg('a') :let var_amode = getregtype('a') .... :call setreg('a', var_a, var_amode) 你可以通过附加空串来改变寄存器的类型: :call setreg('a', '', 'al') setwinvar({nr}, {varname}, {val}) *setwinvar()* 设置窗口 {nr} 的选项或局部变量 {varname} 的值为 {val}。 也可用于全局或者局部于缓冲区的选项,但不能用于全局或者局部于缓 冲区的变量。 如果设置局部于缓冲区的选项,全局值不会改变。 注意 必须使用不带 "w:" 的变量名。 示例: :call setwinvar(1, "&list", 0) :call setwinvar(2, "myvar", "foobar") 该命令在沙盘里不可用 |sandbox|。 simplify({filename}) *simplify()* 在不改变含义的前提下,尽可能简化文件名。快捷方式 (MS-Windows 上) 或者符号链接 (Unix 上) 不会被解析。如果 {filename} 第一个 路径部分指定了当前目录,结果也会是如此。而结尾的路径分隔符也不 会被删除。 示例: simplify("./dir/.././/file/") == "./file/" 注意: 组合 "dir/.." 只有在 "dir" 是可以遍历的或者不存在的目录 才会被删掉。Unix 上,如果 "dir" 是同一目录下的符号链接,也会删 除该组合。为了在简化路径名之前解析所有牵涉到的符号链接,使用 |resolve()|。 strftime({format} [, {time}]) *strftime()* 返回字符串,即经过 {format} 字符串的格式转换的日期和时间。使用 给定的 {time},如果没有给出时间,使用当前时间。可以接受的 {format} 取决于你的系统。这意味着该函数不是可移植的! 可用的格式参见 C 函数 strftime() 的参考手册。返回结果的最大长 度是 80 个字符。另见 |localtime()| 和 |getftime()|。 可以用 |:language| 命令改变语言。 示例: :echo strftime("%c") Sun Apr 27 11:49:23 1997 :echo strftime("%Y %b %d %X") 1997 Apr 27 11:53:25 :echo strftime("%y%m%d %T") 970427 11:53:55 :echo strftime("%H:%M") 11:55 :echo strftime("%c", getftime("file.c")) 显示 file.c 的修改时间。 stridx({haystack}, {needle}) *stridx()* 返回数值,给出字符串 {haystack} 里第一个字符串 {needle} 出现的 位置。搜索对大小写敏感。更高级的搜索需要 |match()|。 如果 {needle} 不出现在 {haystack} 里,返回 -1。 另见 |strridx()|。示例: :echo stridx("An Example", "Example") 3 :echo stridx("Starting point", "Start") 0 :echo stridx("Starting point", "start") -1 *strlen()* strlen({expr}) 返回数值,即字符串 {expr} 的字节长度。如果你要计算多字节字符的 数目,可以这么用: :let len = strlen(substitute(str, ".", "x", "g")) 组合用字符不予计算。 strpart({src}, {start}[, {len}]) *strpart()* 返回字符串,{src} 从第 {start} 个字节开始长度为 {len} 的子串。 如果包含不存在的字节,不会产生错误。只是那些字节被忽略而已。 如果没有提供 {len},子串从 {start} 开始直到 {src} 的结尾。 strpart("abcdefg", 3, 2) == "de" strpart("abcdefg", -2, 4) == "ab" strpart("abcdefg", 5, 4) == "fg" strpart("abcdefg", 3) == "defg" 注意: 要得到第一个字符,{start} 必须是零。比如,要得到光标开始 的三个字节: strpart(getline(line(".")), col(".") - 1, 3) strridx({haystack}, {needle}) *strridx()* 返回数值,给出字符串 {haystack} 里最后一个字符串 {needle} 出现 的位置。搜索对大小写敏感。更高级的搜索需要 |match()|。 如果 {needle} 不出现在 {haystack} 里,返回 -1。 另见 |stridx()|。示例: :echo strridx("an angry armadillo", "an") 3 strtrans({expr}) *strtrans()* 返回等于 {expr} 的字符串,但所有的不可显示字符被翻译成可显示的 字符序列 |'isprint'|,类似于窗口里显示的形式。例如: echo strtrans(@a) 会显示寄存器里的换行符为 "^@" 而不是开启新行。 submatch({nr}) *submatch()* 只用于 |:substitute| 命令里的表达式。返回匹配文本的第 {nr} 个 子匹配。如果 {nr} 为 0,返回整个匹配的文本。 例如: :s/\d\+/\=submatch(0) + 1/ 找到行内第一个数值并加 1。 使用 <NL> 可以包含换行符。 substitute({expr}, {pat}, {sub}, {flags}) *substitute()* 返回等于 {expr} 的字符串,但其中第一个 {pat} 的匹配被替代成 {sub}。 和 ":substitute" 命令类似 (不带任何标志位)。但 {pat} 的匹配总 假定置位了 'magic' 选项而且 'cpoptions' 为空 (为了脚本的可移植 性)。 |string-match| 说明如何使用 {pat}{sub} 里的 '~' 不会被换成前一个 {sub}注意 {sub} 里的一些代码有特殊含义 |sub-replace-special|。比 如,要替换一些文本为 "\n" (两个字符),使用 "\\\\n" 或 '\\n'。 如果 {pat}{expr} 里不能匹配,返回没有修改的 {expr}。 如果 {flags} 为 "g",{expr} 里的所有 {pat} 匹配都被替换。否 则,{flags} 应该为 ""。 示例: :let &path = substitute(&path, ",\\=[^,]*$", "", "") 删除 'path' 选项的最后一部分。 :echo substitute("testing", ".*", "\\U\\0", "") 返回 "TESTING"。 synID({line}, {col}, {trans}) *synID()* 返回数值,即当前窗口 {line}{col} 列所在的语法 ID。 语法 ID 可以用在 |synIDattr()| 和 |synIDtrans()|,以得到文本 的语法信息。 最左列的 {col} 为 1。第一行的 {line} 为 1。 如果 {trans} 非零,透明的项目被简约为它们实际显露的项目。这可 以用于你想知道实际使用的颜色的情形。如果 {trans} 为零,返回透 明的项目本身。这可用于想知道实际有效的语法项目的情形 (比如,在 括号内部)。 警告: 本函数可能很慢。最佳速度可以通过正向遍历文件获得。 例如 (回显光标所在的语法项目的名字): :echo synIDattr(synID(line("."), col("."), 1), "name") synIDattr({synID}, {what} [, {mode}]) *synIDattr()* 返回字符串,syntax ID {synID}{what} 属性。可用于得到语法项 目的相关信息。 {mode} 可以是 "gui"、"cterm" 或 "term",从而得到的是该模式下的 属性。如果忽略 {mode} 或者指定了非法的值,使用当前激活的高亮方 式的属性 (GUI、cterm 或 term)。 使用 synIDtrans() 来跟随链接的高亮组。 {what} 结果 "name" 语法项目的名字 "fg" 前景色 (GUI: 用于设置颜色的色彩名,cterm: 色彩 号,以字符串形式出现,term: 空字符串) "bg" 背景色 (类似于 "fg") "fg#" 类似于 "fg",但只适用于 GUI,而且 GUI 使用的名 字形如 "#RRGGBB"。 "bg#" "bg",但类似于 "fg#" "bold" "1" 如果粗体 "italic" "1" 如果斜体 "reverse" "1" 如果反显 "inverse" "1" 如果反显 (= reverse) "underline" "1" 如果下划线 示例 (回显光标所在的语法项目的颜色): :echo synIDattr(synIDtrans(synID(line("."), col("."), 1)), "fg") synIDtrans({synID}) *synIDtrans()* 返回数值,即 {synID} 经过翻译的语法 ID。这是用于高亮字符的语法 组的 ID。":highlight link" 给出的高亮组被跟随,以找到实际使用 的组。 *system()* system({expr}) 得到外壳命令 {expr} 的输出结果。注意: {expr} 里的换行可能会使 命令失败。'shellquote' 和 'shellxquote' 里的字符也可能会引起麻 烦。 这不是用来执行交互命令的。 返回字符串。示例: :let files = system("ls") 要使结果更独立于所用的系统,外壳输出的结果被过滤,Macintosh 的 <CR> 被换成 <NL>,而 DOS 系列的系统上 <CR><NL> 也被换成 <NL>。 使用若干选项,以下面的方法构造要执行的命令: 'shell' 'shellcmdflag' 'shellxquote' {expr} 'shellredir' {tmp} 'shellxquote' ({tmp} 是自动生成的一个文件名)。 Unix 和 OS/2 上,{expr} 用大括号包围,以便支持连接的多条命令。 返回的错误代码可以在 |v:shell_error| 里找到。 该函数不能运行于 |restricted-mode|。 不同于 ":!cmd",没有自动对改变过的文件的检查。使用 |:checktime| 来强制这种检查。 tempname() *tempname()* *temp-file-name* 返回字符串,它是一个不存在的文件名。可以用作临时文件。该文件在 至少 26 个接连的调用内不会重复。例如: :let tmpfile = tempname() :exe "redir > " . tmpfile Unix 上,文件会在用户个人的目录下 (只有当前用户可以访问) 以避 免一些安全问题 (例如,符号链接攻击,或者有给其他人读取文件的可 能)。 Vim 退出时,该目录和里面的所有文件被删除。 MS-Windows 上,如果置位了 'shellslash' 选项或者 'shellcmdflag' 以 '-' 开始的时候,使用正斜杠。 tolower({expr}) *tolower()* 返回给出字符串的备份,但所有的大写字符变为小写 (就如同在字符串 上应用了 |gu| 一样)。 toupper({expr}) *toupper()* 返回给出字符串的备份,但所有的小写字符变为大写 (就如同在字符串 上应用了 |gU| 一样)。 type({expr}) *type()* 返回数值: 0 如果 {expr} 为数值型 1 如果 {expr} 为字符串型 virtcol({expr}) *virtcol()* 要得到屏幕列的位置,用 |virtcol()|。 注意 只能使用当前文件的位置标记。 返回数值,即 {expr} 给定的文件位置的屏幕列号。也就是,该位置的 字符占据的最后一个屏幕位置,这里假设屏幕有无限的宽度。如果该位 置是一个 <Tab>,返回的数值是 <Tab> 占据的最后一列。比如,如果 <Tab> 在第 1 列,而 'ts' 设为 8 的话,返回 8。 关于字节位置,见 |col()|。 如果在当前模式下使用了虚拟编辑,也可能返回行尾之后的位置。 |'virtualedit'| 可接受的位置是: . 光标位置 $ 光标行的行尾 (返回光标行显示的字符数加 1) 'x 位置标记 x 的位置 (如果该位置标记没有设置,返回 0) 注意 只能使用当前文件的位置标记。 示例: virtcol(".") with text "foo^Lbar", with cursor on the "^L", returns 5 virtcol("$") with text "foo^Lbar", returns 9 virtcol("'t") with text " there", with 't at 'h', returns 6 第一列为 1。返回 0 代表错误。 visualmode([expr]) *visualmode()* 返回字符串,它描述最近使用的可视模式。一开始,它返回空字符串, 一旦使用了可视模式,返回 "v"、"V" 或 "<CTRL-V>" (单个 CTRL-V 字符),分别代表面向字符、面向行、和面向块的可视模式。 例如: :exe "normal " . visualmode() 进入和上次相同的可视模式。也可以用于在脚本里根据最近的可视模式 采取不同的行动。 如果提供的表达式的计算结果是非零数值或者是非空字符串,那么将清 除可视模式,并返回旧的值。注意 " " 和 "0" 也是非空字符串,所以 也会清除该模式。 *winbufnr()* winbufnr({nr}) 返回数值,即窗口 {nr} 相关联的缓冲区号。如果 {nr} 为零,返回当 前窗口的缓冲区号。如果窗口 {nr} 不存在,返回 -1。 示例: :echo "当前窗口的文件是 " . bufname(winbufnr(0)) *wincol()* wincol() 返回数值,窗口光标的虚拟列。亦即从窗口左侧起算的屏幕列数。最左 列为第一列。 winheight({nr}) *winheight()* 返回数值,窗口 {nr} 的高度。如果 {nr} 为零,返回当前窗口的高 度。如果窗口 {nr} 不存在,返回 -1。存在的窗口的宽度至少为零。 示例: :echo "当前窗口有 " . winheight(0) . " 行。" *winline()* winline() 返回数值,窗口光标所在的屏幕行,亦即,从窗口顶部起算的屏幕行 数。第一行返回 1。 *winnr()* winnr() 返回数值,当前窗口的编号。最上面的窗口的编号为 1。该数值可以用 于 |CTRL-W_w| 和 ":wincmd w" |:wincmd|。 *winrestcmd()* winrestcmd() 返回 |:resize| 命令序列,该序列应该能够恢复当前窗口的大写。只 有在没有窗口被打开或关闭且当前窗口没有改变的时候才能正确工作。 示例: :let cmd = winrestcmd() :call MessWithWindowSizes() :exe cmd winwidth({nr}) *winwidth()* 返回数值,窗口 {nr} 的宽度。如果 {nr} 为零,返回当前窗口的宽 度。如果窗口 {nr} 不存在,返回 -1。存在的窗口的宽度至少为零。 示例: :echo "当前窗口有 " . winwidth(0) . " 列。" :if winwidth(0) <= 50 : exe "normal 50\<C-W>|" :endif *feature-list* 有三种类型的特性: 1. 只有在 Vim 编译时加入才会支持的特性 |+feature-list|。例如: :if has("cindent") 2. 只有特定条件满足才会支持的特性。例如: :if has("gui_running") *has-patch* 3. 包含的补丁。先检查 |v:version| 确定 Vim 的版本。然后形如 "patch123" 的特性 意味着补丁 123 已经在本版本里包含了。例如 (确定是 version 6.2.148 或更新的 版本): :if v:version > 602 || v:version == 602 && has("patch148") all_builtin_terms 编译时打开了所有的内建终端。 amiga Vim 的 Amiga 版本。 arabic 编译时加入了阿拉伯语的支持。|Arabic|。 arp 编译时加入了 ARP 的支持。(Amiga)。 autocmd 编译时加入了自动命令的支持。 balloon_eval 编译时加入了|balloon-eval| 的支持。 beos Vim 的 BeOS 版本。 browse 编译时加入了|:browse| 的支持。因而 browse() 可以工作。 builtin_terms 编译时打开了一些内建终端。 byte_offset 编译时加入了的支持。for 'o' in 'statusline' cindent 编译时加入了 'cindent' 的支持。 clientserver 编译时加入了远程调用的支持。|clientserver|。 clipboard 编译时加入了 'clipboard' 的支持。 cmdline_compl 编译时加入了|cmdline-completion| 的支持。 cmdline_hist 编译时加入了|cmdline-history| 的支持。 cmdline_info 编译时加入了 'showcmd' and 'ruler' 的支持。 comments 编译时加入了|'comments'| 的支持。 cryptv 编译时加入了加密的支持。|encryption|。 cscope 编译时加入了|cscope| 的支持。 compatible 编译时确保和 Vi 非常兼容。 debug 编译时定义了 "DEBUG"。 dialog_con 编译时加入了控制台对话框的支持。 dialog_gui 编译时加入了 GUI 对话框的支持。 diff 编译时加入了 |vimdiff| 和 'diff' 的支持。 digraphs 编译时加入了二合字母的支持。 dnd 编译时加入了 "~ 寄存器的支持 |quote_~|。 dos32 Vim 的 32 位 DOS 的 (DJGPP) 版本。 dos16 Vim 的 16 位的 DOS 版本。 ebcdic 在使用 ebcdic 字符集的机器上编译。 emacs_tags 编译时加入了 Emcac 标签的支持。 eval 编译时加入了表达式计算的支持。当然总要打开啦! ex_extra 编译时加入了附加的 Ex 命令 |+ex_extra|。 extra_search 编译时加入了 |'incsearch'| 和 |'hlsearch'| 的支持。 farsi 编译时加入了波斯语的支持。|farsi|。 file_in_path 编译时加入了 |gf| 和 |<cfile>| 的支持。 find_in_path 编译时加入了头文件搜索 |+find_in_path| 的支持。 fname_case 文件名大小写敏感 (在 Amiga、MS-DOS 和 Windows 本特性不 存在)。 folding 编译时加入了 |folding| 的支持。 footer 编译时加入了 GUI 信息页脚的支持。|gui-footer| fork 编译时决定使用 fork()/exec() 而不是 system()。 gettext 编译时加入了信息翻译 |multi-lang|。 gui 编译时加入了 GUI 的支持。 gui_athena 编译时加入了 Athena GUI。 gui_beos 编译时加入了 BeOS GUI。 gui_gtk 编译时加入了 GTK+ GUI (任何版本)。 gui_gtk2 编译时加入了 GTK+ 2 GUI (同时也定义了 gui_gtk)。 gui_mac 编译时加入了 Macintosh GUI。 gui_motif 编译时加入了 Motif GUI。 gui_photon 编译时加入了 Photon GUI。 gui_win32 编译时加入了 MS Windows Win32 GUI。 gui_win32s 同上,使用了 Win32s 系统 (Windows 3.1) gui_running Vim 在 GUI 上运行,或者 GUI 将很快启动。 hangul_input 编译时加入了韩语 (Hangul) 输入的支持。 |hangul| iconv 可以使用 iconv() 进行转换。 insert_expand 编译时加入了插入模式中 CTRL-X 扩展命令的支持。 jumplist 编译时加入了 |jumplist| 的支持。 keymap 编译时加入了 'keymap' 的支持。 langmap 编译时加入了 'langmap' 的支持。 libcall 编译时加入了 |libcall()| 的支持。 linebreak 编译时加入了 'linebreak'、'breakat' 和 'showbreak' 的 支持。 lispindent 编译时加入了 lisp 缩进的支持。 listcmds 编译时加入了缓冲区列表 |:files| 和参数列表 |arglist| 的命令。 localmap 编译时加入了局部映射和缩写。|:map-local| mac Vim 的 Macintosh 版本。 macunix Vim 的 Macintosh 版本,使用 Unix 文件命名 (OS-X)。 menu 编译时加入了 |:menu| 的支持。 mksession 编译时加入了 |:mksession| 的支持。 modify_fname 编译时加入了文件名的修饰符支持。|filename-modifiers| mouse 编译时加入了鼠标的支持。 mouseshape 编译时加入了 'mouseshape' 的支持。 mouse_dec 编译时加入了 Dec 终端的鼠标支持。 mouse_gpm 编译时加入了 gpm (Linux 控制台鼠标) 的支持。 mouse_netterm 编译时加入了 netterm 的鼠标支持。 mouse_pterm 编译时加入了 qnx 的鼠标支持。 mouse_xterm 编译时加入了 xterm 的鼠标支持。 multi_byte 编译时加入了编辑韩语等语言的支持。 multi_byte_ime 编译时加入了 IME 输入方法的支持。 multi_lang 编译时加入了多语言的支持。 netbeans_intg 编译时加入了 |netbeans| 的支持。 ole 编译时加入了 Win32 OLE automation 的支持。 os2 Vim 的 OS/2 版本。 osfiletype 编译时加入了 osfiletypes 的支持。|+osfiletype| path_extra 编译时加入了 'path' 和 'tags' 上下搜索的支持。 perl 编译时加入了 Perl 接口。 postscript 编译时加入了 PostScript 文件打印的支持。 printer 编译时加入了 |:hardcopy| 的支持。 python 编译时加入了 Python 接口。 qnx Vim 的 QNX 版本。 quickfix 编译时加入了 |quickfix| 的支持。 rightleft 编译时加入了 'rightleft' 的支持。 ruby 编译时加入了 Ruby 接口 |ruby|。 scrollbind 编译时加入了 'scrollbind' 的支持。 showcmd 编译时加入了 'showcmd' 的支持。 signs 编译时加入了 |:sign| 的支持。 smartindent 编译时加入了 'smartindent' 的支持。 sniff 编译时加入了 SNiFF interface 的支持。 statusline 编译时加入了 'statusline' 和 'rulerformat' 还有 'titlestring' 和 'iconstring' 的特殊格式的支持。 sun_workshop 编译时加入了 Sun |workshop| 的支持。 syntax 编译时加入了语法高亮的支持。 syntax_items 当前缓冲区有激活的语法高亮项目。 system 编译时决定使用 system() 而不是 fork()/exec()。 tag_binary 编译时加入了标签文件的二分搜索 |tag-binary-search|。 tag_old_static 编译时加入了老的静态标签的支持。|tag-old-static|。 tag_any_white 编译时加入了允许标签文件使用任何空白字符的支持。 |tag-any-white|。 tcl 编译时加入了 Tcl 接口。 terminfo 编译时决定使用 terminfo 而不是 termcap。 termresponse 编译时加入了 |t_RV| 和 |v:termresponse| 的支持。 textobjects 编译时加入了 |text-objects| 的支持。 tgetent 编译时加入了 tgetent 的支持,可以使用外部 termcap 或 terminfo 文件。 title 编译时加入了窗口标题的支持。|'title'|。 toolbar 编译时加入了 |gui-toolbar| 的支持。 unix Vim 的 Unix 版本。 user_commands 用户定义命令支持。 viminfo 编译时加入了 viminfo 的支持。 vim_starting 如果在启动载入脚本的阶段则为真。 vertsplit 编译时加入了垂直分割窗口的支持 |:vsplit|。 virtualedit 编译时加入了 'virtualedit' 选项支持。 visual 编译时加入了可视模式的支持。 visualextra 编译时加入了附加的可视模式命令支持。 |blockwise-operators|。 vms Vim 的 VMS 版本。 vreplace 编译时加入了 |gR| and |gr| 命令支持。 wildignore 编译时加入了 'wildignore' 选项支持。 wildmenu 编译时加入了 'wildmenu' 选项支持。 windows 编译时加入了多窗口的支持。 winaltkeys 编译时加入了 'winaltkeys' 选项。 win16 Vim 的 Win16 版本。(MS-Windows 3.1)。 win32 Vim 的 Win32 版本。(MS-Windows 95/98/ME/NT/2000/XP)。 win64 Vim 的 Win64 版本。(MS-Windows 64 位)。 win32unix Vim 的 Win32 版本。使用 Unix 文件命名 (Cygwin) win95 支持 MS-Windows 95/98/ME 的 Win32 版本。 writebackup 编译时决定缺省打开 'writebackup'。 xfontset 编译时加入了 X 字体集 的支持。|xfontset|。 xim 编译时加入了 X 输入法 的支持。|xim|。 xsmp 编译时加入了 X 会话管理 的支持。 xsmp_interact 编译时加入了交互的 X 会话管理 的支持。 xterm_clipboard 编译时加入了 xterm 剪贴板的支持。 xterm_save 编译时加入了保存和恢复 xterm 屏幕的支持。 x11 编译时加入了 X11 的支持。 *string-match* 字符串里的模式匹配 |pattern| 说明的正规表达式通常用于寻找缓冲区行的匹配。如果匹配用来在字符串里寻 找匹配,几乎所有的功能都相同。唯一的区别是,字符串是作为单行处理的。如果字符串 里包含了 "\n" 字符,它并不看作是模式里的换行。它可以匹配模式里的 "\n",甚至于 "."。示例: :let a = "aaaa\nxxxx" :echo matchstr(a, "..\n..") aa xx :echo matchstr(a, "a.x") a x 不要忘记 "^" 只会在字符串的第一个字符匹配,而 "$" 在字符串的最后一个字符匹配。 它们不会匹配 "\n" 之后和之前的位置。

5. 定义函数 *user-functions*

可以定义新的函数。调用的方式就像内建函数一样。函数执行一系列 Ex 命令。普通模式 下的命令可以用 |:normal| 命令执行。 函数名须以大写字母开始,以免和内建函数引起混淆。要避免在不同脚本使用相同的名 字,避免显见的或者过短的名字。一个好习惯是使用脚本名字作为函数名字的开头,比如 "HTMLcolor()"。 也可以使用花括号,见 |curly-braces-names|。 *local-function* 局部于脚本的函数必须以 "s:" 开始。局部于脚本的函数只能在同一脚本和脚本中定义的 函数、用户命令和自动命令里调用。也可以在脚本定义的映射里调用该函数,但必须使用 |<SID>| 而不是 "s:",如果映射会在脚本之外被扩展的话。 *:fu* *:function* *E128* *E129* *E123* :fu[nction] 列出所有函数和它们的参数。 :fu[nction] {name} 列出 {name} 命名的函数。 *E124* *E125* :fu[nction][!] {name}([arguments]) [range] [abort] 定义 {name} 命名的新函数。名字必须由字母数字和 '_' 字 符组成,而且必须以大写字母或者 "s:" 开头 (见上)。 *function-argument* *a:var* 参数的定义只要给出它的名字。在函数里,可以使用 "a:name" 来访问 ("a:" 代表参数 (argument))。 可以给出不超过 20 个参数,以逗号分隔。 最后,可以给出参数 "...",意味着可以有更多的参数。在函 数里,可以通过 "a:1"、"a:2" 等等访问它们。"a:0" 设为这 些附加参数的数目 (可以为 0)。 如果不使用 "...",实际给出的参数数目必须等于命名参数的 数目。如果使用 "...",参数的数目可以更多。 可以定义没有参数的函数。但你这时仍然需要提供 ()。 函数体在之后的行给出,直到匹配的 |:endfunction| 为止。 可以在函数体里定义别的函数。 *E127* *E122* 如果同名的函数已经存在而且没有使用 [!],给出错误信息。 如果给出 [!],已有的函数被悄然替代。如果该函数正在执行 期间除外。此时,这是一个错误。 *a:firstline* *a:lastline* 如果给出 [range] 参数,则该函数自己能理解并处理行范 围。该范围通过 "a:firstline" 和 "a:lastline" 定义。如 果没有 [range],":{range}call" 会在该范围的每一行分别 执行该函数,每次光标都定位在处理行的行首。见 |function-range-example|。 如果给出 [abort] 参数,该函数在遇到错误时立即中止。 最近使用的搜索模式和重做命令 "." 不会受到函数的影响。 *:endf* *:endfunction* *E126* *E193* :endf[unction] 结束函数定义。必须单起一行,没有任何其它命令。 *:delf* *:delfunction* *E130* *E131* :delf[unction] {name} 删除 {name} 命名的函数。 *:retu* *:return* *E133* :retu[rn] [expr] 从函数返回。如果给出 "[expr]",计算该表达式的结果成为 函数的返回值。如果没有给出 "[expr]",返回 0。 如果函数退出时没有显式的调用 ":return",返回 0。 注意 没有不可到达行的检查,因而,如果有命令在 ":return" 之后,不会给出警告。 如果 ":return" 在 |:try| 之后使用但在匹配的 |:finally| (如果有的话) 之前的话,":finally" 之后直到匹配的 |:endtry| 的命令会先执行。该过程反复应用于所有函数内的 嵌套 ":try" 块。在最外层 ":endtry" 结束之后才真正返 回。 在函数里,可以使用变量。它们是局部变量,在函数返回时就会消失。全局变量的访问需 要通过 "g:"。 例如: :function Table(title, ...) : echohl Title : echo a:title : echohl None : let idx = 1 : while idx <= a:0 : echo a:{idx} . ' ' : let idx = idx + 1 : endwhile : return idx :endfunction 该函数这时可以这样调用: let lines = Table("Table", "line1", "line2") let lines = Table("Empty Table") 要返回多于一个值,传入全局变量的名字: :function Compute(n1, n2, divname) : if a:n2 == 0 : return "fail" : endif : let g:{a:divname} = a:n1 / a:n2 : return "ok" :endfunction 该函数这时可以这样调用: :let success = Compute(13, 1324, "div") :if success == "ok" : echo div :endif 一个替代方案是返回一个可以执行的命令。这时可以使用调用函数的局部变量。例如: :function Foo() : execute Bar() : echo "line " . lnum . " column " . col :endfunction :function Bar() : return "let lnum = " . line(".") . " | let col = " . col(".") :endfunction 名字 "lnum" 和 "col" 可以作为参数传递给 Bar(),使得调用者可以改变这些名字。 *:cal* *:call* *E107* :[range]cal[l] {name}([arguments]) 调用函数。函数名和参数通过 |:function| 指定。可以使用不超过 20 个参数。 如果没有给出范围而函数又接受范围,该函数被调用一次。如果给出范 围,光标在执行函数前定位在该范围的第一行的开始。 如果给出范围但函数自己不能处理之,该函数在范围里的每一行分别执 行。光标定位在每个处理行的第一列。光标留在最后一行 (但可能被最 后一个函数调用移动)。每一行上,参数被重新计算。所以这是可以的: *function-range-example* :function Mynumber(arg) : echo line(".") . " " . a:arg :endfunction :1,5call Mynumber(getline(".")) "a:firstline" 和 "a:lastline" 总是有定义的。它们可以用来在范围 的开始或结束处进行一些不同的处理。 能处理范围本身的函数示例: :function Cont() range : execute (a:firstline + 1) . "," . a:lastline . 's/^/\t\\ ' :endfunction :4,8call Cont() 该函数在范围里的每行开头插入行连续符 "\",除了第一行以外。 *E132* 用户函数的递归调用受到 |'maxfuncdepth'| 选项的限制。 *autoload-functions* 如果使用很多或者很大的函数,可以在需要使用它们的时候才自动提供其定义。这可用 FuncUndefined 自动命令事件完成,它需要一个能匹配等待定义的函数的模式。例如: :au FuncUndefined BufNet* source ~/vim/bufnetfuncs.vim 文件 "~/vim/bufnetfuncs.vim" 这时应该定义 "BufNet" 开始的函数。另见 |FuncUndefined|。

6. 花括号名字 *curly-braces-names*

使用变量的任何地方可以改用 "花括号名字" 变量。和常规的变量名类似,但可以包含一 到多个花括号 {} 包围的表达式,形如: my_{adjective}_variable 如果 Vim 遇到这种情形,它会计算花括号内的表达式,把结果放在表达式所在的位置, 然后重新解释整个字符串为完整的变量名。所以在上例中,如果变量 "adjective" 设为 "noisy",那么引用的将是 "my_noisy_variable"。如果 "adjective" 设为 "quiet",那 么引用的将是 "my_quiet_variable"。 一个这种形式的应用是建立一系列变量,由一个选项管理。比如,语句 echo my_{&background}_message 会显示 "my_dark_message" 或者 "my_light_message" 的内容,取决于 'background' 的当前值。 你可以使用多个花括号对: echo my_{adverb}_{adjective}_message ..甚至嵌套使用: echo my_{ad{end_of_word}}_message 其中 "end_of_word" 可以是 "verb" 或者 "jective"。 不过,花括号里的表达式必须计算出合法的单个变量名,比如,这不行: :let foo='a + b' :echo c{foo}d .. 因为扩展的结果是 "ca + bd",这不是合法的变量名。 *curly-braces-function-names* 类似的,你可以调用和定义计算的出的函数名。比如: :let func_end='whizz' :call my_func_{func_end}(parameter) 会调用函数 "my_func_whizz(parameter)"。

7. 命令 *expression-commands*

:let {var-name} = {expr1} *:let* *E18* 设置内部变量 {var-name} 为表达式 {expr1} 的计算结果。 该变量也会得到 {expr} 的类型。如果 {var-name} 不存在, 它会被创立。 :let ${env-name} = {expr1} *:let-environment* *:let-$* 设置环境变量 {env-name} 为表达式 {expr1} 的计算结果。 它总是字符串型。 :let @{reg-name} = {expr1} *:let-register* *:let-@* 把表达式 {expr1} 的计算结果写到寄存器 {reg-name} 里。 {reg-name} 必须是单个字符,而且是一个可以写入的寄存器 (见 |registers|)。"@@" 可以用来访问无名寄存器,而 "@/" 设置搜索模式。 如果 {expr1} 的结果以 <CR><NL> 结束,该寄存器会成 为面向行类型,不然,它会成为面向字符类型。 这可以用来清除最近的搜索模式: :let @/ = "" 这和搜索空字符串不同,后者会在任何地方得到匹配。 :let &{option-name} = {expr1} *:let-option* *:let-star* 设置选项 {option-name} 为表达式 {expr1} 的计算结果。该 结果总会被转化为选项需要的类型。 对于局部于窗口或者缓冲区的选项而言,这和 |:set| 命令的 效果相同: 局部值和全局值都被改变。 :let &l:{option-name} = {expr1} 同上,但只设置选项的局部值 (如果有的话)。和 |:setlocal| 类似。 :let &g:{option-name} = {expr1} 同上,但只设置选项的全局值 (如果有的话)。和 |:setglocal| 类似。 *E106* :let {var-name} .. 列出变量 {var-name} 的值。可以给出多个变量的名字。 :let 列出所有变量的值。 *:unlet* *:unl* *E108* :unl[et][!] {var-name} ... 删除内部变量 {var-name}。可以给出多个变量的名字。它们 都被删除。如果使用 [!],即使变量不存在也不会给出错误。 :if {expr1} *:if* *:endif* *:en* *E171* *E579* *E580* :en[dif] 如果 {expr} 计算为非零,执行命令直到其后匹配的 ":else" 或者 ":endif" 为止。 从 Vim 版本 4.5 到 5.0,":if" 和 ":endif" 之间的 Ex 命 令被忽略。提供这两个命令只是为了后向兼容 (译者注,原文 如此),以方便未来的扩展。可以嵌套。注意 任何的 ":else" 或 ":elseif" 也被忽略,"else" 部分也一样不会执行。 利用这一点,你可以保持和旧版本的兼容: :if version >= 500 : 版本 5 专用的命令 :endif 为了找到 "endif",仍然需要分析命令。有时,旧版本的 Vim 不能识别新的命令。比如, ":silent" 被识别为 ":substitute" 命令。这种情形可以用 ":execute" 来避免: :if version >= 600 : execute "silent 1,$delete" :endif 注意: ":append" 和 ":insert" 命令在 ":if" 和 ":endif" 之间不能正常工作。 *:else* *:el* *E581* *E583* :el[se] 如果这之前的命令没有被执行,执行命令直到其后匹配的 ":else" 或 ":endif"。 *:elseif* *:elsei* *E582* *E584* :elsei[f] {expr1} ":else" ":if" 的缩写,而且无需另一个 ":endif"。 :wh[ile] {expr1} *:while* *:endwhile* *:wh* *:endw* *E170* *E585* *E588* :endw[hile] 只要 {expr1} 计算的结果非零,重复 ":while" 和 ":endwhile" 之间的命令。 如果发现循环里有命令出错,从 "endwhile" 之后继续执行。 注意: ":append" 和 ":insert" 命令在 ":while" 循环里不 能正常工作。 *:continue* *:con* *E586* :con[tinue] 在 ":while" 的内部,跳转回 ":while"。如果在 ":while" 内部的 |:try| 之后但在匹配的 |:finally| (如果有的话) 之前,":finally" 之后,匹配的 |:endtry| 之前的命令会被 先执行。该过程反复应用于所有函数内的嵌套 ":try" 块。在 最外层 ":endtry" 结束之后才跳回 ":while"。 *:break* *:brea* *E587* :brea[k] 在 ":while" 的内部,跳到匹配的 ":endwhile" 之后的命 令。如果在 ":while" 内部的 |:try| 之后但在匹配的 |:finally| (如果有的话) 之前,":finally" 之后,匹配的 |:endtry| 之前的命令会被先执行。该过程反复应用于所有函 数内的嵌套 ":try" 块。在最外层 ":endtry" 结束之后才跳 到 ":endwhile" 之后的命令。 :try *:try* *:endt* *:endtry* *E600* *E601* *E602* :endt[ry] 改变 ":try" 和 ":endtry" 之间命令的错误处理,包括所有 执行的内容,":source" 里的命令,函数调用,或者自动命令 的激活等。 如果检测到错误或者中断,而其后又跟随了 |:finally| 命 令,执行从 ":finally" 之后继续。否则,或者在那以后遇到 了 ":endtry",则检查是否存在 (动态的) 往外一层的 ":try" 以及其相应的 ":finally" 等等。然后,脚本的处理 被终止。(函数定义里是否有 "abort" 参数都不相干。) 示例: :try | edit too much | finally | echo "cleanup" | endtry :echo "impossible" " 到不了这里,脚本在上面已经终止 另外,":try" 和 ":endtry" 之间的错误或者中断 (动态地) 被转换成一个例外。它的捕获过程如同它被 |:throw| 命令抛 出那样 (见 |:catch|)。这种情况下,脚本的处理不会被终 止。 "Vim:Interrupt" 的值用于中断例外。Vim 命令的错误被转换 成形如 "Vim({command}):{errmsg}" 的值,其它错误被转换 成形如 "Vim:{errmsg}"。这里,{command} 是完整的命令 名,而 {errmsg} 是错误例外如果没有被捕获的时候会显示的 消息,它总以错误号开始。 示例: :try | sleep 100 | catch /^Vim:Interrupt$/ | endtry :try | edit | catch /^Vim(edit):E\d\+/ | echo "error" | endtry *:cat* *:catch* *E603* *E604* *E605* :cat[ch] /{pattern}/ 匹配 {pattern} 的例外抛出时,如果它没有被前一个 ":catch" 捕获,则执行本语句之后的命令,直到遇到和本 ":catch" 处于同一 |:try| 块的下一个 ":catch"、 |:finally| 或者 |:endtry| 为止。否则,这些命令被跳过。 如果没有提供 {pattern},所有的错误都会被捕获。 示例: :catch /^Vim:Interrupt$/ " 捕获中断 (CTRL-C) :catch /^Vim\%((\a\+)\)\=:E/ " 捕获所有的 Vim 错误 :catch /^Vim\%((\a\+)\)\=:/ " 捕获错误和中断 :catch /^Vim(write):/ " 捕获所有 :write 的错误 :catch /^Vim\%((\a\+)\)\=:E123/ " 捕获错误 E123 :catch /my-exception/ " 捕获用户例外 :catch /.*/ " 捕获一切 :catch " 等同于 /.*/ 除了 / 以外,也可以用别的字符包围 {pattern},只要它没 有特殊含义 (比如 '|' 或 '"') 而且不出现在 {pattern} 里。 注意: 依赖 ":catch" 去捕获错误信息的_文本_是不可靠的, 因为不同的 locale 的信息可以不同。 *:fina* *:finally* *E606* *E607* :fina[lly] 任何匹配的 |:try| 和本 ":finally" 之间的部分要离开的时 候都执行本语句之后的命令,直到遇到匹配的 |:endtry| 为 止。包括这些情形: 正常完成且要执行到 ":finally",通过 |:continue|、|:break|、|:finish| 或 |:return|,或者由 于错误或者中断或者例外 (见 |:throw|)。 *:th* *:throw* *E608* :th[row] {expr1} 计算 {expr1} 然后抛出例外。如果 ":throw" 在 |:try| 之 后但在第一个对应的 |:catch| 之前使用,它之后的命令被跳 过,直到遇到第一个匹配 {expr1} 为止。如果没有这样的 ":catch",或者如果 ":throw" 在 ":catch" 之后 |:finally| 之前使用,执行 ":finally" (如果有的话) 之后 直到匹配的 |:endtry| 为止的命令。如果本 ":throw" 在 ":finally" 之后之后出现,直到 ":endtry" 为止的命令都被 跳过。到达 ":endtry" 的时候,在动态计算的往外一层的 ":try" 块上再次重复本过程 (这可能出现在外层调用的函数 或者执行的脚本上),直到找到一个匹配的 ":catch"。如果最 终该例外没有被捕获,命令处理被终止。 示例: :try | throw "oops" | catch /^oo/ | echo "caught" | endtry *:ec* *:echo* :ec[ho] {expr1} .. 回显每个 {expr1},以空格分隔。第一个 {expr1} 开启一个 新行。另见 |:comment|。 使用 "\n" 来开启新行。使用 "\r" 把光标移到第一列。 使用 |:echohl| 命令的高亮设置。 后面不能跟注释。 示例: :echo "'shell' 的值是 " &shell 后来的重画可能使消息再次消失。要避免":echo" 之前的命令 引起它之后的重画 (通常,重画被延迟到有输入的时候才进 行),使用 |:redraw| 命令强制重画。例如: :new | redraw | echo "这里有一个新窗口" *:echon* :echon {expr1} .. 回显每个 {expr1},不附加其它字符。另见 |:comment|。 使用 |:echohl| 命令的高亮设置。 后面不能跟注释。 例如: :echon "'shell' 的值是 " &shell 注意 两者的区别: ":echo" 是一个 Vim 命令,而 ":!echo" 是一个外部的外壳命令: :!echo % --> filename ":!" 的参数被扩展,见 |:_%|。 :!echo "%" --> filename or "filename" 和前例类似,你是否会看到双引号取决于你的 'shell'。 :echo % --> nothing '%' 不是一个表达式合法的字符。 :echo "%" --> % 只会回显 '%' 字符。 :echo expand("%") --> filename 调用 expand() 函数来扩展 '%'。 *:echoh* *:echohl* :echoh[l] {name} 让其后的 |:echo|、|:echon| 和 |:echomsg| 命令使用高亮 组 {name}。也可用于 |input()| 的提示。示例: :echohl WarningMsg | echo "Don't panic!" | echohl None 不要忘记把组设回 "None"。不然其后的 echo 都会被高亮。 *:echom* *:echomsg* :echom[sg] {expr1} .. 回显表达式的结果,将其作为一个真正的消息,并把该消息保 存在 |message-history| 里。 参数之间加入空格,和 |:echo| 类似。但不可显示的字符只 是回显而不会被解释。 使用 |:echohl| 命令的高亮设置。 示例: :echomsg "It's a Zizzer Zazzer Zuzz, as you can plainly see." *:echoe* *:echoerr* :echoe[rr] {expr1} .. 回显表达式的结果,将其作为一个错误消息,并把该消息保 存在 |message-history| 里。如果用在脚本或函数里,会加 入行号。 参数之间加入空格,和 |:echo| 类似。如果在 try 条件句里 使用,该消息会抛出一个错误例外 (见 |try-echoerr|)。 示例: :echoerr "This script just failed!" 如果你只想要使用 |:echohl| 高亮的消息。 要得到铃声: :exe "normal \<Esc>" *:exe* *:execute* :exe[cute] {expr1} .. 计算 {expr1},返回的字符串作为 Ex 命令执行。多个参数用 空格连接。{expr1} 用作被处理的命令,命令行编辑的键不会 被识别。 后面不能跟注释。 示例: :execute "buffer " nextbuf :execute "normal " count . "w" ":execute" 可以用来把命令附加到不能接受 '|' 的命令后 面。比如: :execute '!ls' | echo "theend" ":execute" 也是一个避免在 Vim 脚本里为 ":normal" 命令 输入控制字符的好方法: :execute "normal ixxx\<Esc>" 这里给出一个 <Esc> 字符,见 |expr-string|。 注意: 执行的字符串可以是任何命令行,但不能开始或结束一 个 "while" 或 "if" 命令。所以,这样不行: :execute 'while i > 5' :execute 'echo "test" | break' 但如果执行的字符串里有完整的 "while" 和 "if" 命令就没 有问题: :execute 'while i < 5 | echo i | let i = i + 1 | endwhile' *:comment* ":execute"、":echo" 和 ":echon" 后面不能直接跟注释。 因它们把 '"' 看成字符串的开始。但你可以把注释加到 '|' 后面。例如: :echo "foo" | "这是一个注释

8. 例外处理 *exception-handling*

Vim 脚本语言包含了例外处理特性。本节解释如何在 Vim 脚本里应用该机制。 Vim 在出错或者中断的时候可以抛出例外。见 |catch-errors| 和 |catch-interrupt|。 你也可以显式地使用 ":throw" 命令抛出例外。见 |throw-catch|。 TRY 条 件 句 *try-conditionals* 例外可以被捕获或者用来激发清理代码的运行。你可以使用 try 条件句来指定 catch 子 句 (捕获例外) 和/或 finally 子句 (执行清理)。 try 条件句以 |:try| 命令开始,以匹配的 |:endtry| 命令结束。两者之间,你可以 使用 |:catch| 命令开始 catch 子句,或者用 |:finally| 命令开始 finally 子句。 catch 子句可有零到多个,但 finally 子句至多只有一个,且它之后不能再有 catch 子 句。catch 子句和 finally 子句之前的行称为 try 块。 :try : ... : ... TRY 块 : ... :catch /{pattern}/ : ... : ... CATCH 子 句 : ... :catch /{pattern}/ : ... : ... CATCH 子 句 : ... :finally : ... : ... FINALLY 子 句 : ... :endtry try 子句允许观察代码里是否有例外,并采取合适的行动。try 块里的例外可能被捕获。 try 块和 catch 子句里的例外可能引起清理动作。 如果 try 块的执行过程中没有抛出例外,控制转移到 finally 子句。在它执行后, 脚本从 ":endtry" 之后的行继续。 如果 try 块的执行过程中抛出了例外,该 try 块其余的行被跳过。例外和 ":catch" 命令的模式参数一一比较。第一个匹配的 ":catch" 之后的 catch 子句被采用,其余的 catch 子句则不会执行。catch 子句在下一个最早遇到的 ":catch"、":finally" 或 ":endtry" 命令结束。这时,finally 子句 (如果有的话) 被执行。当遇到 ":endtry" 的时候,脚本从后面的行继续,一如往常。 如果 try 块抛出的例外不能匹配任何 ":catch" 命令的模式,该例外不能由本 try 条件句捕获,因而不会执行任何的 catch 子句。只有 finally 子句,如果有的话,被采 用。该例外在 finally 子句的执行时被暂时搁置。在 ":endtry" 之后才继续。这样, ":endtry" 之后的命令不会被执行,而该例外可以在别的地方捕获,见 |try-nesting|。 如果在 catch 子句的执行过程中抛出了另一个错误,catch 子句的其余部分不再执 行。新的例外不会和试图和同一个 try 条件句的任何 ":catch" 命令的模式匹配,因而 也不会执行任何它的 catch 子句。不过,如果有 finally 子句,它还是会被执行,而在 它的执行过程中暂时搁置新的例外。":endtry" 之后的命令也不会执行。而新的例外仍可 能在别的地方捕获,见 |try-nesting|。 如果在 finally 子句 (如果有的话) 的执行过程中抛出了另一个错误,finally 子句 的其余部分不再执行。如果 finally 子句是因为 try 块或者某个 catch 子句里产生的 例外引起的,原先的 (被暂时搁置的) 例外被放弃。":endtry" 之后的命令也不会执行。 而 finally 子句的这个例外被传播,而可以在别的地方捕获,见 |try-nesting|。 在 ":while" 循环包含的完整的 try 条件句里的 try 块或者某个 catch 子句里遇到 ":break" 或 ":continue" 时,或者在函数或者被执行的脚本里的 try 条件句里的 try 块或者某个 catch 子句里执行 ":return" (函数) 或者 ":finish" (脚本) 的时候,也 会执行 finally 子句。":break"、":continue"、":return" 或者 ":finish" 在 finally 子句的执行时被暂停,而在遇到 ":endtry" 时继续。不过,如果在执行 finally 子句时 抛出例外,它们都被抛弃。 在 ":while" 循环包含的完整的 try 条件句里的 finally 子句里遇到 ":break" 或 ":continue" 时,或者在函数或者被执行的脚本里的 finally 子句里执行 ":return" ( 函数) 或者 ":finish" (脚本) 的时候,finally 子句的其余部分被跳过,而 ":break"、":continue"、":return" 或 ":finish" 会如常继续执行。如果 finally 的 执行是因为例外或者早先的 try 块或者 catch 子句的 ":break"、":continue"、 ":return" 或者 ":finish" 引起的,暂停的例外或者命令被放弃。 例子可见 |throw-catch| 和 |try-finally|。 TRY 条 件 句 的 嵌 套 *try-nesting* try 条件句可以任意嵌套。也就是说,完整的 try 条件句可以在另一个 try 条件句的 try 块、某个 catch 子句或者 finally 子句里出现。如果内层的 try 条件句不能捕获 它的 try 块抛出的例外,或者在它的某个 catch 子句后者 finally 子句里抛出新的例 外的话,那么根据上述规则由外层的 try 条件句继续检查是否能捕获该例外。如果内层 try 条件句在外层 try 条件句的 try 块里,检查外层的 catch 子句,不然只有 finally 子句会被执行。对嵌套的处理而言,内层 try 条件句是直接包含在外层里面, 还是外层执行了脚本或者调用了函数,而后者又包含了内层 try 条件句,无关紧要。 如果没有活动的 try 条件句能捕获某个例外,只有它们的 finally 子句会执行。最后, 脚本结束它的处理。如果是 ":throw" 命令显式地抛出的未捕获的例外,显示错误信息。 对于 Vim 隐含抛出的未捕获的错误或者中断例外,错误信息或者中断信息也会像平常一 样显示。 例子可见 |throw-catch|。 检 查 例 外 处 理 代 码 *except-examine* 例外处理的代码的编写可能很麻烦。如果你不知道发生了什么,把 'verbose' 设为 13, 或者在执行脚本文件时使用 ":13verbose" 命令修饰符。这样,你能看到什么时候例外被 抛出、放弃、捕获、或者最终处理。如果详细程度大于等于 14,finally 子句暂停什么 也会显示。这些信息在调试模式里也会给出 (见 |debug-scripts|)。 抛 出 和 捕 获 例 外 *throw-catch* 你可以抛出任何数值或者字符串作为例外。使用 |:throw| 命令然后把要抛出的值作为参 数传入: :throw 4711 :throw "string" *throw-expression* 你可以指定表达式参数。该表达式先进行计算,然后抛出其结果: :throw 4705 + strlen("string") :throw strpart("strings", 0, 6) 在计算 ":throw" 命令的参数的时候,也可能会抛出例外。除非它被捕获,不然表达式的 计算会被放弃。":throw" 命令这时不会抛出新的例外。 例如: :function! Foo(arg) : try : throw a:arg : catch /foo/ : endtry : return 1 :endfunction : :function! Bar() : echo "in Bar" : return 4710 :endfunction : :throw Foo("arrgh") + Bar() 这里抛出了 "arrgh",而不会显示 "in Bar",因为 Bar() 没有执行。 :throw Foo("foo") + Bar() 却显示 "in Bar" 并且抛出 4711。 别的接受表达式作为参数的命令也可能因为表达式计算过程的 (未捕获的) 例外而被放 弃。例外这时被传播给该命令的调用者。 例如: :if Foo("arrgh") : echo "then" :else : echo "else" :endif 这里 "then" 和 "else" 都不会显示。 *catch-order* try 条件句里的例外可以用一个或多个 |:catch| 命令捕获,见 |try-conditionals|。 每个 ":catch" 命令可以捕获的值通过模式参数指定。捕获匹配的例外时,执行其后的 catch 子句。 例如: :function! Foo(value) : try : throw a:value : catch /^\d\+$/ : echo "Number thrown" : catch /.*/ : echo "String thrown" : endtry :endfunction : :call Foo(0x1267) :call Foo('string') 第一个 Foo() 的调用显示 "Number thrown",第二个 "String thrown"。 按照 ":catch" 命令本身的顺序,依次匹配例外。只用第一个成功匹配。所以,你应该把 更专门的 ":catch" 放在前面。下面的顺序并不合理: : catch /.*/ : echo "String thrown" : catch /^\d\+$/ : echo "Number thrown" 这里,第一个 ":catch" 总是会被匹配,所以第二个子句永远不可能被采用。 *throw-variables* 如果你使用通用的模式捕获到例外,可以通过变量 |v:exception| 得到准确的例外值: : catch /^\d\+$/ : echo "Number thrown. Value is" v:exception 你也许会对在什么地方抛出例外也感兴趣。它被保存在 |v:throwpoint| 里。注意 "v:exception" 和 "v:throwpoint" 可用于最近捕获的例外,只要该例外还没有完成处 理。 例如: :function! Caught() : if v:exception != "" : echo 'Caught "' . v:exception . '" in ' . v:throwpoint : else : echo 'Nothing caught' : endif :endfunction : :function! Foo() : try : try : try : throw 4711 : finally : call Caught() : endtry : catch /.*/ : call Caught() : throw "oops" : endtry : catch /.*/ : call Caught() : finally : call Caught() : endtry :endfunction : :call Foo() 会显示 Nothing caught Caught "4711" in function Foo, line 4 Caught "oops" in function Foo, line 10 Nothing caught 更实际的例子: 下面的命令 ":LineNumber" 显示调用它时,脚本或者函数里的行号: :function! LineNumber() : return substitute(v:throwpoint, '.*\D\(\d\+\).*', '\1', "") :endfunction :command! LineNumber try | throw "" | catch | echo LineNumber() | endtry *try-nested* try 条件句没有捕获的例外可以在包围它的 try 条件句中捕获: :try : try : throw "foo" : catch /foobar/ : echo "foobar" : finally : echo "inner finally" : endtry :catch /foo/ : echo "foo" :endtry 内层的 try 条件句没有捕获例外,只执行了 finally 子句。例外在外层得到捕获。本例 显示 "inner finally" 然后是 "foo"。 *throw-from-catch* 你可以捕获某例外,然后抛出另一个。它在该 catch 子句之外捕获: :function! Foo() : throw "foo" :endfunction : :function! Bar() : try : call Foo() : catch /foo/ : echo "Caught foo, throw bar" : throw "bar" : endtry :endfunction : :try : call Bar() :catch /.*/ : echo "Caught" v:exception :endtry 显示 "Caught foo, throw bar" 然后是 "Caught bar"。 *rethrow* Vim 脚本语言没有真正的 rethrow。但可以抛出 "v:exception" 来代替: :function! Bar() : try : call Foo() : catch /.*/ : echo "Rethrow" v:exception : throw v:exception : endtry :endfunction *try-echoerr* 注意 这个方法不能用来 "rethrow" Vim 错误或者中断例外,因为不能伪造 Vim 的内部 例外。试图这么做会产生一个错误例外。你应该抛出自己的例外来说明这种情形。如果你 想产生 Vim 的错误例外并包含原来的错误例外的值,可以使用 |:echoerr| 命令: :try : try : asdf : catch /.*/ : echoerr v:exception : endtry :catch /.*/ : echo v:exception :endtry 本代码会显示 Vim(echoerr):Vim:E492: Not an editor command: asdf 清 理 代 码 *try-finally* 脚本经常需要改变全局设定然后结束时恢复之。不过,如果用户按了 CTRL-C 中止脚本, 这些设定会处于不一致的状态。如果你处于某脚本的开发阶段而发生了错误或者你显式地 抛出例外而没有试图捕获之,也会有相同的情况。用带有 finally 子句的 try 条件句, 可以恢复设置,从而解决这个问题。可以保证无论是正常的控制流、出错或者显式的例外 ":throw"、还是被中断,都会执行 finally 子句 (注意 try 条件句的错误和中断被转换 成例外。如果没有捕获,它们在 finally 子句执行完之后会终止脚本。) 例如: :try : let s:saved_ts = &ts : set ts=17 : : " 这里执行重要的任务。 : :finally : let &ts = s:saved_ts : unlet s:saved_ts :endtry 无论任何函数还是脚本的一部分,只要它需要修改全局设置,而在失败或者成功退出该函 数或者脚本部分时需要恢复这些设置,就应该在本地应用本方法。 *break-finally* 清理代码也适用于 ":continue"、":break"、":return" 或 ":finish" 退出的 try 块或 catch 子句。 例如: :let first = 1 :while 1 : try : if first : echo "first" : let first = 0 : continue : else : throw "second" : endif : catch /.*/ : echo v:exception : break : finally : echo "cleanup" : endtry : echo "still in while" :endwhile :echo "end" 会显示 "first"、"cleanup"、"second"、"cleanup" 和 "end"。 :function! Foo() : try : return 4711 : finally : echo "cleanup\n" : endtry : echo "Foo still active" :endfunction : :echo Foo() "returned by Foo" 会显示 "cleanup" 和 "4711 returned by Foo"。你不需要在 finally 子句里加上附加 的 ":return"。(最终,它会覆盖原来的返回值。) *except-from-finally* finally 子句里可以使用 ":continue"、":break"、":return"、":finish" 或 ":throw",但不推荐,因为它放弃了 try 条件句的清理工作。不过当然了,finally 子 句里仍然可能有中断或者错误例外。 finally 子句的错误引起中断不能正常工作的例子: :try : try : echo "Press CTRL-C for interrupt" : while 1 : endwhile : finally : unlet novar : endtry :catch /novar/ :endtry :echo "Script still running" :sleep 1 如果你需要在 finally 里放入可能出错的命令,考虑捕获或者忽略这些命令的错误,见 |catch-errors| 和 |ignore-errors|。 捕 获 错 误 *catch-errors* 如果你想捕获特定的错误,你需要把要关注的代码放到 try 块里,然后为该错误消息加 入 catch 子句。try 条件句的存在使得所有的错误被转换为例外。不会显示消息,而 |v:errmsg| 也不会设置。要找到 ":catch" 命令右边的模式,你需要知道错误例外的格 式。 错误例外使用如下的格式: Vim({cmdname}):{errmsg} Vim:{errmsg} {cmdname} 是失败的命令名;第二种形式用于命令名未知的场合。{errmsg} 是错误在 try 条件句发生时,本应产生的错误消息。它总是以大写的 "E" 开始,后面跟两或者三 位的错误号,一个冒号和一个空格。 例如: 命令 :unlet novar 通常产生错误信息 E108: No such variable: "novar" 它在 try 条件句里被转换为例外 Vim(unlet):E108: No such variable: "novar" 命令 :dwim 通常产生错误信息 E492: Not an editor command: dwim 它在 try 条件句里被转换为例外 Vim:E492: Not an editor command: dwim 你可以这样捕获所有的 ":unlet" 错误 :catch /^Vim(unlet):/ 或者这样捕获所有拼错命令名字的错误 :catch /^Vim:E492:/ 有的错误信息可能由不同的命令产生: :function nofunc :delfunction nofunc 都会产生错误信息 E128: Function name must start with a capital: nofunc 它在 try 条件句里被分别转换为例外 Vim(function):E128: Function name must start with a capital: nofunc Vim(delfunction):E128: Function name must start with a capital: nofunc 使用下面的模式,你可以根据其号码捕获错误,而不管产生的命令是什么: :catch /^Vim(\a\+):E128:/ 有些命令,比如 :let x = novar 产生多个错误信息,这里: E121: Undefined variable: novar E15: Invalid expression: novar 只有第一个会用做例外的值,因为它是最专门的那个 (见 |except-several-errors|)。 所以你应该这样捕获它 :catch /^Vim(\a\+):E121:/ 你可以这样捕获所有和名字 "nofunc" 相关的错误 :catch /\<nofunc\>/ 你可以这样捕获 ":write" 和 ":read" 命令产生的所有 Vim 的错误 :catch /^Vim(\(write\|read\)):E\d\+:/ 你可以这样捕获所有的 Vim 错误 :catch /^Vim\((\a\+)\)\=:E\d\+:/ *catch-text* 注意: 永远不要根据错误信息文本本身捕获错误: :catch /No such variable/ 只适用于英语的 locale,如果用户用 |:language| 命令使用别的语言就不行了。不过, 在注释里引用该消息或许有帮助: :catch /^Vim(\a\+):E108:/ " No such variable 忽 略 错 误 *ignore-errors* 你可以通过在本地捕获来忽略某个 Vim 命令的错误: :try : write :catch :endtry 但强烈建议,_不要_使用这种简单的形式,因为它捕获的东西超过你的想象。":write" 命令里,会执行一些自动命令,它们可能引起与写入无关的错误。例如: :au BufWritePre * unlet novar 作为脚本的作者,你不应该负责处理这些错误: 使用你书写的脚本的用户可能定义了这些 自动命令。而你这么做只会屏蔽用户自己的错误。 更好的方法是用 :try : write :catch /^Vim(write):/ :endtry 这样,只捕获真正的 write 错误。总之,只应该捕获你有意忽略的错误。 对于单个不会执行自动命令的命令,你可以用 ":silent!" 命令来关闭错误到例外的转换: :silent! nunmap k 即使在活动的 try 条件句里也能这么用。 捕 获 中 断 *catch-interrupt* 如果有活动的 try 条件句,中断 (CTRL-C) 被转换为例外 "Vim:Interrupt"。你可以和 其他例外一样捕获它。那样,脚本就不会中止。 例如: :function! TASK1() : sleep 10 :endfunction :function! TASK2() : sleep 20 :endfunction :while 1 : let command = input("Type a command: ") : try : if command == "" : continue : elseif command == "END" : break : elseif command == "TASK1" : call TASK1() : elseif command == "TASK2" : call TASK2() : else : echo "\nIllegal command:" command : continue : endif : catch /^Vim:Interrupt$/ : echo "\nCommand interrupted" : " Caught the interrupt. Continue with next prompt. : endtry :endwhile 这里,你可以用 CTRL-C 中止任务;脚本会询问新的命令。如果你在提示上按 CTRL-C, 脚本就会中止。 要测试在你脚本的某一行上如果按了 CTRL-C 会发生什么,使用调试模式,然后在那行上 执行 |>quit| 或 |>interrupt|。见 |debug-scripts|。 捕 获 一 切 *catch-all* 命令 :catch /.*/ :catch // :catch 会捕获一切: 错误例外,中断例外和 |:throw| 命令显式抛出的例外。脚本的顶层可用此 捕获所有意料不到的问题。 示例: :try : : " 这里做重要的工作 : :catch /MyException/ : : " 处理未知的问题 : :catch /^Vim:Interrupt$/ : echo "脚本被中断" :catch /.*/ : echo "内部错误 (" . v:exception . ")" : echo " - 发生在 " . v:throwpoint :endtry :" 脚本结束 注意: 捕获一切可能会捕获到比你想得到的更多的错误。所以,强烈建议你只用指定模式 参数的 ":catch" 来捕获你真正处理的错误。 例如: 捕获一切会使得按 CTRL-C 来中断脚本几乎没有办法: :while 1 : try : sleep 1 : catch : endtry :endwhile 例 外 和 自 动 命 令 *except-autocmd* 执行自动命令的过程中可以使用例外。例如: :autocmd User x try :autocmd User x throw "Oops!" :autocmd User x catch :autocmd User x echo v:exception :autocmd User x endtry :autocmd User x throw "Arrgh!" :autocmd User x echo "Should not be displayed" : :try : doautocmd User x :catch : echo v:exception :endtry 会显示 "Oops!" 和 "Arrgh!"。 *except-autocmd-Pre* 有些命令里,自动命令在命令执行的主要动作之前执行。如果在自动命令的序列中抛 出没有捕获的例外,该序列和导致其执行的命令本身被放弃,而例外被传播到命令的调用 者那里。 例如: :autocmd BufWritePre * throw "FAIL" :autocmd BufWritePre * echo "应该不会显示" : :try : write :catch : echo "Caught:" v:exception "from" v:throwpoint :endtry 这里,":write" 命令不会写入当前编辑的文件 (你可以通过查看 'modified' 发现)。因 为例外来自 BufWritePre 自动命令,它放弃了 ":write"。然后,该例外被捕获而脚本会 显示: Caught: FAIL from BufWrite Auto commands for "*" *except-autocmd-Post* 有些命令里,自动命令在命令执行的主要动作之后执行。如果主要动作失败,而命令包含 在活动的 try 条件句里,将跳过这些自动命令并抛出错误例外,该命令的调用者可以捕 获这些例外。 例如: :autocmd BufWritePost * echo "文件被成功写入!" : :try : write /i/m/p/o/s/s/i/b/l/e :catch : echo v:exception :endtry 只会显示: Vim(write):E212: Can't open file for writing (/i/m/p/o/s/s/i/b/l/e) 如果你真想在主要动作失败的时候也执行自动命令的话,在 catch 子句里激活自动命令 事件。 例如: :autocmd BufWritePre * set noreadonly :autocmd BufWritePost * set readonly : :try : write /i/m/p/o/s/s/i/b/l/e :catch : doautocmd BufWritePost /i/m/p/o/s/s/i/b/l/e :endtry 你也可以用 ":silent!": :let x = "ok" :let v:errmsg = "" :autocmd BufWritePost * if v:errmsg != "" :autocmd BufWritePost * let x = "after fail" :autocmd BufWritePost * endif :try : silent! write /i/m/p/o/s/s/i/b/l/e :catch :endtry :echo x 会显示 "after fail"。 如果命令的主要动作没有失败,可以在命令的调用者那里捕获自动命令产生的例外: :autocmd BufWritePost * throw ":-(" :autocmd BufWritePost * echo "这里不应该被显示" : :try : write :catch : echo v:exception :endtry *except-autocmd-Cmd* 有的命令的正常动作可以被自动命令的序列代替。可以在命令的调用者那里捕获该序列产 生的例外。 例如: 对于 ":write" 命令,调用者并不知道发生例外时,文件是不是已经被写入。 你需要想办法告知调用者。 :if !exists("cnt") : let cnt = 0 : : autocmd BufWriteCmd * if &modified : autocmd BufWriteCmd * let cnt = cnt + 1 : autocmd BufWriteCmd * if cnt % 3 == 2 : autocmd BufWriteCmd * throw "BufWriteCmdError" : autocmd BufWriteCmd * endif : autocmd BufWriteCmd * write | set nomodified : autocmd BufWriteCmd * if cnt % 3 == 0 : autocmd BufWriteCmd * throw "BufWriteCmdError" : autocmd BufWriteCmd * endif : autocmd BufWriteCmd * echo "File successfully written!" : autocmd BufWriteCmd * endif :endif : :try : write :catch /^BufWriteCmdError$/ : if &modified : echo "Error on writing (file contents not changed)" : else : echo "Error after writing" : endif :catch /^Vim(write):/ : echo "Error on writing" :endtry 如果脚本在修改后执行了多次,它先显示 File successfully written! 然后 Error on writing (file contents not changed) 然后 Error after writing 等等。 *except-autocmd-ill* 你不能把一个 try 条件句分散到不同事件的自动命令。 下面的代码是非法的构造: :autocmd BufWritePre * try : :autocmd BufWritePost * catch :autocmd BufWritePost * echo v:exception :autocmd BufWritePost * endtry : :write 例 外 层 次 和 参 数 化 的 例 外 *except-hier-param* 有些编程语言支持使用例外类的层次结构,或者在例外类的对象里传入附加的信息。你可 以在 Vim 里完成类似的工作。 为了抛出属于某层次的例外,只要抛出完整的类名,部件之间用冒号分隔。比如,在 某个数学库里的溢出错误可以抛出字符串 "EXCEPT:MATHERR:OVERFLOW"。 如果你想给例外类传递附加的信息,把它加到括号里。比如写入文件 "myfile" 时的 错误,可以抛出字符串 "EXCEPT:IO:WRITEERR(myfile)"。 在 ":catch" 命令里使用合适的模式,可以捕获你的层次中的基本类或者派生类。括 号里的附加信息也可以运用 ":substitute" 命令从 |v:exception| 里切出。 例如: :function! CheckRange(a, func) : if a:a < 0 : throw "EXCEPT:MATHERR:RANGE(" . a:func . ")" : endif :endfunction : :function! Add(a, b) : call CheckRange(a:a, "Add") : call CheckRange(a:b, "Add") : let c = a:a + a:b : if c < 0 : throw "EXCEPT:MATHERR:OVERFLOW" : endif : return c :endfunction : :function! Div(a, b) : call CheckRange(a:a, "Div") : call CheckRange(a:b, "Div") : if (a:b == 0) : throw "EXCEPT:MATHERR:ZERODIV" : endif : return a:a / a:b :endfunction : :function! Write(file) : try : execute "write" a:file : catch /^Vim(write):/ : throw "EXCEPT:IO(" . getcwd() . ", " . a:file . "):WRITEERR" : endtry :endfunction : :try : : " 一些算术和 I/O : :catch /^EXCEPT:MATHERR:RANGE/ : let function = substitute(v:exception, '.*(\(\a\+\)).*', '\1', "") : echo "Range error in" function : :catch /^EXCEPT:MATHERR/ " 捕获 OVERFLOW 和 ZERODIV : echo "Math error" : :catch /^EXCEPT:IO/ : let dir = substitute(v:exception, '.*(\(.\+\),\s*.\+).*', '\1', "") : let file = substitute(v:exception, '.*(.\+,\s*\(.\+\)).*', '\1', "") : if file !~ '^/' : let file = dir . "/" . file : endif : echo 'I/O error for "' . file . '"' : :catch /^EXCEPT/ : echo "Unspecified error" : :endtry Vim 自己抛出的例外 (错误或者按了 CTRL-C) 使用扁平的层次: 它们都在 "Vim" 类里。 你自己不能抛出带有 "Vim" 前缀的例外;它们是 Vim 保留的。 如果已知失败的命令名,Vim 错误例外使用该命令名作为参数。见 |catch-errors|。 特 别 之 处 *except-compat* 例外处理的概念需要产生例外的命令序列被立即中止,而控制转移到 finally 子句和/或 catch 子句。 在 Vim 脚本语言里,有一些情况下脚本和函数在错误后还会继续: 在没有 "abort" 标志 位的函数或者 ":silent!" 之后的命令里,控制流转到下一行。而在函数外,控制流转到 最外层 ":endwhile" 或者 ":endif" 之后的行。另一方面,错误应该可以作为例外被捕 获 (因而,需要立即被中止)。 这个问题的解决方法是把仅在有活动 try 条件句的时候,把错误转化为例外,并立即中 止 (如果没有用 ":silent!" 抑制的话)。这不是一个限制,因为 (错误) 例外只能在活 动的 try 条件句里被捕获。如果你需要立即终止而不需要捕获错误的话,只要用一个没 有 catch 子句的 try 子句就可以了 (你可以用 finally 子句指定终止前执行的清理代 码。) 如果没有活动的 try 条件句,使用通常的中止和继续行为,而不是立即中止。这样,保 证了与 Vim 6.1 和之前版本编写的脚本的兼容性。 不过,如果在活动的 try 条件句里执行已有的不使用例外处理命令的脚本 (或者调用它 的一个函数),你也许会改变已有脚本发生错误时的控制流。你会在错误时立即中止并且 在新的脚本里捕获错误。如果被执行的脚本通过 ":silent!" 命令抑制了错误 (在合适的 时候测试 |v:errmsg| 来检查错误),它的执行路径没有改变。错误也不会转换为例外。 (见 |:silent|。) 所以唯一留下的可能是不关心错误并产生错误信息的脚本。可能,你 也不希望在新的脚本里使用这样的代码吧。 *except-syntax-err* 例外处理命令的语法错误永远不会被它所属的 try 条件句的任何 ":catch" 命令所捕 获。不过,还是会执行它的 finally 子句。 例如: :try : try : throw 4711 : catch /\(/ " 有语法错误 : echo "in catch with syntax error" : catch : echo "inner catch-all" : finally : echo "inner finally" : endtry :catch : echo 'outer catch-all caught "' . v:exception . '"' : finally : echo "outer finally" :endtry 会显示: inner finally outer catch-all caught "Vim(catch):E54: Unmatched \(" outer finally 原来的例外被丢弃了,抛出的是取而代之的语法错误的错误例外。 *except-single-line* ":try"、":catch"、":finally" 和 ":endtry" 命令可以放在一行里,但这样如果有语法 错误,可能使得 "catch" 行无法被识别。所以,最好不要这么做。 例如: :try | unlet! foo # | catch | endtry ":unlet!" 参数之后的拖尾字符抛出了错误例外,但因此无法看到 ":catch" 和 ":endtry" 命令,从而只能丢弃该错误例外并且显示消息 "E488: Trailing characters"。 *except-several-errors* 如果多个错误在一个命令里出现,第一个错误信息通常是最专门的,因而它被转换为错误 例外。 例如: echo novar 产生 E121: Undefined variable: novar E15: Invalid expression: novar try 条件句里错误例外的值是: Vim(echo):E121: Undefined variable: novar *except-syntax-error* 不过,如果同一命令在普通错误之后发现了语法错误,语法错误被用作抛出的例外。 例如: unlet novar # 产生 E108: No such variable: "novar" E488: Trailing characters try 条件句里错误例外的值是: Vim(unlet):E488: Trailing characters 这么做是因为语法错误可能会以用户意想不到的方式改变执行的路径。例如: try try | unlet novar # | catch | echo v:exception | endtry catch /.*/ echo "outer catch:" v:exception endtry 显示 "outer catch: Vim(unlet):E488: Trailing characters",然后给出错误信息 "E600: Missing :endtry",见 |except-single-line|。

9. 示例 *eval-examples*

用十六进制显示 :" 函数 Nr2Hex() 返回数值的十六进制字符串。 :func Nr2Hex(nr) : let n = a:nr : let r = "" : while n : let r = '0123456789ABCDEF'[n % 16] . r : let n = n / 16 : endwhile : return r :endfunc :" 函数 String2Hex() 把字符串里的每个字符转换成两位十六进制字符串。 :func String2Hex(str) : let out = '' : let ix = 0 : while ix < strlen(a:str) : let out = out . Nr2Hex(char2nr(a:str[ix])) : let ix = ix + 1 : endwhile : return out :endfunc 使用示例: :echo Nr2Hex(32) 返回: "20" :echo String2Hex("32") 返回: "3332" 给行排序 (Robert Webb 提供) 这里是一个给行排序的 Vim 脚本。在 Vim 里高亮要操作的行,然后输入 ":Sort"。它不 会调用任何外部程序,所以在任何平台都可使用。函数 Sort() 实际上使用一个比较函数 的名字作为参数,和 C 里的 qsort() 一样。所以你可以和不同的比较函数配套使用,从 而可以实现,比如说,对日期的排序。 :" Sort() 使用的函数,比较两个字符串。 :func! Strcmp(str1, str2) : if (a:str1 < a:str2) : return -1 : elseif (a:str1 > a:str2) : return 1 : else : return 0 : endif :endfunction :" 给行排序。SortR() 会被递归调用。 :func! SortR(start, end, cmp) : if (a:start >= a:end) : return : endif : let partition = a:start - 1 : let middle = partition : let partStr = getline((a:start + a:end) / 2) : let i = a:start : while (i <= a:end) : let str = getline(i) : exec "let result = " . a:cmp . "(str, partStr)" : if (result <= 0) : " Need to put it before the partition. Swap lines i and partition. : let partition = partition + 1 : if (result == 0) : let middle = partition : endif : if (i != partition) : let str2 = getline(partition) : call setline(i, str2) : call setline(partition, str) : endif : endif : let i = i + 1 : endwhile : " Now we have a pointer to the "middle" element, as far as partitioning : " goes, which could be anywhere before the partition. Make sure it is at : " the end of the partition. : if (middle != partition) : let str = getline(middle) : let str2 = getline(partition) : call setline(middle, str2) : call setline(partition, str) : endif : call SortR(a:start, partition - 1, a:cmp) : call SortR(partition + 1, a:end, a:cmp) :endfunc :" 要给一个范围内的行排序,把该范围和比较行与行的函数的名字传入。 :func! Sort(cmp) range : call SortR(a:firstline, a:lastline, a:cmp) :endfunc :" :Sort 接受行范围作为参数,并为之排序。 :command! -nargs=0 -range Sort <line1>,<line2>call Sort("Strcmp") *sscanf* Vim 里没有 sscanf() 函数。如果你需要提取一行的部分内容,可以使用 matchstr() 和 substitute() 完成。本例子说明如何得到从类似 "foobar.txt, 123, 45" 的行里提取文 件名,行号和列号。 :" 设置匹配模式 :let mx='\(\f\+\),\s*\(\d\+\),\s*\(\d\+\)' :"取得匹配整个表达式的文本部分 :let l = matchstr(line, mx) :"从匹配中提取每个项目 :let file = substitute(l, mx, '\1', '') :let lnum = substitute(l, mx, '\2', '') :let col = substitute(l, mx, '\3', '') 这里,输入是变量 "line",返回值放在变量 "file"、"lnum" 和 "col" 里。(Michael Geddes 提供的方法)

10. 不包含 +eval 特性 *no-eval-feature*

如果编译时关闭了 |+eval| 特性,以上的表达式计算命令都不可用。要避免因此导致你 的 Vim 脚本产生各种错误,":if" 和 ":endif" 命令仍然得到识别。不过 ":if" 的参数 和一切 ":if" 和匹配的 ":endif" 之间的内容都被忽略。可以嵌套 ":if" 块,但只允 许出现在行首。不识别 ":else" 命令。 下例演示如何在不存在 |+eval| 特性时不执行命令: :if 1 : echo "编译加入了表达式求值" :else : echo "你_永远_看不到这条消息" :endif

11. 沙盘 (sandbox) *eval-sandbox* *sandbox* *E48*

'foldexpr'、'includeexpr'、'indentexpr'、'statusline' 和 'foldtext' 选项在沙盘 (sandbox) 里进行计算。这意味着这些表达式不会产生可怕的副作用。因为模式行可能会 给出这些选项,或者在标签文件里也会执行命令,现在这个措施提供了一定的安全性。 这并不能保证 100% 安全,但应该可以挡住大多数攻击。 沙盘里,不允许以下操作: - 修改缓冲区文本 - 定义或者改变映射、自动命令、函数和用户命令 - 设置若干选项 (见 |option-summary|) - 执行外壳命令 - 读入或者写到文件 - 跳转到另一缓冲区或者去编辑文件 vim:tw=78:ts=8:ft=help:norl:

Generated by vim2html on 2006年 06月 24日 星期六 00:27:59 UTC