http://yysfire.github.io/vim/Vim脚本学习笔记.html

 

 


修改记录

  • Last Modified: 2012-12-05 15:43:44
  • First Created: 2012-12-04 11:20:41

变量

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!: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. "Step through each file...
2. for filenum in range(filecount)
3. Show...
4. (filenum /*100).'% done'
5. " Make progress...
6.    call process_file(filenum)
7. endfor

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

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


1. =234
2. /100|" echoes 2
3. 2.34

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

execute 与 normal 命令

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

  1. :execute "normal ". normal_commands

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

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

函数

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


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

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

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

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

范围的使用

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

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


1. :functionCount_words() range
2. :=:firstline
3. :=0
4. :while<=:lastline
5. :=+(split(getline(lnum)))
6. :=+1
7. :  endwhile
8. :"found ".." words"
9. :endfunction

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


1. :10,30callCount_words()

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

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


1. :functionNumber()
2. :"line ".(".")." contains: ".(".")
3. :endfunction

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


1. :10,15callNumber()

它将被执行六次。

可变参数

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


1. :functionShow(start,...)

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


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

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

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

函数引用

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

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

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

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


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

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


1. :echo uk2nl.one
2. ~

这只能用于由 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%:r.s

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

<LocalLeader>

"maplocalleader" 的值,用户可以通过它来自定义文件类型

插件中映射的起始字符。

:map <buffer>

定义一个仅对缓冲区有效的局部映射。

:noremap <script>

仅重映射脚本中以 <SID> 开始的映射。

:setlocal

设定仅对当前缓冲区有效的选项。

:command -buffer

定义一个仅对缓冲区有效的局部命令。

exists("*s:Func")

查看是否已经定义了某个函数。

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