6.4 其他实用函数

第六章 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() 断言逻辑假,或数字0
  • assert_true() 断言逻辑真,或非0数字
  • assert_exception() 断言会抛出异常
  • assert_fails() 断言执行一个命令会失败

这些断言函数用法类似,一般是接收预期值与实际值,如果不满足断言条件,就添加一条 消息至内置变量 v:errors 列表中,其中消息字符串能传入可选参数定制。这些函数本 身不会产生错误输出或中断运行,只是先将错误信息暂存至 v:errors 中。所以在做单 元测试中,应该先将 v:errors 列表置空,调用一系列断言函数,最后检查该列表保存 了哪些错误消息,如果一切正常,该列表应该是空的。