幽谷奇峰 | 燕雀鸣幽谷,鸿鹄掠奇峰

Vim 脚本学习笔记


变量

Vimscript 变量范围

前缀 含义
g: varname 变量为全局变量
s: varname 变量的范围为当前的脚本文件
w: varname 变量的范围为当前的编辑器窗口
t: varname 变量的范围为当前的编辑器选项卡
b: varname 变量的范围为当前的编辑器缓冲区
l: varname 变量的范围为当前的函数
a: varname 变量是当前函数的一个参数
v: varname 变量是 Vim 的预定义变量

变量采用 ":let" 命令赋值,同时也占用内存空间。为了删除一个变量可以使用 ":unlet" 命令。例:

1
:unlet s:count

这将删除 "s:count" 这个脚本局部变量并释放其占用的内存。如果你并不确定这个变量是否存在,但并不希望系统在它不存在时报错,可以在命令后添加 !:

1
:unlet! s:count

当一个脚本结束时,它使用的局部变量不会自动被删除。下一次脚本被执行时,旧的变量值仍可被使用。

exists()函数

"exists()" 函数检查一个变量是否已经被定义过了。它的参数是你想检查的变量的名字。而不是变量本身!如果你这样做:

1
:if !exists(s:call_count)

那么变量 s:call_count 的值将被用来做检测。你不会得到想的结果。

Vimscript 中的真和假

Vim 把任何非零的值当作真。零代表假。

如果期待数值类型,Vim 自动把字符串转换为数值。如果使用不以数位开始的字符串,返回的数值为零。所以小心这种代码:

1
:if "true"

这里 "true" 会被解读为零,也就是假值!

字符串常量

你需要使用字符串常量来为字符串变量赋值。字符串常量有两种。第一种是由双引号括起来的,里面可以包含转义序列,例如,\n用于换行,\"用于双引号,\u263A用于 Unicode 笑脸标志,\<ESC>用于 Escape 键。

如果你不想使用反斜杠,也可以用单引号括起字符串。所有的字符在单引号内都保持其本来面目,只有单引号本身例外: 输入两个你会得到一个单引号。 因为反斜杠在其中也被作为其本身来对待,你无法使用它来改变其后的字符的意义。

表达式

已经提到的那些数值,字符串常量和变量都属于表达式。因此任何可以使用表达式的地方,数值,字符串变量和常量都可以使用。其它基本的表达式有:

表达式 含义
$NAME 环境变量
&name 选项
@r 寄存器

一般的,当 ":echo" 命令遇到多个参数时,会在它们之间加入空格。

逻辑操作

对数值和字符串都可以做逻辑操作。两个字符串的算术差被用来比较它们的值。这个结果是通过字节值来计算的,对于某些语言,这样做的结果未必正确。

在比较一个字符串和一个数值时,该字符串将先被转换成一个数值。这容易出错,因为当一个字符串看起来不像数值时,它会被当作 0 对待。

字符串比较

对于字符串来说还有两种操作:

操作 含义
a =~ b 匹配
a !~ b 不匹配

左边的 "a" 被当作一个字符串。右边的 "b" 被当作一个匹配模式,正如做查找操作一样。

在做字符串比较时用到 'ignorecase' 选项。如果你不希望使用该选项,可以在比较时加上 "#" 或 "?"。"#" 表示大小写敏感;"?" 表示忽略大小写。因此 "==?" 比较两字符串是否相等,不计大小写。"!~#" 检查一个模式是否被匹配,同时也考虑大小写。

":sleep" 命令使 Vim 小憩一下。"50m" 表示休息 50 毫秒。再举一个例子,":sleep 4" 休息 4 秒。

命令的续行与拼接

Vimscript 中一条较长的命令可以分割成多行来写,但必须用反斜杠来作为续行符,反斜杠作为续行符一般写在下一行的开头。

相反地,多条命令也可以通过 '|' 字符拼接到一行中来。

算术说明

在使用算术表达式时,还需要记住一点,在版本 7.2 之前,Vim 只支持整数运算。早期版本中的一个普遍错误是编写类似下面的代码:

1
2
3
4
5
6
7
"Step through each file...
for filenum in range(filecount)
    " Show progress...
    echo (filenum / filecount * 100) . '% done'
    " Make progress...
   call process_file(filenum)
endfor

由于 filenum 始终小于 filecount,整数除法 filenum/filecount 将始终生成 0,因此每次迭代循环都将生成:Now 0% done

即使对于版本 7.2,如果其中一个运算对象被明确声明为浮点类型,那么 Vim 只支持浮点算术:

1
2
3
let filecount = 234
echo filecount/100   |" echoes 2
echo filecount/100.0 |" echoes 2.34

到目前为止,脚本内的语句都是由 Vim 直接运行的。用 ":execute" 命令可以执行一个表达式的结果。这是一个创建并执行命令的非常有效的方法。

executenormal 命令

":execute" 命令只能用来执行冒号命令。":normal" 命令可以用来执行普通模式命令。然而,它的参数只能是按表面意义解释的命令字符,不能是表达式。例如:为了使 ":normal" 命令也可以带表达式,可以把 ":execute" 与其连起来使用。

1
:execute "normal " . normal_commands

变量 "normal_commands" 必须包含要执行的普通模式命令。

必须确保 ":normal" 的参数是一个完整的命令。否则,Vim 碰到参数的结尾就会中止其运行。例如,如果你开始了插入模式,你必须也退出插入模式。

函数

Vim 允许你定义自己的函数。基本的函数声明如下:

1
2
3
:function {name}({var1}, {var2}, ...)
:  {body}
:endfunction

注意: 函数名必须以大写字母开始。

当一个函数执行到 ":endfunction" 或 ":return" 语句没有带参数时,该函数返回零。

如果要重定义一个已经存在的函数,在 "function" 命令后加上!.

范围的使用

":call" 命令可以带一个行表示的范围。这可以分成两种情况。当一个函数定义时给出了 "range" 关键字时,表示它会自行处理该范围。

Vim 在调用这样一个函数时给它传递两个参数: "a:firstline" 和 "a:lastline",用来表示该范围所包括的第一行和最后一行。例如:

1
2
3
4
5
6
7
8
9
:function Count_words() range
:  let lnum = a:firstline
:  let n = 0
:  while lnum <= a:lastline
:    let n = n + len(split(getline(lnum)))
:    let lnum = lnum + 1
:  endwhile
:  echo "found " . n . " words"
:endfunction

你可以这样调用上面的函数:

1
:10,30call Count_words()

这个函数将被调用一次并显示字数。

另一种使用范围的方式是在定义函数时不给出 "range" 关键字。Vim 将把光标移动到范围内的每一行,并分别对该行调用此函数。例如:

1
2
3
:function  Number()
:  echo "line " . line(".") . " contains: " . getline(".")
:endfunction

如果你用下面的方式调用该函数:

1
:10,15call Number()

它将被执行六次。

可变参数

Vim 允许你定义参数个数可变的函数。下面的例子给出一个至少有一个参数 (start),但 可以多达 20 个附加参数的函数:

1
:function Show(start, ...)

变量 "a:1" 表示第一个可选的参数,"a:2" 表示第二个,如此类推。变量 "a:0" 表示 这些参数的个数。例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
:function Show(start, ...)
:  echohl Title
:  echo "Show is " . a:start
:  echohl None
:  let index = 1
:  while index <= a:0
:    echo "  Arg " . index . " is " . a:{index}
:    let index = index + 1
:  endwhile
:  echo ""
:endfunction

上例中 ":echohl" 命令被用来给出接下来的 ":echo" 命令如何高亮输出。":echohl None" 终止高亮。":echon" 命令除了不输出换行符外,和 ":echo" 一样。

你可以用 a:000 变量,它是所有 "..." 参数的列表。详情见 help: a:000

函数引用

有时使变量指向一个或另一个函数可能有用。要这么做,用 function() 函数。它把函数名转换为引用。

注意 保存函数引用的变量名必须用大写字母开头,不然和内建函数的名字会引起混淆。

调用变量指向的函数可以用 call() 函数。它的第一个参数是函数引用,第二个参数是参数构成的列表。

字典项目通常可以用方括号里的索引得到:

1
2
:echo uk2nl['one']
een ~

完成同样操作且无需那么多标点符号的方法:

1
2
:echo uk2nl.one
een ~

这只能用于由 ASCII 字母、数位和下划线组成的键。此方式也可以用于赋值。

函数封装

为了避免你的函数名同其它的函数名发生冲突,使用这样的方法: - 在函数名前加上独特的字符串。我通常使用一个缩写。例如,"OW_" 被用在 option window 函数上。 - 将你的函数定义放在一个文件内。设置一个全局变量用来表示这些函数是否已经被加载 了。当再次 source 这个文件的时候,先将这些函数卸载。

编写插件

首先你得给你的插件起个名字。这个名字应该很清楚地表示该插件的用途。同时应该避免同别的插件用同样的名字而用途不同。请将插件名限制在 8 个字符以内,这样可以使得该插件在老的 Windows 系统也能使用。

<SID><Plug> 都是用来避免映射的键序列和那些仅仅用于其它映射的映射起冲突。

注意 <SID><Plug> 的区别:

标志 说明
<Plug> 在脚本外部是可见的。它被用来定义那些用户可能定义映射的映射。<Plug>
\/ 无法用键盘输入的特殊代码。
\/ 使用结构:<Plug> 脚本名 映射名,可以使得其它插件使用同样次序的字符来定
\/ 义映射的几率变得非常小。在我们上面的例子中,脚本名是 "Typecorr",映射
\/ 名是 "Add"。结果是 <Plug>TypecorrAdd。只有脚本名和映射名的第一个字
\/ 符是大写的,所以我们可以清楚地看到映射名从什么地方开始。
<SID> 是脚本的 ID,用来唯一的代表一个脚本。Vim 在内部将 <SID> 翻译为
\/ <SNR>123_,其中 "123" 可以是任何数字。这样一个函数 <SID>Add() 可能
\/ 在一个脚本中被命名为 <SNR>11_Add(),而在另一个脚本中被命名为
\/ <SNR>22_Add()。如果你用 :function 命令来获得系统中的函数列表你就可
\/ 以看到了。映射中对 <SID> 的翻译是完全一样的。这样你才有可能通过一个映
\/ 射来调用某个脚本中的局部函数。

关于插件的小结:

语句 说明
s:name 脚本的局部变量。
<SID> 脚本 ID,用于局部于脚本的映射和函数。
hasmapto() 用来检测插件定义的映射是否已经存在的函数。
<Leader> "mapleader" 的值。用户可以通过该变量定义插件所定义映射
:map <unique> 如果一个映射已经存在的话,给出警告信息。
:noremap <script> 在映射右边仅执行脚本的局部映射,而不检查全局映射。
exists(":Cmd") 检查一个用户命令是否存在。

用户命令

在使用 :command 命令时,如果加上 "-buffer" 开关,就可以为某一类型的文件加入一个用户命令,而该命令又只能用于一个缓冲区。例:

1
:command -buffer  Make  make %:r.s

以下是有关文件类型插件一些特殊环节:

语句 说明
<LocalLeader> "maplocalleader" 的值,用户可以通过它来自定义文件类型插件中映射的起始字符。
:map <buffer> 定义一个仅对缓冲区有效的局部映射。
:noremap <script> 仅重映射脚本中以 <SID> 开始的映射。
:setlocal 设定仅对当前缓冲区有效的选项。
:command -buffer 定义一个仅对缓冲区有效的局部命令。
exists("*s:Func") 查看是否已经定义了某个函数。

参阅所有插件的特殊环节 :help plugin-special


本作品由 Yysfire 创作,采用知识共享许可协议进行许可。转载时请在显著位置标明本文永久链接:
http://yysfire.github.io/vim/vimscript-note.html


相关文章


最后修改
2012-12-05 15:43
发表时间
2012-12-04 11:20
本文标签
Vim 5 Vimscript 1
关注我

侧栏导航