Skip to content

Commit

Permalink
正文初稿完成tag
Browse files Browse the repository at this point in the history
  • Loading branch information
lymslive committed Dec 26, 2018
1 parent 257dbb6 commit 3176381
Show file tree
Hide file tree
Showing 13 changed files with 2,703 additions and 18 deletions.
34 changes: 20 additions & 14 deletions content.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

+ [前言 ](z/20170816_1.md)

*基础篇*

+ 第一章 VimL 语言主要特点
- [1.1 Hello World 的四种写法 ](z/20170816_2.md)
- [1.2 同源 ex 命令行 ](z/20170816_3.md)
Expand All @@ -25,6 +27,8 @@
- [3.5\* 自动命令与事件 ](z/20170818_5.md)
- [3.6\* 调试命令 ](z/20170818_6.md)

*中级篇*

+ 第四章 VimL 数据结构进阶
- [4.1 再谈列表与字符串 ](z/20170819_1.md)
- [4.2 通用的字典结构 ](z/20170819_2.md)
Expand All @@ -50,21 +54,23 @@
- [7.3 自定义类的组织管理 ](z/20170821_7.md)
<!-- - 7.4\* 面向对象实现示例 20170821_8.md -->

*高级篇*

+ 第八章 VimL 异步编程特性
- 8.1 异步工作简介
- 8.2 使用异步任务
- 8.3 使用通道控制任务

+ 第九章 Vim 插件管理与开发
- 9.1 典型插件的目录规范
- 9.2 插件管理器插件介绍
- 9.3 内置的 pack 机制
- 9.4\* 自定义开发插件基础

+ 第十章 VimL 混合编程
- 10.1 内嵌其他脚本语言
- 10.2 调用其他语言脚本
- 其他待定
- [8.1 异步工作简介](z/20181121_1.md)
- [8.2 使用异步任务](z/20181205_1.md)
- [8.3 使用通道控制任务](z/20181210_1.md)
- [8.4 使用配置内置终端](z/20181212_1.md)

+ 第九章 VimL 混合编程
- [9.1 用外部语言写过滤器](z/20181215_1.md)
- [9.2 外部语言接口编程](z/20181215_2.md)
- [9.3\* Perl 语言接口开发](z/20181217_2.md)

+ 第十章 Vim 插件管理与开发
- [10.1 典型插件的目录规范](z/20181219_1.md)
- [10.2 插件管理器插件介绍](z/20181219_2.md)
- [10.3 插件开发流程指引](z/20181219_3.md)

+ 结语

12 changes: 10 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
# VimL 语言编程指北路

本教程只完成了三分之二,有关 VimL (vim script) 编程的基础知识与中级用法算是完
稿了,但高级应用尚未完成。
本教程期望按技术书籍方式讲叙。书名叫“指北”而不是“指南”,主要是考虑有很多指南类
书籍讲 vim 这编辑器工具的使用,而本书则侧重于 VimL 这种脚本语言编程。

全书正文分十章,约摸可再划分为基础篇、中级篇与高级篇三部分,现已完成初稿。后面
有可以计划再补上番外实战篇,写几章开发具体插件实例的实现思路。

[进入目录](./content.md)

初稿在本地我用自己的笔记插件 [vnote](https://github.com/lymslive/vnote) 写的,
保存在笔记本 [notebook](https://github.com/lymslive/notebook)
然后将这个较为系统化的教程独立出来,可能进行后续的修改与调整。

本书引用的代码段示例都很短,按书照敲或复制也是一种学习方式。 `example/` 目录整
理了部分示例代码,但是建议以书内讲叙或外链接为准。作者自己在 linux 系统下以
vim8.1 版本测试,Windows 与低版本虽未全面测试,但相信 vim 本身的兼容性也基本适
用了。
6 changes: 4 additions & 2 deletions z/20170816_1.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Vim” 表示编辑器;而小写的 “vim” 则是指系统中可执行的
其实,不管是使用 Vim 还是 VimL,最好的资源都是 Vim 的内置帮助文档(`:help`)。
外部教程都不免有所侧重,较适于学习阶段的引领者。

本教程依据的 Vim 版本是 `8.0`,系统环境 Linux。但除了一些新特性,应该也适用
本教程依据的 Vim 版本是 `8.1`,系统环境 Linux。但除了一些新特性,应该也适用
Vim7 以下版本。同时由于 Vim 本身是跨平台的,VimL 自然也与操作系统无关。虽然无
法一一验证,但在一些重要的差异处,尽量在文中指出。

Expand Down Expand Up @@ -61,4 +61,6 @@ Vim7 以下版本。同时由于 Vim 本身是跨平台的,VimL 自然也与
示从 shell 运行的命令行。较短的示例代码,可以直接输入或粘贴入 vim 的命令行,较
长的示例代码,建议保存 `.vim` 文件,然后 `:source`

希望能有节奏地写完全部教程,然人艰不拆,不期何时完稿。
本书正文共十章,可粗略分为三部分。第 1-3 章为基础篇,第 4-7 章为中级篇,第
8-10 为高级篇。在行文组织上尽量循序渐进,建议按顺序阅读。文中经常用提示用
`:help` 命令查阅相关帮助主题,此后忘记细节时可随时查询。
132 changes: 132 additions & 0 deletions z/20181121_1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# 《VimL 语言编程指北路》第八章 VimL 异步编程特性
`+` `book/vimllearn`

## 8.1 异步工作简介

异步机制是 vim8 版本引入的新机制,准确地说,是从 7.4 某个补丁开始引入,不过在
vim8 完善并正式发布。这一全新特性使得 vim 直接跳升一大版本号,可见意义非凡。

### 8.1.1 同步工作可能的问题

要理解异步的特性,不妨先回顾下在此之前只能同步工作的情况,会遭遇哪些不便。

比如要从一个目录下的文本文件中查找某个字符串,我们知道(在 unix 系统中)直接
有个 `grep` 工具可用。而在运行着的 vim 中,也可以通过 `:!grep ...` 命令调用系
统的 `grep` 工具。但是用 `:!` 执行外部命令的话,会临时切回启动当前 vim 的终端
,外部命令的输出在该终端上;当外部命令经过或长或短的时间完成后,还需要等用户按
回车确认才回到 vim 正常的用户界面。如果是 windows 系统的 gVim ,`:!` 执行外部
命令则会弹出 `cmd` 黑框,展示外部命令的输出,也需要由用户确认关闭该黑窗才能回
到 gVim 编辑窗口。

显然按种方式,在运行外部命令的同时,在回到 vim 界面之前,vim 对用户而言是停止
工作的,比如用户暂时无法操纵 vim 进行编辑工作。vim 也有个类似的内部命令
`:vimgrep` 用于在多文件中搜索字符串,并将结果输出在 `quickfix` 窗口。运行该命
令不会切回 shell 终端,与 `:!grep` 很有些不同。但是,如果待搜索的文件很多,尤
其是类似 `**/*` 的递归所有子目录的文件搜索时,`:vimgrep` 命令完成搜索也可能很
慢,需要等待一段时间才能完成搜索。在等待的这段时间内,虽然仍然停留在 vim 界面
,但 vim 也好像停止了与用户的交互工作,譬如按 `j` `k` 不见得会移动光标。事实上
vim 还是监测到你按了 `j` `k` 键,只不过要等 `:vimgrep` 这个慢命令完成后才会响
应后续按键。简单地说,就可能造成明显卡顿。

这就是旧版本 vim 按同步工作方式可能出现的问题。你可以将 vim 编辑器想象为一个单
线程的无限循环程序,等待着用户的按键,并立即根据按键命令处理工作。正常情况下
vim 响应用户按键命令是极快的,所以用户感觉很流畅。因为正常人类的击键速度在计算
机程序看来都太慢了,vim 在大部分时间里都是在等待用户击键的。但是当用户试图让
vim 执行某些“能感觉出来慢”的命令时,问题就浮现了,影响用户体验。

如果上面的 `:vimgrep` 命令没让你感觉到慢,可以用 VimL 定义如下的慢函数:

```vim
function! SlowWork()
sleep 5
echo "done!!"
endfunction
```

然后在命令行输入 `:call SlowWork()` 并回车,你应该就能感觉到 vim 明显卡顿了。
在此期间若按几次 `j` ,也要等该函数返回才能发现光标移动。此外,你也可以试试用
`while 1 | endwhile` 定义一个无限循环函数,调用时会令 vim 完全停止响应,如此
请用 `Ctrl-C` 强制结束当前命令,回到 vim 的正常工作状态来。

### 8.1.2 异步工作想解决的问题

显然,vim8 引入的异步机制,就是试图解决(或部分解决、缓和)上述同步模型中出现
的“慢命令卡顿”问题。当然它也不是直接重定义优化原来命令的工作方式,因为兼容旧习
惯也是 vim 的传统。所以,在 vim8 中,类似 `:!grep``:vimgrep` 命令,该怎么
慢还怎么慢,它真正想优化的类似 `system('grep')` 函数的工作方式。

`system('grep')``:!grep` 的相同之处在于都是调用外部命令(系统可执行程序)
,只不过调用 `system()` 函数不会切到 shell 终端,仍停留在 vim 界面。所调用的外
部命令的输出会被 `system()` 函数所捕获,可以保存在 VimL 变量中,供脚本后续使用
。如果该外部命令执行时间较长, vim 用户仍会感到停止响应或卡顿。

然后在 vim8 中,就提供了另一套不叫 `system()` 名字的函数,用于执行外部命令。
vim 不再等待外部命令结束,而是立即返回给用户,可以立即接着响应用户按键。等外部
命令终于结束了,vim 再调用一个回调函数处理结果。

开启异步工作的具体函数与用法,留待下一切详细介绍。不过你该能感觉与估计到,这个
异步编程模型比本书之前介绍的同步编程模型要复杂些。并且在监测外部命令结束时准备
回调也必然有其他开销,所以异步也不宜滥用,只适合在(可预期)比较耗时的外部命令
上。如果只是简单的可以快速完成的外部命令,仍用原来的 `system()` 函数完成工作即
可。

另外要提及的是,目前 vim8 版本的异步机制,也只能将外部命令以异步的方式开启,并
不能用异步的方式执行内部命令。也就是说,不论是 vim 内置的命令(及常规函数),
还是用 VimL 写的自定义命令(函数),都仍只能按原来的同步方式执行,暂无异步用法

### 8.1.3 异步机制带来的 vim 新特性简介

vim8 提供异步机制后,可以据此实现很多新特性。比如内置终端(从 vim8.1 版本开始
支持)。在命令行执行 `:terminal` 就能打开一个新窗口,体验一下内置终端。在这个
特殊的 vim 新窗口中,就相当于运行着一个 shell ,可以像系统 shell 一样执行任何
命令,甚至也可以在此又运行一个 vim (不过一般情况下不建议这么玩)。用窗口切换
快捷键 `Ctrl-w` 可以回到之前的普通 vim 窗口,正常操作 vim 进行编辑工作。

也就是说,内嵌终端正是异步运行的,并不中断 vim 本来的编辑工作。相比在这个功能
出现之前,用 `:shell` 命令打开的子终端,就会切出 vim 界面,只能在那个子终端中
工作,必须在那执行 `$ exit` 退出子终端,才能回到 vim 。

关于内置终端的详细用法请参考 `:help terminal` ,在那文档中还介绍了在 vim 中“
嵌入”gdb 调试 vim 本身的示例。表明内置终端功能其实不止能执行一个 `shell` ,还
适于执行其他任何交互程序,例如 python 解释器,mysql 客户端,gdb 调试器等。

不过本章不是介绍 vim 的这类新特性,而是侧重介绍 VimL 脚本编程中如何使用这个异
步机制,据此可以完成之前的脚本无法完成的工作,或优化某些插件功能。

### 8.1.4 异步编程的简单运用:定时器

让我们先看一个简单的例子来体验下异步编程的风格,定时器(请确认 vim 编译包含
`+timers` 特性)。将上文按传统同步风格定义的 `SlowWork()` 函数重新改写如下:

```vim
function! SlowWork()
call timer_start(5*1000, 'DoneWork')
endfunction
function! DoneWork(timer)
echo "done!!"
endfunction
call SlowWork()
```

现在再调用 `SlowWork()` 函数时就不会“暂停” 5 秒了,该调用立即返回,用户可如常
操作 vim 。大约过了 5 秒后,函数 `DoneWork()` 被调用,显示 `"done!!"`

这里的关键是在 `SlowWork()` 中用 `timer_start()` 启用了一个定时器。参数一是时
间,单位毫秒;参数二叫回调函数,应该是函数引用,但也可用函数名代替。其意义就是
在指定时间后调用那个回调函数,而不影响现在 vim 对用户的正常响应。还可以指定可
选的第三参数,表示重复回调若干次,默认就只回调一次,然后自动关闭定时器。该函数
有返回值,表示定时器 ID,在 vim 内部就用该 ID 标记这个定时器。回调函数一般是自
定义函数,必须接收一个表示定时器 ID 的参数。不过在这个简单示例中,我们忽略未用
到这个定时器 ID 参数。定时器相关函数的详细用法请参考 `:help timer-functions`

由此可见,异步编程的基本思路是将原来在一个函数内的工作(一般是较费时的工作),
多拆出一个回调函数,用来在工作完成时处理“后事”,关键也就是回调函数的编写。在这
个例子中,我们用定时器来“模拟”了一件慢工作,当然定时器本身也另有用途场景。

定时器可以明确指定延时几秒,不过在实际的慢工作(外部命令)中,需要多长时间完成
工作是不确定的。这就需要另外的机制,根据其他条件来调用回调函数。这就是下一节准
备讲的“任务”,原文档术语叫 `job` 的话题了。
Loading

0 comments on commit 3176381

Please sign in to comment.