【Mastering Vim 2_09】第八章:玩转 Vimscript(上)——相关背景知识与基础语法

【最新版《Mastering Vim》封面,涵盖 Vim 9.0 版特性】

文章目录

  • [第八章 让您的 Vim 技艺脱胎换骨------Vimscript 初探](#第八章 让您的 Vim 技艺脱胎换骨——Vimscript 初探)
    • [1 为何选择 Vimscript](#1 为何选择 Vimscript)
    • [2 Vimscript 的运行方式](#2 Vimscript 的运行方式)
      • [2.1 实测首个 Vim 脚本的执行](#2.1 实测首个 Vim 脚本的执行)
    • [3 脚本在命令模式下的自动续行](#3 脚本在命令模式下的自动续行)
    • [4 关于新版 Vimscript 9](#4 关于新版 Vimscript 9)
    • [5 语法速览](#5 语法速览)
      • [5.1 变量赋值](#5.1 变量赋值)
      • [5.2 关于变量作用域](#5.2 关于变量作用域)
      • [5.3 Vim 配置项的赋值](#5.3 Vim 配置项的赋值)
      • [5.4 Vim 寄存器的赋值](#5.4 Vim 寄存器的赋值)
      • [5.5 字符串的连接](#5.5 字符串的连接)
      • [5.6 关于 Vim 脚本中的引号和注释](#5.6 关于 Vim 脚本中的引号和注释)
      • [5.7 echo、echom 与 messages](#5.7 echo、echom 与 messages)
      • [5.8 条件语句](#5.8 条件语句)

写在前面

最近有事耽搁了,今天有空赶紧补上。本章为 Vim 技巧的升华篇,也是新版大幅更新的一个核心章节。作者将新旧两版的 Vimscript 脚本进行了全面对比(详见随书源码),并摘录核心知识点进行介绍。学习时最好有编程基础或者 Vimscript 插件开发基础,不建议零基础上手。由于篇幅较长,拟分为上、中、下三篇进行介绍。本篇为上篇,介绍 Vimscript 的一些基本情况和基础语法。

第八章 让您的 Vim 技艺脱胎换骨------Vimscript 初探

本章概要

  • Vimscript 的基本语法
  • Vimscript 编程风格指南
  • 从零打造一个 Vim 插件的全过程演示

本章源码:https://github.com/PacktPublishing/Mastering-Vim-Second-Edition/tree/main/Chapter08

这一章可谓全书的精华,不失为 Vim9script 脚本的绝佳入门材料。作者在前一版的基础上,对旧版 Vim 脚本 ^1^ 进行了全面修订,前面章节中埋下的很多伏笔在本章都能找到答案。为了近距离感受 Vimscript 的强大,作者在本章最后还从零开始打造了一款实用的 Vim 插件,让看似零散的脚本语法在实战应用中得到整合,旨在进一步让读者们的 Vim 水平有一个质的飞跃。

本章虽知识点密集,但也仅仅是 Vim 脚本的冰山一角;想要真正掌握它还需要后期大量深入研读相关文档,并配合相当程度的实战项目训练才行。


1 为何选择 Vimscript

Vimscript 实际上是一种图灵完备的脚本语言,这意味着它具备 解决任何可计算问题 的能力。虽然其主要用途是扩展 Vim 编辑器,但这一语言特性使其在理论上能够实现一系列复杂的逻辑和算法。

2 Vimscript 的运行方式

Vimscript 由一系列 Vim 命令构成,既可以在命令模式下执行每条 Vim 命令,也可以用 :source 命令来运行包含 Vimscript 的某个文件(通常以 .vim 作扩展名):

bash 复制代码
:source <vimscript_filename>
# 或者
:so %

这里的 % 表示 当前打开的文件 。实战过程中,如果要立即生效当前 .vimrc 文件的配置内容,还可以写作:

bash 复制代码
:w | so %

注意:务必先保存再运行,否则新内容无法生效。

最佳实践

:so % 运行较长的 Vim 脚本;用 Vim 命令模式来调试脚本。

2.1 实测首个 Vim 脚本的执行

新建并打开一个示例文件 01_variables.vim,输入以下内容(先按旧版 Vimscript 的写法演示):

bash 复制代码
let g:ingredient = 'egg'

echo 'Scene: A cafe. A man and his wife enter.'
echo 'Man: Well, what''ve  you got?'
echo g:ingredient
echo '- said the waitress'

运行 :w | so % + Enter

【图 8.1 实测 :w | so % 命令的执行结果】

3 脚本在命令模式下的自动续行

在命令模式下,如果输入函数定义或流程控制运算符(如 ifwhilefor 等),输入回车后 Vim 会继续留在命令模式下,实现 "自动续行":^2^

【图 8.2 实测 if 语句在 Vim 命令模式下的 "自动续行" 效果】

上述命令还可以用管道符 | 串联成一行:if has('win32') | echo 'this is windows' | else | echo 'this is probably linux' | endif

4 关于新版 Vimscript 9

Vim9 引入了 Vimscript 9,也叫 Vim9script,其核心变更包括------

  • 性能大幅提升:新版执行速度将带来 10 到 100 倍的提升;
  • 注释使用 #,取代此前的 "
  • 变量声明使用 var
  • def 定义函数;
  • 可以显式声明 truefalse 逻辑值;
  • 支持用空格提升可读性;
  • 新语法特性需要手动开启:要么在脚本文件首行添加 vim9script,要么在某个命令前加 vim9cmd

作者建议,Vim9script 最好作为 Vimscript 的有益补充;但如果对性能有要求,则推荐使用 Vim9script,甚至可以用它重写 .vimrc 文件(唯一不足的是无法向下兼容)。

5 语法速览

经典教材推荐:《Learn Vimscript the Hard Way》(作者:Steve Losh)^3^。

5.1 变量赋值

bash 复制代码
# 旧版
let dish = 'spam omelet'
# 新版(还可以用 const、final)
var dish = 'spam omelet'

声明布尔型变量:

bash 复制代码
# 旧版(用 1 或 0 表示布尔值)
let has_spam = 1
has_spam = 0
# 新版(支持显式声明)
var has_spam = true
has_spam = false

5.2 关于变量作用域

通过添加前缀来设置变量的作用域,例如:

bash 复制代码
let g:dish = 'spam omelet'
let w:has_spam = 1

常见的变量作用域如下(旧版):

  • g:表示 global,即全局作用域(默认作用域,但函数内声明除外);
  • v:表示 vim-variables,由 Vim 定义的全局作用域;
  • l:表示 local scope,局部作用域(也是函数内声明的默认作用域);
  • b:表示 buffer,即当前缓冲区;
  • wcurrent window,即当前窗口作用域;
  • tcurrent tab,即当前标签作用域;
  • s:表示 script,即脚本级作用域,其变量只在被 :source 命令调用的脚本文件内可见;
  • a:表示 function argument,即函数参数作用域。

新版调整:

  • 默认作用域改为 s 级作用域;
  • 不再使用 a: 前缀来声明函数参数作用域变量;函数参数作用域改为局部作用域(l:)的一部分。

5.3 Vim 配置项的赋值

例如声明 ignorecase 选项的值:

bash 复制代码
# 旧版
let &ignorecase = 0
# 新版
&ignorecase = 0

5.4 Vim 寄存器的赋值

例如修改寄存器 a" 的值:

bash 复制代码
# 旧版
let @a = 'spam spam spam'
# 新版
@a = 'spam spam spam'

5.5 字符串的连接

旧版使用 . 操作符,新版改为 ..

bash 复制代码
# 旧版
let g:dish = 'spam omelet'
let g:statement = 'Well, we''ve got ' . g:dish

# 新版
g:dish = 'spam omelet'
var statement = 'Well, we''ve got ' .. g:dish

5.6 关于 Vim 脚本中的引号和注释

注意:示例中的单引号是通过重复录入单引号 ' 实现的。虽然外围也可以改用双引号,写作 "Well, we've got ",但由于旧版 Vimscript 的注释也是用双引号 " 标识的,因此容易产生混淆,不建议这样更改;正因如此,某些 Vim 命令的同一行后不能跟一个注释语句,因为会被误判为没写完的字符串(例如 echo 命令):

bash 复制代码
# 旧版
let g:dish = 123
echo g:dish "comment content

运行结果:

【图 8.3 实测旧版 Vimscript 中的 echo 命令与注释语句在同一行时报错】

而在新版 Vim9script 中,注释语句改用 # 标识,上述测试脚本可以写为:

bash 复制代码
# 新版
vim9script
g:dish = 123
echo g:dish #comment content

运行结果:

【图 8.4 实测新版 Vim9script 中的 echo 命令与新版注释语句在同一行时运行不报错】

显然我本地的 PaperColor 主题还不能正确解析这种情况,因此还是尽量不要这样写。

5.7 echo、echom 与 messages

echo 命令会将内容显示到状态栏,但该结果不会被记录,一旦删除将无法查看。

echomechomsg 命令会将输出内容同步记录到当前会话的信息日志,并可通过 :messages + Enter 查看:

【图 8.5 实测 echo、echom 与 messages 命令的执行结果】

更多用法,详见 :h message-history

5.8 条件语句

bash 复制代码
# 旧版
# if 的写法
let ingredient = 'egg'

if ingredient == 'egg' 
  echo 'spam omelet' 
elseif ingredient == 'lobster' 
  echo 'spam lobster thermidor' 
else 
  echo dish . ' and spam' 
endif 

# 三目运算符
echo 'spam ' . (ingredient == 'egg' ? 'omelet' : dish)

# 逻辑运算符 &&、||、!
let is_egg = 0
let is_lobster = 0
if (!is_egg && !is_lobster)
  echo ingredient . ' and spam'
endif

上述示例脚本对应的 vim9script 新版等效写法如下:

bash 复制代码
vim9script

const ingredient = 'egg'

if ingredient == 'egg' 
  echo 'spam omelet' 
elseif ingredient == 'lobster' 
  echo 'spam lobster thermidor' 
else 
  echo dish .. ' and spam' 
endif 

echo 'spam ' .. (ingredient == 'egg' ? 'omelet' : dish)

const is_egg = 0
const is_lobster = 0
if (!is_egg && !is_lobster)
  echo ingredient .. ' and spam'
endif

另外,专用于文本内容比较还有几个具体的写法(也是 Vim 脚本的推荐写法):

比较类型 写法 示例
相等匹配(大小写随系统设置) == 'egg' == 'EGG'(假)
明确忽略大小写的 相等匹配 ==? 'egg' ==? 'EGG'(真)
明确考虑大小写的 相等匹配 ==# 'egg' ==# 'EGG'(假)
检查与右侧模式是否 匹配(大小写随系统设置) =~ 'egg' =~ 'e.\+'(真)
检查与右侧模式是否 匹配(忽略大小写) =~? 'egg' =~? 'E.\+'(真)
检查与右侧模式是否 匹配(考虑大小写) =~# 'egg' =~# 'E.\+'(假)
检查与右侧模式是否 不匹配(大小写随系统设置) !~ 'egg' !~ '.gg'(假)
检查与右侧模式是否 不匹配(忽略大小写) !~? 'egg' !~? 'E.\+'(假)
检查与右侧模式是否 不匹配(考虑大小写) !~# 'egg' !~# 'E.\+'(真)

  1. VimscriptVim8.x 及以前版本的专属 Vim 脚本语言;Vim9scriptVim 之父 Bram Moolenaar 于 2022 年 6 月正式发布。自 2023 年 8 月 Bram Moolenaar 猝然离世(享年 62 岁)后,开源社区的 Vim 核心成员与爱好者们又纷纷组织起来,于 2024 年 1 月推出了改良版的 Vim 9.1 版,并对此前的 Vim9script 存在的诸多问题进行了全面修复,以此纪念这位 Vim 编辑器的缔造者、维护者以及终身领导者。 ↩︎

  2. 先别管 has('win32') 以及上面的 varlet 的含义,因为后面会具体介绍;这里先建立执行 Vim 脚本的直观感受 ↩︎

  3. 完整 PDF 版本我已免费上传到网盘:https://pan.baidu.com/s/1kUzFlLSBBLx5rZVO_TFTZw?pwd=7dnv,提取码:7dnv ↩︎

相关推荐
1024小神3 分钟前
Codemirror编辑器中引入其他语言支持的方式
编辑器
Veropatrinica3 小时前
VScode代码格式化插件black失效问题
ide·vscode·编辑器
-兰天白云-3 小时前
Visual Studio工具
编辑器
不被定义的~wolf7 小时前
vim基本操作及常用命令
linux·编辑器·vim
vortex58 小时前
问题解决:Kali Linux 中配置启用 Vim 复制粘贴功能
linux·运维·网络安全·vim
fxrz1211 小时前
使用 marked.min.js 实现 Markdown 编辑器 —— 我的博客后台选择之旅
javascript·arcgis·编辑器
zeandon21 小时前
Vim复制内容到系统剪切板
vim
去往火星1 天前
解决VSCode鼠标光标指针消失
ide·vscode·编辑器
zeandon1 天前
更新Vim使其支持系统剪切板
vim