Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

lymslive/vimloo

Open more actions menu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

158 Commits
158 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

VimL 脚本模块化的包管理机制及面向对象组织框架

vimloo git/readme

内容简介

这是着重于 VimL 脚本的插件,而非 Vim 的插件。 主要目的与思想是将 VimL 脚本像其他“常规”脚本一样模块化, 然后需要一种包管理机制,将不同模块内的私有函数(与对象、类定义) 通过明确的导入导出方法,在其他脚本模块内共享。

核心是 autoload/ 目录下的几个脚本:

  • package.vim 包管理与模块导入机制,可独立使用。
  • object.vim 通用对象机制,最好与 package.vim 的导出功能联用。
  • class.vim 稍复杂点的类文件规范,不依赖 package.vim(其实是先写的) 但由于将内部处理字典的部分单独抽出(仍在本仓库),却不宜单独使用。

这几个核心脚本,或多或少对 VimL 写法有一定的规范要求,但力求自然与 最小惊讶原则。

至于 autoload/ 目录下的其他子目录,则是一些代码库。有自己写的,也有 从其他优秀插件中提取的通用脚本库。

  • jp# 源自日本社区的 vimtal 但对部分模块作了简单的相互引用调整,改用 package.vim 的方式。
  • cn# 个人或参考国人写的实用模块,打算放此命名空间下。
  • class#class.vim 规范写的类文件,放在该目录下。

本仓库希望有助于开发 VimL 项目,如比较复杂的插件,在需要将不同功能 分解至不同脚本(模块)时,能更方便有序地组织脚本代码。同时也能更方便 地复用别人已经写的可通用模块。

安装使用

无需特殊安装,将本仓库下载至任一能加入 vim &rtp 路径的目录即可。 也可以用任意 vim 插件管理工具安装。

本插件几乎不会影响 vim 的个性环境,不会修改任何设置与快捷键等。 只有 autoload/ 目录下的自动加载脚本,默默等待发挥作用。

模块导入导出实践

一个 VimL 模块就是一个 *.vim 文件,最好是放在某个 &rtp 目录的 autoload/ 子目录。定位一个模块,有以下三种路径表示法:

  1. 标准路径,形如 path#to#mod 的以 # 分隔的路径,遵循 vim 的自动 加载机制。文件只能放在某个 autoload/ 之下,不包括 .vim 后缀。
  2. 绝对路径,形如 /root/path/to/mod.vim./path/to/mod.vim 的路径, 可以表示 autoload/ 之外的文件,且要指定 .vim 后缀名。 路径分隔符 / 取决于实际操作系统。
  3. 相对路径,形如 path.to.mod.mod_in_current 的路径, 以点分隔路径,但在无子目录时,为防与第 1 种标准路径区分,得在最前面 再加个点。同时显然应省略 .vim 后缀名。

在第 3 种点号相对路径与第 2 种以 ./../ 开头的系统相对路径, 都还涉及一个相对基准的问题,这取决于具体使用函数或命令的设计环境。

# 分隔的标准路径,也称为模块的命名空间。为文法区别,将每个模块 下的 s: 特殊字典称为脚本作用域,而不称符号空间。

package.vim 导入方法

  • package#import(module, ...) 模块名使用标准路径定位,返回一个字典。 如果指定额外参数,字典中只包含指定的键。也允许使用完全的绝对路径。
  • package#imports(module, key1, ...) 至少要指定一个键名参数, 在类似导入基础上,只返回指定键的值,不涉及外层字典。
  • :USE[!] moudle [key1, key2] 命令式的导入方法,模块名与额外参数键 不用加引号。模块名可用标准路径、绝对路径与相对路径,相对路径的基准 是当前脚本,即使用该命令的脚本。命令无返回值,故将导入的字典以模块 名的末尾部分命名,注入当前脚本作用域 s:,如果加 ! 变种,则还省略 字典变量这个中间层,直接将每个键名的符号导入。
  • package#new(name, [base]) 返回一个局部的包管理对象,基准路径是 base 参数,如省略则同 name 参数。该对象的 .import() 方法 类似全局的 package#import() ,除了支持标准路径与绝对路径外, 还支持相对路径,相对基准即是构建对象时的 base 参数。

模块导出规范

首先,模块导入可以兼容毫无规范的模块。只要源模块脚本内写了一堆以脚本 作用域的 s: 函数,用 package#import():USE 就能导出这些私有 函数。但不能导入 s: 作用域的私有变量(或对象),也不能(其实不需) 导出全局函数(包括含 # 的全局函数)。

但是,源模块可以提供以下函数,定制自己觉得需要导出的符号表:

  1. #export()s:_export_()
  2. #class()s:_class_()
  3. #package()s:_package_()

然后在导入端,会按以上优先级顺序,调用存在的第一个函数。将其返回值 当作(初步待导入)的符号表,一般应该是字典。但是,又有以下几种纠正:

  • 如果导出表的字典含有 EXPORT 这个键,则改用这个键的值当作实际 待导出的表。
  • 如果导出表不是字典变量,而是一个字符串列表,则将每个字符串视为 s: 私有函数名,以这些键名重建导出字典表。
  • 如果导出表字典中,含有 <SID> 这个特殊键(一般也就还有其他键), 则额外将 s: 私有函数也添加于导出表中。<SID> 键的值,视为正则 表达式,如为空则默认过滤掉以下划线开始的函数名。

简言之,package.vim 机制可以方便导入其他模块中定义的 s: 函数。 如果这不满足需求,可定制导出函数;其他看视复杂的规则,只为手写定制 导出函数更简便一点而已。

以下划线开头的 s:_xxx() 函数,视为“真私有”函数,默认不导出。

模块导入规范

在脚本中使用(导入)其他模块,这更自由,更少限制。

  • 使用合适的变量名接收 package#import() 的返回值。可用在函数内 赋给 l: 变量,但如常用,建议在函数赋给 s: 变量,且写在脚本 开头处,意图表明本模块需要导入其他模块。
  • 在只要用到其他模块的一个(或少量几个)函数时,用 package#imports() 更方便,但在函数内用 l: 变量接收时,注意要大写,函数引用名要大写。
  • USE 命令往本地作用域注入变量,需要开启一个后门,在当前脚本定义 一个 #package() 函数,并返回 s: 。命令相比函数更方便相对导入。 当然符号注入要谨防名字污染与冲突。
  • package#new() 构建本地包管理对象,可用在插件开发中,以插件名当作 对象的基准目录,方便互相导入调用同插件下的脚本。 也可以用于简化长命名空间下的模块导入写法。

通用对象 objcet 规范

object 对象(字典)采用极懒加载工作方法,最初只有一个成员,四个方法:

  • object['@ISA'] 对象的父类对象列表,每个元素是字典,如果是字符串, 则应该能将该字符串作为模块名用 package#import() 导出字典。
  • object.has(key1, ...) 判断对象是否含有某个键(或多个键)。
  • object.get(key, [default]) 获取一个键值,可指定默认值。
  • object.set(key, val) 设定一个键值。
  • object.call(key, ...) 调用一个方法,键值应该是一个函数引用。

在调用上述四个基本方法时,如果当前字典中没有该键,则从 @ISA 列表的 字典中寻找(按实现是深度优先),并将找到的第一键值复制到当前字典中。 此后就可直接引用该键,而不必间接调用了。

使用 object#new(...) 函数创建一个新对象,并将参数视为新对象的父类, 存入新对象的 @ISA 列表中。可以马上调用新对象的 has() 方法,将关心 的键名都传入参数,权当作成员检查或成员声明。

单类模块文件 class 规范

class.vim 的规范,一个模块文件只能定义一个类,类名就是其标准路径。 并有以下规范要求,定义如下自动加载全局函数:

  • #class() 返回类定义字典(必须)
  • #new() 返回一个类实例化对象
  • #ctor() 对象的构造函数,由 #new() 通过内部方法间接调用。

更多规范或建议可参考 autoload/tempclass.vim 模板示例类文件。

class 类的实现相对繁重,在类(与对象)创建时就从祖先处复制了所有键。 这在长继承链中,需要大量创建叶结点时,空间利率可能很不好。 但优点是清晰,与模块文件一一对应,也容易查找类定义。

object 对象相对轻量,允许在一个模块中定义多个(相关的)类。object 继承时可兼容 class 规范的类。

辅助工具

曾为编写与高度规范的类文件写过一些辅助工具,后来觉得这不属于 VimL 模块 机制的范畴,就移到他处了。

  • 类文件模板生成,另见 lymslive/autoplug 仓库 autoload/template 目录。
  • VimL 脚本调试相关,另见 lymslive/autoplug 仓库 autoload/debug 目录。

更多参考文档

主要文件列表及简单说明见 content.md。 VimL 脚本源文件虽都用英文注释,但并未能写英文的 help 文档, 然而提供了几篇中文的 .md 文档可供参考。

About

VimL object orient programming frame and tools

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published
Morty Proxy This is a proxified and sanitized view of the page, visit original site.