*map.txt* For Vim version 6.3b. 原文最后更新: 2004年3月26日
VIM 参考手册 作者: Bram Moolenaar
翻译: con<[email protected]>
VCD主页:http://vimcdoc.sf.net
键映射, 缩写和用户定义的命令.
本主题在用户手册 |05.3|, |24.7| 和 |40.1| 中有过介绍.
1. 键映射 |key-mapping|
2. 缩写 |abbreviations|
3. 局部映射和函数 |script-local|
4. 用户定义的命令 |user-commands|
1. 键映射 *key-mapping* *mapping* *macro* 键映射用于改变输入键的含义. 最通常的用途是为一个功能键定义成一系列的命令. 比如: :map<F2>
a<C-R>
=strftime("%c")<CR>
<Esc>
这个映射在光标之后追加当前的日期和时间. (in<>
notation |<>|) 有很多命令用于定义新的映射, 删除映射和列出当前的映射.可以从 |map-overview| 参考 不同的 "映射" 形式和相关的模式.{lhs}
表示左手边 *{lhs}*{rhs}
表示右手边 *{rhs}* :map{lhs}
{rhs}
*:map* :nm[ap]{lhs}
{rhs}
*:nm* *:nmap* :vm[ap]{lhs}
{rhs}
*:vm* *:vmap* :om[ap]{lhs}
{rhs}
*:om* *:omap* :map!{lhs}
{rhs}
*:map!* :im[ap]{lhs}
{rhs}
*:im* *:imap* :lm[ap]{lhs}
{rhs}
*:lm* *:lmap* :cm[ap]{lhs}
{rhs}
*:cm* *:cmap* 在映射命令作用的模式中把键系列{lhs}
映射为{rhs}
. 并 且映射后的{rhs}
也被进行映射扫描. 这个特性可以用来进 行映射的嵌套和递归. :no[remap]{lhs}
{rhs}
*:no* *:noremap* :nn[oremap]{lhs}
{rhs}
*:nn* *:nnoremap* :vn[oremap]{lhs}
{rhs}
*:vn* *:vnoremap* :ono[remap]{lhs}
{rhs}
*:ono* *:onoremap* :no[remap]!{lhs}
{rhs}
*:no!* *:noremap!* :ino[remap]{lhs}
{rhs}
*:ino* *:inoremap* :ln[oremap]{lhs}
{rhs}
*:ln* *:lnoremap* :cno[remap]{lhs}
{rhs}
*:cno* *:cnoremap* 在映射命令作用的模式中把键序列{lhs}
映射为{rhs}
. 禁 止对映射后的{rhs}
进行映射扫描. 这个特性可以避免映射 的嵌套和递归. 通常用于重定义一个命令.{not in Vi}
:unm[ap]{lhs}
*:unm* *:unmap* :nun[map]{lhs}
*:nun* *:nunmap* :vu[nmap]{lhs}
*:vu* *:vunmap* :ou[nmap]{lhs}
*:ou* *:ounmap* :unm[ap]!{lhs}
*:unm!* *:unmap!* :iu[nmap]{lhs}
*:iu* *:iunmap* :lu[nmap]{lhs}
*:lu* *:lunmap* :cu[nmap]{lhs}
*:cu* *:cunmap* 在映射命令作用的模式中删除{lhs}
的映射. 该映射可能 会在它作用的其它模式中被保留.Note:
{lhs}
包含末尾的空格. 该映射取消操作不会生效: :map @@ foo :unmap @@ | print :mapc[lear] *:mapc* *:mapclear* :nmapc[lear] *:nmapc* *:nmapclear* :vmapc[lear] *:vmapc* *:vmapclear* :omapc[lear] *:omapc* *:omapclear* :mapc[lear]! *:mapc!* *:mapclear!* :imapc[lear] *:imapc* *:imapclear* :lmapc[lear] *:lmapc* *:lmapclear* :cmapc[lear] *:cmapc* *:cmapclear* 在映射命令作用的模式中删除所有的映射.{not in Vi}
警告: 这也会删除缺省的映射. :map :nm[ap] :vm[ap] :om[ap] :map! :im[ap] :lm[ap] :cm[ap] 在映射命令作用的的模式中列出所有的键映射. 注意 ":map" 和":map!"是最常用的, 因为它们包括其它模式. :map{lhs}
*:map_l* :nm[ap]{lhs}
*:nmap_l* :vm[ap]{lhs}
*:vmap_l* :om[ap]{lhs}
*:omap_l* :map!{lhs}
*:map_l!* :im[ap]{lhs}
*:imap_l* :lm[ap]{lhs}
*:lmap_l* :cm[ap]{lhs}
*:cmap_l* 在映射命令作用的模式中列出以{lhs}
开头的键映射的键系列.{not in Vi}
这些命令用于把一个或一个键系列映射成一个字符串. 可以用它来在功能键里放置一系列命令, 把一个键转换成另一个, 等等. 如何保存和恢复当前映射可以参考 |:mkexrc| . *:map-local* *:map-<buffer>* *E224* *E225* 如果这些命令的第一个参数是 "<buffer>
" ,映射将只局限于当前的缓冲內. 例如::map
然后你可以把 ",w" 在另一个缓冲内作另外的映射:<buffer>
,w /[.,;]<CR>
:map
局部缓冲映射在全局映射之前被应用. "<buffer>
,w /[#&!]<CR>
<buffer>
" 参数也可以用于清除映射::unmap
<buffer>
,w:mapclear
当一个缓冲被删除时局部映射也会被清除, 但是在它被卸载时不会. 就象本地选项值. *:map-<silent>* *:map-silent* 要在定义一个映射时不在命令行里回显, 可以使用 "<buffer>
<silent>
" 作为第一个参数, 例如::map
在使用这个映射时搜索字串将不被回显. 被执行的命令的信息仍然会被显示. 要把它也关掉, 可以在执行的命令里加入一个 ":silent" :<silent>
,h /Header<CR>
:map
提示仍然会给出, 比如使用 inputdialog() 的时候. 在一个缩写上使用 "<silent>
,h :exe ":silent normal /Header\r"<CR>
<silent>
" 是可能的, 但将会使重绘命令行失败. *:map-<script>* *:map-script* 如果给这些命令的第一个参数是 "<script>
" 并且它用于定义一个新的映射或缩写, 该映射将仅仅重映射{rhs}
中使用被一个以 "<SID>
" 开头的脚本局部定义的映射中的字符. 它可以用于避免来自外部的脚本的干扰 (举例来说, 在 mswin.vim 中CTRL-V
被重新映射 ), 但是又使用该脚本中定义的其它映射.Note:
":map<script>
" 和 ":noremap<script>
" 做同样的事情. "<script>
" 否决了该命令名. 更推荐使用 ":noremap<script>
" ,因为它更清晰的表示 了重映射已被 (大多数时候) 禁止. *:map-<unique>* *E226* *E227* 如果这些命令的第一个参数是 "<unique>
" 并且它用于定义一个新的映射或缩写时该映射 或缩写已经存在, 则该命令会失败. 例如::map
当定义一个局部映射, 同时也会检查是否已存在了一个相同的全局映射. 这个例子将失败:<unique>
,w /[#&!]<CR>
:map ,w /[#&!]
<CR>
:map
<buffer>
<unique>
,w /[.,;]<CR>
"
<buffer>
", "<silent>
", "<script>
" 和 "<unique>
" 可以以任意顺序被使用. 它们必需出现在命令的右边, 并且在任何其它参数的之前边. 映 射 与 运 行 模 式 有五种映射存在 - 对于普通模式: 当输入命令时. - 对于可视模式: 当输入命令并且 Visual 区域已被设置为高亮时. - 对于 Operator-pending mode: 当一个操作符正在进行中 ("d", "y", "c",等等之后) 例如: ":omap { w" 会使 "y{" 和 "yw" 一样, "d{" 和 "dw"一样. - 对于插入模式: 也被用于替换模式. - 对于命令行模式: 当输入一个 ":" 或 "/" 命令时. 没有独立针对选择模式的映射. 和可视模式在 |Select-mode-mapping| 中的一样. 特殊情况:当在普通模式里为一个命令输入一个数时,对 0 的映射就会禁用。这样 在输入一个带有0的数字时不会受到对 0 键映射的干扰。 *map-overview* *map-modes* 每个映射命令工作的模式概况:命令: 模式:
普通 可视 Operator-pending
:map :noremap :unmap :mapclear yes yes yes :nmap :nnoremap :nunmap :nmapclear yes - - :vmap :vnoremap :vunmap :vmapclear - yes - :omap :onoremap :ounmap :omapclear - - yes插入 命令行 Lang-Arg
:map! :noremap! :unmap! :mapclear! yes yes - :imap :inoremap :iunmap :imapclear yes - - :cmap :cnoremap :cunmap :cmapclear - yes - :lmap :lnoremap :lunmap :lmapclear yes* yes* yes* 原来的 Vi 没有独立针对普通/可视/Operator-pending 模式和插入/命令行模式的映射. 因此 ":map" 和 ":map!" 命令对不同的模式都起作用。在 Vim 中你可以使用 ":nmap", ":vmap", ":omap", ":cmap" 和 ":imap" 命令来对每个不同的模式分别定义输入映射。 要为普通和可视模式但不包括 Operator-pending 模式输入一个映射, 首先在所有的 三个模式中为它定义, 然后为它在 Operator-pending 模式中取消映射: :map xx something-difficult :ounmap xx 对于一个可视和 Operator-pending 模式或 普通和 Operator-pending 模式的映射 也一样. *language-mapping* ":lmap" 定义一个映射应用于: - 插入模式 - 命令行模式 - 当输入一个搜索模式 - 接受一个文本字符作为参数的命令, 比如 "r" 和 "f" - 对于 input() 行 通常: 不论何时被输入的字符是缓冲区文本的一部分, 而非一个 Vim 命令字符. "Lang-Arg" 不是真正的另外一个模式, 它在这里仅在这种情况下被使用. 载入一个相关语言映射集合的最简单的方法是通过使用 'keymap' 选项. 参考 |45.5|. 在插入模式和命令行模式中可以用CTRL-^
命令来禁止映射|i_CTRL-^| |c_CTRL-^|. 当开始输入一个普通命令行 (非搜索模式)时, 映射被禁止 直到输入一个CTRL-^
. 上次使用的状态在 Insert 模式和搜索模式中被单独记录. Insert 模式的状态也被用于当输入一个字符作为 "f" 或 "t" 之类命令的参数时. 当加入一个 ":lmap" 映射在Insert 模式和搜索模式中这些映射将被打开. 语言映射永远不能应用于已经映射的字符上. 它们仅用于已经输入的字符上. 假设语言映射已经完成当输入映射时. *map-multibyte* 可以对多字节字符映射, 但只能是整个字符. 不能仅映射第一个字节. 这是为了避免下面场景中的问题: :set encoding=latin1 :imap<M-C>
foo :set encoding=utf-8 对于<M-C>
的映射是在 latin1 解码中被定义的, 结果是一个 0xc3 字节. 如果你在UTF-8解码中输入 ?(0xea<M-a>
) 它是双字节 0xc3 0xa1. 这个时候你不希望 0xc3 字节被映射, 否则的话将不能输入 ? 字符了. *map-listing* 当列出映射时, 前面两栏的字符表示:CHAR MODE
<Space>
Normal, Visual 和 Operator-pending n Normal v Visual o Operator-pending ! Insert 和 Command-line i Insert l ":lmap" 为 Insert, Command-line 和 Lang-Arg映射 c Command-line 在紧跟{rhs}
会显示一个特殊字符: * 表示它不可重映射 & 表示仅 script-local 映射可以被重映射 @ 表示一个 buffer-local 映射{lhs}
从以后的第一个非空字符到行的末尾(或 '|') 都被认为是{rhs}
的一部分. 这允许{rhs}
以一个空格结尾. 注意: 在 Visual 模式里使用映射时, 你可以使用 "'<" 标记, 它表示当前缓冲区中 最后被选中的 Visual 区域的开始 |'<|. *map_backslash* 注意在这里仅有CTRL-V
被提及作为映射和缩写时的一个特殊字符. 当 'cpoptions' 不包含 'B', 一个反斜杠也可如CTRL-V
一样被使用. 而且<>
符号能被完全可以被使用 |<>|. 但你不能使用 "<C-V>
" 如CTRL-V
来转换后面的特殊含义. 要映射一个反斜杠, 或者在{rhs}
中使用一个字面意义的反斜杠, 可以使用特殊字符 序列 "<Bslash>
" . 这可以避免在使用嵌套映射时需要使用双反斜杠. *map-ambiguous* 当两个映射以相同的字符顺序开始,它们是含糊的. 例如::imap aa foo
:imap aaa bar
当 Vim 读如 "aa", 它将需要取得另外一个字符才能决定是否是 "aa" 或是 "aaa" 应该被映射. 这意味这当输入 "aa" 以后映射还不会展开, Vim还在等待另一个字符. 如果你输入一个空格, 那么 "foo" 将被插入, 而不是空格. 如果你输入一个 "a", 那么 "bar" 将被插入.{Vi does not allow ambiguous mappings}
*map_CTRL_C* 在{lhs}
中不可能使用一个CTRL-C
. 你不能映射CTRL-C
. 原因是CTRL-C
必需总是能够中断一个运行中的命令. 例外: 当在MS-Windows上使用 GUI 版本时CTRL-C
能被映射来允许一个Copy命令到 剪裁板. 使用CTRL-Break
来中断 Vim. *map_space_in_lhs* 要在{lhs}
中包含一个空格, 在前面输入一个CTRL-V
(每个空格输入两个CTRL-Vs
). *map_space_in_rhs* 如果你需要{rhs}
以空格开头, 使用 "<Space>
". 要与Vi完全兼容 (但不可读), 不要使用 |<>| 符号, 在{rhs}
前面输入一个单独的CTRL-V
(你必需输入CTRL-V
两次). *map_empty_rhs* 你可以通过在一个单独的CTRL-V
(你必需输入CTRL-V
两次) 后面什么也不输入 来建立一个空的{rhs}
. 不幸的是在一个vimrc文件中你不能使用这种方式. *<Nop>* 得到一个什么都不做的映射的更容易的一个方法是在{rhs}
中使用 "<Nop>
". 仅当 |<>| 符号被允许时这种方法才生效. 例如确保功能键F8什么事情都不做: :map<F8>
<Nop>
:map!<F8>
<Nop>
*<Leader>* *mapleader* 要定义一个使用 "mapleader" 变量的映射, 可以使用特殊字串 "<Leader>
". 它会被 "mapleader" 的字串值所替换. 如果 "mapleader" 未设置或为空,则用 反斜杠代替, 例如: :map<Leader>
A oanother line<Esc>
和下面一样::map \A oanother line
但是当:<Esc>
:let mapleader = ","
和下面一样::map ,A oanother line
<Esc>
注意 "mapleader" 的值仅当定义映射时被使用. 后来改变的 "mapleader" 不会 影响已定义的映射. *<LocalLeader>* *maplocalleader* 和
<Leader>
类似, 除了它使用 "maplocalleader" 而非 "mapleader".<LocalLeader>
用来针对局部于一个缓冲的映射多所使用, 例如::map
在一个全局插件里应该使用<LocalLeader>
q \DoItNow<Leader>
而在一个 filetype 插件里应该使用<LocalLeader>
. "mapleader" 和 "maplocalleader" 可以是相同的. 尽管如此, 如果你 把它们设置为不同, 全局插件和filetype插件的映射冲突的机会也是很小的. 例如, 你 可以保持把 "mapleader" 设置为缺省的反斜杠, 并且设置 "maplocalleader" 设置为 下划线. *map-<SID>* 在一个脚本中有一个特殊关键字叫 "<SID>
" 能被用来定义一个局部于脚本中的映射. 具体细节请参考 |<SID>|. *<Plug>* 叫做 "<Plug>
" 的特殊关键字可以用于一个内部映射, 它不于任何键顺序匹配. 这在插件中有用 |using-<Plug>|. *<Char>* *<Char->* 要以一个字符的十进制, 八进制或十六进制数字映射, 可以使用<Char>
来构造:<Char-123>
character 123<Char-033>
character 27<Char-0x7f>
character 127 它可以用来在一个 'keymap' 文件里指定一个 (多字节) 字符. 大小写的区别被忽略. *map-comments* 在这些命令的后面不可能放置注释, 因为 '"' 字符被认为是{lhs}
或{rhs}
的一部分. *map_bar* 因为字符 '|' 用来分隔映射命令和后面的命令, 所以包括 '
|' 的{rhs}
要 做一些特殊的处理, 有三种方法:use works when example
<Bar>
'<' is not in 'cpoptions' :map _l :!ls <Bar>
more^M \|'b' is not in 'cpoptions' :map _l :!ls \
| more^M ^V|always, in Vim and Vi :map _l :!ls ^V
| more^M (这里 ^V 表示CTRL-V
; 要输入一个CTRL-V
你必需按键两次; 在这里不能使用<>
符号 "<C-V>
"). 当你使用 'cpoptions' 的缺省设置时三种方式都可以正常工作. 当 'b' 出现在 'cpoptions' 中时, "\|" 会被认为是一个映射的结束, 后面的 是另一个命令. 这是为了和Vi兼容, 但是当和其它命令比较时有点不合逻辑. *map_return* 当你的一个映射包含一个 Ex 命令时, 你需要放置一个行终结符在它后面才能让它执行. 在这里推荐推荐使用<CR>
(参考 |<>|). 例如::map _ls :!ls -l %
<CR>
:echo "the end"<CR>
在Command-line模式中输入时要避免字符被映射, 可以先输入一个
CTRL-V
. 在Insert模式中如果 'paste' 选项被打开的话, 映射会被禁止. 注意当遇到错误时 (会导致一个错误信息或蜂鸣) 剩下的映射将不会被执行. 这是为了保持和Vi兼容. 注意这些命令 @zZtTfF[]rm'`"v 的第二个字符 (参数) 和CTRL-X
不被映射. 这样做是为了能够使用所有的命名寄存器和标记, 甚至当同样的名字被映射时 也是如此. *map-which-keys* 如果你要做一些映射, 你得选择在{lhs}
中要用哪些键. 你应该避免使用 Vim 命令所 使用的那些键. 否则你将不再能使用这些命令了. 下面是一些建议: - 功能键<F2>
,<F3>
, 等等.. 和Shift加功能键<S-F1>
,<S-F2>
, 等等. 注意<F1>
已经用做了帮助命令. - Meta键 (和 ALT 键一起按下). - 使用 '_' 或 ',' 字符然后加上任何其它的字符. "_" 和 "," 命令在 Vim 中是存在 的 (参考 |_| 和 |,|), 但你也许永远不会用到它们. - 使用一个键作为其它命令的同义字. 例如:CTRL-P
和CTRL-N
. 使用更多的字符做更多的映射. 参考文件 "index" 可以知道哪些键没有被使用从而使映射不会覆盖任何内建的功能. 也可使用 ":help{key}
^D" 来找出是否一个键已经用于某些命令. ({key}
用于指定你要寻找的键, ^D 是CTRL-D
). *map-examples* 以下是一些例子 (照字面输入它们, 对于 "<CR>
" 你输入四个字符; 当 '<' 标志不出现在 'cpoptions' 中时它们能正常工作).
:map
<F3>
o#include:map
<M-g>
/foo<CR>
cwbar<Esc>
:map _x d/END/e
<CR>
:map! qq quadrillion questions
*map-typing* 当你输入一个映射键顺序时 Vim 会比较你的输入. 如果还不匹配, 它会等待更多的 字符输入直到可以确定是否匹配. 例如: 如果你映射了 map! "qq", 然后你输入的第 一个 'q' 将不会显示在屏幕上直到你输入另一个 'q' 或其它字符. 如果打开了 'timeout' 选项 (这是缺省选项) Vim 仅会等待一秒钟 (或者等待 'timeoutlen' 所 指定的时间). 然后假设 'q' 已经不会再被输入. 如果你的输入很慢, 或者你的系统 很慢, 重置 'timeout' 选项. 或者你可能需要设置 'ttimeout' 选项. *map-keys-fails* 有一种情况键编码可能不被识别: - Vim 仅能读取部分的键编码. 通常仅仅是第一个字符. 在某些 Unix 版本的 xterm 上有这种情况. - 键编码后面的字符已经被映射. 举例来说, "<F1>
<F1>
" 或 "g<F1>
". 其结果是在这种情况下键编码不会被识别, 所以映射失败. 有两种方法可以避免这种问题: - 从 'cpoptions' 中删除 'K' 标志. 这会使 Vim 等待功能键剩下的部分. -<F1>
到<F4>
实际产生的键编码与<xF1>
到<xF4>
符合. 这是<xF1>
到<F1>
,<xF2>
到<F2>
的映射, 等等, 但是这在映射的另一半不会被 识别. 确认从<F1>
到<F4>
的键编码是正确的::set
以四个字符输入<F1>
=<type
CTRL-V
><type F1>
<F1>
. "=" 号后面的部分必需以实际的字符输入, 而不是字面的文本. 另一种解决方法是在映射中为第二个特殊键使用实际的键编码::map
不要输入一个真正的<F1>
<Esc>
OP :echo "yes"<CR>
<Esc>
, 总之 Vim 将识别键编码并把它替换为<F1>
. *recursive_mapping* 如果{rhs}
中包括了{lhs}
那么就是一个递归映射. 当{lhs}
被输入, 它会被替换成{rhs}
. 当遇到{rhs}
中包含的{lhs}
它会被替换成{rhs}
, 依此类推. 这可能会使一个命令被重复无数次. 这种情况唯一的问题是出错是停止它的唯一方法. 有一些宏用来解决这种混乱情况, 看下面的例子. 有一个例外: 如果{rhs}
以{lhs}
开始, 第一个字符不会被再次映射 (这与 Vi 兼容). 例如::map ab abcd
将执行 "a" 命令并且在文本中插入 "bcd".{rhs}
中的 "ab" 不会被再次映射. 如果你要交换两个键的含义应该使用 :noremap 命令. 例如::noremap k j
:noremap j k
这会交换光标上移和光标下移命令. 如果使用普通 :map 命令, 并且 'remap' 选项被打开, 映射一直进行直到文本不再是{lhs}
的一部分. 例如, 如果你用::map x y
:map y x
Vim 将把 x 替换成 y, 并把 y 替换成 x, 等等. 这种情况会发生 'maxmapdepth' 次 (缺省为 1000), 然后 Vim 会给出错误信息 "recursive mapping" (递归映射). *:map-undo* 如果你在一个被映射的序列中包含了一个 undo 命令, 将会把文本带回宏执行前的状态 这和原始的 Vi 是兼容的, 只要被映射的序列仅包含一个 undo 命令 (原始的 Vi 中 被映射的序列有两个 undo 命令是无意义的, 在第一个 undo 处你就会回到文本). *:map-special-keys* 有三中方法来映射一个特殊键: 1. Vi 兼容的方法: 映射键编码. 通常这是一个以<Esc>
开头的序列. 要输入一个 这样的映射先输入 ":map " 然后再敲入功能键之前得先输入一个CTRL-V
. 注意 这时键的编码是在 termcap (the t_ options) 里, 它会被自动转换到内部编码并it will 变成映射的第二种方法 (除非 'cpoptions' 里包括了 'k' 标志). 2. 第二种方法是为功能键使用内部编码. 要输入这样的映射输入CTRL-K
并敲要映射 的功能键, 或者使用 "#1", "#2", .. "#9", "#0", "<Up>
", "<S-Down>
", "<S-F7>
", 等等的形式.(参考键表 |key-notation|, 所有从<Up>
开始的键都可以使用) enter such a mapping typeCTRL-K
and then hit the function key, or use the form "#1", "#2", .. "#9", "#0", "<Up>
", "<S-Down>
", "<S-F7>
", etc. 头十个功能键能以两种方式被定义: 仅用数字, 比如 "#2", 或者使用"<F>
", 如 "<F2>
". 两种都代表功能键 F2. "#0" 表示功能键 F10, 用选项 't_f10' 定义, 它在某些键盘上可能是F0.<>
的形式在 'cpoptions' 包含 '<' 标志时不能使用. 3. 使用 termcap 条目, 以<t_xx>
的形式, 这里 "xx" 是 termcap 条目的名字. 任何字串条目都可以使用. 例如::map
把功能键 13 映射成 "G". 当 'cpoptions' 包括 '<' 标志时这种方式不能使用. 第二种和第三种方法的优点是不加修改就可以在不同的终端上使用 (功能键会被转换成 相同的内部编码或实际的键编码, 而不论使用何种终端. termcap 必需是正确的才能正 常工作, 并且必需使用相同的映射). 细节: Vim 首先检查是否从键盘输入的序列是否已被映射. 否的话将试图使用终端键编 码 (参考 |terminal-options|). 如果一个终端键编码被找到它会被替换成内部编码. 然后再次检查一个映射是否已完成 (因此你也能把一个内部编码映射成其它东西). 在脚本文件中写入什么东西取决于识别了什么东西. 如果终端键编码被识别为一个映射 它本身的键编码会被写入脚本. 如果它被识别为一个终端编码则在脚本中写入内部编码.<t_F3>
G
2. 缩写 *abbreviations* *Abbreviations* 缩写在插入, 替换和命令行模式中使用. 如果你输入一个是缩写的单词, 它会被替换成 所表示的东西. 这可以在经常输入的长单词时节省键击. 并且能用它来自动更正经常犯 的拼写错误. 例如: :iab ms MicroSoft :iab tihs this 有三种类型的缩写: full-id "full-id" 类型由所有键盘字符组成 ('iskeyword' 选项里的字母和字符). 这是最普通的缩写. 例如: "foo", "g3", "-1" end-id "end-id" 类型以一个键盘字符结尾, 但所有其它字符都不是键盘字符. 例如: "#i", "..f", "$/7" non-id "non-id" 类型以一个非键盘字符结尾, 其它字符可以是任意类型, 除了空格 和 Tab.{Vi 不支持这种类型}
例如: "def#", "4/7$" 不能被缩写的字串例子: "a.b", "#def", "a b", "_$r" 仅当你输入一个非键盘字符时缩写才会被识别. 或者用<Esc>
退出插入模式或用<CR>
结束一个命令. 结束缩写的非键盘字符被插入到缩写的扩展后面. 一个例外是字符<C-]>
, 它用来不插入任何附加字符时扩展一个缩写. 例如::ab hh hello
"hh<Space>
" 被扩展为 "hello<Space>
" "hh<C-]>
" 被扩展为 "hello" 光标前的字符必需和缩写匹配. 每种类型都有一个附加规则: full-id 匹配的前面是一个非键盘字符, 或者是在行或插入的开始. 例外: 当缩写仅 有一个字符时, 如果它前面有一个非键盘字符则不会被识别, 除了空格和<Tab>
. end-id 匹配的前面是一个键盘字符, 或者一个空格或<Tab>
, 或者是行或插入的开始. non-id 匹配的前面是一个空格,<Tab>
或者行或插入的开始. 例如: ({CURSOR}
是你输入一个非键盘字符的地方):ab foo four old otters
" foo{CURSOR}
" 被扩展为 " four old otters" " foobar{CURSOR}
" 不被扩展 "barfoo{CURSOR}
" 不被扩展:ab #i #include
"#i{CURSOR}
" 被扩展为 "#include" ">#i{CURSOR}
" 不被扩展:ab ;;
"test;;" 不被扩展 "test ;;" 被扩展为 "test<endofline>
"<endofline>
" 要避免在插入模式中避免缩写: 输入缩写的一部分, 以<Esc>
退出插入模式, 再用 'a' 进入插入模式并输入剩下的部分. 或者在缩写之后的字符前面输入CTRL-V
. 要在命令行模式中避免缩写: 在缩写的某处输入CTRL-V
两次来避免它被替换. 此外一个 普通字符前面的CTRL-V
通常会被忽略. 把光标移动到缩写的后面是可能的::iab if if ()
如果 'cpoptions' 里面包含 '<' 标志时不能正常工作. |<>| 你甚至可以做更复杂的事情. 例如, 要消灭一个缩写后面输入的空格: func Eatchar(pat) let c = nr2char(getchar()) return (c =~ a:pat) ? '' : c endfunc iabbr<Left>
<silent>
if if ()<Left>
<C-R>
=Eatchar('\s')<CR>
没有缺省的缩写. 缩写永远不会递归. 你可以设置 ":ab f f-o-o" 而不会有任何问题. 但是缩写能 被映射.{不清除原因, 一些版本的Vi支持递归缩写}
'paste' 选项打开时, 缩写被禁止. *:abbreviate-local* *:abbreviate-<buffer>* 和映射一样, 缩写可以被局部于一个缓冲之内. 这经常被用于一个 |filetype-plugin| 文件. 例如一个 C 插件文件::abb
*:ab* *:abbreviate* :ab[breviate] 列出所有的缩写. 第一栏中的字符表示该缩写作用的模式: 'i' 指插入模式, 'c' 指命令行模式, '!' 指两种模式都有. 这和映射的相同,参看 |map-listing| 。 :ab[breviate]<buffer>
FF for (i = 0; i < ; ++i){lhs}
列出以{lhs}
开头的缩写 :ab[breviate]{lhs}
{rhs}
增加一个从{lhs}
到{rhs}
的缩写. 如果{lhs}
已经存在 则它会被替换成新的{rhs}
.{rhs}
可能包含空格. *:una* *:unabbreviate* :una[bbreviate]{lhs}
从列表中删除{lhs}
的缩写 *:norea* *:noreabbrev* :norea[bbrev] [lhs] [rhs] 与 ":ab" 一样, 但是不为这个{rhs}
重映射{not ni Vi}
*:ca* *:cabbrev* :ca[bbrev] [lhs] [rhs] 与 ":ab" 一样, 但仅在命令行模式中使用.{not in Vi}
*:cuna* *:cunabbrev* :cuna[bbrev]{lhs}
与 ":una" 一样, 但仅在命令行模式中使用.{not in Vi}
*:cnorea* *:cnoreabbrev* :cnorea[bbrev] [lhs] [rhs] 与 ":ab" 一样, 但仅在命令行模式中使用并且不为这个{rhs}
重映射.{Vi 中没有}
*:ia* *:iabbrev* :ia[bbrev] [lhs] [rhs] 与 ":ab" 一样, 但仅在插入模式中使用.{not in Vi}
*:iuna* *:iunabbrev* :iuna[bbrev]{lhs}
与 ":una" 一样, 但仅在插入模式中使用. {not in Vi} *:inorea* *:inoreabbrev* :inorea[bbrev] [lhs] [rhs] 与 ":ab" 一样, 但仅在插入模式中使用并且 不为这个{rhs}
重映射.{not in Vi}
*:abc* *:abclear* :abc[lear] 删除所有的缩写.{not in Vi}
*:iabc* *:iabclear* :iabc[lear] 为插入模式删除所有的缩写.{not in Vi}
*:cabc* *:cabclear* :cabc[lear] 为命令行模式删除所有的缩写. {not in Vi} *using_CTRL-V* 在一个缩写的{rhs}
中使用特殊字符是可能的.CTRL-V
可以用来避免多数非打印字符 的特殊含义. 需要输入多少个CTRL-V
取决于你如何输入缩写. 这在映射中也可以应用. 这里使用一个例子说明. 假设你需要把 "esc" 缩写为输入一个<Esc>
字符. 当你在 Vim 中输入 ":ab" 命令, 你必需这样输入: (这里 ^V 是一个CTRL-V
并且 ^[ is<Esc>
) 你输入: ab esc ^V^V^V^V^V^[ 所有的键盘输入都遵从 ^V 引用解释, 所以第一个, 第三个, 和第五个 ^V 字符只是为了允许把第二个, 和第四个 ^V 和 ^[ 输入到命令行里. 你看到: ab esc ^V^V^[ 命令行里在 ^[ 之前包含两个实际的 ^V. 如果你采用这种方法, 这是 它应该出现在你的 .exrc 文件的样子. 第一个 ^V 作为它自己引用的字符, 所以你能在缩写中包含被引用的空白字符或 | 字符. :ab 命令对 ^[ 字符 并不做特殊的事情, 所以不需要被引用. (尽管引用是无害的; 这就是输入7个 [而不是 8 个!] ^V 会工作的原因.) 被保存为: esc ^V^[ 解析后, 该缩写的简短形式 ("esc") 和扩展形式 (两字符 "^V^[") 被保存 在缩写表中. 如果输入不带参数的 :ab 命令, 这是该缩写被显示的形式. 然后当用户输入单词 "esc" 而该缩写被扩展时, 扩展形式服从和键盘输入 同样形式的 ^V 解释. 所以 ^V 保护 ^[ 字符不被解释为 "退出输入模式" 的字符, 而是把 ^[ 插入到文本里面. 扩展为: ^[ [example given by Steve Kirkendall]
3. 局部映射和函数 *script-local* 当使用多个 Vim 脚本文件时, 一个脚本和另一个脚本使用同样名字的映射和函数是危险 的. 为了避免这种情况, 它们可以被局部在脚本中. *<SID>* *<SNR>* *E81* 字串 "<SID>
" 能被用于一个映射或菜单中. 这要求 'cpoptions' 中没有 '<' 标志. 当执行映射命令时, Vim 将把 "<SID>
" 替换成特殊的键码<SNR>
, 后跟一个每个脚 本唯一的数字编号, 和一个下划线. 例如: :map<SID>
Add 会定义一个映射 "<SNR>
23_Add". 当在一个脚本中定义一个函数的时候, 可以在名字的前面用一个 "s:" 来使它局部于脚 本中. 但当一个映射从脚本外面被执行时, 它不知道该函数在哪个脚本中被定义. 为了 避免这种情况, 使用 "<SID>
" 来代替 "s:". 它会做和映射一样的转换. 这使得在映射 里可以定义一个函数调用. 当一个局部函数被执行时, 它在定义他的脚本的上下文中运行. 这意味着, 它定义的新 的函数和映射也可以使用 "s:" 或 "<SID>
" 并且当函数自己被定义时它也会使用同样 唯一的数字编码. 此外, 能使用 "s:var" 脚本局部变量. 当执行一个自动命令或一个用户命令时, 它将在定义它的脚本的上下文中运行. 这使得 该命令可以调用一个局部函数或者使用一个局部映射. 此外, 在一个脚本上下文之外使用 "<SID>
" 是错误的. 如果你需要在一个复杂的脚本中取得脚本的数字编号, 可以使用这样的技巧: :map<SID>
xx<SID>
xx :let s:sid = maparg("<SID>
xx") :unmap<SID>
xx 然后删除尾部的 "xx". 当列表函数和映射时 "<SNR>
" 会被显示. 要找出它们在哪里被定义时有用. 命令 |:scriptnames| 可以用来查看哪些脚本已经被读入以及它们的<SNR>
数字编号. 这些都{not in Vi}
并且 {not available when compiled without the +eval feature}.
4. 用户定义的命令 *user-commands* 可以定义你自己的 Ex 命令. 一个用户定义命令可以和内建命令一样运行 (他可以有一个 类型或参数, 参数可以是自动完成的文件名或缓冲名, 等等), 除了当这些命令被执行的时候, 会被转换成一个普通的 ex 命令然后再被执行. 对于初学者来说: 参考用户手册中的 |40.2| . *E183* *user-cmd-ambiguous* 所有用户定义的命令都必需以一个大写字母开头, 来避免与内建命令的冲突. (要注意 的是, 有一些内建命令比如 :Next, :Print and :X, 也以大写字母开头. 在这些情况 下内建的命令总是优先执行的). 用户命令的其它字符可以是大写字母, 小写字母或数字. 当使用数字时, 小心会和其它以数字作为参数的命令混淆. 例如, 命令 ":Cc2" 可能是 不带参数的用户命令 ":Cc2", 也可能是参数为 "2" 的命令 "Cc". 建议在命令名和参数 之间放置一个空格来避免这些问题. 当使用一个用户定义的命令时, 该命令可以是缩写. 但是, 如果一个缩写不唯一, 会发生 错误. 此外, 一个内建命令将总是被优先执行. 例如::command Rename ...
:command Renumber ...
:Rena " Means "Rename"
:Renu " Means "Renumber"
:Ren " Error - ambiguous
:command Paste ...
:P " The built-in :Print
建议在脚本中使用用户定义命令的全名. :com[mand] *:com* *:command* 列出所有用户定义命令. 在列出命令时, 前两栏的字符表示 ! 命令有 -bang 属性 " 命令有 -register 属性 b 命令局部于当前缓冲 (下面有属性的详细描述) :com[mand]
{cmd}
列出以{cmd}
开头的用户命令 *E174* *E182* :com[mand][!] [{attr}
...]{cmd}
{rep}
定义一个用户命令. 命令的名字是{cmd}
并且它替换的文本是{rep}
. 该命令的属性 (参考下面)是{attr}
. 如果该命令已存在, 会报告错误, 除非已经指定了一个 !, 这种情况下命令被重定义. :delc[ommand]{cmd}
*:delc* *:delcommand* *E184* 删除用户定义命令{cmd}
. :comc[lear] *:comc* *:comclear* 删除所有用户定义命令. 命令属性 Vim 和任何其它 ex 命令一样对待用户定义命令. 它能有参数, 或者指定一个类别. 以文件名, 缓冲等等作为参数. 它怎样工作完全取决于命令的属性, 属性在命令被 定义时被指定. 有很多种属性, 分为四大类: 参数处理, 填充行为, 范围处理, 和特殊情况. 属性在下面分类描述. 参数处理 *E175* *E176* 缺省时, 一个用户定义的命令将不接受参数 (如果使用了任何参数会报错). 但是指定命令可以接受参数是可能的, 使用 -nargs 属性, 有效的情况有: -nargs=0 不允许有参数 (缺省情况) -nargs=1 要求一个参数. -nargs=* 允许任何数目的参数 (0, 1, or many) -nargs=? 允许 0 或 1 个参数 -nargs=+ 必需给出参数, 但是数目任意 在这个上下文里, 参数被认为是以空格或Tab来分隔的. 自动完成行为 *:command-completion* *E179* *E180* *E181* 缺省时, 用户定义命令的参数不进行自动完成. 但是, 通过指定以下的一个或多个属性 后, 参数可以进行自动完成 -complete=augroup autocmd 组 -complete=buffer 缓冲名 -complete=command Ex 命令 (及其参数) -complete=dir 目录名 -complete=environment 环境变量名 -complete=event autocommand 事件 -complete=expression Vim 表达式 -complete=file 文件和目录名 -complete=function 函数名 -complete=help 帮助主题 -complete=highlight 高亮显示的部分 -complete=mapping 映射名 -complete=menu 菜单 -complete=option 选项 -complete=tag 标签 -complete=tag_listfiles 当敲入CTRL-D
时显示的标签, 文件名 -complete=var 用户变量 -complete=custom,{func}
用户自定制的自动完成, 通过{func}
来定义 用户定制的自动完成 *:command-completion-custom* *E467* *E468* 通过 "custom,{func}
" 自动完成参数可以定义定制的自动完成方案. 其中{func}
是有 如下原型的函数: :function{func}
(ArgLead, CmdLine, CursorPos) 该函数不需要使用所有的这些参数, 但是应该提供自动完成候选作为返回值, 每行一个字 串用换行符分隔. 该函数的参数是: ArgLead 当前自动完成的前导参数 CmdLine 完整的命令行 CursorPos 里面的光标位置 该函数可能要根据这些来判别上下文. 它不必要来过滤对应 ArgLead (里面的隐式规 则) 的候选. 在函数返回时 Vim 将用它的正则表达式引擎来进行过滤, 这种方式在 大多数情况下效率更高. 以下的例子为一个 Finger 命令列出用户名:com -complete=custom,ListUsers -nargs=1 Finger !finger
<args>
:fun ListUsers(A,L,P)
: return system("cut -d: -f1 /etc/passwd")
:endfun
范围处理 *E177* *E178* 缺省时, 用户定义的命令不接受一个行号范围. 但是, 也可以指定命令能够接受 一个范围 (-range 熟悉), 或者接受一个任意的数量值, 可以是行号位置 (-range=N, 与 |:split| 命令类似) 或者作为一个 "数量" 参数 (-count=N, 与 |:Next| 命令类似). 可能的属性有: -range 允许使用范围, 缺省为当前行 -range=% 允许使用范围, 缺省是整个文件 (1,$) -range=N 一个数量 (缺省是 N) 用来指定行号位置 (与 |:split|类似) -count=N 一个数量 (缺省是 N) 用来指定行号位置, 或者是作为一个初始 参数 (与 |:Next| 类似) 指定一个 -count (不设缺省值) 和 -count=0 一样 注意 -range=N 和 -count=N 是互斥的 - 只应该指定其中的一个. 特殊情况 有一些特殊情况: -bang 这些命令可以使用一个 ! 修饰服 (和 :q 或 :w 类似) -bar 这些命令可以跟随一个 "|" 和其它命令. 那么命令参数中就 不允许有 "|" . 用一个 " 可以开始一个注释. -register 给这些命令的第一个参数可以是一个可选的寄存器名 (和 :del, :put, :yank 类似). -buffer 这些命令仅在当前缓冲里有效. 在 -count 和 -register 属性的情况里, 如果可选的参数被应用, 它会被从参数列 表中删除, 并且在分别的文本替换中有效. 文本替换 一个用户定义命令的文本替换被以特殊的变换序列来扫描, 使用
<...>
符号. 变换 序列被从命令行输入的值替换, 并且所有其它文本都被不变的拷贝. 结果字串被作为 一个 Ex 命令来执行. 如果初始的 < 变换序列前面有一个反斜杠, 该序列被不变的 拷贝. 有效的变换序列有 *<line1>*<line1>
命令处理范围的开始行. *<line2>*<line2>
命令处理范围的末尾行. *<count>*<count>
被应用的任意数量 (在 '-range' 和 '-count' 属性中描述过的). *<bang>*<bang>
(参考 '-bang' 属性) 扩展出一个 ! 如果该命令执行的 时候带了一个 ! 修饰符, 否则什么也不扩展. *<reg>* *<register>*<reg>
(参考 '-register' 属性) 可选的寄存器, 如果指定的话. 否则什么也不扩展.<register>
是它的一个同义词. *<args>*<args>
命令的参数, 与被提供的精确相同 (但正如上面提到过的, 任何 数量值或寄存器会消耗一些参数, 这时它们已不是<args>
的一 部分了).<lt>
一个单独的 '<' (小于号) 字符. 在变换序列扩展时如果需要得到 一个字面意义的大小于号的拷贝时需要. - 例如, 要获得<bang>
, 使用<lt>
bang>. *<q-args>* 如果一个变换序列的最前两个字符是 "q-" (例如,<q-args>
) 那么该值以一种使它在 用于一个表达式时有效的方式被引用. 这种方式把参数当做单个值. 要允许命令把它们的参数传送给一个用户定义的函数, 有一种特殊的形式<f-args>
("function args"). 它在空格和Tab的地方分裂命令行参数, 分别的引用每个参数, 并且<f-args>
序列被逗号分隔的引用参数列表所替换. 参考下面的 Mycmd 示例. 当没有参数时,<f-args>
也没有参数. 示例
" 删除从这里到末尾的所有东西
:com Ddel +,$d
" 把当前缓冲改名
:com -nargs=1 -bang -complete=file Ren f
<args>
|w<bang>
" 用一个文件的内容来替换某个范围内的内容
" (以一行输入所有这些东西)
:com -range -nargs=1 -complete=file
Replace
<line1>
-pu_|<line1>,<line2>d
|r<args>
|<line1>
d
" 计算范围内的行数
:com! -range -nargs=0 Lines :echo
<line2>
-<line1>
+ 1 "lines"
" 调用一个用户函数 (
<f-args>
的示例):com -nargs=* Mycmd call Myfunc(
<f-args>
)当被这样执行时:
:Mycmd arg1 arg2
它将调用::call Myfunc("arg1","arg2")
:" 一个更实用的例子
:function Allargs(command)
: let i = 0
: while i < argc()
: if filereadable(argv(i))
: execute "e " . argv(i)
: execute a:command
: endif
: let i = i + 1
: endwhile
:endfunction
:command -nargs=+ -complete=command Allargs call Allargs(
<q-args>
)命令 Allargs 以任意 Vim 命令作为参数并在参数列表里的所有文件上执行它. 使用示例 (注意使用 "e" 标志来忽略错误并且用 "update" 命令来写被修改的缓冲区): :Allargs %s/foo/bar/ge|update 它将调用:
:call Allargs("%s/foo/bar/ge|update")
当在一个脚本里定义一个用户命令时, 它可以调用局部与脚本中的函数并且使用映射 来局部于该脚本. 当用户调用用户命令时, 该命令将运行在定义它的脚本的上下文里, 如果一个命令中使用了 |<SID>| 的时候这很重要. vim:tw=78:ts=8:ft=help:norl:
Generated by vim2html on Tue Jul 27 00:35:24 CST 2004