You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

9.8 KiB

第六章 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 列表置空,调用一系列断言函数,最后检查该列表保存 了哪些错误消息,如果一切正常,该列表应该是空的。