不久前,我无意间看到了过去的一个功能请求: vim edit mode

vi 类编辑器,其最具特色的,便是 modal editing. 正如其名,是个逻辑流在决策树上流动,变换的过程。

大多编辑器,或多或少,都会带有一些 modal editing, 最常见的,便是快捷键组合,如 Ctrl-c, Ctrl-v. 当编辑者按下 Ctrl 后,即进入了 Ctrl 的决策分支,而在这个分支下面,有许多片叶子. c 和 v 即是这片分支下的两片叶子之一。次常见的,快捷键序列。比如,在 emacs 里,顺序地按 Ctrl-x, Ctrl-c 就会执行退出编辑器的操作。

vim 作为 modal editing 的一个典范,其情态处理已经渗透进编辑过程的方方面面。于是,它成了一片决策树森林,逻辑流在森林的每颗树间跳动飞舞。形成了一片精妙绝伦,流光溢彩的图景。

单一决策树的流动变换

normal mode 的示例

在 normal 模式下,我们键入数字,比如 12, 进入决策树的一个分支,即重复操作计数分支,然后我们键入字母 d (delete),则在计数12次的决策上进入删除分支。然后我们键入 2w (2 words)即对象为两个词组。于是 vim 作出了最终决策,连续进行12次操作,每次删除当前光标后的连续两个词组。

如果我们在 normal 模式下,键入 12c2w 和之前语句的唯一区别是从 d delete 换成了 c change 于是 vim 在做出了和之前语句同样的操作后,因为决决定的操作是 change, 而非 delete, vim 预期使用者还需键入一些新的文本来替换旧的。最后改变模式,变为 insert mode.

normal mode

从按键绑定开始,来瞥一眼模式群

上面一段谈了了 normal 模式下,比较单一的决策树过程。和执行 c 操作后进入了 insert 模式。接下来的表格,从按键绑定开始,引入了更多模式的一张简表。

Overview of which map command works in which mode.  More details below.
   COMMANDS                    MODES
:map   :noremap  :unmap     Normal, Visual, Select, Operator-pending
:nmap  :nnoremap :nunmap    Normal
:vmap  :vnoremap :vunmap    Visual and Select
:smap  :snoremap :sunmap    Select
:xmap  :xnoremap :xunmap    Visual
:omap  :onoremap :ounmap    Operator-pending
:map!  :noremap! :unmap!    Insert and Command-line
:imap  :inoremap :iunmap    Insert
:lmap  :lnoremap :lunmap    Insert, Command-line, Lang-Arg
:cmap  :cnoremap :cunmap    Command-line
:tmap  :tnoremap :tunmap    Terminal-Job

其中我们可以看到

  • normal
  • insert
  • visual
  • select
  • command-line
  • operator pending
  • terminal-job
  • lang-arg

列表中的前几种模式,是一些容易被我们感知的常用模式,我们花费大多的时间,最经常性地在这些模式下和编辑器交流。

每个模式,都有其各自的决策树。其中大多决策的终点是当前模式下的一个操作;其中的另一些决策是转变当前模式为另一种模式。于是,模式们和它们之间的转换,就形成了基本的树木之间的交互,就这样交织成了一片小林子。

回到按键绑定

一般情况下,按键的接受,处理,应不会有情态树的参与。编辑器只是如实的接受使用者按键,然后如实转发,交给模式的决策树来处理而已。但正如前文所述,vim 的情态处理已经渗透进编辑过程的方方面面。按键的处理,如上述的按键绑定和模式列表所示,毫无疑问,也成了另一维度的,前置的决策树林。

使用者在 command 模式下的每次 map, 就给相应模式的前置读取过程新增了一条用于决策使用者输入和输出转换的决策链。于是,这些决策链就在各个模式下形成了一颗颗相应的决策树。

所以呢

模式之间可以互相跳转,那么,我们就可以在一条决策链中任意穿梭各个模式,进行各种操作。

例如,每次在文本的结尾,我们都需要添加日期信息,并附上签名。那么我们就可以绑定按键序列 date

map date :r !date<Enter>4dwoDoYe

先进入 command 模式,读取执行外部命令 date 的结果。然后回到 normal 模式。在 normal 模式下,执行 4dw 删掉外部命令 date 的结果最开头的年月信息,然后通过按键 o 新开一行后进入 insert 模式,在此模式下键入 DoYe 签名。于是,每个文本的结尾,键入 date 四个按键的过程,本身即是一个决策树的流转过程,键入完成后,更是在决策树之林间跳跃着流动,完成各种工作。期间甚至还带有外部工具的交互。

决策链递归

因为 vim 不对按键绑定的创造有任何限制。自然,需要支持决策链的交叠状态,比如 ab 链和 abc 链的共存。更进一步的,决策链的自递归和互相递归。如前文所述,绑定按键可以在任意模式中穿梭,在决策链的森林中跳转变换。那么,最后,我们也能构造一些在跨越树木的,在森林中任意傲游,递归的精灵们。 比如

map yin oyin<Esc>yang
map yang oyang<Esc>yin

于是,若我们在 normal 模式下键入 yin 或者 yang, vim 就开始无限地,在文本中添加 "yin" "yang" 两个单词,永不停息。在这个永不停息的过程中,按键的决策解析,到添加 "yin" "yang" 的过程中的模式跳转和决策变换。就这么相互交织,支撑,递归地......

mew mew 喵喵

开头提到,一切起源于一个功能请求。思考这个功能请求的过程中。发现 vim 的精妙背后有一个更通用的模型。vim 是这个模型的一个特化,由附加的特性和限制塑造了 vim 的样子。其背后的模型,若可单独提取构建。那我们就可以构造种种不同的 modal editing 编辑引擎。

提取的结果,暂命名为 Modal Editing Witch, mew~ 它并非简单地去除掉一些特化功能,而是把特性和限制的方法结构化地规范了接口,交给了使用者。

特化和限制有哪些类别呢?并发机制,键盘布局,编辑模式。这些,又是另外的故事了。

- ZAN DoYe


Comments

comments powered by Disqus

© 2020 ZAN DoYe