You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
269 lines
13 KiB
269 lines
13 KiB
2 years ago
|
# 第一章 VimL 语言主要特点
|
||
|
|
||
|
## 1.2 同源 ex 命令行
|
||
|
|
||
|
那么,VimL 到底是种什么样的语言。这里先说结论吧,VimL 就是富有程序流程控制的
|
||
|
`ex` 命令。用个式子来表示就是:
|
||
|
```
|
||
|
VimL = ex 命令 + 流程控制
|
||
|
```
|
||
|
|
||
|
VimL 源于 ex ,基于 ex,即使它后来加了很多功能,也始终兼容 ex 命令。
|
||
|
|
||
|
然则什么是 ex 命令,这不好准确定义,形象地说,就是可以在 Vim 底部命令行输入并
|
||
|
执行的语句。什么是流程控制,这也不好定义呢,类比地说,就是像其他大多语言支持的
|
||
|
选择、循环分支,还有函数,因为函数调用也是种流程跳转。
|
||
|
|
||
|
下面,还是用些例子来阐述。
|
||
|
|
||
|
### 第一个脚本:vimrc
|
||
|
|
||
|
为了说明 `vimrc` 先假设你没有 `vimrc` 。这可以通过以下参数启动 vim:
|
||
|
```bash
|
||
|
$ cd ~/.vim/vimllearn/
|
||
|
$ vim -u NONE
|
||
|
```
|
||
|
这样启动的 vim 不会加载任何配置文件,可以假装自己是个只会用裸装 vim 的萌新。同
|
||
|
时也保证以下示例中所遇命令没有被重映射,始终能产生相同的结果。
|
||
|
|
||
|
Vim 主要功能是要用来编辑一些东西的,所以我们需要一些语料文本。这也可以用 vim
|
||
|
的普通命令生成,请在普通模式下依次输入以下按键(命令):
|
||
|
```vim
|
||
|
20aHello World!<ESC>
|
||
|
yy
|
||
|
99p
|
||
|
: w helloworld.txt<CR>
|
||
|
```
|
||
|
其中输入的按键不包括换行符,上面分几行显示,只为方便分清楚几个步骤的命令。
|
||
|
|
||
|
首先是个 `a` 命令,进入插入模式,输入字符串“Hello World!”,然后按 `<ESC>` 键
|
||
|
返回普通模式(这里`<ESC>`表示那个众所周知的特殊键,不是五个字符啦)。`a` 之前
|
||
|
的 `20` 是还在普通模式下输入的数字参数,(它不会显示在光标所在的当前行,而是临时
|
||
|
显示在右下角,然而一般不必关注)这表示后来的命令重复执行多少次。所以结果是在当
|
||
|
前行插入了 20 个 “Hello World!”,也就是新文件的第一行。
|
||
|
|
||
|
接着命令 `yy` 是复制当前行,`99p` 是再粘贴 99 行。于是总共得到 100 行 “Hello
|
||
|
World!” ——满屏尽是 Hello World!,应该相当壮观。
|
||
|
|
||
|
最后的命令是用冒号 `:` 进入 ex 命令行,保存文件,`<CR>` 表示回车,ex 命令需要
|
||
|
回车确认执行。
|
||
|
|
||
|
现在,我们已经在用 vim 编辑一个名为 `helloworld.txt` 的文件了。看着有点素是不
|
||
|
是?可以用下面的 ex 命令设置行号选项:
|
||
|
```vim
|
||
|
: set number
|
||
|
```
|
||
|
如此就会在文本窗口左则增加几列特殊列,为文件中的每行编号,确认一下是不是恰好
|
||
|
100 行,用 `G` 普通命令翻到最后一行。
|
||
|
|
||
|
还有,是不是觉得每一行太长了,超过了窗口右边界。(如果你用的是超大显示屏,Vim
|
||
|
的窗口足够大还没超过,那么在一开始的示例中,把数字参数 `20` 调大吧)如果想让
|
||
|
vim 折行显示,则用如下命令设置选项:
|
||
|
```vim
|
||
|
: set wrap
|
||
|
```
|
||
|
可以看到,长行都折行显示了,但是行编号并没有改变。也就是说文件中仍是只有 100
|
||
|
行,只有太长的行,vim 自动分几行显示在屏幕窗口上了。
|
||
|
|
||
|
你可以继续输入些设置命令让 vim 的外观更好看些,或让其操作方式更贴心些。但是等
|
||
|
等,这样通过冒号一行行输入实在是太低效,应该把它保存到一个 vim 脚本文件中。
|
||
|
|
||
|
按冒号进入命令行后,再按 `<Ctrl-F>` 将打开一个命令行窗口,里面记录着刚才输入的
|
||
|
ex 历史命令。这个命令窗口的设计用意是为了便于重复执行一条历史命令,或在某条历
|
||
|
史命令的基础上小修后再执行。不过现在我们要做的是将刚才输入的两条命令保存到一个
|
||
|
文件中,比如就叫 `vimrc.vim`,整个按键序列是:
|
||
|
```vim
|
||
|
: <Ctrl-F>
|
||
|
Vk
|
||
|
: '<, '> w vimrc.vim<CR>
|
||
|
: q<CR>
|
||
|
```
|
||
|
|
||
|
解释一下:进入命令窗口后光标自动在最后一行,`V` 表示进入行选择模式,`k` 上移一
|
||
|
行,即选择了最后两行。在选择模式下按 `:` 进入命令行,会自动添加 `'<, '>`,这是
|
||
|
特殊的行地址标记法,表示选区,然后用 `:w` 命令将这两行写入 `vimrc.vim` 文件(
|
||
|
注意当前目录应在 `~/.vim/vimllearn` 中)。最后的 `:q` 命令只是退出命令窗口,但
|
||
|
vim 仍处于编辑 `helloworld.txt` 状态中。
|
||
|
|
||
|
你需要再输入一个 `:q` 退出 vim,然后用刚才保存的脚本当作启动配置文件重新打开文
|
||
|
件,看看效果:
|
||
|
```vim
|
||
|
:q<CR>
|
||
|
$ vim -u vimrc.vim helloworld.txt
|
||
|
```
|
||
|
可见,重新打开 `helloworld.txt` 文件后也自动设置了行号与折行。你可以换这个参
|
||
|
数启动 vim 对比下效果,确认是 `vimrc.vim` 的功效:
|
||
|
```bash
|
||
|
$ vim -u NONE helloworld.txt
|
||
|
```
|
||
|
|
||
|
可以手动编辑 `vimrc.vim` 增加更多配置命令:
|
||
|
```vim
|
||
|
: e vimrc.vim
|
||
|
```
|
||
|
这样就切换到编辑 'vimrc.vim' 状态了,里面已经有了两行,用普通命令 `Go` 在末尾
|
||
|
打开新行进入插入模式,加入如下两行(还可自行添加一些注释):
|
||
|
```vim
|
||
|
: nnoremap j gj
|
||
|
: nnoremap k gk
|
||
|
```
|
||
|
|
||
|
按 `<ECS>` 回普通模式再用 `:w` 保存文件。可以退出 vim 后重新用
|
||
|
`$ vim -u vimrc.vim helloworld.txt` 参数启动 vim 打开文件观察效果。也可以在当
|
||
|
前的 vim 环境中重新加载 `vimrc.vim` 令其生效:
|
||
|
```vim
|
||
|
: source %
|
||
|
: e #
|
||
|
```
|
||
|
其中,`:e #` 或快捷键 `<Ctrl-^>` 表示切换到最近编辑的另一个文件,这里就是
|
||
|
`helloworld.txt` 文件啦,在这个文件上移动 `j` `k` 键,看看是否有什么不同体验了
|
||
|
。
|
||
|
|
||
|
不过,表演到此为止吧。这段演示示例主要想说明几点:
|
||
|
|
||
|
1. VimL 语言没什么神秘,把一些 ex 命令保存到文件中就是 vim 脚本了。
|
||
|
2. vimrc 配置文件是 vim 启动时执行的第一个脚本,也应是大多数 Vim 初学者编写的
|
||
|
第一个实用脚本。
|
||
|
|
||
|
关于 vim “命令” 这个名词,还有一点要区分。普通模式下的按键也叫“命令”,可称之为
|
||
|
“普通命令”,但由于普通模式是 Vim 的主模式,所以“普通命令”也往往简称为“命令”了
|
||
|
。通过冒号开始输入而用回车结束输入的,叫 "ex 命令",vim 脚本文件不外是记录
|
||
|
“ex 命令”集。(注:宏大多是记录普通命令)
|
||
|
|
||
|
### 默认的 vimrc 位置
|
||
|
|
||
|
正常使用 vim 时不会带 `-u` 启动参数,它会从默认的位置去找配置文件。这可以在
|
||
|
shell 中执行这个命令来查看:
|
||
|
```bash
|
||
|
$ vim --version
|
||
|
```
|
||
|
或者在任一已启动的 vim 中用这个 ex 命令 `:version` 也是一样的输出。在中间一段
|
||
|
应该有类似这样几行:
|
||
|
```
|
||
|
系统 vimrc 文件: "$VIM/vimrc"
|
||
|
用户 vimrc 文件: "$HOME/.vimrc"
|
||
|
第二用户 vimrc 文件: "~/.vim/vimrc"
|
||
|
用户 exrc 文件: "$HOME/.exrc"
|
||
|
defaults file: "$VIMRUNTIME/defaults.vim"
|
||
|
```
|
||
|
|
||
|
它告诉了我们 vim 搜索 vimrc 的位置与顺序。主要是这两个地方,`~/.vimrc`,
|
||
|
`~/.vim/vimrc`。用户可用其中一个做为自定义配置,强烈建议用第二个 `~/.vim/vimrc`。
|
||
|
因为配置可能渐渐变得很复杂,将所有配置放在一个目录下管理会更方便。不过有些低版
|
||
|
本的 vim 可能不支持 `~/.vim/vimrc` 配置文件,在 unix/linux 系统下可将其软链接
|
||
|
为 `~/.vimrc` 即可。
|
||
|
|
||
|
需要注意的是,`vimrc` 是个特殊的脚本,习惯上没有 `.vim` 后缀。
|
||
|
|
||
|
如何配置 vimrc 属于使用 Vim 的知识(或经验)范畴,不是本 VimL 教程的重点。不过
|
||
|
为了说明 VimL 的特点,也给出一个简单的示例框架如下:
|
||
|
|
||
|
```vim
|
||
|
" File: ~/.vim/vimrc
|
||
|
|
||
|
let $VIMHOME = $HOME . '/.vim'
|
||
|
if has('win32') || has ('win64')
|
||
|
let $VIMHOME = $VIM . '/vimfiles'
|
||
|
endif
|
||
|
|
||
|
source $VIMHOME/setting.vim
|
||
|
source $VIMHOME/remap.vim
|
||
|
source $VIMHOME/plug.vim
|
||
|
|
||
|
if has('gui')
|
||
|
" source ...
|
||
|
endif
|
||
|
|
||
|
finish
|
||
|
let $USER = 'vimer'
|
||
|
echo 'Hello ' . $USER '! Working on: ' . strftime("%Y-%m-%d %T")
|
||
|
```
|
||
|
|
||
|
一般地,一份 vimrc 配置包括选项设置,快捷键映射,插件加载等几部分,每部分都可
|
||
|
能变得复杂起来,为方便管理,可以分别写在不同的 vim 脚本中,然后在主 vimrc 脚本
|
||
|
中用 `:source` 命令调用。这就涉及脚本路径全名问题了,若期望能跨平台,就可创建
|
||
|
一个变量,根据运行平台设置不同路径,这就用到了 `:if` 分支命令了。
|
||
|
|
||
|
最后两行打印个欢迎词。你可以将自己的大名赋给变量 `$USER`。如果,你觉得这很傻很
|
||
|
天真,可以移到 finish 之后就不生效了。
|
||
|
|
||
|
在 vimrc 中,选择分支可能很常见,根据不同环境加载合适的配置嘛。但循环就很少见
|
||
|
了。因为 vim 向来还有个追求是小巧,启动快,那么你在启动脚本中写个循环是几个意
|
||
|
思啊,万一写个死循环BUG还启不起来了。
|
||
|
|
||
|
### 流程控制语句也是 ex 命令
|
||
|
|
||
|
在 VimL 中,每一行都是 `ex 命令`。作为一门脚本语言,最常见的,创建变量要用
|
||
|
`:let` 命令,调用函数要用 `:call` 命令。初学者最易犯与迷惑的错误,就是忘了
|
||
|
`let` 或 'call',裸用变量,裸调函数,比如:
|
||
|
```vim
|
||
|
i = -1
|
||
|
abs(-1)
|
||
|
```
|
||
|
用过其他语言的可能会觉得这很自然,但在 VimL 中是个错误,因为它要求第一个词是钦
|
||
|
定的 `ex 命令`!正确的写法是:
|
||
|
```vim
|
||
|
let i = -1
|
||
|
call abs(-1)
|
||
|
```
|
||
|
|
||
|
从这个意义上讲,VimL 与 shell 脚本很类似的,把命令行语句保存到文件中就成了脚本
|
||
|
。每一行都以可执行命令开始,后面的都算作该命令的参数。
|
||
|
|
||
|
在 VimL 中,`:if` `:for` `:while` 也当作扩展的 `ex 命令` 处理,在 vim 脚本中,
|
||
|
这些“关键词”前面,可以像 `:set` 一样加个可选的冒号。同时,也可以像其他 ex 命令
|
||
|
一样在无歧义时任意简写。比如:
|
||
|
|
||
|
* `:endif` 可简写为 `en` 或 `end` 或 `endi` 或 `endif`
|
||
|
* `:endfor` 可简写为 `endfo`
|
||
|
* `:endfunction` 可简写为 `endf`,后面补上 `unction` 任意前几个字符也可以。
|
||
|
|
||
|
这套缩写规则,就与替换命令 `:substitute` 简写为 `:s`,设置命令 `:set` 简写为
|
||
|
`:se` 一样一样的。但是,在写脚本时,强烈建议都写命令全称。命令简写只为在命令行
|
||
|
中快速输入,而在脚本中只要输入一次,一劳永逸,就应以可读性为重了。
|
||
|
|
||
|
当有了这个意识,VimL 的一些奇怪语法约定,也就显得容易理解多了。比如:
|
||
|
|
||
|
* ex 命令以回车结束,所以 VimL 语句也按行结束,不要在末尾加分号,加了反而是语
|
||
|
法错误,在 Vim 中每个符号都往往有奇葩意义。
|
||
|
* VimL 的续行符 `\` 写在下一行的开始,其他一些语言是把 `\` 写在上一行结束,表
|
||
|
示转义掉换行符,合为一行。但在 VimL 中,每一行都需要一个命令,你可以把 `\`
|
||
|
想象为一个特殊命令,意思是“合并到上一行”。
|
||
|
* 在 VimL 中,不推荐在一行写多个语句,要写也可以,把反斜杠 `\` 扶正为竖线 `|`
|
||
|
表示语句分隔吧。这在 vim 下临时手输单行命令时可能较为常见,减少额外按回车与
|
||
|
冒号。在很多键盘布局中,`|`(与`\`)恰好在回车键上面。
|
||
|
|
||
|
### 关键命令列表
|
||
|
|
||
|
Vim 的 `ex 命令`集是个很大的集合,比绝大多数的语言的关键字都多一个数量级。幸运
|
||
|
的是,我们写 VimL 语言的脚本,并不需要掌握或记住这所有的命令,只要记住一些主要
|
||
|
的关键命令就可以完成大部分需求了。
|
||
|
|
||
|
我从 VimL 语言的角度,按常用度与重要度并结合功能性将那些主要的命令分类如下,
|
||
|
仅供参考:
|
||
|
|
||
|
1. let call (unlet)
|
||
|
2. if for while function try (endif, endfor, end...)
|
||
|
3. break continue return finish
|
||
|
4. echo echomsg echoerr
|
||
|
5. execute normal source
|
||
|
6. set map command
|
||
|
7. augroup autocmd
|
||
|
8. wincmd tabnext
|
||
|
9. 其他着重于编辑功能的命令
|
||
|
|
||
|
其中,第 5 类恰好是个分界线,之上的是形成 VimL 语言的关键命令,之下是作为 Vim
|
||
|
编辑器的重要命令。没有后面的编辑器命令,纯 VimL 语言也可以写脚本,作为一种(没
|
||
|
有什么优势的)通用脚本而已;只有利用后面的编辑器命令,才可以调控 Vim。
|
||
|
|
||
|
后面的编辑器命令也可以单独使用,所以 Vim 高手也未必一定需要会 VimL。不过有了
|
||
|
VimL 语言命令的助力,那些编辑器命令可变得更高效与灵活。不过最后一大类纯编辑命
|
||
|
令,可能较少出现在 VimL 语言脚本中。因为按 Vim 可视化编辑的理念,是需要使用者
|
||
|
对这些编辑结果作出及时反馈的。同时,很多编辑命令也有相应的函数,在 VimL 中调用
|
||
|
函数,可能更显得像脚本语言。所以,VimL 中不仅有“库函数”的概念,还有“库命令”呢
|
||
|
。
|
||
|
|
||
|
总之,语句与命令,是联结 VimL 与 Vim 的重要纽带。这是 VimL 语言的重要特点,也
|
||
|
是初学者的一大疑难点。尤其是对有其他语言编程经验的,可能还需要一定的思维转换过
|
||
|
程吧。
|