Usr_44

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


VIM 用户手册 - 作者:Bram Moolenaar
您自己的语法高亮
Vim 附带了对数百种不同文件类型的语法高亮支持。如果您正在编辑的文件未包含在其中,请阅读本章,了解如何为这种类型的文件添加高亮。另请参阅参考手册中的 :syn-define
44.1 基本语法命令 44.2 关键字 44.3 匹配 44.4 区域 44.5 嵌套项 44.6 后续组 44.7 其他参数 44.8 集群 44.9 包含另一个语法文件 44.10 同步 44.11 安装语法文件 44.12 可移植语法文件布局
下一章:usr_45.txt 选择您的语言 前一章:usr_43.txt 使用文件类型 目录:usr_toc.txt

基本语法命令

使用现有的语法文件作为起点可以节省大量时间。尝试在 $VIMRUNTIME/syntax 中查找类似语言的语法文件。这些文件还将向您展示语法文件的常规布局。要理解它,您需要阅读以下内容。
让我们从基本参数开始。在开始定义任何新语法之前,我们需要清除所有旧的定义
:syntax clear
在最终的语法文件中,这并不是必需的,但在进行实验时非常有用。
本章中还有更多简化。如果您要编写供其他人使用的语法文件,请通读全文以了解详细信息。

列出定义的项

要检查当前定义了哪些语法项,请使用以下命令
:syntax
您可以使用此命令检查哪些项已实际定义。当您使用新的语法文件进行实验时,这非常有用。它还显示了每个项使用的颜色,这有助于找出哪些是什么。要列出特定语法组中的项,请使用
:syntax list {group-name}
这也可以用来列出集群(在 44.8 中解释)。只需在名称中包含 @ 即可。

匹配大小写

某些语言不区分大小写,例如 Pascal。其他语言,如 C,区分大小写。您需要使用以下命令告诉 Vim 您使用的是哪种类型
:syntax case match
:syntax case ignore
"match" 参数意味着 Vim 将匹配语法元素的大小写。因此,"int" 不同于 "Int" 和 "INT"。如果使用 "ignore" 参数,则以下等效:"Procedure"、"PROCEDURE" 和 "procedure"。":syntax case" 命令可以出现在语法文件中的任何位置,并影响其后的语法定义。在大多数情况下,您的语法文件中只有一个 ":syntax case" 命令;但是,如果您使用包含区分大小写和不区分大小写元素的非正常语言,则可以在整个文件中分散 ":syntax case" 命令。

44.2 关键字

最基本的语法元素是关键字。要定义关键字,请使用以下形式
:syntax keyword {group} {keyword} ...
{group} 是语法组的名称。使用 ":highlight" 命令,您可以为 {group} 分配颜色。{keyword} 参数是实际的关键字。以下是一些示例
:syntax keyword xType int long char
:syntax keyword xStatement if then else endif
此示例使用组名 "xType" 和 "xStatement"。按照惯例,每个组名都以要定义的语言的文件类型为前缀。此示例定义了 x 语言(eXample language,没有有趣的名称)的语法。在用于 "csh" 脚本的语法文件中,将使用名称 "cshType"。因此,前缀等于 'filetype' 的值。这些命令导致单词 "int"、"long" 和 "char" 以一种方式高亮显示,而单词 "if"、"then"、"else" 和 "endif" 则以另一种方式高亮显示。现在您需要将 x 组名连接到标准 Vim 名称。您可以使用以下命令来完成此操作
:highlight link xType Type
:highlight link xStatement Statement
这告诉 Vim 将 "xType" 高亮显示为 "Type",并将 "xStatement" 高亮显示为 "Statement"。有关标准名称,请参阅 group-name

不寻常的关键字

关键字中使用的字符必须位于 'iskeyword' 选项中。如果您使用其他字符,该单词将永远不会匹配。Vim 不会为此发出警告消息。x 语言在关键字中使用 '-' 字符。这是完成方法
:setlocal iskeyword+=-
:syntax keyword xStatement when-not
":setlocal" 命令用于仅更改当前缓冲区的 'iskeyword'。但它确实改变了像 "w" 和 "*" 这样的命令的行为。如果不需要这样做,请不要定义关键字,而是使用匹配项(将在下一节中解释)。
x 语言允许缩写。例如,"next" 可以缩写为 "n"、"ne" 或 "nex"。您可以使用以下命令定义它们
:syntax keyword xStatement n[ext]
这不会匹配 "nextone",关键字总是只匹配整个单词。

44.3 匹配

考虑定义更复杂的东西。您希望匹配普通的标识符。为此,您定义一个匹配语法项。这个匹配任何仅由小写字母组成的单词
:syntax match xIdentifier /\<\l\+\>/
注意:关键字会覆盖任何其他语法项。因此,"if"、"then" 等关键字将像上面使用 ":syntax keyword" 命令定义的那样是关键字,即使它们也匹配 xIdentifier 的模式。
末尾的部分是模式,就像它用于搜索一样。// 用于包围模式(就像在 ":substitute" 命令中一样)。您可以使用任何其他字符,例如加号或引号。
现在为注释定义一个匹配项。在 x 语言中,它是从 # 到行尾的任何内容
:syntax match xComment /#.*/
由于您可以使用任何搜索模式,因此您可以使用匹配项突出显示非常复杂的事物。有关搜索模式的帮助,请参阅 pattern

44.4 区域

在示例 x 语言中,字符串用双引号 (") 括起来。要突出显示字符串,您定义一个区域。您需要一个区域开始(双引号)和一个区域结束(双引号)。定义如下
:syntax region xString start=/"/ end=/"/
"start" 和 "end" 指令定义用于查找区域开始和结束的模式。但是,字符串看起来像这样怎么办?
"A string with a double quote (\") in it"
这会导致一个问题:字符串中间的双引号将结束该区域。您需要告诉 Vim 跳过字符串中任何转义的双引号。使用 skip 关键字来完成此操作
:syntax region xString start=/"/ skip=/\\"/ end=/"/
双反斜杠匹配单个反斜杠,因为反斜杠在搜索模式中是一个特殊字符。
何时使用区域而不是匹配项?主要区别在于匹配项是一个单一模式,必须整体匹配。只要 "start" 模式匹配,区域就会开始。是否找到 "end" 模式并不重要。因此,当该项依赖于 "end" 模式才能匹配时,您无法使用区域。否则,区域通常更容易定义。并且更容易使用嵌套项,如下一节所述。

44.5 嵌套项

看看这个注释
%Get input TODO: Skip white space
您希望将 TODO 高亮显示为大黄色字母,即使它位于以蓝色高亮显示的注释中。要让 Vim 知道这一点,您定义以下语法组
:syntax keyword xTodo TODO contained
:syntax match xComment /%.*/ contains=xTodo
在第一行中,"contained" 参数告诉 Vim 该关键字只能存在于另一个语法项中。下一行有 "contains=xTodo"。这表明 xTodo 语法元素在其中。结果是,整个注释行将与 "xComment" 匹配并变为蓝色。其中的单词 TODO 由 xTodo 匹配并以黄色高亮显示(xTodo 的高亮显示已为此设置)。

递归嵌套

x 语言在花括号中定义代码块。代码块可能包含其他代码块。可以这样定义
:syntax region xBlock start=/{/ end=/}/ contains=xBlock
假设您有以下文本
while i < b {
if a {
b = c;
}
}
首先,xBlock 在第一行的 { 处开始。在第二行中,又找到了一个 {。由于我们在一个 xBlock 项中,并且它包含它自己,因此这里将开始一个嵌套的 xBlock 项。因此,"b = c" 行位于第二级 xBlock 区域中。然后在下一行中找到了一个 },它与该区域的结束模式匹配。这结束了嵌套的 xBlock。因为 } 包含在嵌套区域中,所以它对第一个 xBlock 区域隐藏。然后在最后一个 } 处,第一个 xBlock 区域结束。

保留结束符

考虑以下两个语法项
:syntax region xComment start=/%/ end=/$/ contained
:syntax region xPreProc start=/#/ end=/$/ contains=xComment
您将注释定义为从 % 到行尾的任何内容。预处理器指令是从 # 到行尾的任何内容。因为您可以在预处理器行上添加注释,所以预处理器定义包含一个 "contains=xComment" 参数。现在看看使用以下文本会发生什么
#define X = Y % Comment text
int foo = 1;
您会看到第二行也被高亮显示为 xPreProc。预处理器指令应在行尾结束。这就是为什么您使用 "end=/$/" 的原因。那么哪里出了问题?问题在于包含的注释。注释以 % 开头,以行尾结束。注释结束后,预处理器语法继续。这是在看到行尾之后,因此下一行也被包含在内。为了避免此问题,并避免包含的语法项占用所需的结束符,请使用 "keepend" 参数。这将解决双结束符匹配问题
:syntax region xComment start=/%/ end=/$/ contained
:syntax region xPreProc start=/#/ end=/$/ contains=xComment keepend

包含多个项

您可以使用 contains 参数指定所有内容都可以包含。例如
:syntax region xList start=/\[/ end=/\]/ contains=ALL
所有语法项都将包含在此项中。它也包含它自己,但不在同一位置(这会导致无限循环)。您可以指定某些组不包含。因此包含所有组,但列出的那些除外
:syntax region xList start=/\[/ end=/\]/ contains=ALLBUT,xString
使用 "TOP" 项,您可以包含所有没有 "contained" 参数的项。"CONTAINED" 用于仅包含具有 "contained" 参数的项。有关详细信息,请参阅 :syn-contains

44.6 后续组

x 语言以这种形式具有语句
if (condition) then
您希望以不同的方式突出显示这三个项目。但是 "(condition)" 和 "then" 也可能出现在其他地方,在那里它们会获得不同的突出显示。以下是您可以执行此操作的方式
:syntax match xIf /if/ nextgroup=xIfCondition skipwhite
:syntax match xIfCondition /([^)]*)/ contained nextgroup=xThen skipwhite
:syntax match xThen /then/ contained
"nextgroup" 参数指定哪个项目可以接下来出现。这不是必需的。如果未找到指定的任何项目,则不会发生任何事情。例如,在此文本中
if not (condition) then
"if" 由 xIf 匹配。"not" 不匹配指定的 nextgroup xIfCondition,因此仅高亮显示 "if"。
"skipwhite" 参数告诉 Vim 在项目之间可以出现空格(空格和制表符)。类似的参数有 "skipnl",它允许项目之间出现换行符,以及 "skipempty",它允许出现空行。请注意,"skipnl" 不会跳过空行,换行符后必须匹配某些内容。

44.7 其他参数

MATCHGROUP

定义区域时,整个区域将根据指定的组名突出显示。例如,要使用组 xInside 突出显示括号 () 中的文本,请使用以下命令
:syntax region xInside start=/(/ end=/)/
假设你想用不同的方式突出显示括号。你可以使用很多复杂的区域语句来做到这一点,或者可以使用 "matchgroup" 参数。这告诉 Vim 使用不同的突出显示组 (在本例中为 xParen 组) 来突出显示区域的开始和结束
:syntax region xInside matchgroup=xParen start=/(/ end=/)/
"matchgroup" 参数适用于出现在它之后的开始或结束匹配。在前面的示例中,开始和结束都使用 xParen 突出显示。要使用 xParenEnd 突出显示结束
:syntax region xInside matchgroup=xParen start=/(/
        \ matchgroup=xParenEnd end=/)/
使用 "matchgroup" 的一个副作用是,包含的项目不会在区域的开始或结束处匹配。 "transparent" 的示例使用了这一点。

透明

在 C 语言文件中,你想在 "while" 后面的 () 文本与 "for" 后面的 () 文本不同的方式进行突出显示。在这两种情况下,都可能存在嵌套的 () 项目,应该以相同的方式进行突出显示。你必须确保 () 突出显示在匹配的 ) 处停止。以下是一种实现方法
:syntax region cWhile matchgroup=cWhile start=/while\s*(/ end=/)/
        \ contains=cCondNest
:syntax region cFor matchgroup=cFor start=/for\s*(/ end=/)/
        \ contains=cCondNest
:syntax region cCondNest start=/(/ end=/)/ contained transparent
现在你可以为 cWhile 和 cFor 提供不同的突出显示。cCondNest 项目可以出现在其中任何一个中,但会接管它所包含项目的突出显示。 "transparent" 参数导致了这种情况。注意,"matchgroup" 参数与项目本身具有相同的组。那么为什么还要定义它呢?嗯,使用 matchgroup 的一个副作用是,包含的项目在与开始项目匹配时不会在匹配中找到。这避免了 cCondNest 组与 "while" 或 "for" 之后的 ( 匹配。如果发生这种情况,它将跨越整个文本直到匹配的 ),并且区域将在其之后继续。现在,cCondNest 仅在与开始模式匹配后匹配,因此在第一个 ( 之后匹配。

偏移

假设你想为 "if" 之后的 ( 和 ) 之间的文本定义一个区域。但你不想包含 "if" 或 ( 和 )。你可以通过为模式指定偏移量来做到这一点。示例
:syntax region xCond start=/if\s*(/ms=e+1 end=/)/me=s-1
开始模式的偏移量为 "ms=e+1"。 "ms" 代表匹配开始。这为匹配的开始定义了一个偏移量。通常情况下,匹配从模式匹配的地方开始。 "e+1" 表示匹配现在从模式匹配的结束处开始,然后向前一个字符。结束模式的偏移量为 "me=s-1"。 "me" 代表匹配结束。 "s-1" 表示模式匹配的开始,然后向后一个字符。结果是,在这段文本中
if (foo == bar)
只有 "foo == bar" 文本将被突出显示为 xCond。
有关偏移量的更多信息,请参见::syn-pattern-offset.

单行

"oneline" 参数表示区域不跨越行边界。例如
:syntax region xIfThen start=/if/ end=/then/ oneline
这定义了一个从 "if" 开始,到 "then" 结束的区域。但是,如果 "if" 之后没有 "then",则区域不匹配。
注意:当使用 "oneline" 时,如果结束模式在同一行中不匹配,则区域不会开始。如果没有 "oneline",Vim 不会检查结束模式是否匹配。即使结束模式在文件的其余部分不匹配,区域也会开始。

续行和避免续行

现在事情变得有点复杂。让我们定义一个预处理行。这从第一列的 # 开始,一直延续到行的末尾。以 \ 结尾的行使下一行成为续行。处理此问题的方法是允许语法项包含一个续行模式
:syntax region xPreProc start=/^#/ end=/$/ contains=xLineContinue
:syntax match xLineContinue "\\$" contained
在本例中,虽然 xPreProc 通常匹配单行,但它包含的组 (即 xLineContinue) 允许它延续多行。例如,它将匹配这两行
#define SPAM spam spam spam \
bacon and spam
在本例中,这就是你想要的。如果你不想要这样,你可以通过在包含的模式中添加 "excludenl" 来要求区域在一行上。例如,你想在 xPreProc 中突出显示 "end",但只在行的末尾。为了避免使 xPreProc 像 xLineContinue 那样在下一行继续,请使用 "excludenl",如下所示
:syntax region xPreProc start=/^#/ end=/$/
        \ contains=xLineContinue,xPreProcEnd
:syntax match xPreProcEnd excludenl /end$/ contained
:syntax match xLineContinue "\\$" contained
"excludenl" 必须放在模式之前。由于 "xLineContinue" 没有 "excludenl",因此与它匹配将像以前一样将 xPreProc 扩展到下一行。

44.8 集群

当你开始编写语法文件时,你会注意到你最终会生成很多语法组。Vim 使你能够定义一个称为集群的语法组集合。假设你有一种语言包含 for 循环、if 语句、while 循环和函数。它们都包含相同的语法元素:数字和标识符。你像这样定义它们
:syntax match xFor /^for.*/ contains=xNumber,xIdent
:syntax match xIf /^if.*/ contains=xNumber,xIdent
:syntax match xWhile /^while.*/ contains=xNumber,xIdent
每次都必须重复相同的 "contains="。如果你想添加另一个包含的项目,你必须添加三次。语法集群通过使一个集群代表多个语法组来简化这些定义。要为三个组包含的两个项目定义一个集群,请使用以下命令
:syntax cluster xState contains=xNumber,xIdent
集群就像任何语法组一样,在其他语法项中使用。它们的名称以 @ 开头。因此,你可以像这样定义三个组
:syntax match xFor /^for.*/ contains=@xState
:syntax match xIf /^if.*/ contains=@xState
:syntax match xWhile /^while.*/ contains=@xState
你可以使用 "add" 参数向此集群添加新的组名
:syntax cluster xState add=xString
你也可以从此列表中删除语法组
:syntax cluster xState remove=xNumber

44.9 包含另一个语法文件

C++ 语言语法是 C 语言的超集。因为你不想编写两个语法文件,你可以让 C++ 语法文件使用以下命令读取 C 的语法文件
:runtime! syntax/c.vim
":runtime!" 命令在 'runtimepath' 中搜索所有 "syntax/c.vim" 文件。这使得 C++ 语法的 C 部分像 C 文件一样被定义。如果你已经替换了 c.vim 语法文件,或者使用额外的文件添加了项目,这些项目也将被加载。加载 C 语法项后,可以定义特定的 C++ 项目。例如,添加 C 中未使用过的关键字
:syntax keyword cppStatement        new delete this friend using
这与任何其他语法文件的工作方式相同。
现在考虑 Perl 语言。Perl 脚本包含两个不同的部分:以 POD 格式编写的文档部分,以及用 Perl 本身编写的程序。POD 部分以 "=head" 开头,以 "=cut" 结束。你想要在一个文件中定义 POD 语法,并在 Perl 语法文件中使用它。":syntax include" 命令读取一个语法文件,并将它定义的元素存储在一个语法集群中。对于 Perl,语句如下
:syntax include @Pod <sfile>:p:h/pod.vim
:syntax region perlPOD start=/^=head/ end=/^=cut/ contains=@Pod
在 Perl 文件中找到 "=head" 时,perlPOD 区域开始。在此区域中,包含 @Pod 集群。在 pod.vim 语法文件中定义的所有顶级项目将在此处匹配。当找到 "=cut" 时,区域结束,我们回到 Perl 文件中定义的项目。":syntax include" 命令足够聪明,可以忽略包含文件中的 ":syntax clear" 命令。而 "contains=ALL" 这样的参数只会包含在包含文件中定义的项目,而不会包含在包含它的文件中。"<sfile>:p:h/" 部分使用当前文件的名称 (<sfile>),将其扩展为完整路径 (:p),然后取头部 (:h)。这会导致包含同一目录中的 pod.vim 文件。

44.10 同步

编译器很简单。它们从文件的开头开始解析,一直解析到结尾。Vim 没有那么容易。它必须从中间开始,也就是正在进行编辑的地方。那么它如何知道自己在哪里呢?秘密在于 ":syntax sync" 命令。这告诉 Vim 如何找出它在哪里。例如,以下命令告诉 Vim 向后扫描 C 样式注释的开始或结束,并从那里开始语法着色
:syntax sync ccomment
你可以使用一些参数来调整这个过程。 "minlines" 参数告诉 Vim 向后查看的最小行数,"maxlines" 参数告诉编辑器扫描的最大行数。例如,以下命令告诉 Vim 在屏幕顶部之前至少查看 10 行
:syntax sync ccomment minlines=10 maxlines=500
如果它在该空间中无法确定自己的位置,它就会开始向后查看,直到它弄清楚该怎么做。但它最多向后查看 500 行。(较大的 "maxlines" 会降低处理速度。较小的 "maxlines" 可能会导致同步失败。)为了使同步速度更快,请告诉 Vim 哪些语法项可以跳过。每个仅在实际显示文本时才需要使用的匹配和区域都可以使用 "display" 参数。默认情况下,要查找的注释将被着色为 Comment 语法组的一部分。如果你想用其他方式着色,你可以指定不同的语法组
:syntax sync ccomment xAltComment
如果你的编程语言没有 C 样式注释,你可以尝试其他同步方法。最简单的方法是告诉 Vim 向后移动一定数量的行,并尝试从那里开始解析。以下命令告诉 Vim 向后移动 150 行,并从那里开始解析
:syntax sync minlines=150
较大的 "minlines" 值会使 Vim 变慢,尤其是在文件中向后滚动时。最后,你可以使用以下命令指定要查找的语法组
:syntax sync match {sync-group-name}
        \ grouphere {group-name} {pattern}
这告诉 Vim,当它看到 {pattern} 时,名为 {group-name} 的语法组就在给定模式之后开始。 {sync-group-name} 用于为这个同步规范命名。例如,sh 脚本语言以 "if" 开头,以 "fi" 结尾
if [ --f file.txt ] ; then
echo "File exists"
fi
要为这个语法定义 "grouphere" 指令,请使用以下命令
:syntax sync match shIfSync grouphere shIf "\<if\>"
"groupthere" 参数告诉 Vim 模式结束了一个组。例如,if/fi 组的结束如下所示
:syntax sync match shIfSync groupthere NONE "\<fi\>"
在这个例子中,NONE 告诉 Vim 你不在任何特殊的语法区域。特别是,你不在 if 块中。
你也可以定义没有 "grouphere" 或 "groupthere" 参数的匹配和区域。这些组用于在同步期间跳过的语法组。例如,以下内容跳过 {} 中的任何内容,即使它通常会匹配其他同步方法
:syntax sync match xSpecial /{.*}/
有关同步的更多信息,请参见参考手册::syn-sync.

44.11 安装语法文件

当你的新语法文件准备使用时,将其放到 'runtimepath' 中的 "syntax" 目录中。对于 Unix,该目录应该是 "~/.config/nvim/syntax"。语法文件的名称必须与文件类型相同,并在后面添加 ".vim"。因此,对于 x 语言,文件的完整路径应该是
~/.config/nvim/syntax/x.vim
你还要使文件类型得到识别。参见 43.2.
如果你的文件运行良好,你可能希望让其他 Vim 用户也能使用它。首先阅读下一节,以确保你的文件对其他人也能正常工作。然后将其发送给 Vim 维护者:<[email protected]>。还要说明如何检测文件类型。如果运气好,你的文件将被包含在下一个 Vim 版本中!

添加到现有语法文件中

我们之前假设您要添加一个全新的语法文件。如果现有语法文件可以正常工作,但缺少某些项,则可以在单独的文件中添加这些项。这样可以避免修改分发的语法文件,因为在安装新版本的 Vim 时,这些更改将会丢失。在您的文件中编写语法命令,可能使用现有语法中的组名。例如,要将新的变量类型添加到 C 语法文件中
:syntax keyword cType off_t uint
使用与原始语法文件相同的名称编写文件。在本例中为 "c.vim"。将它放在 'runtimepath' 末尾附近的一个目录中。这样可以确保它在原始语法文件之后加载。对于 Unix 系统,这将是
~/.config/nvim/after/syntax/c.vim

44.12 可移植语法文件布局

如果所有 Vim 用户都能交换语法文件,那该多好啊!为了实现这一点,语法文件必须遵循一些准则。
以一个标题开头,说明语法文件的用途、维护者以及最后更新时间。不要包含过多关于变更历史的信息,因为很少有人会阅读它。示例
" Vim syntax file
" Language:        C
" Maintainer:        Bram Moolenaar <[email protected]>
" Last Change:        2001 Jun 18
" Remark:        Included by the C++ syntax.
使用与其他语法文件相同的布局。使用现有的语法文件作为示例可以节省大量时间。
为您的语法文件选择一个好的、描述性的名称。使用小写字母和数字。不要太长,因为它在许多地方都会用到:语法文件名 "name.vim"、'filetype'、b:current_syntax 以及每个语法组的开头(nameType、nameStatement、nameString 等)。
以检查 "b:current_syntax" 开头。如果它已定义,则表示 'runtimepath' 中更早的某个其他语法文件已加载。
if exists("b:current_syntax")
  finish
endif
在最后将 "b:current_syntax" 设置为语法的名称。不要忘记包含的文件也会执行此操作,如果您包含了两个文件,则可能需要重置 "b:current_syntax"。
不要包含任何用户偏好设置。不要设置 'tabstop''expandtab' 等。这些设置应该放在文件类型插件中。
不要包含映射或缩写。仅在识别关键字真正必要时才包含设置 'iskeyword'
为了允许用户选择他们自己喜欢的颜色,为每种类型的突出显示项创建一个不同的组名。然后将它们链接到一个标准突出显示组。这样可以使其与所有配色方案兼容。如果您选择特定的颜色,它在某些配色方案中看起来会很糟糕。并且不要忘记,有些人使用不同的背景颜色,或者只有 8 种颜色可用。
对于链接,使用 "hi def link",以便用户可以在加载您的语法文件之前选择不同的突出显示。示例
hi def link nameString        String
hi def link nameNumber        Number
hi def link nameCommand        Statement
... etc ...
将 "display" 参数添加到在同步时不使用的项,以加快向后滚动和 CTRL-L 的速度。
下一章:usr_45.txt 选择您的语言
版权:请参阅 manual-copyright vim:tw=78:ts=8:noet:ft=help:norl
主页面
命令索引
快速参考