usr_52

usr_52.txt 适用于 Vim 9.0 版本。 最近更新: 2022年8月 VIM 用户手册 - by Bram Moolenaar 译者: Willis 编写更大型插件 插件在开始完成更复杂的功能时,会越来越庞大。本文件说明如何确保仍然可以快速载 入,和如何把大文件分割成小部分。 52.1 导出和导入 52.2 自动载入 52.3 不经导入/导出的自动载入 52.4 可用的其它机制 52.5 在老式脚本中使用 Vim9 脚本 下一章: usr_90.txt 安装 Vim 前一章: usr_51.txt 创建插件 目录: usr_toc.txt

52.1 导出和导入

Vim9 脚本设计使大型 Vim 脚本的编写更为容易。它看来更像其它脚本语言,尤其是 Typescript。另外,函数被编译为指令以快速执行。这使 Vim9 脚本快很多,多达百倍。 这里的基本想法是脚本文件包含只用于脚本文件内部的私有项目,和可被脚本文件之外使 用的导出项目。然后,这些导出项目可用于导入它们的脚本里。这样什么在哪里定义就会 很清晰。 让我们从一个例子开始,有一个导出函数和有一个私有函数的脚本: vim9script export def GetMessage(count: string): string var nr = str2nr(count) var result = $'To {nr} we say ' result ..= GetReply(nr) return result enddef def GetReply(nr: number): string if nr == 42 return 'yes' elseif nr = 22 return 'maybe' else return 'no' endif enddef vim9script 命令是必须的, export 只能用于 Vim9 脚本里。 `export def GetMessage(...` 行以 export 开始,意味着此函数可以被其它脚本调 用。`def GetPart(...` 行不以 export 开头,意味着这是局部于脚本的函数,只能在 此脚本文件中使用。 现在说说导入此脚本的脚本。此例中使用如下布局,这适用于放在 "pack" 目录之下的插 件: .../plugin/theplugin.vim .../lib/getmessage.vim 假定 "..." 目录已添加进 'runtimepath',Vim 会寻找 "plugin" 目录里的插件并载入 "theplugin.vim"。Vim 并不识别 "lib" 目录,你可以在那里放任何脚本。 上面导出 GetMessage() 的脚本放在 lib/getmessage.vim 里。GetMessage() 函数在 plugin/theplugin.vim 里调用: vim9script import "../lib/getmessage.vim" command -nargs=1 ShowMessage echomsg getmessage.GetMessage(<f-args>) import 命令使用了相对路径,因为它以 "../" 开始,也就是上一层目录。其它类型的 路径可见 :import 命令。 现在试试插件提供的命令吧: ShowMessage 1 To 1 we say no ShowMessage 22 To 22 we say maybe 注意 GetMessage() 使用了导入脚本名字 "getmessage" 作为前缀。这样,对每个导入函 数,你会知道它是从哪里导入的。如果导入多个脚本,每个脚本都可以定义自己的 GetMessage() 函数: vim9script import "../lib/getmessage.vim" import "../lib/getother.vim" command -nargs=1 ShowMessage echomsg getmessage.GetMessage(<f-args>) command -nargs=1 ShowOther echomsg getother.GetMessage(<f-args>) 如果导入脚本名字太长或者需要在很多地方使用,可以通过 "as" 参数来缩短名字: import "../lib/getmessage.vim" as msg command -nargs=1 ShowMessage echomsg msg.GetMessage(<f-args>) 重 载 记住一件事: 导入的 "lib/getmessage.vim" 脚本只会载入一次。第二次载入时会跳过, 因为里面的项目都已经创建了。不管是在其它脚本还是同一脚本里再次执行导入命令,都 是如此。 对插件的使用这是高效的,但在插件开发期间,这意味着导入 "lib/getmessage.vim" 之 后对它的修改不会生效。为此,只能退出 Vim 后重启。(理据: 脚本定义的项目可能在编 译过的函数里使用,再次载入脚本可能会破坏这些函数)。 使 用 全 局 值 有时需要使用全局变量或函数,以便在任何地方都能使用。一个全局变量的好例子是控制 插件行为的首选项。为了避免其它脚本使用的重名,使用别人不太可能使用的前缀。例 如,如果插件是 "mytags" 的话: g:mytags_location = '$HOME/project' g:mytags_style = 'fast'

52.2 自动载入

在把大脚本分割为若干部分后,使用脚本时所有行仍然被载入和执行。每个 import 都 会载入被导入的脚本以找到其中定义的项目。虽然这样容易早点发现错误,但颇耗时。如 果提供的功能不常用,这很浪费。 import 除了立即载入脚本之外,也可以延迟载入。使用上面的例子,只要在 plugin/theplugin.vim 里改一处就可以了: import autoload "../lib/getmessage.vim" 脚本其余部分无须改动。不过,不会检查类型。甚至 GetMessage() 函数存在与否,也是 在实际使用时才会检查。需要你自己决定是快速启动还是早点发现错误更加重要。也可以 在检查完一切都正常之后再加上 "autoload" 参数。 自 动 载 入 目 录 另一种使用自动载入的形式,脚本名既不是绝对也不是相对路径: import autload "monthlib.vim" 会在 'runtimepath' 的 autoload 目录下搜索脚本 "monthlib.vim"。Unix 一个常见目 录是 "~/.vim/autoload"。 这种形式的主要好处是容易和其它脚本共享。但要确保脚本名唯一,因为 Vim 会在 'runtimepath' 下搜索所有的 "autoload" 目录,如果使用插件管理器管理多个插件,它 会在 'runtimepath' 下新增目录,每个目录都可能有 "autoload" 目录。 没有自动载入的话: import "monthlib.vim" Vim 会在 'runtimepath' 的 import 目录下搜索脚本 "monthlib.vim"。注意 这种形式 里,加上或去掉 "autoload" 关键字会影响脚本寻找的位置。而使用相对或绝对路径的形 式时位置是不会改变的。

52.3 不经导入/导出的自动载入

write-library-script 在导入/导出机制引入之前,已经有另一套有用的机制,有些用户会觉得它比较简单些。 基本想法是调用一个有特殊名字的函数,然后这个函数在某自动载入脚本中。我们把这种 脚本称为库脚本。 此自动载入机制是基于有 "#" 字符的函数名的: mylib#myfunction(arg) Vim 会识别有内嵌 "#" 字符的函数名,如果该函数还没有定义,查找 'runtimepath' 里的 "autoload/mylib.vim"。该脚本必须定义 "mylib#myfunction()" 函数。显然 "mylib" 名字是 "#" 之前的部分,加上 ".vim" 后就被用作脚本名。 在 mylib.vim 脚本里可以放上许多其它函数,你可以自由组织库脚本的函数。但必须使 函数名 "#" 前面的部分匹配脚本名。否则 Vim 无法知道载入哪个脚本。这是和导入/导 出机制不同之处。 如果你真的热情高涨写了很多库脚本,现在可能想要用子目录吧。例如: netlib#ftp#read('somefile') 这里脚本名取自函数名最后 "#" 之前的部分。中间的 "#" 用斜杠替代,最后加上 ".vim"。这样得到的名字就是 "netlib/ftp.vim"。Unix 上,这里使用的库脚本可以是: ~/.vim/autoload/netlib/ftp.vim 其中的函数应该如此定义: def netlib#ftp#read(fname: string) " 用 ftp 读入文件 fname enddef 注意定义所用的函数名必须和调用的函数名完全相同。最后一个 '#' 之前的部分必须准 确匹配子目录和脚本名。 同样的机制可以用来定义变量: var weekdays = dutch#weekdays 会载入脚本 "autoload/dutch.vim",它应该包含这样的内容: var dutch#weekdays = ['zondag', 'maandag', 'dinsdag', 'woensdag', \ 'donderdag', 'vrijdag', 'zaterdag'] 进一步的阅读可见: autoload

52.4 可用的其它机制

有些人觉得维护多个文件很麻烦,而把所有内容放在一个脚本。为了避免这样引起的启动 时的延迟,有一个机制只定义很小的一部分,而其余部分的载入会延迟到实际使用的时 候。 write-plugin-quickload 基本的方法是调用插件两次。第一次定义用户命令和映射,提供需要的功能。第二次定义 实现这些功能的函数。 听起来很吓人,快速载入意味着载入两次!我们的意思是,第一次载入很快,把脚本的大 部分内容延迟到第二次才载入,只有实际使用这些功能时才会这么做。当然,如果你总是 用这些功能,实际上更慢了! 这里使用 FuncUndefined 自动命令。和上述的 autoload 功能不同。 下例演示如何这是如何完成的: " 演示快速载入的 Vim 全局插件 " Last Change: 2005 Feb 25 " Maintainer: Bram Moolenaar <Bram@vim.org> " License: This file is placed in the public domain. if !exists("s:did_load") command -nargs=* BNRead call BufNetRead(<f-args>) map <F19> :call BufNetWrite('something')<CR> let s:did_load = 1 exe 'au FuncUndefined BufNet* source ' .. expand('<sfile>') finish endif function BufNetRead(...) echo 'BufNetRead(' .. string(a:000) .. ')' " 读入功能在此 endfunction function BufNetWrite(...) echo 'BufNetWrite(' .. string(a:000) .. ')' " 写回功能在此 endfunction 第一次载入脚本时,没有设置 "s:did_load"。这时执行 "if" 和 "endif" 之间的命令。 它以 :finish 命令结束,这样脚本的其余部分不再执行。 第二次载入脚本时,"s:did_load" 已经存在,这时执行 "endif" 之后的命令。这里定义 (可能很长的) BufNetRead() 和 BufNetWrite() 函数。 如果把该脚本放到你的 plugin 目录,Vim 启动时会执行它。下面列出发生事件的序列: 1. 启动期间执行脚本时,定义 "BNRead" 命令并映射 <F19> 键。定义 FuncUndefined 自动命令。":finish" 命令使脚本提前终止。 2. 用户输入 BNRead 命令或者按了 <F19> 键。BufNetRead() 或 BufNetWrite() 函数会 被调用。 3. Vim 找不到这些函数并因此激活了 FuncUndefined 自动命令事件。因为模式 "BufNet*" 匹配要调用的函数,执行命令 "source fname",其中 "fname" 被赋予脚 本的名字,不管它实际在何处都没问题。这是因为该名字来自 "<sfile>" 扩展的结果 (见 expand() )。 4. 再次执行脚本。"s:did_load" 变量已经存在,此时定义函数。 注意后来载入的函数匹配 FuncUndefined 自动命令的模式。要确信其它插件没有定义 匹配此模式的函数。

52.5 在老式脚本中使用 Vim9 脚本 source-vim9-script

有些情况下,在老式脚本里你会想使用 Vim9 脚本里的项目。例如在 .vimrc 里初始化插 件。最好的方法是用 :import 。例如: import 'myNicePlugin.vim' call myNicePlugin.NiceInit('today') 这会找到 Vim9 脚本文件里的 "NiceInit" 导出函数,并使之成为局部于脚本的项目 "myNicePlugin.NiceInit"。即使不给出 "s:", :import 也总使用脚本命名空间,如果 "myNicePlugin.vim" 已经执行过,不会再次执行。 除了避免把任何项目放进全局命名空间 (因为命名冲突会导致意料之外的错误) 以外,这 也意味着无论其中的项目被导入多少次,一份脚本只执行一次。 有些情况下,例如为了测试用途,可能就想要直接执行 Vim9 脚本。这没问题,但你只能 得到其中的全局项目。该 Vim9 脚本要确保这些全局项目使用独一无二的名字。例如: source ~/.vim/extra/myNicePlugin.vim call g:NicePluginTest()

下一章: usr_90.txt 安装 Vim 版权: 参见 manual-copyright vim:tw=78:ts=8:noet:ft=help:norl: