重复

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


重复命令、Vim 脚本和调试
用户手册的第 26 章介绍了重复 usr_26.txt

单个重复 single-repeat

.
. 重复上次更改,使用 [count] 替换计数。当 'cpoptions' 中包含 'y' 标志时,也会重复 yank 命令。不会重复命令行命令。
可以使用 "." 命令重复简单的更改。没有计数时,使用上次更改的计数。如果您输入计数,它将替换上次的计数。 v:countv:count1 将被设置。
如果上次更改包括对编号寄存器的指定,则寄存器编号将递增。请参阅 redo-register 以了解如何使用此功能的示例。
请注意,当重复使用 Visual 选择的命令时,将使用相同大小的区域,请参阅 visual-repeat
@:
@: 重复上次命令行 [count] 次。

多个重复 multi-repeat

:g :global E148 :[range]g[lobal]/{pattern}/[cmd] 在 [range] 中包含 {pattern} 匹配的行上执行 Ex 命令 [cmd](默认值:":p")。
:[range]g[lobal]!/{pattern}/[cmd] 在 [range] 中包含 {pattern} 不匹配的行上执行 Ex 命令 [cmd](默认值:":p")。
:v :vglobal :[range]v[global]/{pattern}/[cmd] 与 :g! 相同。
示例
:g/^Obsolete/d _
:d 后使用下划线可以避免覆盖寄存器或剪贴板。这也使其更快。
您可以在 {pattern} 周围使用除字母字符、'\'、'"'、'|' 或 '!' 之外的任何其他单字节字符,而不是 '/'。如果您想要在搜索模式或替换字符串中包含 '/',这很有用。
有关模式的定义,请参阅 pattern
注意 [cmd] 可能包含一个范围;请参阅 collapseedit-paragraph-join 以了解示例。
全局命令通过首先扫描 [range] 行并标记每行匹配的位置(对于多行模式,只有匹配的开头很重要)来工作。在第二次扫描中,对于每行标记,都会执行 [cmd],就好像光标位于该行一样。对于 ":v" 和 ":g!",对于每行未标记的行,都会执行该命令。如果删除了一行,其标记会消失。[range] 的默认值为整个缓冲区 (1,$)。使用 "CTRL-C" 中断命令。如果对某行给出了错误消息,则该行的命令将被中止,全局命令将继续执行下一行标记的行或未标记的行。 E147
当递归使用该命令时,它仅对一行起作用。因此不允许指定范围。这对于查找匹配某个模式但与另一个模式不匹配的所有行很有用
:g/found/v/notfound/{cmd}
这首先查找包含 "found" 的所有行,但只有当 "notfound" 没有匹配时才执行 {cmd}
可以使用任何 Ex 命令,请参阅 ex-cmd-index。要执行 Normal 模式命令,可以使用 :normal 命令
:g/pat/normal {commands}
确保 {commands} 以一个完整的命令结尾,否则 Vim 将等待您为每次匹配输入命令的其余部分。屏幕将不会更新,因此您不知道自己在做什么。请参阅 :normal
撤消/重做命令将一次撤消/重做整个全局命令。上一个上下文标记将仅设置一次(使用 "''",您可以返回到全局命令之前光标所在的位置)。
全局命令同时设置了上次使用的搜索模式和上次使用的替换模式(这与 vi 兼容)。这使得全局替换字符串变得容易
:g/pat/s//PAT/g
这将用 "PAT" 替换 "pat" 的所有出现。可以使用相同的方法来完成
:%s/pat/PAT/g
它缩短了两个字符!
在 Ex 模式下使用 "global" 时,使用 ":visual" 作为命令是一个特殊情况。这将移动到匹配的行,进入 Normal 模式,让您在那里执行命令,直到您使用 gQ 返回到 Ex 模式。这将对每个匹配的行重复进行。在执行此操作时,您不能使用 ":global"。要中止此操作,请键入 CTRL-C 两次。

复杂重复 complex-repeat

q recording q{0-9a-zA-Z"} 将键入的字符记录到寄存器 {0-9a-zA-Z"} 中(大写表示追加)。在执行寄存器时,'q' 命令将被禁用,它在映射和 :normal 中不起作用。
注意:如果用于记录的寄存器也用于 yp,则结果很可能不是预期的,因为 put 将粘贴记录的宏,yank 将覆盖记录的宏。
注意:记录在您键入时发生,重放寄存器就像键来自映射一样。例如,这对于撤消很重要,撤消仅在键入命令时同步。
q 停止记录。实现说明:停止记录的 'q' 不会存储在寄存器中,除非它是映射的结果
@
@{0-9a-z".=*+} 执行寄存器 {0-9a-z".=*+} 的内容 [count] 次。请注意,寄存器 '%'(当前文件名称)和 '#'(备用文件名称)不能使用。寄存器像映射一样执行,这意味着 'wildchar''wildcharm' 之间的区别适用,撤消可能不会以相同的方式同步。对于 "@=",系统会提示您输入表达式。然后执行该表达式的结果。另请参阅 @:
@@ E748 @@ 重复上一个 @{0-9a-z":*} [count] 次。
v_@-default
{Visual}@{0-9a-z".=*+} 在行式 Visual 模式下,对每个选定行执行 {Visual}@@ 寄存器的内容。请参阅 visual-repeatdefault-mappings
Q
Q 重复上次记录的寄存器 [count] 次。请参阅 reg_recorded()
v_Q-default
{Visual}Q 在行式 Visual 模式下,对每个选定行重复上次记录的寄存器。请参阅 visual-repeatdefault-mappings
:@
:[addr]@{0-9a-z".=*+} 将寄存器 {0-9a-z".=*+} 的内容作为 Ex 命令执行。首先将光标设置为 [addr] 行(默认值为当前行)。当寄存器中的最后一行没有 <CR> 时,如果 'cpoptions' 中存在 'e' 标志,它将自动添加。对于 ":@=",将使用上次使用的表达式。执行计算表达式的结果作为 Ex 命令。映射在这些命令中不被识别。当 line-continuation 字符 (\) 存在于行式寄存器中的一行的开头时,它将与前一行合并。这对于 yank 和执行 Vim 脚本的部分内容很有用。
:@:
:[addr]@: 重复上次命令行。首先将光标设置为 [addr] 行(默认值为当前行)。
:[addr]@ :@@
:[addr]@@ 重复上一个 :@{register}。首先将光标设置为 [addr] 行(默认值为当前行)。

使用 Vim 脚本 using-scripts

有关编写 Vim 脚本的信息,请参阅用户手册的第 41 章 usr_41.txt
:so :source load-vim-script :[range]so[urce] [file] 从 [file] 运行 Ex-commands 或 Lua 代码(".lua" 文件)。如果没有 [file],则使用当前缓冲区并将其视为 Lua 代码(如果 'filetype' 为 "lua" 或其文件名以 ".lua" 结尾)。触发 SourcePre 自动命令。 :source!
:[range]so[urce]! {file}{file} 运行 Normal-mode 命令。当在 :global:argdo:windo:bufdo 之后使用,在循环中使用或当另一个命令跟随时,在执行命令期间显示将不会更新。
:ru :runtime :ru[ntime][!] [where] {file} .. 从 {file}(相对路径)中读取并源代码 Ex 命令或 Lua 代码(".lua" 文件)在由 'runtimepath' 和/或 'packpath' 给出的每个目录中。忽略不存在的文件。
示例
:runtime syntax/c.vim
:runtime syntax/c.lua
可以有多个以空格分隔的 {file} 参数。每个 {file}'runtimepath' 中的第一个目录中搜索,然后在第二个目录中搜索,依此类推。
如果包含 [!],则源代码所有找到的文件。否则,只源代码第一个找到的文件。
如果省略 [where],则仅使用 'runtimepath'。其他值:START 仅在 'packpath' 中的 "start" 下搜索 OPT 仅在 'packpath' 中的 "opt" 下搜索 PACK 在 'packpath' 中的 "start" 和 "opt" 下搜索 ALL 首先使用 'runtimepath',然后在 'packpath' 中的 "start" 和 "opt" 下搜索
{file} 包含通配符时,它会扩展到所有匹配的文件。示例
:runtime! plugin/**/*.{vim,lua}
这就是 Nvim 在启动时用来加载插件文件的方式。这个类似的命令
:runtime plugin/**/*.{vim,lua}
将只源代码第一个文件。
对于每个 {file} 模式,如果两个 .vim.lua 文件名匹配且仅在扩展名上有所不同,则首先源代码 .vim 文件。
'verbose' 为 1 或更高时,如果找不到文件,则会显示一条消息。当 'verbose' 为 2 或更高时,将显示有关每个搜索文件的消息。
:pa :packadd E919 :pa[ckadd][!] {name}'packpath' 中搜索可选插件目录,并源代码找到的任何插件文件。目录必须匹配
pack/*/opt/{name}
如果该目录不在 'runtimepath' 中,则将其添加到 'runtimepath'。如果目录 pack/*/opt/{name}/after 存在,则将其添加到 'runtimepath' 的末尾。
如果从 "pack/*/start" 加载包被跳过,则首先搜索此目录
pack/*/start/{name}
请注意,{name} 是目录名称,而不是 .vim 文件的名称。所有与以下模式匹配的文件
pack/*/opt/{name}/plugin/**/*.vim
pack/*/opt/{name}/plugin/**/*.lua
将被源代码化。这允许使用 "plugin" 下面的子目录,就像在 'runtimepath' 中使用插件一样。
如果文件类型检测已启用(这通常在你的 vimrc 中使用 syntax enablefiletype on 命令完成,或者在 初始化 期间自动完成),并且在 "pack/*/opt/{name}" 中找到了包,此命令还会查找 "{name}/ftdetect/*.vim" 文件。
当添加可选的 ! 时,不会加载任何插件文件或 ftdetect 脚本,只会将匹配的目录添加到 'runtimepath'。这在你的 init.vim 中很有用。然后,插件将在 初始化 期间加载,参见 load-plugins(注意,加载顺序将被反转,因为每个目录都插入在其他目录之前)。在这种情况下,ftdetect 脚本将在 初始化 期间加载,在 load-plugins 步骤之前。
另请参阅 pack-add
:packl :packloadall :packl[oadall][!] 加载 'packpath' 中每个条目下的 "start" 目录中的所有包。
首先,将找到的所有目录添加到 'runtimepath',然后源代码化在目录中找到的插件。这允许一个插件依赖于另一个插件的某些内容,例如一个 "autoload" 目录。参见 packload-two-steps,了解这如何有用。
这通常在启动期间,在加载你的 vimrc 文件之后自动完成。使用此命令,它可以在更早的时间完成。
包将只加载一次。第二次使用 :packloadall 将不会有任何效果。当添加可选的 ! 时,即使在完成之前,此命令也会加载包。
请注意,当在 vimrc 文件中使用 :packloadall 时,'runtimepath' 选项将被更新,并且之后将加载 'runtimepath' 中的所有插件,这意味着它们将再次加载。预计插件可以处理这种情况。
错误只会导致源代码化发生中止的脚本,其他插件将被加载。参见 packages
:scripte[ncoding] [encoding] :scripte :scriptencoding E167 指定脚本中使用的字符编码。如果以下几行不同,则将从 [encoding] 转换为 'encoding' 选项的值。示例
scriptencoding iso-8859-5
scriptencoding cp932
当 [encoding] 为空时,不会进行任何转换。这可以用来限制对一系列行的转换
scriptencoding euc-jp
... lines to be converted ...
scriptencoding
... not converted ...
当系统不支持转换时,不会出现任何错误消息,也不会进行任何转换。当无法转换一行时,不会出现错误,并且会保留原始行。
不要使用 "ucs-2" 或 "ucs-4",脚本不能使用这些编码(它们将包含 NUL 字节)。当源代码化的脚本以 utf-8 格式的 BOM(字节顺序标记)开头时,Vim 会识别它,因此无需再使用 ":scriptencoding utf-8"。
:scr :scriptnames :scr[iptnames] 列出所有源代码化的脚本名称,按它们首次被源代码化的顺序排列。该数字用于脚本 ID <SID>。另请参阅 getscriptinfo()
:scr[iptnames][!] {scriptId} :script
编辑脚本 {scriptId}。虽然 ":scriptnames name" 有效,但建议使用 ":script name"。当无法 放弃 当前缓冲区且 ! 不存在时,命令将失败。
:fini :finish E168 :fini[sh] 停止源代码化脚本。只能在 Vim 脚本文件中使用。这是一种快速跳过文件其余部分的方法。如果它在 :try 之后但匹配的 :finally(如果存在)之前使用,则首先执行匹配的 :endtry 之前的 ":finally" 之后的命令。此过程适用于脚本中的所有嵌套 ":try"。然后,最外面的 ":endtry" 将停止源代码化脚本。
所有命令和命令序列都可以通过将它们放入命名寄存器并执行它们来重复。有两种方法可以将命令放入寄存器
使用记录命令 "q"。你只需输入一次命令,并在执行命令时,它们就会存储在寄存器中。这很简单,因为你可以看到你在做什么。如果你犯了错误,可以将寄存器 "p"ut 到文件中,编辑命令序列,然后将其再次删除到寄存器中。你可以通过追加到寄存器来继续记录(使用大写字母)。
将命令序列删除或粘贴到寄存器中。
经常使用的命令序列可以使用 ":map" 命令放在一个功能键下。
另一种方法是将命令放入文件中,并使用 ":source!" 命令执行它们。对于长命令序列很有用。可以与 ":map" 命令结合使用,将复杂命令放在一个功能键下。
":source" 命令逐行从文件中读取 Ex 命令。你将必须输入任何必要的键盘输入。":source!" 命令逐字符地从脚本文件读取,将每个字符解释为好像你输入了一样。
例如:当你给出 ":!ls" 命令时,你会得到 hit-enter 提示符。如果你使用 ":source" 命令源代码化一个包含 "!ls" 行的文件,你将必须自己输入 <Enter>。但如果你使用 ":source!" 命令源代码化一个包含 ":!ls" 行的文件,则将从该文件读取下一批字符,直到找到一个 <CR>。你将不必自己输入 <CR>,除非 ":!ls" 是文件中的最后一行。
可以在脚本文件中放入 ':source[!]' 命令,这样你就可以创建脚本文件的自顶向下层次结构。":source" 命令可以嵌套到一次可以打开的文件数量(约 15 个)的深度。":source!" 命令可以嵌套到 15 层深度。
你可以在源代码化文件内部使用 "<sfile>" 字符串(按字面意义,这不是一个特殊键),在需要文件名的位置使用。它将被替换为源代码化文件的名称。例如,如果你在 init.vim 文件所在的目录中有一个 "other.vimrc" 文件,你可以使用以下命令从你的 init.vim 文件中源代码化它
:source <sfile>:h/other.vimrc
在脚本文件中,终端相关的键代码用终端无关的两位字符代码表示。这意味着它们可以在不同类型的终端上以相同的方式使用。键代码的第一个字符是 0x80 或 128,在屏幕上显示为 "~@"。第二个字符可以在 key-notation 列表中找到。任何这些代码也可以使用 CTRL-V 后跟三位十进制代码来输入。
:source_crnl W15 Windows:使用 ":source" 读取的文件通常包含 <CR><NL> <EOL>。这些始终有效。如果你使用的是包含 <NL> <EOL> 的文件(例如,在 Unix 上创建的文件),如果 'fileformats' 不为空且第一行不以 <CR> 结尾,则会识别它。如果第一行以类似于 ":map <F1> :help^M" 的内容结尾,其中 "^M" 是一个 <CR>,则会失败。如果第一行以 <CR> 结尾,但后面的行不以 <CR> 结尾,你将收到一条错误消息,因为第一行的 <CR> 将丢失。
在其他系统上,Vim 期望 ":source" 的文件以 <NL> 结尾。这些始终有效。如果你使用的是包含 <CR><NL> <EOL> 的文件(例如,在 MS-Windows 上创建的文件),所有行都将包含一个尾随的 <CR>。这可能会导致某些命令出现问题(例如,映射)。没有自动 <EOL> 检测,因为它很常见的是以一行开头,该行定义一个以 <CR> 结尾的映射,这将使自动机感到困惑。
行延续
":source" 的 Ex 命令脚本文件中的长行可以通过在下一行的开头插入行延续符号 "\"(反斜杠)来分割。反斜杠前面可以有空格,这些空格将被忽略。
例如:以下几行
:set comments=sr:/*,mb:*,el:*/,
             \://,
             \b:#,
             \:%,
             \n:>,
             \fb:-
被解释为如果它们在同一行中给出。
:set comments=sr:/*,mb:*,el:*/,://,b:#,:%,n:>,fb:-
反斜杠前面的行中的所有前导空格字符都将被忽略。但是请注意,反斜杠前面的行中的尾随空格不能随意插入;它取决于命令被分割的位置,以及是否允许额外的空格。
当需要空格时,最好将其放在反斜杠的后面。行末的空格很难看出来,也可能被意外删除。
:syn match Comment
        \ "very long regexp"
        \ keepend
":append" 和 ":insert" 命令存在一个问题
:1append
\asdf
.
反斜杠被视为行延续符号,因此这将导致以下命令
:1appendasdf
.
为了避免这种情况,请将 'C' 标志添加到 'cpoptions' 选项
:set cpo+=C
:1append
\asdf
.
:set cpo-=C
请注意,当命令在函数内部时,你需要在定义函数时添加 'C' 标志,它在执行函数时并不相关。
:set cpo+=C
:function Foo()
:1append
\asdf
.
:endfunction
:set cpo-=C
行延续注释
要在行之间添加注释,请以 '"\ ' 开头。请注意反斜杠后面的空格。例如
let array = [
        "\ first entry comment
        \ 'first',
        "\ second entry comment
        \ 'second',
        \ ]
基本原理:大多数程序使用尾随反斜杠来表示行延续。在 Vim 中使用它会导致与 Vi 不兼容。例如,对于以下 Vi 映射
:map xx  asdf\
因此,使用了不寻常的前导反斜杠。
在延续行中开始注释会导致所有后续延续行成为注释的一部分。由于这种情况已经存在很长时间,当能够在延续行序列的中间添加注释时,无法使用 \,因为它是一个有效的延续行。使用 '"\ ' 最接近,即使它可能看起来有点奇怪。要求反斜杠后面的空格是为了使它非常不可能是一个正常的注释行。

使用 Vim 包 packages

Vim "package" 是一个包含 plugin 的目录。与普通插件相比,package 可以...
下载为一个存档文件并解压缩到它自己的目录中,因此文件不会与其他插件的文件混合在一起。
是一个 git、mercurial 等存储库,因此易于更新。
包含多个相互依赖的插件。
包含在启动时自动加载的插件("start" 包,位于 "pack/*/start/*" 中)以及只有在需要时才使用 :packadd 加载的插件("opt" 包,位于 "pack/*/opt/*" 中)。
运行时搜索路径
Nvim 在以下位置搜索 :runtime 文件:1. 'runtimepath' 中的所有路径 2. 所有 "pack/*/start/*" 目录
请注意,"pack/*/start/*" 路径并未明确包含在 'runtimepath' 中,因此不会通过 ":set rtp" 或 "echo &rtp" 报告。脚本可以使用 nvim_list_runtime_paths() 列出所有使用的目录,并使用 nvim_get_runtime_file() 查询运行时路径中的特定文件或子文件夹。示例
" List all runtime dirs and packages with Lua paths.
:echo nvim_get_runtime_file("lua/", v:true)
使用包并自动加载
假设您的 Nvim 文件位于 "~/.local/share/nvim/site",并且您想要添加来自 zip 档案 "/tmp/foopack.zip" 的包
% mkdir -p ~/.local/share/nvim/site/pack/foo
% cd ~/.local/share/nvim/site/pack/foo
% unzip /tmp/foopack.zip
目录名 "foo" 是任意的,您可以选择任何您喜欢的名称。
您现在将拥有以下文件:~/.local/share/nvim/site: pack/foo/README.txt pack/foo/start/foobar/plugin/foo.vim pack/foo/start/foobar/syntax/some.vim pack/foo/opt/foodebug/plugin/debugger.vim
启动后,在处理您的 配置 后,Nvim 会扫描 'packpath' 中的所有目录,寻找 "pack/*/start/*" 中的插件,然后加载这些插件。
为了允许在解析您的 vimrc 时调用包功能,:colorschemeautoload 都会自动搜索 'packpath''runtimepath'。有关详细信息,请参阅每个命令的文档。
在示例中,Nvim 将找到 "pack/foo/start/foobar/plugin/foo.vim" 并加载它。
如果 "foobar" 插件启动并将 'filetype' 设置为 "some",Nvim 将找到 syntax/some.vim 文件,因为它的目录在运行时搜索路径中。
Nvim 还将加载 ftdetect 文件(如果有)。
请注意,"pack/foo/opt" 下面的文件不会自动加载,只有 "pack/foo/start" 下面的文件才会。有关 "opt" 目录的用途,请参阅下面的 pack-add
如果禁用加载插件,则不会自动加载包,请参阅 load-plugins
要更早地加载包,以便源代码加载 plugin/ 文件::packloadall 这在禁用加载插件时也有效。自动加载只发生一次。
如果包具有 "after" 目录,该目录将被添加到 'runtimepath' 的末尾,因此其中的任何内容都将在稍后加载。
使用单个插件并自动加载
如果您没有包,而只有一个插件,则需要创建额外的目录级别
% mkdir -p ~/.local/share/nvim/site/pack/foo/start/foobar
% cd ~/.local/share/nvim/site/pack/foo/start/foobar
% unzip /tmp/someplugin.zip
您现在将拥有以下文件:pack/foo/start/foobar/plugin/foo.vim pack/foo/start/foobar/syntax/some.vim
从这里开始,它的工作原理与上面相同。
可选插件
pack-add
要从包中加载可选插件,请使用 :packadd 命令
:packadd foodebug
这将在 'packpath' 中搜索 "pack/*/opt/foodebug",并找到 ~/.local/share/nvim/site/pack/foo/opt/foodebug/plugin/debugger.vim 并源代码加载它。
这可以在满足某些条件时完成。例如,这取决于 Nvim 是否支持某个功能或是否缺少依赖项。
您还可以在启动时加载可选插件,方法是在您的 配置 中添加此命令
:packadd! foodebug
额外的 "!" 是为了防止在 Nvim 使用 --noplugin 启动时加载插件。
包只有 "opt" 目录中的文件是完全正常的。然后,您需要在想要使用它时加载每个插件。
将哪些内容放在哪里
由于使用 :colorscheme 加载的颜色方案位于 "pack/*/start" 和 "pack/*/opt" 下面,因此您可以将它们放在任何位置。我们建议您将它们放在 "pack/*/opt" 下面,例如 "~/.config/nvim/pack/mycolors/opt/dark/colors/very_dark.vim"。
文件类型插件应放在 "pack/*/start" 下面,以便始终可以找到它们。除非您为一种文件类型有多个插件,并且想要使用 :packadd 选择要加载的插件。例如,这取决于编译器版本
if foo_compiler_version > 34
  packadd foo_new
else
  packadd foo_old
endif
"after" 目录在包中可能不太有用。不过,它并非禁止。

创建 Vim 包 package-create

假设您编写了一个或多个插件,并将其作为包进行分发。
如果您有两个不相关的插件,您将使用两个包,以便 Vim 用户可以选择要包含的内容。或者,您可以决定使用一个带有可选插件的包,并告诉用户使用 :packadd 添加首选的插件。
决定如何分发包。您可以创建一个存档,也可以使用存储库。存档可以被更多用户使用,但更新到新版本会比较困难。存储库通常可以轻松地保持最新,但它需要像 "git" 这样的程序。您可以同时进行这两种操作,github 可以自动为发行版创建存档。
您的目录布局如下:start/foobar/plugin/foo.vim " 始终加载,定义命令 start/foobar/plugin/bar.vim " 始终加载,定义命令 start/foobar/autoload/foo.vim " 在使用 foo 命令时加载 start/foobar/doc/foo.txt " foo.vim 的帮助 start/foobar/doc/tags " 帮助标签 opt/fooextra/plugin/extra.vim " 可选插件,定义命令 opt/fooextra/autoload/extra.vim " 在使用 extra 命令时加载 opt/fooextra/doc/extra.txt " extra.vim 的帮助 opt/fooextra/doc/tags " 帮助标签
这允许用户执行以下操作
mkdir ~/.local/share/nvim/site/pack
cd ~/.local/share/nvim/site/pack
git clone https://github.com/you/foobar.git myfoobar
这里 "myfoobar" 是用户可以选择的名称,唯一条件是它与其他包不同。
在您的文档中,您解释了插件的功能,并告诉用户如何加载可选插件
:packadd! fooextra
您可以在其中一个插件中添加此 packadd 命令,以便在需要可选插件时执行它。
运行 :helptags 命令以生成 doc/tags 文件。将此生成的 文件包含在包中意味着用户可以将包放在 pack 目录中,并且 help 命令会立即起作用。不要忘记在更改插件帮助后重新运行此命令
:helptags path/start/foobar/doc
:helptags path/opt/fooextra/doc
插件之间的依赖关系
packload-two-steps
假设您有两个依赖于相同功能的插件。您可以将通用功能放在 autoload 目录中,这样它将被自动找到。您的包将包含以下文件
pack/foo/start/one/plugin/one.vim
call foolib#getit()
pack/foo/start/two/plugin/two.vim
call foolib#getit()
pack/foo/start/lib/autoload/foolib.vim
func foolib#getit()
这可行,因为在源代码加载插件时,将搜索 start 包以查找 autoload 文件。

调试脚本 debug-scripts

除了可以添加到脚本中的明显消息来找出脚本正在执行的操作之外,Vim 还提供了一个调试模式。这使您可以单步执行源代码文件或用户函数,并设置断点。
注意:调试模式远非完美。调试将对 Vim 的工作方式产生副作用。您无法使用它来调试所有内容。例如,调试消息会弄乱显示。
调试模式的替代方法是设置 'verbose' 选项。使用更大的数字将提供有关 Vim 正在执行的操作的更详细的消息。

启动调试模式 debug-mode

要进入调试模式,请使用以下方法之一:1. 使用 -D 参数启动 Vim
vim -D file.txt
调试将在源代码加载第一个 vimrc 文件后立即开始。这有助于找出 Vim 启动时发生的事情。一个副作用是,Vim 将在完成初始化之前切换终端模式,结果不可预测。对于仅限 GUI 的版本(Windows),调试将在 GUI 窗口打开后立即开始。为了使这尽早发生,请在 vimrc 文件中添加 ":gui" 命令。 :debug
2. 在命令前添加 ":debug" 运行命令。调试仅在执行此命令时进行。这对于调试特定脚本或用户函数很有用。以及自动命令使用的脚本和函数。示例
:debug edit test.txt.gz
3. 在源代码文件或用户函数中设置断点。您可以在命令行中执行此操作
vim -c "breakadd file */explorer.vim" .
这将运行 Vim 并停止在 "explorer.vim" 脚本的第一行。也可以在调试模式下设置断点。
在调试模式下,每个执行的命令都将在执行之前显示。注释行、空行和未执行的行将被跳过。当一行包含两个由 "|" 分隔的命令时,每个命令将分别显示。

调试模式

在调试模式下,可以使用通常的 Ex 命令。例如,要检查变量的值
echo idx
在用户函数内部,这将打印局部变量 "idx" 的值。在前面添加 "g:" 以获取全局变量的值
echo g:idx
所有命令都在当前函数或脚本的上下文中执行。您也可以设置选项,例如设置或重置 'verbose' 将显示发生的事情,但您可能希望在执行您感兴趣的行之前设置它
:set verbose=20
应避免需要更新屏幕的命令,因为它们的效果在退出调试模式之前不会被注意到。例如
:help
将无济于事。
调试模式有一个单独的命令行历史记录。
函数行的行号相对于函数的开头。如果您难以弄清楚您在哪里,请在另一个 Vim 中编辑定义函数的文件,搜索函数的开头并执行 "99j"。将 "99" 替换为行号。
此外,可以使用以下命令: >cont
cont 继续执行,直到遇到下一个断点。 >quit
quit 中断执行。这类似于使用 CTRL-C,某些操作可能仍会执行,并非完全中断。仍然会在下一个断点处停止。 >next
next 执行命令,并在命令完成后返回到调试模式。这将跳过用户函数调用和源代码文件。 >step
step 执行命令,并在执行下一个命令时返回到调试模式。这将进入调用的用户函数和源代码文件。 >interrupt
interrupt 这类似于使用 CTRL-C,但与 ">quit" 不同的是,它会返回到调试模式以执行下一个命令。这对于测试 :finally:catch 中的中断异常很有用。 >finish
finish 完成当前脚本或用户函数,并在源代码加载或调用它的命令之后的命令返回到调试模式。 >bt
>backtrace
>where
backtrace 显示当前调试会话的调用堆栈跟踪。bt where >frame
frame N 转到第 N 个回溯级别。+ 和 - 符号使移动相对。例如,":frame +3" 将向上移动三个框架。 >up
up 从调用堆栈跟踪中向上移动一级。 >down
down 从调用堆栈跟踪中向下移动一级。
关于调试模式中的附加命令
这些命令没有命令行补全功能,您只能获得正常 Ex 命令的补全功能。
您可以缩短它们,最多缩短到单个字符,除非多个命令以相同的字母开头。“f”代表“finish”,使用“fr”代表“frame”。
按下 `<CR>` 将重复上一个命令。执行另一个命令时,此功能将重置(因为不清楚您要重复什么)。
当您要使用名称相同的 Ex 命令时,请在前面加上冒号:“:cont”、“:next”、“:finish”(或更短)。
回溯显示函数调用的层次结构,例如
>bt
3 function One[3]
2 Two[3]
->1 Three[3]
0 Four
line 1: let four = 4
“->” 指向当前帧。使用“up”、“down”和“frame N” 选择另一个帧。
在当前帧中,您可以评估局部函数变量。目前还没有方法查看当前行的命令。

定义断点

:breaka :breakadd :breaka[dd] func [lnum] {name} 在函数中设置断点。示例
:breakadd func Explore
不检查函数名是否有效,因此可以在定义函数之前设置断点。
:breaka[dd] file [lnum] {name} 在源文件中设置断点。示例
:breakadd file 43 init.vim
:breaka[dd] here 在当前文件的当前行设置断点。就像执行
:breakadd file <cursor-line> <current-file>
请注意,这只适用于在源文件时执行的命令,不适用于在该文件中定义的函数。
:breaka[dd] expr {expression} 设置断点,当 {expression} 的值发生变化时,就会触发断点。示例
:breakadd expr g:lnum
当全局变量 lnum 发生变化时,就会触发断点。
表达式求值中的错误会被抑制,您可以使用尚未存在的变量的名称。这也意味着如果表达式有错误,您将不会注意到任何问题。
请注意,如果您监视 脚本变量,则在切换脚本时会触发断点,因为脚本变量仅在定义它的脚本中有效,并且如果该脚本从其他多个脚本中调用,则每当该特定变量再次可见或不可访问时,都会触发断点。
[lnum] 是断点的行号。Vim 将在此行或之后停止。如果省略,则使用第一行。
:debug-name
{name} 是一个模式,它与文件或函数名匹配。该模式类似于用于自动命令的模式。必须进行完全匹配(就像模式以“^”开头并以“$”结尾一样)。“*” 匹配任何字符序列。'ignorecase' 不使用,但可以在模式中使用 “\c” 来忽略大小写 /\c。不要包含函数名的 ()!
针对源脚本的匹配是针对完整文件名进行的。如果未指定路径,则使用当前目录。示例
breakadd file explorer.vim
匹配当前目录中的“explorer.vim”。
breakadd file *explorer.vim
匹配“.../plugin/explorer.vim”、“.../plugin/iexplorer.vim”等。
breakadd file */explorer.vim
匹配“.../plugin/explorer.vim”和任何其他目录中的“explorer.vim”。
针对函数的匹配是针对在“:function”的输出中显示的名称进行的。对于局部函数,这意味着会添加类似“<SNR>99_”的内容。
请注意,函数先加载,后执行。当它们被加载时,会检查“file”断点,当它们被执行时,会检查“func”断点。

删除断点

:breakd :breakdel E161 :breakd[el] {nr} 删除断点 {nr}。使用 :breaklist 查看每个断点的编号。
:breakd[el] * 删除所有断点。
:breakd[el] func [lnum] {name} 删除函数中的断点。
:breakd[el] file [lnum] {name} 删除源文件中的断点。
:breakd[el] here 删除当前文件的当前行上的断点。
如果省略 [lnum],则会删除函数或文件中第一个断点。{name} 必须与为“:breakadd”命令键入的内容完全相同。“explorer”、“*explorer.vim”和“*explorer*”是不同的。

列出断点

:breakl :breaklist :breakl[ist] 列出所有断点。

模糊

:debugg :debuggreedy :debugg[reedy] 从普通输入流读取调试模式命令,而不是直接从用户那里获取。只对测试脚本有用。示例
echo 'q^Mq' | vim -e -s -c debuggreedy -c 'breakadd file script.vim' -S script.vim
:0debugg[reedy] 取消“:debuggreedy”:直接从用户那里获取调试模式命令,不要使用预输入内容作为调试命令。

性能分析 profile profiling

性能分析是指 Vim 测量执行函数和/或脚本所花费的时间。
您也可以使用 reltime() 函数来测量时间。
有关性能分析语法高亮,请参阅 :syntime
例如,要分析 one_script.vim 脚本文件
:profile start /tmp/one_script_profile
:profile file one_script.vim
:source one_script.vim
:exit
:prof[ile] start {fname} :prof :profile E750 开始性能分析,在退出时或执行 :profile stop:profile dump 命令时,将输出写入 {fname}{fname} 中的“~/” 和环境变量将被展开。如果 {fname} 已经存在,它将被静默覆盖。变量 v:profiling 设置为 1。
:prof[ile] stop 将收集的性能分析信息写入日志文件,并停止性能分析。您可以使用 :profile start 命令来清除性能分析统计信息并重新开始性能分析。
:prof[ile] pause 停止性能分析,直到下一个 :profile continue 命令。可以在执行不应计入统计信息的操作时使用(例如,外部命令)。不嵌套。
:prof[ile] continue 在 :profile pause 之后继续性能分析。
:prof[ile] func {pattern} 分析与模式 {pattern} 匹配的函数。有关如何使用 {pattern},请参阅 :debug-name
:prof[ile][!] file {pattern} 分析与模式 {pattern} 匹配的脚本文件。有关如何使用 {pattern},请参阅 :debug-name。这只会分析脚本本身,不会分析其中定义的函数。当 [!] 被添加时,脚本中定义的所有函数也将被分析。请注意,性能分析仅在脚本加载后才会开始。脚本本身中的 :profile 命令无效。
:prof[ile] dump 立即将当前性能分析状态写入日志文件。执行此命令后,Vim 将继续收集性能分析统计信息。
:profd[el] ... :profd :profdel 停止对指定参数进行性能分析。有关参数,请参阅 :breakdel。示例
profdel func MyFunc
profdel file MyScript.vim
profdel here
您必须始终以“:profile start fname”命令开始。结果文件会在 Vim 退出时写入。例如,要分析一个特定函数
profile start /tmp/vimprofile
profile func MyFunc
以下是输出示例,其中包含行号,用于解释
1 FUNCTION Test2()
2 Called 1 time
3 Total time: 0.155251
4 Self time: 0.002006
5
6 count total (s) self (s)
7 9 0.000096 for i in range(8)
8 8 0.153655 0.000410 call Test3()
9 8 0.000070 endfor
10 " Ask a question
11 1 0.001341 echo input("give me an answer: ")
标题(第 1-4 行)给出了整个函数的时间。“总计”时间是函数执行时经过的时间。“自身”时间是“总计”时间减去在以下时间中花费的时间
其他用户定义的函数
源脚本
执行的自动命令
外部(shell)命令
第 7-11 行显示了在每行执行中花费的时间。未执行的行不计入。因此,注释行永远不会计入。
Count 列显示一行执行了多少次。请注意,第 7 行中的“for”命令比接下来的行多执行一次。这是因为该行还会执行一次,以检测循环的结束。
Vim 花费在等待用户输入上的时间根本不会计入。因此,您在响应 input() 提示时所花费的时间无关紧要。
性能分析应该很好地指示时间花在哪里,但请记住,有许多事情可能会影响结果
测量实际经过的时间,如果其他进程繁忙,它们可能会在不可预测的时间点造成延迟。您可能需要运行几次性能分析,并使用最低的结果。
如果您在一行中有多个命令,您只会获得一个时间。拆分该行以查看各个命令的时间。
累加的行的总时间通常少于整个函数的时间。两者之间存在一些开销。
在 Vim 退出之前被删除的函数不会生成性能分析信息。如果需要,您可以检查 v:profiling 变量
:if !v:profiling
:   delfunc MyFunc
:endif
性能分析可能会在多处理器系统上产生奇怪的结果,当休眠模式启动或处理器频率降低以节省电能时。
当函数递归使用时,“自身”时间是错误的。

上下文 Context context

编辑器状态由 Context 概念表示。这包括以下内容,如当前 跳跃列表寄存器 的值以及更多内容,这些内容将在下面进行描述。
context-types
支持以下 Context 项目:“jumps” 跳跃列表 “regs” 寄存器 “bufs” 缓冲区列表 “gvars” 全局变量 “sfuncs” 脚本局部 函数 “funcs” 全局和 脚本局部 函数
context-dict
Context 对象是包含以下键值对的字典
“jumps”、“regs”、“bufs”、“gvars”:readfile() 样式 List 表示相应的 msgpack 对象(请参阅 msgpackdump()msgpackparse())。
“funcs”(也包括 脚本局部 函数):List :function 定义。
context-stack
ctx 族函数(请参阅 ctx 函数)维护一个最初为空的内部 Context 堆栈。
命令索引
快速参考