2012 年 10 月 20 日 by name5566 Categories: Computer Science, Tools

参考文献列表:
http://vimdoc.sourceforge.net/htmldoc/usr_41.html

本文面向一些有编程经验的人(特别是有 C/C++、Java 等编程经验的人),因此对一些显而易见的知识点未做详细的阐述。

Introduction

我们在 Vim 脚本文件中编写 Vim 脚本。常见的 vimrc、Syntax 等文件都是 Vim 脚本文件。我们来看一个简单的例子。首先打开 Vim 创建一个新文件 test.vim 并输入:

  1. :let i = 1
  2. :while i < 5
  3. : echo "count is" i
  4. : let i += 1
  5. :endwhile

保存后敲击命令(这里假定 test.vim 在 Vim 当前工作目录下)

  1. :source test.vim

我们可以看到输出如下:

  1. count is 1
  2. count is 2
  3. count is 3
  4. count is 4

从上面的例子我们可以看到,Vim 脚本实际上是由冒号命令组成的。其实,当我们在脚本文件中编写脚本时,冒号是可以省略不写的。回到上面的例子中:

  1. let 命令用于变量的赋值,通常的形式为:

    1. let {variable} = {expression}
  2. while 命令和 endwhile 命令总是配合在一起使用,通常的形式为:
    1. while {condition}
    2. {statements}
    3. endwhile
  3. echo 命令用于打印其参数,此例子中 echo 命令有两个参数,字符串 "count is" 和变量 i
变量(Variables)

使用 let 命令可以打印出当前定义的变量。首先来了解一下变量的作用域:

  1. 定义一个全局变量 var 其可以在任何地方使用:

    1. let var = 1
    2. # 或者这样写
    3. let g:var = 1
  2. 定义一个局部变量 var 其只能在某个脚本文件中使用:
    1. let s:var = 1
  3. 定义一个变量 var 其只能在某个 buffer 中使用:
    1. let b:var = 1
  4. 定义一个变量 var 其只能在某个 window 中使用:
    1. let w:var = 1

另外,还有一类变量 v:name 其为 Vim 预先定义的变量。如果需要删除一个变量,使用 unlet 命令,例如删除 s:var 变量:

  1. unlet s:var

如果你不确定某个变量是否存在,不希望在使用 unlet 命令的时候出现一个错误消息,那么使用强制命令修饰符:

  1. unlet! s:var

另外需要注意的是(和某些脚本语言不同)变量的生命周期,局部变量不会因为脚本文件执行完成而销毁:

  1. if !exists("s:call_count")
  2. let s:call_count = 0
  3. endif
  4. let s:call_count = s:call_count + 1
  5. echo "called" s:call_count "times"

我们执行这个脚本多次,输入结果为:

  1. called 1 times
  2. called 2 times
  3. ...

这里稍加说明的是 if !exists("s:call_count"),首先 ! 这里的含义是取反,而不是强制命令修饰符(注意 ! 符号的位置),另外 exists 不是一个命令,而是一个函数,函数使用括号包裹住参数列表而命令不需要括号。

在 Vim 中,0 为 false,非 0 为 true,Vim 很多时候会自动转换一个字符串为一个数值以确定其为 true 还是 false:

  1. if "true"

这里,"true" 会被转化为数值 0。

我们还有一些有用的东西:

  1. $NAME 为环境变量 NAME 的值
  2. &name 为选项 name 的值
  3. @r 为寄存器 r 的值

Vim 中字符串可以使用 " 或者 ' 字符包裹,只有 " 字符包裹的字符串支持转义字符,例如:

  1. echo '\n'

输出 \n(而非一个空行)。

语句(Statements)

Vim 条件控制(和其他语言类似):

  1. if {condition}
  2. {statements}
  3. elseif {condition}
  4. {statements}
  5. else
  6. {statements}
  7. endif

其中 elseif 和 else 是可选的。Vim 还支持条件控制表达式 a ? b : c(同于 C/C++ 等语言)

Vim 逻辑和算术操作符:

  1. a == b 等于
  2. a != b 不等于
  3. a > b 大于
  4. a >= b 大于等于
  5. a < b 小于
  6. a <= 小于等于
  7. a + b 加
  8. a - b 减
  9. a * b 乘
  10. a / b 除
  11. a % b 模

对于字符串还有两个运算符用于进行字符串匹配:

  1. a =~ b 字符串匹配
  2. a !~ b 字符串不匹配

这里的 a 为字符串,b 为模式,例如:

  1. if str =~ " "
  2. echo "str contains a space"
  3. endif
  4. if str !~ '\.$'
  5. echo "str does not end in a full stop"
  6. endif

字符串比较和匹配时会受到 ignorecase 选项的影响,你可以避免此选项的影响:

  1. 在操作符后添加 # 表示不忽略大小写
  2. 在操作符后添加 ? 表示忽略大小写
  1. if 'hello' ==? 'Hello'
  2. echo 'Here 1'
  3. endif
  4. if 'hello' ==# 'Hello'
  5. echo 'Here 2'
  6. endif

以上脚本输出 Here 1

另外,还有一个特别要注意的地方是:

  1. # 输出 matched
  2. if "a" =~ '\<'
  3. echo 'matched'
  4. else
  5. echo 'unmatched'
  6. endif
  7.  
  8. # 输出 unmatched
  9. if "a" =~ "\<"
  10. echo 'matched'
  11. else
  12. echo 'unmatched'
  13. endif

这里可以看到我们使用了不进行字符串转义的 ' 包裹字符串时得到了正确的结果,这是我们在使用正则表达式时特别需要注意的地方。

在使用 while 命令的时候,还可以配合 continue 和 break 命令使用,例如:

  1. while counter < 40
  2. call do_something()
  3. if skip_flag
  4. continue
  5. endif
  6. if finished_flag
  7. break
  8. endif
  9. sleep 50m
  10. endwhile

这里的 continue 命令用于跳转到循环的开始,break 命令用于结束循环。

execute 命令可以用于执行一个表达式,例如:

  1. let s:normal_command = 'gg=G'
  2. execute 'normal ' . s:normal_command

这里的 normal 命令表示执行一个普通模式下的命令,注意到上面的 . 字符,其用于连接字符串。

eval 函数可以用于获取一个表达式的值,例如:

  1. let optname = "path"
  2. let optval = eval('&' . optname)
函数(Functions)

Vim 定义了很多函数,文档在这里:

http://vimdoc.sourceforge.net/htmldoc/usr_41.html#function-list

http://vimdoc.sourceforge.net/htmldoc/eval.html#functions

直接调用函数使用 call 命令:

  1. call search("Date: ", "W")

注意,区别函数和命令:

  1. search("Date: ", "W")

这样使用会出错,因为 search 是一个函数,而非命令。

我们可以自己定义函数:

  1. function {name}({var1}, {var2}, ...)
  2. {body}
  3. endfunction

注意,我们定义的函数的函数名必须为大写字母开头。我们来定义一个函数 Min 来获取两个数中较小的一个(此函数主要是为了解释语法,编写的并不优雅):

  1. function! s:Min(num1, num2)
  2. if a:num1 < a:num2
  3. let smaller = a:num1
  4. else
  5. let smaller = a:num2
  6. endif
  7. return smaller
  8. endfunction

首先需要意识到的是,函数也是变量,因此这里 s:Min 的作用域为定义函数的文件。function 命令后加上强制命令修饰符表示如果函数存在则替换,这样做很有必要,假定此函数位于某个脚本文件中,如果没有加上强制命令修饰符,脚本文件被载入两次时就会报错:函数已存在。

其次,我们看到在函数内使用 num1 时加上了 a: 前缀,这可以告诉 Vim 此为函数参数。之后我们使用了 let 命令定义了 smaller 变量,此变量是一个局部变量,不能在函数外访问。

最后,我们使用了 return 命令返回 smaller。如果函数未使用 return 命令或者 return 命令没有参数,那么函数返回 0。

删除一个函数使用命令 delfunction。直接使用 function 命令可以打印用户定义的函数,查看函数的定义使用:

  1. function Funcname
数据结构 List

一个 list 包含一组有序的元素,每个元素可以为任意类型,元素可以通过索引进行访问,第一个元素的索引为 0。创建一个 list,list 使用两个中括号包裹:

  1. " 创建一个空的 list
  2. let list1 = []
  3. " 创建一个 list,其中含有两个类型不同的元素
  4. let list2 = ['a', 2]

list 相关的操作:

  1. 元素的访问

    1. let list[0] = 1
    2. echo list[0]
  2. 增加新的元素
    1. " 添加新的值到 list 的尾部
    2. call add(list, val)
    3. " 添加新的值到 list 的头部
    4. call insert(list, val)
  3. 删除元素
    1. " 删除索引为 index 的元素并返回此元素
    2. call remove(list, index)
    3. " 删除索引为 startIndex 到 endIndex(含 endIndex)的元素
    4. " 返回一个 list 包含了这些被删除的元素
    5. call remove(list, startIndex, endIndex)
    6. " 清空 list,这里索引 -1 对应 list 中最后一个元素
    7. call remove(list, 0, -1)
  4. 判断 list 是否为空
    1. if empty(list)
    2. " ...
    3. endif
  5. 获取 list 的大小
    1. echo len(list)
  6. 拷贝 list
    1. " 浅拷贝 list
    2. let copyList = copy(list)
    3. " 深拷贝 list
    4. let deepCopyList = deepcopy(list)
    5. call deepcopy()
  7. list 的遍历可以使用 for 命令
    1. let list = ['one', 'two', 'three']
    2. for element in list
    3. echo element
    4. endfor

for 命令的用法如下:

  1. for {varname} in {listexpression}
  2. {commands}
  3. endfor
数据结构字典(Dictionaries)

Dictionary 是一个关联数组。每个元素都有一个 key 和一个 value,我们可以通过 key 获取到 value。创建一个 dictionary,dictionary 使用两个大括号包裹:

  1. " 创建一个空的 dictionary
  2. let dict = {}
  3. " 创建一个非空的 dictionary
  4. let dict = {'one': 1, 'two': 2, 'three': 3 }

dictionary 的基本形式为:

  1. {<key> : <value>, ...}

dictionary 的相关操作:

  1. 元素的访问和修改

    1. let dict = {'one': 1, 'two': 2}
    2. " 通过 key 访问
    3. echo dict['one']
    4. " 当 key 为 ASCII 字符串时还可以这样访问
    5. echo dict.one
    6. " 修改元素的 value
    7. let dict['one'] = '1'
  2. 元素的增加和删除
    1. " 增加一个元素
    2. let dict[key] = value
    3. " 删除一个元素
    4. unlet dict[key]
  3. 获取 dictionary 大小
    1. echo len(dict)
  4. 使用 for 遍历一个 dictionary
    1. let dict = {'one': 1, 'two': 2}
    2. for key in keys(dict)
    3. echo key
    4. endfor
    5. " 遍历时 key 是未排序的,如果希望按照一定顺序访问可以这么做:
    6. for key in sort(keys(dict))
    7. " ...
    8. endfor
    9.  
    10. " keys 函数用于返回一个 list,包含 dictionary 的所有 key
    11. " values 函数用于返回一个 list,包含 dictionary 的所有 value
    12. " items 函数用于返回一个 list,包含 dictionary 的 key-value 对
    13. for value in values(dict)
    14. echo value
    15. endfor
    16. for item in items(dict)
    17. echo item
    18. endfor
编写插件(plugins)

Vim 官方网站上存在大量的插件。现在来了解一下 Vim 插件。Vim 插件有两种类型:

  1. 全局插件:用于所有类型的文件
  2. filetype 插件:用于特定类型的文件(详细参考:http://vimdoc.sourceforge.net/htmldoc/usr_41.html#write-filetype-plugin

为了让你编写的插件能够总是正常工作,那么有一些规则需要遵循。

通常来 line-continuation 可以很好的工作:

  1. let str = 'Hello'
  2. \ . 'World'

但是如果设置了 compatible 则会出现错误。我们使用以下方式避免错误的发生:

  1. " 保存 cpoptions
  2. let s:save_cpo = &cpo
  3. " 设置 cpoptions 为 Vim 默认值
  4. set cpo&vim
  5.  
  6. "
  7. " plugin content
  8. "
  9.  
  10. " 还原 cpoptions
  11. let &cpo = s:save_cpo

插件的载入。有时候用户并不希望去载入某个插件,这时候我们需要给用户一种避免插件载入的机制:

  1. if exists("g:loaded_pluginname")
  2. " finish 用于避免 Vim 读取此文件剩余的部分
  3. finish
  4. endif
  5. let g:loaded_pluginname = 1
  6.  
  7. "
  8. " plugin content
  9. "

这样,当我们在 vimrc 中设置了变量 g:loaded_pluginname 的值为 1 就避免了插件的载入。除了让用户可以控制是否能够载入插件之外,这样做还可以避免插件被重复的载入。另外,我们注意到 g:loaded_pluginname 的命名,这是建议的命名方式。

定义用户命令。编写插件时常常需要定义用户命令:

  1. " MyCommand 为命令名称
  2. " -nargs 用于指定命令参数的个数
  3. " s:funcName 为命令执行时执行的函数
  4. if !exists(':MyCommand')
  5. command -nargs=1 MyCommand :call s:funcName(<q-args>)
  6. endif

(转)Vim 脚本语言的更多相关文章

  1. vim脚本语言

    转自:http://man.chinaunix.net/newsoft/vi/doc/usr_41.html#usr_41.txt Vim 脚本语言在很多地方用到,包括 vimrc 文件, 语法文件, ...

  2. Vim技能修炼教程(12) - Vim的脚本语言支持

    Vim的脚本语言支持 本节开始,我们正式接触vimscript这门古老的脚本语言. 首先要说明,vim支持的扩展语言很多,比如python, python3, ruby, lua,tcl等常见脚本语言 ...

  3. Fedora 19 vim c语言开发环境

    1. Fedora 19 居然没有自带 gcc 和 g++: sudo yum -y install gcc gcc-c++ 2. 安装 vim 和 cvim 插件: sudo yum -y vim ...

  4. groovy脚本语言基础1

    一 搭建groovy环境 安装JDK [root@node1 ~]# yum  -y install java-11-openjdk 官网下载groovySDK下载 https://groovy.ap ...

  5. InstallShield 脚本语言学习笔记

    InstallShield脚本语言是类似C语言,利用InstallShield的向导或模板都可以生成基本的脚本程序框架,可以在此基础上按自己的意愿进行修改和添加.     一.基本语法规则      ...

  6. JS脚本语言是什么意思?

    javascript,Javascript是一种浏览器端的脚本语言,用来在网页客户端处理与用户的交互,以及实现页面特效.比如提交表单前先验证数据合法性,减少服务器错误和压力.根据客户操作,给出一些提升 ...

  7. 使用Lua脚本语言开发出高扩展性的系统,AgileEAS.NET SOA中间件Lua脚本引擎介绍

    一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...

  8. .NET 动态脚本语言Script.NET 入门指南 Quick Start

    Script.NET是一种动态的脚本语言,它使得程序可扩展,可定制,和维护性好.和Office系列的VB Script相似,可以在应用中嵌入大量的代码块,以便在运行时才执行这些代码. Script.N ...

  9. 使用expect脚本语言写一键发布服务(代码发布、所有服务重启)

    互联网服务有很多台服务,但是在上线的时候需要将这些服务版本都更新与个个都重启,下面的脚本语言,就是一键发布服务~ 1.在/home/weihu/deploy/ 目录下建下publish .publis ...

随机推荐

  1. Asp.net core中Migration工具使用的交流分享

    a,ul>li,em{ color:deeppink !important; } h2>a{ text-decoration:none; } ul>li{ padding:3px; ...

  2. 调度器&负载均衡调度算法整理

    一.Linux 调度器   Linux中进程调度器已经经过很多次改进了,目前核心调度器是在CFS(Completely Fair Scheduler),从2.6.23开始被作为默认调度器.用作者Ing ...

  3. LeetCode 2——两数相加

    1. 题目 2. 解答 循环遍历两个链表 若两个链表都非空,将两个链表结点的值和进位相加求出和以及新的进位 若其中一个链表为空,则将另一个链表结点的值和进位相加求出和以及新的进位 然后将每一位的和添加 ...

  4. sql between and 边界问题

    1.不同的数据库对 BETWEEN...AND 操作符的处理方式是有差异的.需要自己测试 2.一般情况下.SQL Server中 between and是包括边界值的,not between不包括边界 ...

  5. 剑指offer:斐波那契数列

    目录 题目 解题思路 具体代码 题目 题目链接 剑指offer:斐波那契数列 题目描述 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0). n< ...

  6. Java集合(2)——深入理解ArrayList、Vector和LinkedList

    回顾 Java集合主要分为两个体系结构,Collection和Map.这篇博客主要介绍Collection子接口List下的三个经常使用的实现类:ArrayList.Vector和LinkedList ...

  7. Redis的高级应用——数据安全

    Redis的数据保存在内存中,速度十分快.这也就意味着,一个恶意破解redis数据库密码的用户,可以在一秒钟进行更多的尝试.如果用户密码级别较低或更换频率过长,就会造成致命的危害. 1.设置用户 Re ...

  8. 批处理之FOR命令

  9. zTree删除节点

    zTree 是一个依靠 jQuery 实现的多功能 “树插件”.优异的性能.灵活的配置.多种功能的组合是 zTree 最大优点. zTree删除节点. <!DOCTYPE html> &l ...

  10. hihocoder 后缀自动机专题

    一.后缀自动机基本概念的理解 1.首先后缀自动机的状态是由子串的endpos来决定的 子串的endpos是指一个子串可以在原字符串的哪些位置进行匹配, endpos构成的不同集合划分成不同的状态 关于 ...