地图

Nvim :help 页面,生成来源 使用 tree-sitter-vimdoc 解析器。


按键映射、缩写和用户定义的命令。
本主题在用户手册的 05.324.740.1 节中介绍。

1. 按键映射 映射

按键映射用于更改键入键的含义。最常见的用法是为功能键定义命令序列。示例
:map <F2> a<C-R>=strftime("%c")<CR><Esc>
这会在光标后追加当前日期和时间(以 <> 表示法 <>)。

1.1 MAP 命令 :map-commands

有一些命令用于输入新的映射、删除映射和列出映射。有关“map”的各种形式及其与模式的关系,请参见 map-overview
{lhs} 表示左侧 {lhs} {rhs} 表示右侧 {rhs}
:map {lhs} {rhs} mapmode-nvo :map
:nm[ap] {lhs} {rhs} mapmode-n :nm :nmap :vm[ap] {lhs} {rhs} mapmode-v :vm :vmap :xm[ap] {lhs} {rhs} mapmode-x :xm :xmap :smap {lhs} {rhs} mapmode-s :smap
:om[ap] {lhs} {rhs} mapmode-o :om :omap :map! {lhs} {rhs} mapmode-ic :map!
:im[ap] {lhs} {rhs} mapmode-i :im :imap :lm[ap] {lhs} {rhs} mapmode-l :lm :lma :lmap :cm[ap] {lhs} {rhs} mapmode-c :cm :cmap :tma[p] {lhs} {rhs} mapmode-t :tma :tmap 将键序列 {lhs} 映射到 {rhs},适用于 map 命令适用的模式。然后,包括 {rhs} 在内的结果将进一步扫描映射。这允许嵌套和递归使用映射。 注意: {rhs} 中包含尾随空格,因为空格是有效的 Normal 模式命令。参见 map-trailing-white
:nore :norem :no[remap] {lhs} {rhs} mapmode-nvo :no :noremap :nor :nn[oremap] {lhs} {rhs} mapmode-n :nn :nnoremap :vn[oremap] {lhs} {rhs} mapmode-v :vn :vnoremap :xn[oremap] {lhs} {rhs} mapmode-x :xn :xnoremap :snor[emap] {lhs} {rhs} mapmode-s :snor :snore :snoremap :ono[remap] {lhs} {rhs} mapmode-o :ono :onoremap :no[remap]! {lhs} {rhs} mapmode-ic :no! :noremap! :ino[remap] {lhs} {rhs} mapmode-i :ino :inor :inoremap :ln[oremap] {lhs} {rhs} mapmode-l :ln :lnoremap :cno[remap] {lhs} {rhs} mapmode-c :cno :cnor :cnoremap :tno[remap] {lhs} {rhs} mapmode-t :tno :tnoremap 将键序列 {lhs} 映射到 {rhs},适用于 map 命令适用的模式。禁止映射 {rhs},以避免嵌套和递归映射。通常用于重新定义命令。 注意: {rhs} 中的键也不会触发缩写,但 i_CTRL-]c_CTRL-] 除外。 注意:<Plug> 出现在 {rhs} 中时,即使禁止重新映射,此部分也始终应用。
:unm[ap] {lhs} mapmode-nvo :unm :unmap :nun[map] {lhs} mapmode-n :nun :nunmap :vu[nmap] {lhs} mapmode-v :vu :vunmap :xu[nmap] {lhs} mapmode-x :xu :xunmap :sunm[ap] {lhs} mapmode-s :sunm :sunmap :ou[nmap] {lhs} mapmode-o :ou :ounmap :unm[ap]! {lhs} mapmode-ic :unm! :unmap! :iu[nmap] {lhs} mapmode-i :iu :iunmap :lu[nmap] {lhs} mapmode-l :lu :lunmap :cu[nmap] {lhs} mapmode-c :cu :cun :cunmap :tunma[p] {lhs} mapmode-t :tunma :tunmap 删除 {lhs} 的映射,适用于 map 命令适用的模式。映射可能仍为其他适用模式定义。它在 {lhs} 与映射的 {rhs} 匹配时也有效。这是因为应用了缩写。 注意: {lhs} 中包含尾随空格。参见 map-trailing-white
:mapc[lear] mapmode-nvo :mapc :mapclear :nmapc[lear] mapmode-n :nmapc :nmapclear :vmapc[lear] mapmode-v :vmapc :vmapclear :xmapc[lear] mapmode-x :xmapc :xmapclear :smapc[lear] mapmode-s :smapc :smapclear :omapc[lear] mapmode-o :omapc :omapclear :mapc[lear]! mapmode-ic :mapc! :mapclear! :imapc[lear] mapmode-i :imapc :imapclear :lmapc[lear] mapmode-l :lmapc :lmapclear :cmapc[lear] mapmode-c :cmapc :cmapclear :tmapc[lear] mapmode-t :tmapc :tmapclear 删除适用于 map 命令的模式的所有映射。使用 <buffer> 参数删除缓冲区本地映射 :map-<buffer> 警告: 这还会删除 default-mappings
:map mapmode-nvo :nm[ap] mapmode-n :vm[ap] mapmode-v :xm[ap] mapmode-x :sm[ap] mapmode-s :om[ap] mapmode-o :map! mapmode-ic :im[ap] mapmode-i :lm[ap] mapmode-l :cm[ap] mapmode-c :tma[p] mapmode-t 列出适用于 map 命令的模式的所有按键映射。请注意,":map" 和 ":map!" 使用最频繁,因为它们包含其他模式。
:map {lhs} mapmode-nvo :map_l
:nm[ap] {lhs} mapmode-n :nmap_l
:vm[ap] {lhs} mapmode-v :vmap_l
:xm[ap] {lhs} mapmode-x :xmap_l
:sm[ap] {lhs} mapmode-s :smap_l
:om[ap] {lhs} mapmode-o :omap_l
:map! {lhs} mapmode-ic :map_l!
:im[ap] {lhs} mapmode-i :imap_l
:lm[ap] {lhs} mapmode-l :lmap_l
:cm[ap] {lhs} mapmode-c :cmap_l
:tma[p] {lhs} mapmode-t :tmap_l
列出适用于 map 命令的模式中以 {lhs} 开头的键序列的按键映射。
这些命令用于将键或键序列映射到字符串。您可以使用它将命令序列放在功能键下,将一个键翻译成另一个键,等等。有关如何保存和恢复当前映射,请参见 :mkexrc
map-ambiguous
当两个映射以相同的字符序列开头时,它们是模棱两可的。示例
:imap aa foo
:imap aaa bar
当 Vim 读取到 "aa" 时,它需要读取另一个字符才能决定是映射 "aa" 还是 "aaa"。这意味着在输入 "aa" 后,映射不会立即展开,Vim 会等待另一个字符。如果你输入空格,那么 "foo" 将被插入,加上空格。如果你输入 "a",那么 "bar" 将被插入。
尾随空白
map-trailing-white
此 unmap 命令不起作用
:map @@ foo
:unmap @@ | print
因为它尝试解除 "@@ " 的映射,包括命令分隔符 "|" 之前的空格。其他包含尾随空格的示例
unmap @@ 
unmap @@     " comment
将会出现一个错误,很难识别,因为 `unmap @@` 中的尾随空格字符不可见。
一个通用的解决方案是将命令分隔符 "|" 放置在映射键的后面。之后可以跟随空格和注释
unmap @@|    " comment

1.2 特殊参数 :map-arguments

"<buffer>", "<nowait>", "<silent>", "<script>", "<expr>" 和 "<unique>" 可以按任意顺序使用。它们必须出现在命令之后,任何其他参数之前。
:map-local :map-<buffer> :map-buffer E224 E225 如果这些命令的第一个参数是 "<buffer>",则映射将仅在当前缓冲区内有效。示例
:map <buffer>  ,w  /[.,;]<CR>
然后你可以在另一个缓冲区中将 ",w" 映射到其他内容
:map <buffer>  ,w  /[#&!]<CR>
局部缓冲区映射在全局映射之前使用。请参阅下面的 `<nowait>`,以使较短的局部映射在存在较长的全局映射时不生效。"<buffer>" 参数也可以用于清除映射
:unmap <buffer> ,w
:mapclear <buffer>
当缓冲区被删除时,局部映射也会被清除,但当缓冲区被卸载时不会被清除。就像局部选项值一样。另请参阅 map-precedence
:map-<nowait> :map-nowait 当为 "," 定义一个缓冲区局部映射时,可能存在一个以 "," 开头的全局映射。然后你需要输入另一个字符才能让 Vim 知道是使用 "," 映射还是更长的映射。为了避免这种情况,请添加 `<nowait>` 参数。然后当映射匹配时将使用它,Vim 不会等待输入更多字符。但是,如果字符已经输入,它们会被使用。请注意,当 `<nowait>` 映射完全匹配并在任何部分匹配之前找到时,这将起作用。当以下情况发生时,这将起作用
只有一个匹配的缓冲区局部映射,因为它们总是先于全局映射找到。
存在另一个部分匹配的缓冲区局部映射,但它是在前面定义的(最后定义的映射优先找到)。
:map-<silent> :map-silent 为了定义一个在命令行上不会被回显的映射,请添加 "<silent>" 作为第一个参数。示例
:map <silent> ,h /Header<CR>
使用此映射时,搜索字符串不会被回显。但是,执行的命令发出的消息仍然会给出。要同时关闭它们,请在执行的命令中添加 ":silent"
:map <silent> ,h :exe ":silent normal /Header\r"<CR>
请注意,命令的效果也可能被静默,例如,当映射选择命令行自动完成的另一个条目时,它不会被显示。提示仍然会给出,例如,对于 inputdialog()。在缩写中使用 "<silent>" 是可能的,但会导致命令行的重绘失败。
:map-<script> :map-script 如果这些命令的第一个参数是 "<script>",并且它用于定义一个新的映射或缩写,则映射将只使用在脚本中局部定义的映射(以 "<SID>" 开头)来重新映射 `{rhs}` 中的字符。这可以用于避免来自脚本外部的映射干扰(例如,当在 mswin.vim 中重新映射 `CTRL-V` 时),但可以使用在脚本中定义的其他映射。注意:":map `<script>`" 和 ":noremap `<script>`" 做相同的事情。"<script>" 覆盖命令名称。使用 ":noremap `<script>`" 更好,因为这样更清楚地表明重新映射是被(大部分)禁用的。
:map-<unique> :map-unique E226 E227 如果这些命令的第一个参数是 "<unique>",并且它用于定义一个新的映射或缩写,则如果映射或缩写已经存在,则该命令将失败。示例
:map <unique> ,w  /[#&!]<CR>
当定义一个局部映射时,还会检查是否已经存在一个等于它的全局映射。示例:什么会失败
:map ,w  /[#&!]<CR>
:map <buffer> <unique> ,w  /[.,;]<CR>
如果你想映射一个键,然后让它执行它最初被映射到的操作,请查看 maparg()
:map-<expr> :map-expression 如果这些命令的第一个参数是 "<expr>",并且它用于定义一个新的映射或缩写,则参数是一个表达式。表达式将被求值以获得用于的 `{rhs}`。示例
:inoremap <expr> . <SID>InsertDot()
s:InsertDot() 函数的结果将被插入。它可以检查光标之前的文本,并在满足某些条件时启动 omni 自动完成。使用脚本局部函数更好,以避免污染全局命名空间。在 RHS 中使用 `<SID>`,这样就可以找到定义映射的脚本。
对于缩写,v:char 被设置为用来触发缩写的字符。你可以使用它来决定如何扩展 `{lhs}`。你既不应该插入也不应该更改 v:char。
如果你想让映射不做任何事情,你可以让表达式求值为一个空字符串。如果发生了一些需要 Vim 遍历主循环的事情(例如更新显示),请返回 "\<Ignore>"。这类似于 "nothing",但可以让 Vim 从等待输入的循环中返回。
请记住,表达式可能在查找键入提示时被求值,在执行前一个命令之前。例如
func StoreColumn()
  let g:column = col('.')
  return 'x'
endfunc
nnoremap <expr> x StoreColumn()
nmap ! f!x
你会注意到 g:column 的值来自执行 "f!" 之前,因为 "x" 在执行 "f!" 之前被求值。这可以通过在表达式映射的字符之前插入 `<Ignore>` 来解决
nmap ! f!<Ignore>x
对副作用要非常小心!表达式在获取字符时被求值,你很可能使命令无法正常工作。因此,以下内容被禁止用于 `<expr>` 映射
更改缓冲区文本 textlock
编辑另一个缓冲区。
:normal 命令。
移动光标是允许的,但之后会恢复。
如果 cmdline 被改变,则会恢复旧的文本和光标位置。如果你想让映射执行这些操作中的任何一个,请让返回的字符执行这些操作,或者使用 <Cmd> 映射。
你可以使用 getchar(),它会消耗任何键入提示。例如,如果你有以下映射
inoremap <expr> <C-L> nr2char(getchar())
inoremap <expr> <C-L>x "foo"
如果你现在输入 `CTRL-L`,什么也不会发生,Vim 需要下一个字符来决定使用哪个映射。如果你输入 'x',则使用第二个映射并插入 "foo"。如果你输入任何其他键,则使用第一个映射,getchar() 获取输入的键并将其返回。
下面是一个插入递增列表编号的示例
let counter = 0
inoremap <expr> <C-L> ListItem()
inoremap <expr> <C-R> ListReset()
func ListItem()
  let g:counter += 1
  return g:counter .. '. '
endfunc
func ListReset()
  let g:counter = 0
  return ''
endfunc
CTRL-L 插入下一个数字,`CTRL-R` 重置计数。`CTRL-R` 返回一个空字符串,因此不会插入任何内容。
请注意,在其他文本之前使用 0x80 作为单个字节不起作用,它将被视为一个特殊键。
<Cmd> :map-cmd `<Cmd>` 伪键开始一个 "命令映射",它直接执行命令,而不会改变模式。在映射的 `{rhs}` 中,你可能使用 ":...<CR>" 的地方,你可以改为使用 "<Cmd>...<CR>"。示例
noremap x <Cmd>echo mode(1)<CR>
这比在 Visual 和 Operator-pending 模式下使用 `:<C-U>` 或者在 Insert 模式下使用 `<C-O>:` 更灵活,因为命令直接在当前模式下执行,而不是总是转到 Normal 模式。Visual 模式被保留,所以不需要使用 gv 的技巧。命令可以直接在 Command-line 模式下调用(否则需要使用计时器技巧)。使用 `<Cmd>` 在 Insert 模式中执行一半的示例
nnoremap <F3> aText <Cmd>echo mode(1)<CR> Added<Esc>
与 `<expr>` 映射不同,`<Cmd>` 命令没有特殊限制:它就像一个(不受限制的)autocommand 被调用或者一个异步事件被处理一样被执行。
注意
因为 `<Cmd>` 避免了模式改变(不像 ":"),所以它不会触发 CmdlineEnterCmdlineLeave 事件。这有助于提高性能。
出于同样的原因,像 `<C-R>` `<C-W>` 这样的 keycodes 被解释为普通的、未映射的键。
该命令不会被回显,不需要 `<silent>`。
`{rhs}` 不受缩写或其他映射的影响,即使映射是递归的。
在 Visual 模式下,你可以使用 `line('v')` 和 `col('v')` 来获取 Visual 区域的一端,光标位于另一端。
E1255 E1136 `<Cmd>` 命令必须终止,也就是说,它们必须在映射定义的 `{rhs}` 中后面跟着 `<CR>`。Command-line 模式永远不会进入。要在 `{rhs}` 中使用一个字面 `<CR>`,请使用 <lt>
有七组映射
对于 Normal 模式:当输入命令时。
对于 Visual 模式:当 Visual 区域被高亮显示时输入命令。
对于 Select 模式:与 Visual 模式类似,但输入文本会替换选择内容。
对于 Operator-pending 模式:当一个操作正在等待时(在 "d"、"y"、"c" 等之后)。请参阅下面的内容:omap-info
对于 Insert 模式。这些也在 Replace 模式下使用。
对于 Command-line 模式:当输入 ":" 或 "/" 命令时。
对于 Terminal 模式:当在一个 :terminal 缓冲区中输入时。
特殊情况:在 Normal 模式下为命令输入计数时,禁用映射零。这样就可以映射零,而不会导致无法输入带零的计数。
map-overview map-modes 概述哪个 map 命令在哪个模式下有效。更多细节请见下文。
命令模式
:map :noremap :unmap Normal, Visual, Select, Operator-pending :nmap :nnoremap :nunmap Normal :vmap :vnoremap :vunmap Visual 和 Select :smap :snoremap :sunmap Select :xmap :xnoremap :xunmap Visual :omap :onoremap :ounmap Operator-pending :map! :noremap! :unmap! Insert 和 Command-line :imap :inoremap :iunmap Insert :lmap :lnoremap :lunmap Insert, Command-line, Lang-Arg :cmap :cnoremap :cunmap Command-line :tmap :tnoremap :tunmap Terminal
相同信息在表格中:map-table
模式 | Norm | Ins | Cmd | Vis | Sel | Opr | Term | Lang |
命令 +------+-----+-----+-----+-----+-----+------+------+
[nore]map | yes | - | - | yes | yes | yes | - | - | n[nore]map | yes | - | - | - | - | - | - | - | [nore]map! | - | yes | yes | - | - | - | - | - | i[nore]map | - | yes | - | - | - | - | - | - | c[nore]map | - | - | yes | - | - | - | - | - | v[nore]map | - | - | - | yes | yes | - | - | - | x[nore]map | - | - | - | yes | - | - | - | - | s[nore]map | - | - | - | - | yes | - | - | - | o[nore]map | - | - | - | - | - | yes | - | - | t[nore]map | - | - | - | - | - | - | yes | - | l[nore]map | - | yes | yes | - | - | - | - | yes |
命令模式
普通 可视+选择 操作符等待
:map :noremap :unmap :mapclear yes yes yes :nmap :nnoremap :nunmap :nmapclear yes - - :vmap :vnoremap :vunmap :vmapclear - yes - :omap :onoremap :ounmap :omapclear - - yes
:nunmap 也可用于寺院之外。 mapmode-x mapmode-s 一些命令在可视和选择模式下都有效,而另一些则只在一个模式下有效。请注意,通常在可视和选择模式都适用时会提到“可视”。 选择模式映射 注意:在选择模式下映射可打印字符可能会使用户感到困惑。最好在可打印字符中显式使用 :xmap 和 :smap。或者在定义映射后使用 :sunmap。
命令模式
可视 选择
:vmap :vnoremap :vunmap :vmapclear yes yes :xmap :xnoremap :xunmap :xmapclear yes - :smap :snoremap :sunmap :smapclear - yes
mapmode-ic mapmode-i mapmode-c mapmode-l 一些命令在插入模式和命令行模式下都有效,而另一些则无效
命令模式
插入 命令行 语言-参数
:map! :noremap! :unmap! :mapclear! yes yes - :imap :inoremap :iunmap :imapclear yes - - :cmap :cnoremap :cunmap :cmapclear - yes - :lmap :lnoremap :lunmap :lmapclear yes* yes* yes*
* 如果 'iminsert' 为 1,请参见下文 语言映射
原始的 Vi 没有为普通/可视/操作符等待模式和插入/命令行模式设置单独的映射。因此,":map" 和 ":map!" 命令会输入和显示多种模式的映射。在 Vim 中,您可以使用 ":nmap"、":vmap"、":omap"、":cmap" 和 ":imap" 命令分别输入每种模式的映射。
omap-info
操作符等待映射可用于定义可与任何操作符一起使用的移动命令。简单示例
:omap { w
使 "y{" 类似于 "yw",而 "d{" 类似于 "dw"。
要忽略起始光标位置并选择不同的文本,您可以使 omap 启动可视模式以选择要操作的文本。操作当前行中函数名的示例
onoremap <silent> F :<C-U>normal! 0f(hviw<CR>
CTRL-U (<C-U>) 用于删除 Vim 可能插入的范围。普通模式命令找到第一个 '(' 字符并选择它之前的第一个单词。这通常是函数名。
要为普通和可视模式输入映射,但不要为操作符等待模式输入映射,请先为所有三种模式定义映射,然后取消映射操作符等待模式的映射
:map    xx something-difficult
:ounmap xx
类似地,用于可视和操作符等待模式或普通和操作符等待模式的映射。
language-mapping
":lmap" 定义适用于以下内容的映射:
插入模式
命令行模式
输入搜索模式时
接受文本字符的命令的参数,如 "r" 和 "f"
用于 input() 行 一般而言:无论何时要键入缓冲区中的文本的一部分,而不是 Vim 命令字符。"语言-参数" 并不是真正的另一种模式,它只是在此用于这种情况。加载一组相关语言映射的最简单方法是使用 'keymap' 选项。请参见 45.5。在插入模式和命令行模式下,可以使用 CTRL-^ 命令 i_CTRL-^ c_CTRL-^ 禁用映射。这些命令会更改 'iminsert' 选项的值。当开始输入普通命令行(而不是搜索模式)时,映射将被禁用,直到键入 CTRL-^ 为止。最后使用的状态将分别记住插入模式和搜索模式。插入模式的状态在将字符作为命令(如 "f" 或 "t")的参数键入时也会使用。语言映射永远不会应用于已映射的字符。它们仅用于键入的字符。这假定在键入映射时已完成语言映射。相应地,语言映射在录制宏时应用,而不是在应用宏时应用。

1.4 列出映射 map-listing

列出映射时,前两列中的字符为
CHAR MODE
<Space> 普通、可视、选择和操作符等待 n 普通 v 可视和选择 s 选择 x 可视 o 操作符等待 ! 插入和命令行 i 插入 l ":lmap" 映射,用于插入、命令行和语言-参数 c 命令行 t 终端作业
{rhs} 之前,可能会出现特殊字符: * 表示不可重新映射 & 表示只有脚本本地映射可重新映射 @ 表示缓冲区本地映射
{lhs} 之后的第一个非空格字符到行尾(或 '|')的所有内容都被视为 {rhs} 的一部分。这允许 {rhs} 以空格结尾。
注意:当使用可视模式的映射时,您可以使用 "'<" 标记,它是当前缓冲区中最后选择的可视区域的开头 '<
:filter 命令可用于选择要列出的映射。模式与 {lhs}{rhs} 在原始形式中匹配。如果使用 nvim_set_keymap()nvim_buf_set_keymap() 添加了描述,则模式也会与它匹配。
:map-verbose
'verbose' 非零时,列出键映射也会显示其最后定义的位置。示例
:verbose map <C-W>*
n  <C-W>*      * <C-W><C-S>*
        Last set from ~/.config/nvim/init.vim
有关更多信息,请参见 :verbose-cmd

1.5 映射特殊键 :map-special-keys

要映射功能键,请使用其内部代码。要输入此类映射,请键入 CTRL-K,然后按功能键,或使用形式 "<F2>"、"<F10>"、"<Up>"、"<S-Down>"、"<S-F7>" 等(请参见键表 key-notation,所有从 <Up> 开始的键都可以使用)。

1.6 特殊字符 :map-special-chars

map_backslash map-backslash 请注意,这里只提到 CTRL-V 作为映射和缩写的特殊字符。当 'cpoptions' 不包含 'B' 时,反斜杠也可以像 CTRL-V 一样使用。然后可以使用 <> 符号 <>。但是您不能像 CTRL-V 一样使用 "<C-V>" 来转义后续内容的特殊含义。
要映射反斜杠,或在 {rhs} 中按字面意思使用反斜杠,可以使用特殊序列 "<Bslash>"。这样可以避免在使用嵌套映射时需要双反斜杠。
map_CTRL-C map-CTRL-C{lhs} 中使用 CTRL-C 是可能的,但它只会在 Vim 等待键时有效,而不会在 Vim 忙于其他操作时有效。当 Vim 忙时,CTRL-C 会中断/终止命令。在 MS-Windows 上使用 GUI 版本时,可以将 CTRL-C 映射到允许将复制命令复制到剪贴板。使用 CTRL-Break 中断 Vim。
map_space_in_lhs map-space_in_lhs 要在 {lhs} 中包含空格,请在其前面加上 CTRL-V(对于每个空格,请键入两次 CTRL-V)。 map_space_in_rhs map-space_in_rhs 如果您想要一个以空格开头的 {rhs},请使用 "<Space>"。为了完全兼容 Vi(但不可读),请不要使用 <> 符号,请在 {rhs} 前面加上一个 CTRL-V(您必须键入两次 CTRL-V)。 map_empty_rhs map-empty_rhs 您可以通过在单个 CTRL-V 后什么都不键入来创建一个空的 {rhs}(您必须键入两次 CTRL-V)。不幸的是,您无法在 vimrc 文件中执行此操作。 <Nop> 获取不产生任何内容的映射的更简单方法是使用 "<Nop>" 作为 {rhs}。例如,要禁用功能键 8
:map  <F8>  <Nop>
:map! <F8>  <Nop>
map-multibyte
可以映射多字节字符,但只能映射整个字符。您不能只映射第一个字节。这样做是为了防止出现以下情况
:set encoding=latin1
:imap <M-C> foo
:set encoding=utf-8
<M-C> 的映射使用 latin1 编码定义,结果为 0xc3 字节。如果您在 UTF-8 编码中键入字符 á (0xe1 <M-a>),则它将是两个字节 0xc3 0xa1。您不希望 0xc3 字节被映射,否则将无法键入 á 字符。
<Leader> mapleader 要定义使用 "g:mapleader" 变量的映射,可以使用特殊字符串 "<Leader>"。它将被替换为 "g:mapleader" 的字符串值。如果 "g:mapleader" 未设置或为空,则使用反斜杠。示例
map <Leader>A  oanother line<Esc>
就像
map \A  oanother line<Esc>
但是之后
let mapleader = ","
它就像
map ,A  oanother line<Esc>
请注意,"g:mapleader" 的值在定义映射时使用。在之后更改 "g:mapleader" 对已定义的映射没有影响。
<LocalLeader> maplocalleader <LocalLeader><Leader> 相同,只是它使用 "maplocalleader" 而不是 "mapleader"。<LocalLeader> 用于对缓冲区本地的映射。示例
:map <buffer> <LocalLeader>A  oanother line<Esc>
在全局插件中,应使用 <Leader>,在文件类型插件中,应使用 <LocalLeader>。"mapleader" 和 "maplocalleader" 可以相等。尽管如此,如果将它们设为不同,则全局插件的映射与文件类型插件的映射冲突的可能性较小。例如,您可以将 "mapleader" 保持为默认的反斜杠,并将 "maplocalleader" 设置为下划线。
map-<SID>
在脚本中,可以使用特殊键名 "<SID>" 来定义对脚本本地的映射。有关详细信息,请参见 <SID>
<Plug>
特殊键名 "<Plug>" 可用于内部映射,它不会与任何键序列匹配。这在插件中很有用 使用-<Plug>
<MouseMove>
可以使用特殊键名“<MouseMove>”来处理鼠标移动。需要使用 'mousemoveevent' 启用它。可以使用 getmousepos() 函数获取鼠标位置。
<Char> <Char-> 要通过十进制、八进制或十六进制数字映射字符,可以使用 <Char> 结构:<Char-123> 字符 123 <Char-033> 字符 27 <Char-0x7f> 字符 127 <S-Char-114> 字符 114 ('r') <S-Char-114> 字符 114 ('r') 这是在 'keymap' 文件中指定(多字节)字符的有效方法。忽略大小写差异。
map-comments
在这些命令之后添加注释是不可能的,因为 " 字符被认为是 {lhs}{rhs} 的一部分。但是,可以使用 |",因为它会启动一个带有注释的新空命令。
map_bar map-bar 由于 '|' 字符用于将映射命令与下一个命令分隔开,因此您必须执行一些特殊操作才能在 {rhs} 中包含 '|'。有三种方法
use works when example
<Bar> always :map _l :!ls <Bar> more^M \| 'b' 不在 'cpoptions' 中:map _l :!ls \| more^M ^V| always :map _l :!ls ^V| more^M
(此处 ^V 代表 CTRL-V;要获取一个 CTRL-V,您必须键入两次;您不能在此处使用 <> 符号“<C-V>”)。
当您使用 'cpoptions' 的默认设置时,这三种方法都有效。
当 'b' 出现在 'cpoptions' 中时,“\|”将被识别为以 '\' 结尾的映射,然后是另一个命令。这与 Vi 兼容,但在与其他命令相比时不合理。
map_return map-return 当您的映射包含一个 Ex 命令时,您需要在它之后添加一个行终止符以执行它。建议为此使用 <CR>(参见 <>)。示例
:map  _ls  :!ls -l %:S<CR>:echo "the end"<CR>
要避免映射在插入或命令行模式下输入的字符,请先键入 CTRL-Vmap-error
请注意,当遇到错误(导致错误消息或可能导致蜂鸣声)时,不会执行映射的其余部分。这与 Vi 兼容。
请注意,命令 @zZtTfF[]rm'`"v 和 CTRL-X 的第二个字符(参数)不会被映射。这样做是为了能够使用所有命名的寄存器和标记,即使具有相同名称的命令已被映射。

1.7 映射哪些键 map-which-keys

如果您要映射某些内容,则需要选择要用于 {lhs} 的键。您必须避免用于 Vim 命令的键,否则您将无法再使用这些命令。以下是一些建议
功能键 <F2><F3> 等。以及移位的功能键 <S-F1><S-F2> 等。请注意,<F1> 已用于帮助命令。
Meta 键(按 ALT 键)。根据您的键盘,也可能使用重音字符。 :map-alt-keys
使用 '_' 或 ',' 字符,然后使用任何其他字符。'_' 和 ',' 命令在 Vim 中确实存在(参见 _,),但您可能从未使用过它们。
使用与另一个命令同义的键。例如:CTRL-PCTRL-N。使用额外的字符以允许更多映射。
<Leader> 定义的键和一个或多个其他键。这在脚本中尤其有用。 mapleader
请查看“index”文件以了解未使用的键,因此可以映射它们而不会丢失任何内置函数。您也可以使用“:help {key}^D”来确定一个键是否用于某个命令。({key} 是您要了解的特定键,^D 是 CTRL-D)。

1.8 示例 map-examples

一些示例(当您键入它们时:对于“<CR>”您键入四个字符)。
:map <F3>  o#include
:map <M-g> /foo<CR>cwbar<Esc>
:map _x    d/END/e<CR>
:map! qq   quadrillion questions
乘以计数
当您在触发映射之前键入一个计数时,它就像在 {lhs} 之前键入了计数一样。例如,使用此映射
:map <F4>  3w
键入 2<F4> 将导致“23w”。因此,不会移动 2 * 3 个单词,而是 23 个单词。如果您想乘以计数,请使用表达式寄存器
:map <F4>  @='3w'<CR>
引号之间的部分是正在执行的表达式。 @=

1.9 使用映射 map-typing

Vim 将把您键入的内容与已映射序列的开头进行比较。如果存在不完整的匹配,它将获取更多字符,直到存在完全匹配或根本不存在匹配为止。示例:如果您将 ! 映射到“qq”,则第一个 'q' 不会显示在屏幕上,直到您键入另一个字符为止。这是因为 Vim 不知道下一个字符是否将是 'q'。如果 'timeout' 选项已启用(这是默认设置),Vim 将仅等待一秒钟(或与 'timeoutlen' 选项指定的时长一样长)。之后,它假设 'q' 应该被解释为它本身。如果您键入速度很慢,或者您的系统速度很慢,请重置 'timeout' 选项。然后,您可能需要设置 'ttimeout' 选项。
map-precedence
缓冲区局部映射(使用 :map-<buffer> 定义)优先于全局映射。当缓冲区局部映射与全局映射相同,Vim 将使用缓冲区局部映射。此外,如果 Vim 使用 <nowait> 定义了一个完整的映射,即使更长的映射具有相同的词缀,Vim 也会立即使用它。例如,给定以下两个映射
:map <buffer> <nowait> \a   :echo "Local \a"<CR>
:map                   \abc :echo "Global \abc"<CR>
键入 \a 时,将立即使用缓冲区局部映射。Vim 不会等待更多字符来查看用户是否可能正在键入 \abc。
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 命令,映射将持续进行,直到发现文本不是 {lhs} 的一部分为止。例如,如果您使用
:map x y
:map y x
Vim 将用 y 替换 x,然后用 x 替换 y,依此类推。当这种情况发生 'maxmapdepth' 次(默认值 1000)时,Vim 将给出错误消息“递归映射”。
:map-undo
如果您在映射序列中包含撤销命令,这将使文本回到执行宏之前的状态。只要映射序列中只有一个撤销命令,这与原始 Vi 兼容(在映射序列中具有两个撤销命令在原始 Vi 中没有意义,您将回到第一个撤销之前的文本)。

1.10 映射 ALT 键 :map-alt-keys

为了使映射命令更易读,可以使用 <A-k> 形式。请注意,<A-k><A-K> 不同,后者将使用大写字母。实际上,<A-K><A-S-K> 是相同的。您也可以使用“M”代替“A”。如果您有实际的 Meta 修饰键,请参见 :map-meta-keys
在 GUI 中,Nvim 自己处理 ALT 键,因此使用 ALT 映射键应该始终有效。但在终端中,Nvim 会获得一系列字节,并且必须确定是否按下了 ALT。终端可以使用 ESC 来指示已按下 ALT。如果 ESC 后面跟着 {key}'ttimeoutlen' 毫秒内,ESC 将被解释为:<ALT-{key}> 否则它将被解释为两次按键:<ESC> {key}

1.11 映射 META 键 :map-meta-keys

使用 Meta 修饰符映射键的工作方式与使用 Alt 键非常相似。键盘上哪个键产生 Meta 修饰符取决于您的键盘和配置。
请注意,映射 <M-a> 实际上是使用 Alt 键。这可能会让人困惑!它无法更改,它不向后兼容。
对于 Meta 修饰符,使用字符“T”。例如,要在插入模式下映射 Meta-b
:imap <T-b> terrible
1.12 映射 SUPER 键或 COMMAND 键 :map-super-keys :map-cmd-key
如果终端或 GUI 支持 Super / Command 修饰符,则可以使用它。字符“D”用于 Super / Command 修饰符。
例如,要在插入模式下映射 Command-b
:imap <D-b> barritone

1.13 映射运算符 :map-operator

运算符用于 {motion} 命令之前。要定义您自己的运算符,您必须创建一个映射,该映射首先设置 'operatorfunc' 选项,然后调用 g@ 运算符。在用户键入 {motion} 命令之后,将调用指定的函数。
g@ E774 E775 g@{motion} 调用由 'operatorfunc' 选项设置的函数。'[ 标记位于 {motion} 移动的文本的开头,']' 标记位于文本的最后一个字符上。该函数使用一个字符串参数调用:"line" {motion}行级别的 "char" {motion}字符级别的 "block" {motion}块级别的视觉模式 可以强制类型,参见 强制运动
以下示例使用 <F4> 统计空格数量。
nnoremap <expr> <F4> CountSpaces()
xnoremap <expr> <F4> CountSpaces()
" doubling <F4> works on a line
nnoremap <expr> <F4><F4> CountSpaces() .. '_'
function CountSpaces(context = {}, type = '') abort
  if a:type == ''
    let context = #{
      \ dot_command: v:false,
      \ extend_block: '',
      \ virtualedit: [&l:virtualedit, &g:virtualedit],
      \ }
    let &operatorfunc = function('CountSpaces', [context])
    set virtualedit=block
    return 'g@'
  endif
  let save = #{
    \ clipboard: &clipboard,
    \ selection: &selection,
    \ virtualedit: [&l:virtualedit, &g:virtualedit],
    \ register: getreginfo('"'),
    \ visual_marks: [getpos("'<"), getpos("'>")],
    \ }
  try
    set clipboard= selection=inclusive virtualedit=
    let commands = #{
      \ line: "'[V']",
      \ char: "`[v`]",
      \ block: "`[\<C-V>`]",
      \ }[a:type]
    let [_, _, col, off] = getpos("']")
    if off != 0
      let vcol = getline("'[")->strpart(0, col + off)->strdisplaywidth()
      if vcol >= [line("'["), '$']->virtcol() - 1
        let a:context.extend_block = '$'
      else
        let a:context.extend_block = vcol .. '|'
      endif
    endif
    if a:context.extend_block != ''
      let commands ..= 'oO' .. a:context.extend_block
    endif
    let commands ..= 'y'
    execute 'silent noautocmd keepjumps normal! ' .. commands
    echomsg getreg('"')->count(' ')
  finally
    call setreg('"', save.register)
    call setpos("'<", save.visual_marks[0])
    call setpos("'>", save.visual_marks[1])
    let &clipboard = save.clipboard
    let &selection = save.selection
    let [&l:virtualedit, &g:virtualedit] = get(a:context.dot_command ? save : a:context, 'virtualedit')
    let a:context.dot_command = v:true
  endtry
endfunction
使用 <expr> 映射可以获取任何前缀计数和寄存器。这还避免了使用命令行,因为命令行会触发 CmdlineEnter 和 CmdlineLeave 自动命令。
请注意,'selection' 选项暂时设置为 "inclusive",以便能够使用从 '[' 到 ']' 标记的视觉模式精确地粘贴正确的文本。
还要注意,'clipboard' 选项暂时清空,以避免覆盖 "*"+ 寄存器,如果其值包含项目 unnamedunnamedplus
mode() 函数将返回应用操作符后的状态。
以下示例使用 lambda 函数创建正常模式操作符,在当前行文本周围添加引号
nnoremap <F4> <Cmd>let &opfunc='{t ->
                        \ getline(".")
                        \ ->split("\\zs")
                        \ ->insert("\"", col("'']"))
                        \ ->insert("\"", col("''[") - 1)
                        \ ->join("")
                        \ ->setline(".")}'<CR>g@

2. 缩写词 缩写词 缩写词 缩写词

缩写词在插入模式、替换模式和命令行模式中使用。如果输入的单词是缩写词,它将被替换为它代表的单词。这可以用于为常用长词节省输入时间。您还可以使用它来自动更正明显的拼写错误。示例
: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" 类型以非关键字字符结尾,其他字符可以是任何类型,除了空格和制表符。
示例:"def#"、"4/7$"
无法作为缩写词的字符串示例:"a.b"、"#def"、"a b"、"_$r"
只有在输入非关键字字符时才会识别缩写词。这也可以是结束插入模式的 <Esc> 或结束命令的 <CR>。结束缩写词的非关键字字符将在扩展的缩写词后插入。例外情况是字符 <C-]>,它用于扩展缩写词而不插入任何额外的字符。
示例
:ab hh        hello
"hh<Space>" 将扩展为 "hello<Space>" "hh<C-]>" 将扩展为 "hello"
光标前的字符必须与缩写词匹配。每种类型都有一个额外的规则
full-id 匹配之前是非关键字字符,或者这是行或插入的开始位置。例外:当缩写词只有一个字符时,如果它前面有非关键字字符(空格或制表符除外),则不会被识别。但是,对于命令行,"'<,'>"(或任何其他标记)将被忽略,就好像命令行从它之后开始一样。
end-id 匹配之前是关键字字符,或者空格或制表符,或者这是行或插入的开始位置。
non-id 匹配之前是空格、制表符或行的开头或插入的开头。
示例:({CURSOR} 是您键入非关键字字符的位置)
:ab foo   four old otters
" foo{CURSOR}" 将扩展为 " four old otters" " foobar{CURSOR}" 不会扩展 "barfoo{CURSOR}" 不会扩展
:ab #i #include
"#i{CURSOR}" 将扩展为 "#include" ">#i{CURSOR}" 不会扩展
:ab ;; <endofline>
"test;;" 不会扩展 "test ;;" 将扩展为 "test <endofline>"
要避免在插入模式中使用缩写词:在会触发缩写词的字符之前键入 CTRL-V。例如,CTRL-V <Space>。或者键入部分缩写词,使用 <Esc> 退出插入模式,使用 "a" 重新进入插入模式,然后键入其余部分。
要避免在命令行模式中使用缩写词:在缩写词中的某个位置两次键入 CTRL-V 以避免它被替换。否则,在普通字符前面的 CTRL-V 通常会被忽略。
可以在缩写词后移动光标
:iab if if ()<Left>
您甚至可以执行更复杂的操作。例如,要使用在缩写词后键入的空格
func Eatchar(pat)
   let c = nr2char(getchar(0))
   return (c =~ a:pat) ? '' : c
endfunc
iabbr <silent> if if ()<Left><C-R>=Eatchar('\s')<CR>
没有默认的缩写词。
缩写词永远不会递归。您可以使用 ":ab f f-o-o" 而不出现任何问题。但是缩写词可以被映射。
:abbreviate-local :abbreviate-<buffer> 就像映射一样,缩写词可以是缓冲区本地化的。这主要用于 文件类型插件 文件中。C 插件文件的示例
:abb <buffer> FF  for (i = 0; i < ; ++i)
:ab :abbreviate :ab[breviate] 列出所有缩写词。第一列中的字符表示使用缩写词的模式:'i' 表示插入模式,'c' 表示命令行模式,'!' 表示两者。这些与映射相同,请参见 映射列表
:abbreviate-verbose
'verbose' 非零时,列出缩写词还会显示它上次定义的位置。示例
:verbose abbreviate
!  teh                 the
        Last set from /home/abcd/vim/abbr.vim
有关更多信息,请参见 :verbose-cmd
:ab[breviate] {lhs} 列出以 {lhs} 开头的缩写词。您可能需要插入 CTRL-V(键入两次)以避免键入的 {lhs} 被扩展,因为命令行缩写词在这里适用。
:ab[breviate] [<expr>] [<buffer>] {lhs} {rhs}{lhs} 添加缩写词到 {rhs}。如果 {lhs} 已经存在,它将被新的 {rhs} 替换。{rhs} 可能包含空格。请参见 :map-<expr> 以了解可选的 <expr> 参数。请参见 :map-<buffer> 以了解可选的 <buffer> 参数。
:una :unabbreviate :una[bbreviate] [<buffer>] {lhs} 从列表中删除 {lhs} 的缩写词。如果找不到,则删除 {lhs}{rhs} 匹配的缩写词。这样做是为了使您甚至可以在扩展后删除缩写词。要避免扩展,请插入 CTRL-V(键入两次)。
:norea :noreabbrev :norea[bbrev] [<expr>] [<buffer>] [lhs] [rhs] 与 ":ab" 相同,但不会为 {rhs} 重新映射
:ca :cab :cabbrev :ca[bbrev] [<expr>] [<buffer>] [lhs] [rhs] 与 ":ab" 相同,但仅适用于命令行模式。
:cuna :cunabbrev :cuna[bbrev] [<buffer>] {lhs} 与 ":una" 相同,但仅适用于命令行模式。
:cnorea :cnoreabbrev :cnorea[bbrev] [<expr>] [<buffer>] [lhs] [rhs] 与 ":ab" 相同,但仅适用于命令行模式,并且不会为 {rhs} 重新映射
:ia :iabbrev :ia[bbrev] [<expr>] [<buffer>] [lhs] [rhs] 与 ":ab" 相同,但仅适用于插入模式。
:iuna :iunabbrev :iuna[bbrev] [<buffer>] {lhs} 与 ":una" 相同,但仅适用于插入模式。
:inorea :inoreabbrev :inorea[bbrev] [<expr>] [<buffer>] [lhs] [rhs] 与 ":ab" 相同,但仅适用于插入模式,并且不会为 {rhs} 重新映射
:abc :abclear :abc[lear] [<buffer>] 删除所有缩写词。
:iabc :iabclear :iabc[lear] [<buffer>] 删除插入模式的所有缩写词。
:cabc :cabclear :cabc[lear] [<buffer>] 删除命令行模式的所有缩写词。
使用_CTRL-V
可以在缩写词的 rhs 中使用特殊字符。CTRL-V 必须用于避免大多数不可打印字符的特殊含义。需要键入多少个 CTRL-V 取决于您如何输入缩写词。这也适用于映射。让我们在这里使用一个示例。
假设您想将 "esc" 缩写为输入 <Esc> 字符。当您在 Vim 中键入 ":ab" 命令时,您必须输入以下内容:(这里 ^V 是 CTRL-V,^[] 是 <Esc>
您键入:ab esc ^V^V^V^V^V^[
所有键盘输入都经过 ^V 引号解释,因此第一个、第三个和第五个 ^V 字符只是允许第二个和第四个 ^V 以及 ^[] 输入到命令行中。
您看到:ab esc ^V^V^[
命令行包含 ^[] 之前的两个实际 ^V。如果您选择走这条路线,这就是它在您的 vimrc 文件中应该出现的方式。第一个 ^V 用于引用第二个 ^V;:ab 命令使用 ^V 作为它自己的引号字符,因此您可以在缩写词中包含引用的空格或 | 字符。:ab 命令不会对 ^[] 字符执行任何特殊操作,因此它不需要引用。(尽管引用没有害处;这就是为什么键入 7 个 [而不是 8 个!] ^V 有效。)
存储为:esc ^V^[
解析后,缩写词的简短形式 ("esc") 和长形式(两个字符 "^V^[]")将存储在缩写词表中。如果您在没有参数的情况下给出 :ab 命令,这就是缩写词的显示方式。
后来,当用户键入单词 "esc" 时,由于扩展了缩写词,长形式将经历与键盘输入相同的 ^V 解释类型。因此 ^V 保护 ^[] 字符不被解释为 "退出插入模式" 字符。相反,^[] 将插入到文本中。
扩展为:^[
[Steve Kirkendall 提供的示例]

3. 局部映射和函数 脚本本地化

在使用多个 Vim 脚本文件时,存在一个危险,即在一个脚本中使用的映射和函数可能与其他脚本中的相同名称。为了避免这种情况,可以将它们设为脚本本地化。
<SID> <SNR> E81 字符串 "<SID>" 可用于映射或菜单中。如果您有一个脚本本地化的函数,您想从同一脚本中的映射中调用它,这很有用。当执行 map 命令时,Vim 将使用特殊键码 <SNR> 替换 "<SID>",后面跟着一个对脚本唯一的数字和一个下划线。示例
:map <SID>Add
将定义一个映射 "<SNR>23_Add"。
在脚本中定义函数时,可以在名称前添加 "s:" 以使其成为脚本本地化。但是,当映射从脚本外部执行时,它不知道该函数是在哪个脚本中定义的。为了避免这个问题,请使用 "<SID>" 而不是 "s:"。与映射相同,执行相同的转换。这使得能够在映射中定义对该函数的调用。
当执行本地函数时,它在定义它的脚本上下文中运行。这意味着它定义的新函数和映射也可以使用“s:”或“<SID>”,它将使用与定义函数本身时相同的唯一编号。此外,还可以使用“s:var”本地脚本变量。
执行自动命令或用户命令时,将在定义它的脚本上下文中运行。这使得命令可以调用本地函数或使用本地映射成为可能。
如果值在无法正确扩展<SID>的上下文中使用,请使用expand()函数
let &includexpr = expand('<SID>') .. 'My_includeexpr()'
否则,在脚本上下文之外使用“<SID>”将导致错误。
如果需要在复杂的脚本中获取脚本编号以供使用,可以使用此函数
func s:ScriptNumber()
  return matchstr(expand('<SID>'), '<SNR>\zs\d\+\ze_')
endfunc
列出函数和映射时将显示“<SNR>”。这有助于找出它们的定义。
可以使用:scriptnames命令查看哪些脚本已加载以及它们的<SNR>编号是多少。

4. 用户定义命令 user-commands

可以定义自己的 Ex 命令。用户定义的命令可以像内置命令一样工作(它可以有范围或参数,参数可以像文件名或缓冲区名称一样完成,等等),区别在于当命令执行时,它将被转换为一个普通的 Ex 命令,然后执行。
入门:请参阅用户手册中第40.2节。
E183 E841 user-cmd-ambiguous 所有用户定义的命令必须以大写字母开头,以避免与内置命令混淆。以下内置命令除外::Next 它们不能用作用户定义的命令。
用户命令的其他字符可以是大写字母、小写字母或数字。使用数字时,请注意其他需要数字参数的命令可能会变得模棱两可。例如,命令“:Cc2”可以是用户命令“:Cc2”而不带参数,也可以是命令“:Cc”带参数“2”。建议在命令名称和参数之间添加一个空格以避免这些问题。
使用用户定义的命令时,可以缩写命令。但是,如果缩写不唯一,将会发出错误。此外,内置命令将始终优先于用户定义的命令。
示例
:command Rename ...
:command Renumber ...
:Rena                                " Means "Rename"
:Renu                                " Means "Renumber"
:Ren                                " Error - ambiguous
:command Paste ...
建议在脚本中使用用户定义命令的完整名称。
:com[mand] :com :command 列出所有用户定义的命令。列出命令时,第一列中的字符表示: ! 命令具有-bang属性 " 命令具有-register属性 | 命令具有-bar属性 b 命令仅限于当前缓冲区(有关属性的详细信息,请参见下文) 可以使用:filter根据命令名称过滤列表,例如,列出所有名称包含“Pyth”的命令。
filter Pyth command
:com[mand] {cmd} 列出以{cmd}开头的用户定义的命令。
:command-verbose
'verbose'非零时,列出命令也将显示其最后定义的位置以及任何完成参数。例如
:verbose command TOhtml
名称 参数 范围 完成 定义
TOhtml 0 % :call Convert2HTML(<line1>, <line2>)
最后从 /usr/share/vim/vim-7.0/plugin/tohtml.vim 设置
有关更多信息,请参见 :verbose-cmd
E174 E182 :com[mand][!] [{attr}...] {cmd} {repl} 定义一个用户命令。命令的名称为{cmd},其替换文本为{repl}。命令的属性(见下文)为{attr}。如果命令已存在,则会报告错误,除非指定了 !,在这种情况下,命令将被重新定义。有一个例外:当再次加载脚本时,之前在该脚本中定义的命令将被静默替换。
:delc[ommand] {cmd} :delc :delcommand E184 删除用户定义的命令{cmd}
:delc[ommand] -buffer {cmd} E1237
删除为当前缓冲区定义的用户定义的命令{cmd}
:comc[lear] :comc :comclear 删除所有用户定义的命令。
命令属性
command-attributes
Nvim 将用户定义的命令视为其他任何 Ex 命令。它们可以具有参数或指定范围。参数可以像文件名、缓冲区等一样完成。这究竟是如何工作的取决于命令的属性,这些属性是在定义命令时指定的。
在脚本中定义用户命令时,它将能够调用脚本中的本地函数并使用脚本中的本地映射。当用户调用用户命令时,它将在定义它的脚本上下文中运行。如果在命令中使用<SID>,这将很重要。
有许多属性,分为四类:参数处理、完成行为、范围处理和特殊情况。属性将在下面按类别描述。
参数处理
E175 E176 :command-nargs 默认情况下,用户定义的命令不接受任何参数(如果提供任何参数,则会报告错误)。但是,可以使用-nargs属性指定命令可以接受参数。有效的情况是
-nargs=0 不允许使用任何参数(默认值)-nargs=1 必须使用一个参数,它包括空格-nargs=* 允许使用任意数量的参数(0、1 或多个),用空格分隔-nargs=? 允许使用 0 或 1 个参数-nargs=+ 必须提供参数,但允许使用任意数量的参数
在这种情况下,参数被认为是用(未转义的)空格或制表符分隔的,除非只有一个参数,那么空格是参数的一部分。
请注意,参数用作文本,而不是表达式。具体来说,“s:var”将在定义命令的脚本中使用脚本本地变量,而不是在调用命令的脚本中使用!例如:script1.vim
:let s:error = "None"
:command -nargs=1 Error echoerr <args>
script2.vim
:source script1.vim
:let s:error = "Wrong!"
:Error s:error
执行 script2.vim 将导致回显“None”。这不是你想要的结果!调用函数可能是另一种选择。
完成行为
:command-completion E179 E180 E181 :command-complete
默认情况下,用户定义的命令的参数不进行完成。但是,通过指定以下属性之一,可以启用参数完成
-complete=arglist 参数列表中的文件名 -complete=augroup 自动命令组 -complete=breakpoint :breakadd 子选项 -complete=buffer 缓冲区名称 -complete=color 颜色方案 -complete=command Ex 命令(和参数) -complete=compiler 编译器 -complete=diff_buffer diff 缓冲区名称 -complete=dir 目录名 -complete=dir_in_path 'cdpath' 中的目录名 -complete=environment 环境变量名 -complete=event 自动命令事件 -complete=expression Vim 表达式 -complete=file 文件名和目录名 -complete=file_in_path 'path' 中的文件名和目录名 -complete=filetype 文件类型名 'filetype' -complete=function 函数名 -complete=help 帮助主题 -complete=highlight 高亮组 -complete=history :history 子选项 -complete=keymap 键盘映射 -complete=locale 本地化名(作为 locale -a 的输出) -complete=lua Lua 表达式 :lua -complete=mapclear 缓冲区参数 -complete=mapping 映射名称 -complete=menu 菜单 -complete=messages :messages 子选项 -complete=option 选项 -complete=packadd 可选包 pack-add 名称 -complete=runtime 'runtimepath' 中的文件名和目录名 -complete=scriptnames 加载的脚本名 -complete=shellcmd Shell 命令 -complete=shellcmdline 第一个是一个 Shell 命令,随后的一个是文件名。与:!cmd相同 -complete=sign :sign 子选项 -complete=syntax 语法文件名 'syntax' -complete=syntime :syntime 子选项 -complete=tag 标记 -complete=tag_listfiles 标记,在按下CTRL-D时显示文件名 -complete=user 用户名 -complete=var 用户变量 -complete=custom,{func} 自定义完成,通过{func}定义 -complete=customlist,{func} 自定义完成,通过{func}定义
如果在没有要完成的内容时指定完成(-nargs=0,默认值),则会得到错误E1208注意:某些完成方法可能会扩展环境变量。
自定义完成
:command-completion-custom
:command-completion-customlist E467 E468 可以通过“custom,{func}”或“customlist,{func}”完成参数来定义自定义完成方案。{func}部分应该是一个具有以下签名的函数
:function {func}(ArgLead, CmdLine, CursorPos)
该函数不必使用所有这些参数。该函数应提供完成候选者作为返回值。
对于“custom”参数,该函数应返回每个完成候选者占一行的新行分隔字符串。
对于“customlist”参数,该函数应返回一个 Vim 列表作为完成候选者。列表中的非字符串项将被忽略。
函数参数是: ArgLead 当前正在完成的参数的引导部分 CmdLine 整个命令行 CursorPos 光标在其中的位置(字节索引) 该函数可以使用这些参数来确定上下文。对于“custom”参数,无需根据(ArgLead 中的隐式模式)过滤候选者。Vim 将在函数返回后使用其正则表达式引擎过滤候选者,这在大多数情况下可能更有效。如果'wildoptions'包含“fuzzy”,则将使用模糊匹配过滤候选者。对于“customlist”参数,Vim 不会过滤返回的完成候选者,用户提供的函数应过滤候选者。
以下示例将用户名列出到 Finger 命令
:com -complete=custom,ListUsers -nargs=1 Finger !finger <args>
:fun ListUsers(A,L,P)
:    return system("cut -d: -f1 /etc/passwd")
:endfun
以下示例完成'path'选项中指定的目录中的文件名
:com -nargs=1 -bang -complete=customlist,EditFileComplete
                    \ EditFile edit<bang> <args>
:fun EditFileComplete(A,L,P)
:    return split(globpath(&path, a:A), "\n")
:endfun
此示例不适用于包含空格的文件名!
范围处理
E177 E178 :command-range :command-count 默认情况下,用户定义的命令不接受行号范围。但是,可以指定命令是否接受范围(-range 属性),或者它是否接受任意计数值,无论是在行号位置(-range=N,如:split命令)还是作为“计数”参数(-count=N,如:Next命令)。然后,计数将在参数中使用<count>提供。
可能的属性是
-range 允许使用范围,默认值为当前行 -range=% 允许使用范围,默认值为整个文件 (1,$) -range=N 一个计数(默认值为 N),它在行号位置指定(如:split);允许使用零行号。 -count=N 一个计数(默认值为 N),它在行号位置或作为初始参数指定(如:Next)。 -count 作用与 -count=0 相同
请注意,-range=N 和 -count=N 是互斥的,只能指定其中一个。
:command-addr
范围中的特殊字符(如 .$%)默认情况下分别对应于当前行、最后一行和整个缓冲区,它们可能与参数、(已加载的)缓冲区、窗口或标签页有关。
可能的值是(第二列是在列表中使用的简短名称):-addr=lines 行范围(这是 -range 的默认值) -addr=arguments arg 参数范围 -addr=buffers buf 缓冲区范围(包括未加载的缓冲区) -addr=loaded_buffers load 已加载缓冲区范围 -addr=windows win 窗口范围 -addr=tabs tab 标签页范围 -addr=quickfix qf 快速修复条目范围 -addr=other ? 其他类型的范围;可以使用“.”、“$”和“%”与“lines”一样(这是 -count 的默认值)
增量预览
:command-preview {nvim-api} 命令可以通过定义预览处理程序来显示 'inccommand'(即时输入)预览(仅限 Lua,参见 nvim_create_user_command())。
在执行预览回调之前,Nvim 将临时禁用 'cursorline''cursorcolumn' 以避免突出显示问题。
预览回调必须是具有以下签名的 Lua 函数
function cmdpreview(opts, ns, buf)
其中“opts”的格式与传递给 nvim_create_user_command() 回调的格式相同,“ns”是突出显示的预览命名空间 ID,“buf”是预览例程将直接修改以显示预览结果的缓冲区(对于“inccommand=split”,或对于“inccommand=nosplit”为 nil)。
您的命令预览例程必须实现此协议
1. 根据需要修改目标缓冲区以进行预览(参见 nvim_buf_set_text()nvim_buf_set_lines())。 2. 如果提供了预览缓冲区,请向预览缓冲区添加必要的文本。 3. 向目标缓冲区添加必要的突出显示。如果提供了预览缓冲区,请也向预览缓冲区添加必要的突出显示。所有突出显示都必须添加到作为预览回调参数提供的预览命名空间中(有关如何向命名空间添加突出显示的信息,请参见 nvim_buf_add_highlight()nvim_buf_set_extmark())。 4. 返回一个整数(0、1、2),它控制 Nvim 的行为,如下所示: 0:不显示预览。 1:显示预览,但没有预览窗口(即使使用“inccommand=split”)。 2:显示预览并打开预览窗口(如果“inccommand=split”)。对于“inccommand=nosplit”,这与 1 相同。
预览结束后,Nvim 会丢弃预览期间对所有缓冲区所做的所有更改,并清除预览命名空间中的所有突出显示。
以下是一个用于修剪尾部空格的命令示例,该命令支持增量命令预览
-- If invoked as a preview callback, performs 'inccommand' preview by
-- highlighting trailing whitespace in the current buffer.
local function trim_space_preview(opts, preview_ns, preview_buf)
  vim.cmd('hi clear Whitespace')
  local line1 = opts.line1
  local line2 = opts.line2
  local buf = vim.api.nvim_get_current_buf()
  local lines = vim.api.nvim_buf_get_lines(buf, line1 - 1, line2, false)
  local preview_buf_line = 0
  for i, line in ipairs(lines) do
    local start_idx, end_idx = string.find(line, '%s+$')
    if start_idx then
      -- Highlight the match
      vim.api.nvim_buf_add_highlight(
        buf,
        preview_ns,
        'Substitute',
        line1 + i - 2,
        start_idx - 1,
        end_idx
      )
      -- Add lines and set highlights in the preview buffer
      -- if inccommand=split
      if preview_buf then
        local prefix = string.format('|%d| ', line1 + i - 1)
        vim.api.nvim_buf_set_lines(
          preview_buf,
          preview_buf_line,
          preview_buf_line,
          false,
          { prefix .. line }
        )
        vim.api.nvim_buf_add_highlight(
          preview_buf,
          preview_ns,
          'Substitute',
          preview_buf_line,
          #prefix + start_idx - 1,
          #prefix + end_idx
        )
        preview_buf_line = preview_buf_line + 1
      end
    end
  end
  -- Return the value of the preview type
  return 2
end
-- Trims all trailing whitespace in the current buffer.
local function trim_space(opts)
  local line1 = opts.line1
  local line2 = opts.line2
  local buf = vim.api.nvim_get_current_buf()
  local lines = vim.api.nvim_buf_get_lines(buf, line1 - 1, line2, false)
  local new_lines = {}
  for i, line in ipairs(lines) do
    new_lines[i] = string.gsub(line, '%s+$', '')
  end
  vim.api.nvim_buf_set_lines(buf, line1 - 1, line2, false, new_lines)
end
-- Create the user command
vim.api.nvim_create_user_command(
  'TrimTrailingWhitespace',
  trim_space,
  { nargs = '?', range = '%', addr = 'lines', preview = trim_space_preview }
)
-bang 该命令可以带一个 ! 修饰符(如 :q 或 :w) -bar 该命令后面可以跟一个 "|" 和另一个命令。然后,命令参数中的 "|" 不允许。此外,还会检查 " 是否以开始注释。 -register 该命令的第一个参数可以是一个可选的寄存器名称(如 :del、:put、:yank)。 -buffer 该命令只会在当前缓冲区中可用。 -keepscript 不要使用定义用户命令的位置来显示详细消息,而要使用调用用户命令的位置。
在 -count 和 -register 属性的情况下,如果提供了可选参数,则会将其从参数列表中删除,并可单独用于替换文本。请注意,这些参数可以缩写,但这是已弃用的功能。在新的脚本中使用完整名称。
替换文本
用户定义命令的替换文本 {repl} 将使用 <...> 符号扫描特殊转义序列。转义序列将替换为从输入的命令行中获取的值,所有其他文本将被原样复制。结果字符串将作为 Ex 命令执行。要避免替换,请在初始 < 的位置使用 <lt>。因此,要包含“<bang>”,请使用“<lt>bang>”。
有效的转义序列是
<line1>
<line1> 命令范围的起始行。 <line2>
<line2> 命令范围的结束行。 <range>
<range> 命令范围中的项目数量:0、1 或 2 <count>
<count> 提供的任何计数(如 '-range' 和 '-count' 属性中所述)。 <bang>
<bang>(参见 '-bang' 属性)如果命令使用 ! 修饰符执行,则展开为 !,否则展开为空。 <mods> <q-mods> :command-modifiers <mods> 命令修饰符(如果已指定)。否则,展开为空。支持的修饰符是 :aboveleft:belowright:botright:browse:confirm:hide:horizontal:keepalt:keepjumps:keepmarks:keeppatterns:leftabove:lockmarks:noautocmd:noswapfile:rightbelow:sandbox:silent:tab:topleft:unsilent:verbose:vertical。请注意,:filter 不受支持。示例
command! -nargs=+ -complete=file MyEdit
            \ for f in expand(<q-args>, 0, 1) |
            \ exe '<mods> split ' .. f |
            \ endfor
function! SpecialEdit(files, mods)
    for f in expand(a:files, 0, 1)
        exe a:mods .. ' split ' .. f
    endfor
endfunction
command! -nargs=+ -complete=file Sedit
            \ call SpecialEdit(<q-args>, <q-mods>)
<reg> <register> <reg>(参见 '-register' 属性)可选的寄存器(如果已指定)。否则,展开为空。<register> 是它的同义词。 <args>
<args> 命令参数,与提供的完全相同(但如上所述,任何计数或寄存器都可能消耗一些参数,这些参数随后不再是 <args> 的一部分)。 <lt> 单个 '<'(小于)字符。如果您想将这些转义序列中的一个字面副本获取到扩展中,则需要此字符 - 例如,要获取 <bang>,请使用 <lt>bang>。
<q-args>
如果转义序列的前两个字符是“q-”(例如,<q-args>),则该值将用引号括起来,使其成为在表达式中使用的有效值。这将参数视为一个值。当没有参数时,<q-args> 是一个空字符串。请参见下面的 q-args-example<f-args>
为了允许命令将其参数传递给用户定义的函数,有一个特殊形式 <f-args>(“函数参数”)。这将在空格和制表符处拆分命令参数,分别为每个参数加引号,并且 <f-args> 序列将被用引号括起来的逗号分隔的参数列表替换。请参见下面的 Mycmd 示例。如果未给出任何参数,则会删除 <f-args>。要将空格嵌入 <f-args> 的参数中,请在其前面加上一个反斜杠。<f-args> 会将每一对反斜杠 (\\) 替换为一个反斜杠。反斜杠后跟除空格或反斜杠以外的其他字符将保持不变。另请参见下面的 f-args-example。概述
命令 <f-args>
XX ab "ab" XX a\b 'a\b' XX a\ b 'a b' XX a\ b 'a ', 'b' XX a\\b 'a\b' XX a\\ b 'a\', 'b' XX a\\\b 'a\\b' XX a\\\ b 'a\ b' XX a\\\\b 'a\\b' XX a\\\\ b 'a\\', 'b' XX [无]
请注意,如果要处理“无参数”情况,您必须确保函数可以在没有参数的情况下调用。
用户命令示例
" Delete everything after here to the end
:com Ddel +,$d
" Rename the current buffer
:com -nargs=1 -bang -complete=file Ren f <args>|w<bang>
" Replace a range with the contents of a file
" (Enter this all as one line)
:com -range -nargs=1 -complete=file
      Replace <line1>-pu_|<line1>,<line2>d|r <args>|<line1>d
" Count the number of lines in the range
:com! -range -nargs=0 Lines  echo <line2> - <line1> + 1 "lines"
f-args-example
调用用户函数(<f-args> 的示例)
:com -nargs=* Mycmd call Myfunc(<f-args>)
当执行为
:Mycmd arg1 arg2
这将调用
:call Myfunc("arg1","arg2")
q-args-example
一个更完整的示例
: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 命令(s)作为参数并将其在参数列表中的所有文件上执行。使用示例(注意使用“e”标志忽略错误和“update”命令写入已修改的缓冲区)
:Allargs %s/foo/bar/ge|update
这将调用
:call Allargs("%s/foo/bar/ge|update")
主要
命令索引
快速参考