第六章 VimL 内建函数使用
6.4 其他实用函数
在本章的最末,再介绍一些不太分类,或不常用(但不甚复杂)的函数。须要再次强调的 是,VimL 的函数,是为了访问或(与)控制相应的 Vim 功能而设的。必须理解相应的功 能才能用好相应的函数。Vim 提供的功能很多,对于个体用户而言,可能对某些功能并不 关注或并不感兴趣。
因此,本章罗列介绍这些函数,无法求细致,只为说明 VimL 有这么一种类的函数可用。 当你真正需要用到时,再回去查手册。任一编程语言的 api 函数,只能通过手册学习, 通过实践提高。而这无法从任一书籍教程中学得,书籍只能是引入门,帮用户建立个相关 概念而已。
特性检测函数拾遗
- has() 当前 Vim 版本是否编译进某个功能,似
:verstion
功能 - exists() 检查是否存在某个变量、命令、函数等对象
- type() 返回变量类型
其中 exists()
函数功能强大,用参数字符串前缀来表示哪类对象,主要有以下几种:
- 选项:
&option_name
只判断选项存在,+option_name
判断选项是否有效 - 环境变量:
$ENV_NAME
- 函数:
*function_name
- 命令:
:command_name
对于函数与命令,都可检查内建的或自定义的 - 变量:
variable_name
即没有特殊前缀时检查变量是否存在 自动事件:
#event
#group
等hasmapto() 检查某个命令(键序列)是否有映射(
{rhs}
)- mapcheck() 检查某个按键序列是否有被映射(
{lhs}
) - maparg() 获得某个被映射的键序列实际映射键序
- wildmenumod() 在命令行映射中检查是否出现补全模式
这几个检查映射状态的函数,常用于设计可定制插件的映射,避免重复、覆盖或冗余的映 射。maparg()
与 mapcheck()
的主参数都是映射的 {lhs}
,前者要求精确匹配, 后者只需匹配前缀,返回映射到的 {rhs}
。hasmapto()
是反向检查是否有任意的 {lhs}
映射到参数指定的 {rhs}
。
类型文件语法相关
文件类型是 Vim 的重要概念,简单理解的话,不同的文件名后缀往往代表不同类型的文 件(&filetype
)。比如不同编程语言的源代码文件。不过 Vim 只是编辑器,它并不能 理解(编译或解释)任一种编程语言(VimL 除外)。所以 Vim 语境下的“语法”,本质上 只是“词法”,旨在说明可以如果对文件的不同部分进行不同的高亮着色,不过习惯上仍称 为语法着色。
这主要分两步实现。首先是定义高亮组(highlight group
),它说明“如何高亮”,描 叙了该高亮下的颜色、字体等信息。然后是定义语法项(syntax
),它说明高亮什么, 主要是基于正则表达式,将不同匹配(match
)部分应用不同的高亮组。这样就有可能 将一个缓冲文件在窗口中以五颜六色的方式呈现出来。
一般不同的文件类型有相应的语法文件,文件中主要用 :highlight
与 :syntax
命 令定义了各种高亮组与语法项。这里要介绍的 VimL 内置函数,可以检索当前缓冲中实际 生效的语法信息,以及临时增加修改部分高亮方式。
- matchadd() 增加一种匹配应用某种高亮组
- matchaddpos() 在特定(行列)位置上匹配应用高亮
- matchdelete() 删除一处匹配的高亮
- clearmatches() 清除所有匹配高亮
- matcharg() 获取
:match
命令的参数信息 - getmatches() 获取所有自定义匹配高亮
- setmatches() 恢复一组自定义匹配高亮
这几个函数用于手动控制一些文本的高亮方式(相对于语法文件的自动加载)。虽然这种 方式似乎比较原始,但有助于理解 Vim 语法高亮的处理机制,并且在临时微调语法高亮 或处理一些简单非常规文件类型时也有奇效。
首先要了解 :match
命令,它于之前介绍的用于匹配字符串的 match()
函数是不同 的功能。:match {group} /{patter}/
的意思相当于临时定义一种语法匹配,将匹配正 则表达式的文本应用指定的高亮组。你可以初步地认为每种文件类型的特定语法文件中都 正式地定义了很多种类似的语法匹配。不过 :match
的临时定义只能定义三种不同的匹 配,另外两个命令是 :2match
与 :3match
。实际上它只是 :match
命令的数字参 数,默认是 :1match
而已。限制三种可能是出于性能、管理与实用的综合考虑。
然后 matchadd()
是 :match
命令的函数方式。所不同的是它不限于只定义三种匹配 ,可以调用这个函数定义许多匹配,并且返回不同的 ID 用以表示所定义的不同匹配。当 然 1-3
这前三个 ID 被保留给 :match
命令使用。
matchaddpos()
的功能类似,不过它不是通过正则表式式来定义匹配,而是准确地给出 一组位置信息,说明在哪些行(列)上要高亮。因为基于正则的语法高亮是比较低效的, (在一些旧机器上打开大文件时若发现卡,可尝试关闭语法着色),按位置高亮就不那么 耗性能了。
matchdelete()
用于删除自定义匹配,参数就是 matchadd()
返回的匹配ID(或者 1-3
代表通过 :match
命令定义的)。clearmatches()
则清除所有自定义匹配, 不需要参数。
matcharg()
就是用于查看之前的自定义匹配,接收匹配ID为参数,返回其定义的信息。 getmatches()
用于获得所有自定义匹配的详细信息,返回的是字典列表,它可传给 setmatches()
恢复自定义匹配。
- hlexists() 由高亮组名判断其是否存在
- hlID() 由高亮组名返回其数字ID
- synID() 返回当前缓冲指定行列位置处所使用的语法项ID
- synIDattr() 由语法项ID返回可读的属性信息
- synIDtrans() 返回一个语法项ID实际所链接的语法项ID
- synstack() 返回当前缓冲指定位置处所有的语法项ID堆栈
- synconcealed() 返回当前缓冲指定位置处的隐藏语法信息
在 Vim 内部,为每个高亮组与语法项都分配了 ID,其中高亮组有名字,而语法项是个更 虚拟的概念,没有名字,只能用 ID 表示。在当前缓冲的某部分文本(以行列号为参数) 使用了什么语法高亮,可用 synID()
获得,据此可进一步由 synIDattr()
获得该语 法高亮的详情属性。
在语法文件中,普遍使用 :highlight link
命令链接高亮组。因为 Vim 预设了大量通 用的高亮组名字,但允许用户在为不同类型文件的语法中使用更有意义的高亮组名,为避 免重复定义高亮属性,就可以将新高亮组链接到既有高亮组。synIDtrans()
函数就是 用于获得某个语法项ID实际链接的语法项ID。
Vim 的语法定义其实不是简单的正则匹配,还有更复杂的区域(region
)与嵌套规则。 于是对于一部分文本(缓冲的行列位置)而言,它可能不只应用了一个语法项高亮,而是 由内向外形成了语法高亮栈。synstack()
就是获取这样的栈的函数,它返回一个列表 ,最后一个元素其实就是 synID()
。
- foldclosed() 检查当前缓冲指定行是否被折叠,返回折叠区的第一行
- foldclosedend() 同上,返回折叠区的最后一行
- foldlevel() 返回当前缓冲指定行应该折叠的最深层次,不要求已折叠
- foldtext() 默认的折叠行显示文本计算函数
- foldtextresult() 当前缓冲指定行如果折叠,折叠行应该显示的最终文本
折叠从某种意义来说,也属于语法规则的范畴,只是它不是关于如何着色的,而是定义文 本层次的。折叠方法用很多种,可由选项 &foldmethod
指定,其中一种就是 syntax
由语法定义决定折叠层次。有很多普通命令(z
开头的系列)处理折叠。这里的几个函 数只是访问折叠信息。
如果当前缓冲的某行已被折叠中,函数 foldcolsed()
与 foldclosedend()
分别返 回整个折叠区的首行与尾行;若未折叠,返回 -1
。据此可知缓冲的实际文本与窗口显 示的文本行的差异。foldlevel()
返回折叠层级。任何折叠方法最终都由每行的折叠层 级决定折叠行为。其中有种自定义折叠函数(表达式),就由用户自己控制、计算返回每 行的折叠层级。其他折叠方法 Vim 会自动计算折叠层级。
折叠后,会在折叠首行显示一行特征文本,这行文本的计算方法由选项 &foldtext
配 置指定。其默认值就是 foldtext()
,该函数也只能在计算 &foldtext
时调用。某 行折叠后实际将显示的特征文本,则由 foldtextresult()
给出。
测试函数
当程序或脚本变得复杂起来,单元测试就可能很有必要了。从 Vim8 版本始,也提供了许 多函数方便用户写单元测试。
assert_equal()
断言两个值相等,包括变量类型相同assert_notequal()
断言不相等assert_inrange()
断言一个值处在某个范围assert_match()
断言匹配正则表达式assert_notmatch()
断言不匹配正则表达式assert_false()
断言逻辑假,或数字0assert_true()
断言逻辑真,或非0数字assert_exception()
断言会抛出异常assert_fails()
断言执行一个命令会失败
这些断言函数用法类似,一般是接收预期值与实际值,如果不满足断言条件,就添加一条 消息至内置变量 v:errors
列表中,其中消息字符串能传入可选参数定制。这些函数本 身不会产生错误输出或中断运行,只是先将错误信息暂存至 v:errors
中。所以在做单 元测试中,应该先将 v:errors
列表置空,调用一系列断言函数,最后检查该列表保存 了哪些错误消息,如果一切正常,该列表应该是空的。