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} 的映射. 该映射可能会在它作用的其它模式中被保留.备注: {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 <buffer>  ,w  /[.,;]<CR>
然后你可以把 ",w" 在另一个缓冲内作另外的映射:
        :map <buffer>  ,w  /[#&!]<CR>
局部缓冲映射在全局映射之前被应用. "<buffer>" 参数也可以用于清除映射:
        :unmap <buffer> ,w
        :mapclear <buffer>
当一个缓冲被删除时局部映射也会被清除, 但是在它被卸载时不会. 就象本地选项值.*: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>" 并且它用于定义一个新的映射或缩写,
该映射将仅仅重映射 {rhs} 中使用被一个以 "<SID>" 开头的脚本局部定义的映射中的字符.
它可以用于避免来自外部的脚本的干扰 (举例来说, 在 mswin.vim 中 CTRL-V 被重新映射 ),
但是又使用该脚本中定义的其它映射.
备注: ":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-pendingn       Normalv       Visualo       Operator-pending!       Insert 和 Command-linei       Insertl       ":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-PCTRL-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 type CTRL-K and then hit the function key, or usethe 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 <t_F3> G把功能键 13 映射成 "G".  当 'cpoptions' 包括 '<' 标志时这种方式不能使用.第二种和第三种方法的优点是不加修改就可以在不同的终端上使用 (功能键会被转换成
相同的内部编码或实际的键编码, 而不论使用何种终端. termcap 必需是正确的才能正
常工作, 并且必需使用相同的映射).细节: Vim 首先检查是否从键盘输入的序列是否已被映射.  否的话将试图使用终端键编
码 (参考 |terminal-options|).  如果一个终端键编码被找到它会被替换成内部编码.
然后再次检查一个映射是否已完成 (因此你也能把一个内部编码映射成其它东西).
在脚本文件中写入什么东西取决于识别了什么东西. 如果终端键编码被识别为一个映射
它本身的键编码会被写入脚本. 如果它被识别为一个终端编码则在脚本中写入内部编码.

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 ;; <endofline>""test;;"          不被扩展"test ;;"         被扩展为 "test <endofline>"要避免在插入模式中避免缩写: 输入缩写的一部分, 以 <Esc> 退出插入模式, 再用 'a'
进入插入模式并输入剩下的部分. 或者在缩写之后的字符前面输入 CTRL-V.
要在命令行模式中避免缩写: 在缩写的某处输入 CTRL-V 两次来避免它被替换. 此外一个
普通字符前面的 CTRL-V 通常会被忽略.把光标移动到缩写的后面是可能的:
   :iab if if ()<Left>
如果 'cpoptions' 里面包含 '<' 标志时不能正常工作. |<>|你甚至可以做更复杂的事情.  例如, 要消灭一个缩写后面输入的空格:func Eatchar(pat)let c = nr2char(getchar())return (c =~ a:pat) ? '' : cendfunciabbr <silent> if if ()<Left><C-R>=Eatchar('\s')<CR>没有缺省的缩写.缩写永远不会递归.  你可以设置 ":ab f f-o-o" 而不会有任何问题. 但是缩写能
被映射. {不清除原因, 一些版本的Vi支持递归缩写}'paste' 选项打开时, 缩写被禁止.*:abbreviate-local* *:abbreviate-<buffer>*
和映射一样, 缩写可以被局部于一个缓冲之内.  这经常被用于一个
|filetype-plugin| 文件.  例如一个 C 插件文件:
        :abb <buffer> FF  for (i = 0; i < ; ++i)*:ab* *:abbreviate*
:ab[breviate]           列出所有的缩写.  第一栏中的字符表示该缩写作用的模式:'i' 指插入模式, 'c' 指命令行模式, '!' 指两种模式都有.这和映射的相同,参看 |map-listing| 。:ab[breviate] {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 inVi}*: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]             为命令行模式删除所有的缩写.  {notin 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:

转载地址:http://man.chinaunix.net/newsoft/vi/doc/map.html

GVIM 键映射, 缩写和用户定义的命令相关推荐

  1. 进阶:变量 1.系统变量 说明:变量由系统定义,不是用户定义,属于服务器层面。 注意:(1) 全局变量需要添加global关键字 (2) 会话变量需要添加session键字 (3)

    进阶:变量 1.系统变量 说明:变量由系统定义,不是用户定义,属于服务器层面. 注意:(1) 全局变量需要添加global关键字 (2) 会话变量需要添加session关键字 (3) 如果不写,默认s ...

  2. 使用脚本编写 Vim 编辑器,第 2 部分: 用户定义函数

    用户定义函数 Haskell 或 Scheme 程序员会告诉您,函数对于任何严肃的编程语言来说都是最重要的特性.对于 C 或 Perl 程序员,他们也会告诉您完全相同的观点. 函数为严肃的程序员提供了 ...

  3. 键盘组合键映射_如何在键盘上重新映射Office键

    键盘组合键映射 The Office Key is a new key that you'll find on Microsoft keyboards. It lets you quickly lau ...

  4. oeasy教您玩转vim - 76 - # 组合键映射map

    会话session 回忆组合键映射的细节 上次我们定义了一系列的复合键 主要是和ctrl键一起 快速跳转window窗口 map <c-j> <c-w>j map <c- ...

  5. 彻底聊聊关系数据库中的完整性约束:实体完整性、参照完整性和用户定义的完整性

    1.结论描述 先用最简单的话总结一下,实体.参照和用户定义的完整性,避免理论性的论述性的内容过长过繁. 实体完整性:是用来唯一标识实体的要求(即现实世界中唯一存在的一个事物.人或个体).不能为空,需要 ...

  6. ylbtech-LanguageSamples-UserConversions(用户定义的转换)

    ylbtech-Microsoft-CSharpSamples:ylbtech-LanguageSamples-UserConversions(用户定义的转换) 1.A,示例(Sample) 返回顶部 ...

  7. 存储过程中定义sql语句_学习SQL:用户定义的存储过程

    存储过程中定义sql语句 Stored procedures (SPs) are one more powerful database object we have at our disposal. ...

  8. sql自定义函数学习思路_学习SQL:用户定义的函数

    sql自定义函数学习思路 You can create several user-defined objects in a database. One of these is definitely u ...

  9. SQL Prompt数据库教程:标量用户定义函数误用作常量

    SQL Prompt是一款实用的SQL语法提示工具.SQL Prompt根据数据库的对象名称.语法和代码片段自动进行检索,为用户提供合适的代码选择.自动脚本设置使代码简单易读–当开发者不大熟悉脚本时尤 ...

最新文章

  1. Xcode文件被锁定:The file .xcodeproj could not be unlocked
  2. crnn自编码网络(类似递归)
  3. pytorch 打印模型参数
  4. ML:基于自定义数据集利用Logistic、梯度下降算法GD、LoR逻辑回归、Perceptron感知器、SVM支持向量机、LDA线性判别分析算法进行二分类预测(决策边界可视化)
  5. ateq测漏仪f620中文说明书_Yaman雅萌美容仪详细介绍及2019年韩国免税店最新报价...
  6. 没有计算机的一天英语作文带翻译,初一英语作文我的一天带翻译
  7. android自定义底部中间突出导航栏,Android选中突出背景效果的底部导航栏功能
  8. c替代if else_答应我,别再if/else走天下了可以吗
  9. 细嚼浏览器兼容----条件注释判断浏览器版本
  10. 20岁生日快乐c语言,C语言怎样编程生日快乐代码
  11. 【信号处理】基于高阶统计量特征的通信系统中微弱信号检测附matlab代码
  12. 社区智能健康手环方案/APP/小程序/项目
  13. 笔记本计算机无法开机,笔记本电脑无法开机黑屏?故障分析大全,及时解决办法...
  14. moviepy音视频开发:audio_fadein、fadeout实现声音淡入淡出
  15. vassonic PHP,轻量级、高性能的 VasSonic 框架,听说开源了?
  16. php简写是哪个国家的货币,世界197个国家的180种货币列表、数字符号、名称及对应国家简称...
  17. php毕业论文致谢,科学网—我也晒一下毕业论文致谢,感谢诸多帮助过我的人 - 何浩宇的博文...
  18. 缓存为王:老码农眼中的分布式缓存
  19. 网易我的世界服务器如何装组件,网易我的世界组件包怎么使用-网易我的世界组件包如何使用...
  20. 新编计算机科学概论考试,热门计算机科学概论论文题目 计算机科学概论论文题目怎么定...

热门文章

  1. 安川A1000变频器通过DriveWizard Plus设置加减速时间
  2. 神奇的 toLocaleString
  3. FFmpeg 音视频截取
  4. mac下查看本机在某wifi下所对应的ip地址
  5. 全面屏下的沉浸式状态栏的返回键、home键、菜单键的显示。
  6. 数据分析软件解读王者荣耀一年的“王者”成绩单
  7. [IOS初学]ios 第一篇 storyboard 与viewcontroller的关系 - Zoe_J
  8. 清空RMON统计的数据
  9. java计算机毕业设计计算机课程在线培训学习管理系统MyBatis+系统+LW文档+源码+调试部署
  10. jsp 页面进行debug 断点找错误