📄 usr_41.cnx
字号:
3 endif ~ endfunction ~调 试当调试或者遇到错误信息的时候,行号是很有用的。有关调试模式请参阅|debug-scripts|。 你也可以通过将 'verbose' 选项设为 12 以上来察看所有函数调用。将该参数设为 15以上可以查看所有被执行的行。删 除 函 数为了删除 Show() 函数: > :delfunction Show如果该函数不存在,你会得到一个错误信息。==============================================================================*41.8* 异常让我们从一个例子开始: > :try : read ~/templates/pascal.tmpl :catch /E484:/ : echo "Sorry, the Pascal template file cannot be found." :endtry如果该文件不存在的话,":read" 命令就会失败。这段代码可以捕捉到该错误并向用户给出一个友好的信息,而不是一个普通的出错信息。在 ":try" 和 ":endtry" 之间的命令产生的错误将被转变成为异常。异常以字符串的形式出现。当异常是错误时该字符串就是出错信息。而每一个出错信息都有一个对应的错误码。在上面的例子中,我们捕捉到的错误包括 "E484"。Vim 确保这个错误码始终不变 (文字可能会变,例如被翻译)。当 ":read" 命令引起其它错误时,模式 "E484:" 不会被匹配。因此该异常不会被捕获,结果是一个普通的出错信息。你可能想这样做: > :try : read ~/templates/pascal.tmpl :catch : echo "Sorry, the Pascal template file cannot be found." :endtry这意味着所有的错误都将被捕获。然而你就无法得到那些有用的错误信息,比如说一个错误的模式行。另一个有用的机制是 ":finally" 命令: > :let tmp = tempname() :try : exe ".,$write " . tmp : exe "!filter " . tmp : .,$delete : exe "$read " . tmp :finally : call delete(tmp) :endtry这个例子将自光标处到文件尾的所有行通过过滤程序 "filter"。该程序的参数是文件名。无论在 ":try" 和 ":finally" 之间发生了什么,"call delete(tmp)" 命令始终被执行。这可以确定你不会留下一个临时文件。关于异常处理更多的讨论可以阅读参考手册: |exception-handling|.==============================================================================*41.9* 其他的讨论这里集中了一些和 Vim 脚本相关的讨论。别的地方其实也提到过,这里算做一个整理。行结束符取决于所在的系统。Unix 系统使用单个的 <NL> 字符。MS-DOS, Windows, OS/2机器类似系统使用 <CR><LF>。这对于那些使用 <CR> 的映射来说很重要。参阅 |:source_crnl|。空 白 字 符空行是允许的,但将被忽略。行首的空白字符 (空格和制表符) 总是被忽略的。参数间的空白字符 (例如象下面命令中'set' 和 'cpoptions' 之间的空白字符) 仅用作分隔符,会被减少到一个。根据情况的不同,最后一个 (可见) 的字符后的空白字符可能会被忽略也可能不会,见下。对于一个带有等号 "=" 的 ":set" 命令,如下: > :set cpoptions =aABceFst紧接着等号之前的空白字符会被忽略。然而其后的空白字符是不允许的!为了在一个选项值内使用空格,必须像下面例子那样使用反斜杠: > :set tags=my\ nice\ file如果写成这样 > :set tags=my nice fileVim 会给出错误信息,因为它被解释成: > :set tags=my :set nice :set file注 释双引号字符 " 标记注释的开始。除了那些不考虑注释的命令外(见下例),从双引号起的直到行末的所有字符都将被忽略。注释可以从一行的任意位置开始。对于某些命令来说,这里有一个小小的 "陷阱"。例如: > :abbrev dev development " shorthand :map <F3> o#include " insert include :execute cmd " do it :!ls *.c " list C files缩写 'dev' 会被展开成 'development " shorthand'。<F3> 的键盘映射会使包括'" insert include' 在内的那一整行。"execute" 命令会给出错误。"!" 命令会将其后的所有字符传给 shell,从而引起一个不匹配 '"' 的错误。 因此,在 ":map", ":abbreviate", ":execute" 和 "!" 命令之后不能有注释。(另外还有几个命令也是如此)。对于这些命令有一个小窍门: > :abbrev dev development|" shorthand :map <F3> o#include|" insert include :execute cmd |" do it'|' 字符被用来将两个命令分隔开。后一个命令仅仅是一个注释。注意在 '|' 之前没有空格。这是因为对于这些命令,该行上直到行尾或者 '|' 字符的内容都是有效的。结果是:你没法总看到这些命令后面包括的空白字符: > :map <F4> o#include 要避开这个问题,你可以在你的 vimrc 文件内设置 'list' 选项。陷 阱下面的例子的问题就更大了: > :map ,ab o#include :unmap ,ab 这里,unmap 命令是行不通的,因为它试着 unmap ",ab "。而这个映射根本就不存在。因为 'unmap ,ab ' 的末尾的那个空白字符是不可见的,这个错误很难被找出。在下面这个类似的例子里, 'unmap' 后面带有注释: > :unmap ,ab " comment注释将被忽略。然而,Vim 会尝试 unmap 不存在的 ',ab '。可以重写成: > :unmap ,ab|" comment恢 复 一 个 视 图有时有你想做一些改动然后回到光标原来的位置。如果能恢复相对位置,把和改动前同样的行置于窗口顶端就更好了。 这里的例子拷贝当前行,粘贴到文件的第一行,然后恢复视图: > map ,p ma"aYHmbgg"aP`bzt`a解析: > ma"aYHmbgg"aP`bzt`a< ma 在当前位置做标记 a "aY 将当前行拷贝至寄存器 a Hmb 移动到窗口的顶行并做标记 b gg 移动到文件首行 "aP 粘贴拷贝的行到上方 `b 移动到刚才的顶行 zt 重置窗口中的文本 `a 回到标记 a 的地方封 装为了避免你的函数名同其它的函数名发生冲突,使用这样的方法:- 在函数名前加上独特的字符串。我通常使用一个缩写。例如,"OW_" 被用在 option window 函数上。- 将你的函数定义放在一个文件内。设置一个全局变量用来表示这些函数是否已经被加 载了。当再次 source 这个文件的时候,先将这些函数卸载。例如: > " This is the XXX package if exists("XXX_loaded") delfun XXX_one delfun XXX_two endif function XXX_one(a) ... body of function ... endfun function XXX_two(b) ... body of function ... endfun let XXX_loaded = 1==============================================================================*41.10* 编写插件 *write-plugin*用约定方式编写的脚本能够被除作者外的很多人使用。这样的脚本叫做插件。Vim 用户只要把你写的脚本放在 plugin 目录下就可以立即使用了 |add-plugin|。实际上有两种插件: 全局插件: 适用于所有类型的文件。文件类型插件: 仅适用于某种类型的文件。这一节将介绍第一种。很多的东西也同样适用于编写文件类型插件。仅适用于编写文件类型插件的知识将在下一节 |write-filetype-plugin| 做介绍。插 件 名首先你得给你的插件起个名字。这个名字应该很清楚地表示该插件的用途。同时应该避免同别的插件用同样的名字而作不同的事。请将插件名限制在 8 个字符以内,这样可以使的该插件在老的 Windows 系统也能使用。一个纠正打字错误的插件可能被命名为 "typecorr.vim"。我们将用这个名字来举例。为了使一个插件能被所有人使用,要注意一些事项。下面我们将一步步的讲解。最后会给出这个插件的完整示例。插 件 体让我们从做实际工作的插件体开始: > 14 iabbrev teh the 15 iabbrev otehr other 16 iabbrev wnat want 17 iabbrev synchronisation 18 \ synchronization 19 let s:count = 4当然,真正的清单会比这长的多。上面的行号只是为了方便解释,不要把它们也加入到你的插件文件中去!插 件 头你很可能对这个插件做新的修改并很快就有了好几个版本。并且当你发布文件的时候,别人也想知道是谁编写了这样好的插件或者给作者提点意见。所以,在你的插件头部加上一些描述性的注释是很必要的: > 1 " Vim global plugin for correcting typing mistakes 2 " Last Change: 2000 Oct 15 3 " Maintainer: Bram Moolenaar <Bram@vim.org>关于版权和许可:由于插件很有用,而且几乎不值得限制其发行,请考虑对你的插件使用公共域 (public domain) 或 Vim 许可 |license|。在文件顶部放置一个 note 就行了。例如: > 4 " License: This file is placed in the public domain.续 行,避 免 副 效 应 *use-cpo-save*在上面的第 18 行中,用到了续行机制 |line-continuation|。那些设置了 'compatible'选项的用户可能会在这遇到麻烦。他们会得到一个错误信息。我们不能简单的复位'compatible' 选项,因为那样会带来很多的副效应。为了避免这些副效应,我们可以将'cpoptions' 选项设为 Vim 缺省值并在后面恢复之。这将允许续行功能并保证对大多数用户来讲脚本是可用的。就像下面这样: > 11 let s:save_cpo = &cpo 12 set cpo&vim .. 42 let &cpo = s:save_cpo我们先将 'cpoptions' 的旧值存在 s:save_cpo 变量中。在插件的最后该值将被恢复。注意上面使用了脚本本地变量 |s:var|。因为可能有一个全局变量已经在使用了。对于仅在脚本内用到的变量应该总使用脚本本地变量。停 止 加 载有可能一个用户并不总希望加载这个插件。或者系统管理员在系统的插件目录中已经把这个插件删除了,而用户希望使用它自己安装的插件。用户应该有机会选择不加载指定的插件。下面的一段代码就是用来实现这个目的的: > 6 if exists("loaded_typecorr") 7 finish 8 endif 9 let loaded_typecorr = 1这同时也避免了同一个脚本被加载两次以上。因为那样用户会得到各种各样的错误信息。比如函数被重新定义,自动命令被多次加入等等。映 射现在让我们把这个插件变得更有趣些:我们将加入一个映射用来校正当前光标下的单词。我们可以任意选一个键组合,但是用户可能已经将其定义为其它的什么功能了。为了使用户能够自己定义在插件中的键盘映射使用的键,我们可以使用 <Leader> 标示: > 22 map <unique> <Leader>a <Plug>TypecorrAdd那个 "<Plug>TypecorrAdd" 会做实际的工作,后面我们还会做更多解释。用户可以将 "mapleader" 变量设为他所希望的开始映射的键组合。比如假设用户这样做: > let mapleader = "_"映射将定义为 "_a"。如果用户没有这样做,Vim 将使用缺省值反斜杠。这样就会定义一个映射 - "\a"。Note 其中用到了 <unique>,这会使得 Vim 在映射已经存在时给出错误信息。|:map-<unique>|但是如果用户希望定义自己的键操作呢?我们可以用下面的机制来解决: > 21 if !hasmapto('<Plug>TypecorrAdd') 22 map <unique> <Leader>a <Plug>TypecorrAdd 23 endif我们先检查对 "<Plug>TypecorrAdd" 的映射是否存在。仅当不存在是我们才定义映射"<Leader>a"。这样用户就可以在他自己的 vimrc 文件中加入: > map ,c <Plug>TypecorrAdd那么键操作就会是 ",c" 而不是 "_a" 或者 "\a"了。分 割如果一个脚本变得相当长,你通常希望将其分割成几部分。你可以在其中使用函数或映射。但同时又不希望他们在脚本之间相互干扰。例如,你定义了一个函数Add(),但另一个脚本也试图定一同名的函数。为了避免这样的情况发生,我们可以在本地函数的前面加上 "s:"。我们来定义一个用来添加新的错误更正的函数: > 30 function s:Add(from, correct) 31 let to = input("type the correction for " . a:from . ": ") 32 exe ":iabbrev " . a:from . " " . to .. 36 endfunction这样我们就可以在这个脚本之内调用函数 s:Add()。如果另一个脚本也定义 s:Add(),该函数将只能在其所定义的脚本内部被调用。还可能会存在独立于这两个函数的全局的 Add() 函数 (不带 "s:")。<SID> 可以和映射一起使用,用来产生一个脚本的标示。在我们的错误更正插件中我们可以做以下的定义: > 24 noremap <unique> <script> <Plug>TypecorrAdd <SID>Add .. 28 noremap <SID>Add :call <SID>Add(expand("<cword>"), 1)<CR>这样当用户键入 "\a" 时,将触发下面的次序: > \a -> <Plug>TypecorrAdd -> <SID>Add -> :call <SID>Add()如果另一个脚本也定义了映射 <SID>Add,该脚本将产生另一个脚本标示。所以它定义的映射也与前面定义的不同。Note(注意) 在这里我们用了 <SID>Add() 而不是 s:Add()。这是因为该映射将被用户键入,因此是从脚本外部调用的。<SID> 被翻译成该脚本的标示。这样 Vim 就知道在那个脚本里寻找相应的 Add() 函数了。这的确是有点复杂,但又是使多个插件一起工作所必需的。基本规则是:在映射中使用<SID>Add();在其它地方 (该脚本内部,自动命令,用户命令) 使用 s:Add()。我们还可以用同一个映射来添加一个菜单选项: > 26 noremenu <script> Plugin.Add\ Correction <SID>Add建议把插件定义的菜单项都加入到 "Plugin" 菜单下。上面的情况只定义了一个菜单选项。当有多个选项时,可以创建一个子菜单。例如,一个提供 CVS 操作的插件 可以添加"Plugin.CVS" 子菜单,并在其中定义 "Plugin.CVS.checkin", "Plugin.CVS.checkout"等菜单项。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -