usr_46.txt 适用于 Vim 8.2 版本。 最近更新: 2020年7月
VIM 用户手册 - by Bram Moolenaar
译者
: Willis
编写使用 Vim9 脚本的插件
Vim9 脚本语言可用于编写插件,尤其是使用多个文件的大型插件。本章解释如何把插件
分割为模块、导入和导出项目,并保持其余部分在局部。
46.1 介绍
46.2 变量声明
46.3 函数和类型
46.? 在老式脚本中使用 Vim9 脚本
下一章: usr_90.txt 安装 Vim
前一章: usr_45.txt 选择你的语言 (locale)
目录: usr_toc.txt
Vim9 脚本设计使大型 Vim 脚本的编写更为容易。它看来更像其它脚本语言,尤其是
Typescript。另外,函数被编译为指令以快速执行。这使 Vim9 脚本快很多,多达百倍。
这里的基本想法是脚本文件包含只用于脚本文件内部的私有项目,和可被脚本文件之外使
用的导出项目。然后,这些导出项目可用于导入它们的脚本里。这样什么在哪里定义就会
很清晰。
让我们从一个例子开始,有一个导出函数和有一个私有函数的脚本:
vim9script " 指示这是 Vim9 脚本文件。
export def GetMessage(): string
let result = ''
...
result = GetPart(count)
...
return result
enddef
def GetPart(nr: number): string
if nr == 4
return 'yes'
else
return 'no'
endif
enddef
vim9script 命令必须是文件最开头的命令。不然,Vim 会假定老式脚本语法。
`export def GetMessage(): string` 行以 export
开始,意味着此函数可以被其它脚
本导入和调用。行 `def GetPart(...` 不以 export
开头,这是局部于脚本的函数,
只能在此脚本文件中使用。
在 `export def GetMessage(): string` 行,注意
到冒号和返回类型。 def
定义的
Vim9 函数必须指定参数类型和返回类型。这样 Vim 才能有效地编译代码。GetPart 函数
定义了参数 "nr",带类型 "number"。
注意
到赋值 `result = GetPart(count)` 不使用 let
命令。下一小节有解释。
Vim9 脚本变量使用 :let 或 :const 命令声明一次。赋值不使用 :let ,也不能用
:unlet 取消变量的声明。
多数情况下,你希望同时进行变量声明和初始化:
let myText = 'some text'
...
myText = 'other text'
变量类型会从表达式中推断。在此例中是字符串。如果用数值初始化,则类型为数值:
let myNumber = 1234
...
myNumber = 0
如果试图给此变量赋值字符串,会报错:
let myNumber = 'this fails!'
少数情况下你希望变量可接受任何类型的值,那就要显式说明类型:
let myVar: any = 1234
myVar = 'text also works'
也可先声明变量而不赋值。此时 Vim 会初始化该变量为零或空:
let word: string
if condition
word = 'yes'
else
word = 'no'
endif
另管有更短的形式:
let word = condition ? 'yes' : 'no'
老式 Vim 脚本有类型检查,但在运行时进行,也就是代码执行时。而且是容错的,通常
计算会返回一个意外的值而不是报错。这样在函数定义时你可能会认为没问题,仅在之后
调用该函数时才发现问题存在:
let s:collected = ''
func ExtendAndReturn(add)
let s:collected += a:add
return s:collected
endfunc
能发现错误吗?试试:
echo ExtendAndReturn('text')
会看到零。为什么?因为老式 Vim 脚本中 "+=" 会把参数转换为数值,而没有数字的字
符串会转换为零!
用 :def 时,类型检查在函数编译时发生。为此,需要指定参数类型和返回类型。还要
注意
到使用参数时没有 "a:" 前缀:
let s:collected = ''
def ExtendAndReturn(add: string): string
s:collected += add
return s:collected
enddef
defcompile
这里我们用 :defcompile 立即进行编译,否则编译只在函数调用时才会进行。Vim 此
时会告知你出错了:
E1013: type mismatch, expected number but got string
Vim9 脚本是严格的 "+" 操作符只能用于数值和浮点。字符串连接必须使用 ".."。这避
免错误和导致上述意外结果的自动转换。如果把函数的首行换成:
s:collected ..= add
一切就好了。
如果函数不返回任何值,只要省略返回类型就可以:
def ReportResult(result: string)
echo 'The result is: ' .. result
enddef
这也会检查,试图返回任何值时会报错。
如果你不关心类型,或者函数需要接受多种类型,可用 "any" 类型:
def Store(key: string, value: any)
resultDict[key] = value
enddef
有些情况下,在老式脚本里你会想使用 Vim9 脚本里的项目。例如在 .vimrc 里初始化插
件。最好的方法是用 :import 。例如:
import Init as NiceInit from 'myNicePlugin.vim'
call NiceInit('today')
这会找到 Vim9 脚本文件里的 "Init" 导出函数,并使之成为局部于脚本的项目
"NiceInit"。即使不给出 "s:", :import 也总使用脚本命名空间,如果
"myNicePlugin.vim" 已经执行过,不会再次执行。
除了避免把任何项目放进全局命名空间 (因为命名冲突会导致意料之外的错误) 以外,这
也意味着无论其中的项目被导入多少次,一份脚本只执行一次。
有些情况下,例如为了测试用途,可能就想要直接执行 Vim9 脚本。这没问题,但你只能
得到其中的全局项目。该 Vim9 脚本要确保这些全局项目使用独一无二的名字。例如:
source ~/.vim/extra/myNicePlugin.vim
call g:NicePluginTest()