分类: LINUX
2010-04-13 21:42:37
Vimscript 是一种用于改造和扩展 Vim 编辑器的机制。使用脚本可以创建新工具、简化常见任务,甚至重新设计并取代已有的编辑特性。本文是本系列文章的第一篇,介绍了 Vimscript 编程语言的基本元素:值、变量、表达式、语句、函数和命令。将通过一系列简单的示例演示和解释这些特性。
:help 获得帮助。请查看 参考资料 小节中的链接。vim --version:version。如果使用的是旧版本的 Vim,强烈建议您升级到最新版本,因为早期版本并不支持我们将要讨论的大部分 Vimscript 特性。参考资料 小节提供了下载和升级 Vim 的链接。:help vim-script-intro:source::source /full/path/to/the/scriptfile.vim:call MyBackupFunc(expand('%'), { 'all':1, 'save':'recent'}):nmap ;s :source /full/path/to/the/scriptfile.vim
:nmap \b :call MyBackupFunc(expand('%'), { 'all': 1 });s 将执行指定的脚本文件,而 \b 序列将调用 MyBackupFunc() 函数(该函数大概也是在 .vimrc 文件的某个位置中定义的)。:syntax enable 命令启用,并使用 :syntax off 命令关闭。
function! ToggleSyntax()
if exists("g:syntax_on")
syntax off
else
syntax enable
endif
endfunction
nmap |
;s 序列将开启或关闭语法突出显示功能。让我们看看这段脚本的每个部分。ToggleSyntax() 的函数,它没有包含任何参数。这个用户定义函数首先调用名为 exists() 的 Vim 函数,为它传递一个字符串。exists() 函数确定是否定义了一个具有由字符串定义的名称的变量(在本例中为全局变量 g:syntax_on)。if 语句将执行一个 syntax off 命令;否则它将执行 syntax enable 命令。由于 syntax enable 定义了 g:syntax_on 变量,而 syntax off 解除了变量定义,反复调用 ToggleSyntax() 函数将不停地启用和禁用语法突出显示。;s)来调用 ToggleSyntax() 函数:nmap ;s :call ToggleSyntax() nmap 表示 “normal-mode key mapping”。nmap 之后的 选项使得映射不能回传它所执行的任何命令,确保新的 ;s 命令默默地完成它的工作。它的职责就是执行下面的命令::call ToggleSyntax() 序列是字符 <、C、R 和 > 的字母序列。Vimscript 将它与回车符等同起来。事实上,Vimscript 理解非打印字符的许多其他类似的表示。例如,可以创建一个键盘映射,使空格键起到下翻键的功能(和在大多数 Web 浏览器中一样),如下所示::nmap :help keycodes,可以看到这些特殊符号的完整列表。ToggleSyntax() 能够直接调用内置 syntax 命令。这是因为 Vim 中的每个内置冒号命令都自动成为 Vimscript 中的一个语句。例如,要为使用 Vim 编写的文档更方便地创建中心标题,可以创建一个函数,将当前行中的每个字母大写,将整个行集中在一起,然后跳到下一行,如下所示:function! CapitalizeCenterAndMoveDown() s/\<./\u&/g "Built-in substitution capitalizes each word center "Built-in center command centers entire line +1 "Built-in relative motion (+1 line down) endfunction nmap |
call SetName( \ first_name, \ middle_initial, \ family_name \ ) |
echo "Starting..." | call Phase(1) | call Phase(2) | echo "Done"
if exists("g:syntax_on")
syntax off "Not 'syntax clear' (which does something else)
else
syntax enable "Not 'syntax on' (which overrides colorscheme)
endif
|
echo "> " "Print generic promptecho "> " |"Print generic promptlet:
let name = "Damian"
let height = 165
let interests = [ 'Cinema', 'Literature', 'World Domination', 101 ]
let phone = { 'cell':5551017346, 'home':5558038728, 'work':'?' }
|
"\n"(用于换行符)、"\t"(用于制表符)、"\u263A"(用于 Unicode 笑脸标志)或者 "\" (用于转义字符)。相比之下,使用单引号分隔的字符串将包括在其中的所有内容视为文字字符 — 两个连续的单引号是一个例外,它们被单纯地当作单引号。"Damian" 或 165['Cinema', 'Literature', 'World Domination', 101]{'cell':5551017346, 'home':5558038728, 'work':'?'}name 和 height 变量现在都是标量(就是说,它们现在只能存储字符串或数字),interests 现在是一个 list 变量(就是说,它只能存储列表),而 phone 现在是一个 dictionary 变量(并且只能存储字典)。变量类型一旦分配后,就会保持不变并在运行时严格遵守:let interests = 'unknown' " Error: variable type mismatch| 前缀 | 含义 |
| g: varname | 变量为全局变量 |
| s: varname | 变量的范围为当前的脚本文件 |
| w: varname | 变量的范围为当前的编辑器窗口 |
| t: varname | 变量的范围为当前的编辑器选项卡 |
| b: varname | 变量的范围为当前的编辑器缓冲区 |
| l: varname | 变量的范围为当前的函数 |
| a: varname | 变量是当前函数的一个参数 |
| v: varname | 变量是 Vim 的预定义变量 |
| 前缀 | 含义 |
| & varname | 一个 Vim 选项(如果指定的话,则为本地选项,否则为全局选项) |
| &l: varname | 本地 Vim 选项 |
| &g: varname | 全局 Vim 选项 |
| @ varname | 一个 Vim 注册器 |
| $ varname | 一个环境变量 |
nmap ]] :let &tabstop += 1
nmap [[ :let &tabstop -= &tabstop > 1 ? 1 : 0 [[ 键映射使用了一个包含类似 C 的 “三元表达式” 的表达式:&tabstop > 1 ? 1 : 0| 运算 | 运算符语法 |
| 赋值 数值相加并赋值 数值相减并赋值 字符串连接并赋值 | let var = expr let var += expr let var -= expr let var .= expr |
| 三元运算符 | bool ? expr-if-true : expr-if-false |
| 逻辑 OR | bool || bool |
| 逻辑 AND | bool && bool |
| 数值或字符串相等 数值或字符串不相等 数值或字符串大于 数值或字符串大于等于 数值或字符串小于 数值或字符串小于等于 | expr == expr expr != expr expr > expr expr >= expr expr < expr expr <= expr |
| 数值相加 数值相减 字符串连接 | num + num num - num str . str |
| 数值相乘 数值相除 数值系数 | num * num num / num num % num |
| 转换为数值 求负数 逻辑 NOT | + num - num ! bool |
| 括号优先 | ( expr ) |
let result_string = GetResult(); if !result_string echo "No result" endif |
result_string 被分配了一个空字符串后,上面的代码就不能正常工作,如果 result_string 包含诸如 "I am NOT an empty string" 之类的字符串,它仍然表示 "No result",因为该字符串首先将被转换为一个数字(0),然后再转换为布尔值(假)。if empty(result_string) echo "No result" endif |
let ident = 'Vim'
if ident == 0 "Always true (string 'Vim' converted to number 0)if ident == '0' "Uses string equality if ident contains string
"but numeri
c equality if ident contains number
|
ignorecase 选项的本地设置,但是任何字符串比较函数都可以被显式地标记为大小写敏感(通过附加一个 #)或大小写不敏感(通过附加一个 ?):if name ==? 'Batman' |"Equality always case insensitive echo "I'm Batman" elseif name <# 'ee cummings' |"Less-than always case sensitive echo "the sky was can dy lu minous" endif |
"Step through each file... for filenum in range(filecount) " Show progress... echo (filenum / filecount * 100) . '% done' " Make progress... call process_file(filenum) endfor |
filenum 始终小于 filecount,整数除法 filenum/filecount 将始终生成 0,因此每次迭代循环都将生成:Now 0% donelet filecount = 234 echo filecount/100 |" echoes 2 echo filecount/100.0 |" echoes 2.34 |
;p),能够按照下面的方式在 Vim 中显式文本(比如前一个段落):It's easy to adapt the syntax-toggling script shown earlier to create other useful tools. For example, if there is a set of words that you frequently misspell or misapply, you could add a script to your.vimrc to activate Vim's match mechanism and highlight problematic words when you're proofreading text.
"Create a text highlighting style that always stands out...
highlight STANDOUT term=bold cterm=bold gui=bold
"List of troublesome words...
let s:words = [
\ "it's", "its",
\ "your", "you're",
\ "were", "we're", "where",
\ "their", "they're", "there",
\ "to", "too", "two"
\ ]
"Build a Vim command to match troublesome words...
let s:words_matcher
\ = 'match STANDOUT /\c\<\(' . join(s:words, '\|') . '\)\>/'
"Toggle word checking on or off...
function! WordCheck ()
"Toggle the flag (or set it if it doesn't yet exist)...
let w:check_words = exists('w:check_words') ? !w:check_words : 1
"Turn match mechanism on/off, according to new state of flag...
if w:check_words
exec s:words_matcher
else
match none
endif
endfunction
"Use ;p to toggle checking... |
w:check_words 被用作一个布尔标记,用来启用或关闭单词检查。WordCheck() 函数的第一行代码检查标记是否已经存在,如果存在的话,变量分配仅仅启用变量的布尔值:let w:check_words = exists('w:check_words') ? !w:check_words : 1w:check_words 不存在,那么将通过向它分配值 1 来创建:let w:check_words = exists('w:check_words') ? !w:check_words : 1w: 作为前缀,这表示标记变量对于当前窗口来说始终是本地变量。这将允许在每个编辑器窗口中独立启用单词检查(这与 match 命令的行为是一致的,后者的作用也只对当前窗口有效)。match 命令启用的。match 要求一个文本突出显示说明(本例中为 STANDOUT),紧接着是一个正则表达式,指定要突出显示哪段文本。在本例中,正则表达式的构建方式是对脚本 s:words list 变量中指定的所有单词执行 OR 运算(即 join(s:words, '\|'))。这组待选对象随后使用大小写不敏感的单词边界括起(\c\<\(...\)\>),确保只有完整的单词被匹配,而不需要关心大小写。WordCheck() 函数随后将结果字符串转换为一个 Vim 命令并执行它(exec s:words_matcher),从而启用匹配功能。当 w:check_words 被关闭后,函数将执行 match none 命令,解除特殊匹配。imap 或 iabbrev 命令设置键映射或缩写词,可以在插入文本时使用。例如:imap =strftime("%e %b %Y")
imap =strftime("%l:%M %p") strftime() 函数并插入生成的日期,同样,两次按下 CTRL-T 将插入当前时间。= (告诉 Vim 插入后面内容的计算结果)和最后一个 (告诉 Vim 实际地执行前面的表达式)之间。但是,要注意,(Vim 对 CTRL-R 使用的缩写)和 (Vim 对回车使用的缩写)是不同的。getcwd() 函数为当前的工作目录创建缩写,如下所示:iabbrev CWD =getcwd() imap =string(eval(input("Calculate: "))) string( eval( input("Calculate: ") ) )input() 函数来请求用户输入他们的计算,随后 input() 作为一个字符串返回。该输入字符串被传递到内置的 eval() 中,后者将它作为一个 Vimscript 表达式进行计算并返回结果。接下来,内置 string() 函数将数值结果转换回一个字符串,键映射的 = 序列将能够插入该字符串。Glib jocks quiz nymph to vex dwarf
Glib jocks quiz ny_Glib jocks quiz nymph to vex dwarf
Glib jocks quiz ny_
"Locate and return character "above" current cursor position...
function! LookUpwards()
"Locate current column and preceding line from which to copy...
let column_num = virtcol('.')
let target_pattern = '\%' . column_num . 'v.'
let target_line_num = search(target_pattern . '*\S', 'bnW')
"If target line found, return vertically copied character... |
LookUpwards() 函数首先判断插入点目前位于屏幕上的哪个列(即 “虚拟列”),使用内置的 virtcol() 函数可以做到这点。'.' 参数指定当前游标位置的列编号:let column_num = virtcol('.')LookUpwards() 随后使用内置 search() 函数从当前的游标位置倒退着浏览文件:let target_pattern = '\%' . column_num . 'v.'
let target_line_num = search(target_pattern . '*\S', 'bnW')\%column_numv.*\S)来查找最近的一个行,这个行在游标所处的这一列(\%column_numv)或后一列(.*)应当具有一个非空字符(\S)。search() 的第二个参数是配置字符串 bnW,它告诉函数向后搜索,而不是移动游标,也不是封装搜索。如果搜索成功,search() 将返回相应行的行号;如果搜索失败,它将返回 0。if 语句将判断哪个字符 — 如果有的话 — 将被复制到插入点。如果没有找到符合条件的行,target_line_num 将被分配一个 0 值,因此,第一个返回语句被执行并返回空字符串(表示 “没有插入任何内容”)。return matchstr(getline(target_line_num), target_pattern)search() 并成功匹配的只包含一个字符的字符串:return matchstr(getline(target_line_num), target_pattern)LookUpwards() 内部实现了这个新的纵向复制行为后,剩下的工作就是在 Insert 模式中覆盖标准的 CTRL-Y 命令,使用下面的 imap:imap =LookUpwards() = 调用 Vimscript 函数调用,因此本例使用 = 。使用 single-CTRL-R 表单插入后面的表达式的结果,就好象是直接输入的一样,这意味着结果中的任何特殊字符将保留它们的特殊含义和行为。另一方面,double-CTRL-R 表单将按原样逐字插入结果,不做任何处理。= ,那么从前面的行中复制字母转义字符就相当于输入字符,而这将会造成编辑器立刻退出 Insert 模式。:help functions:help function-list