【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](#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](#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](#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 ↩︎

相关推荐
m0_疾风9 小时前
VSCODE
ide·vscode·编辑器
小妖66611 小时前
怎么开机自动启动vscode项目
ide·vscode·编辑器
藥瓿亭14 小时前
Vim常用快捷键
linux·ubuntu·centos·vim
qq. 280403398416 小时前
vim 的基本使用
linux·编辑器·vim
爱吃巧克力的程序媛16 小时前
Vim 中设置插入模式下输入中文
linux·编辑器·vim
今天又在摸鱼17 小时前
vscode实用配置
ide·vscode·编辑器
摆渡搜不到你21 小时前
Vim 支持多种编程语言编辑器
vim
深色風信子21 小时前
Eclipse 插件开发 5.2 编辑器 获取当前编辑器
java·eclipse·编辑器·eclipse 编辑器获取·eclipse 插件
jie188945758661 天前
ubuntu中,文本编辑器nano和vim区别,vim的用法
ubuntu·vim·excel
hbwhmama1 天前
WIN11使用vscode搭建c语言开发环境
ide·vscode·编辑器