<< Back to man.ChinaUnix.net

TIPS

*tips.txt*      For Vim version 6.3.  最近更新: 2005年3月


                     Vim 参考手册    作者:Bram Moolenaar
           译者: iCrazy <icrazy@ustc.edu>  http://vimcdoc.sf.net


使用 Vim 的技巧                                            *tips*

别忘记浏览用户手册,里面有很多实用的技巧 |usr_toc.txt|.

编辑 C 程序                                 |C-editing|
查找使用标识符的地方                          |ident-search|
在 xterm 中切换屏幕                               |xterm-screens|
在插入模式下滚屏                                |scroll-insert|
平滑的滚屏                                 |scroll-smooth|
纠正普通的录入错误                             |type-mistakes|
统计单词,行数                                   |count-items|
恢复光标位置                                      |restore-position|
文件更名                                    |rename-files|
加速外部命令的执行                             |speed-up|
一些有用的映射                                   |useful-mappings|
压缩帮助文件                                      |gzip-helpfile|
十六进制编辑                                      |hex-editing|
在一个窗口中执行 shell 命令                   |shell-window|
在自动命令中使用符号 <>                               |autocmd-<>|


编辑 C 程序                                         *C-editing*

Vim 里面有不少功能可以帮助你们编辑 C 程序。以下是一个概括,你们可以使用标签
跳转到具体的内容中去:

|usr_29.txt|            用户手册中关于在程序的不同部分间移动的内容。
|usr_30.txt|            用户手册中关于编辑程序的内容。
|C-indenting|           输入时自动设置每行的缩进。
|=|                     重新缩进一些行。
|format-comments|       对注释进行编排。

|:checkpath|            显示所有被包含的文件 (嵌套)。
|[i|                    在当前和被包含的文件中查找光标当前位置的标识符。
|[_CTRL-I|              跳转到 "[i" 的匹配。
|[I|                    显示在当前和被包含的文件中现出光标所在位置标识符的
                        那些行。
|[d|                    在当前和被包含的文件中查找光标所在位置的标识符的定义
                        (define)。

|CTRL-]|                跳转到光标当前位置的标签处 (例如:一个函数的定义)。
|CTRL-T|                跳转到执行 CTRL-] 命令前的地方。
|:tselect|              从一连串匹配的标签中选出一个。

|gd|                    跳转到光标当前位置的局部变量的声明处。
|gD|                    跳转到光标当前位置的全局变量的声明处。

|gf|                    跳转到光标当前位置的文件名表示的文件。

|%|                     跳转到匹配的(), {}, [], /* */, #if, #else, #endif 处。
|[/|                    跳转到上一个注释开始的位置。
|]/|                    跳转到下一个注释结束的位置。
|[#|                    返回到未闭合的 #if, #ifdef, or #else 处。
|]#|                    前进到未闭合的 #else 或 #endif 处。
|[(|                    返回到未闭合的 '(' 处。
|])|                    前进到未闭合的 ')' 处。
|[{|                    返回到未闭合的 '{' 处。
|]}|                    前进到未闭合的 '}' 处。

|v_ab|                  选中一个“块” ("a block"),从 "[(" 至 "])",含括号
|v_ib|                  选中一个“内部块” ("inner block"),从 "[(" 至 "])"
|v_aB|                  选中一个“块” ("a block"),从 "[{" 至 "]}",含括号
|v_iB|                  选中一个“内部块” ("inner block"),从 "[{" 至 "]}"


查找使用标识符的地方                                  *ident-search*

你应该已经知道 |tags| 可以被用来跳转到定义函数和变量的地方。但是有时你希望跳
转到使用函数和变量的地方。可以用以下2种方法实现:
1. 使用 |:grep| 命令。这个应该可以在大多数 Unix 系统上工作,但是速度会比较慢
(因为它读取所有的文件),并且只能在一个目录中搜索。
2. 使用 ID 工具集。这个速度比较块而且可以搜索多个目录。它使用一个数据库来存放
定位信息。你需要一些额外的程序来使它得以工作,并且你必须使数据库不断保持更新。

使用 GNU id 工具集:

你所需要的:
- 安装 GNU id 工具集(mkid 是用来创建 ID 的,lid 是用来运行宏的)
- 一个在当前目录下名为 "ID" 的标识符数据库。你可以用 shell 命令 "mkid file1
  file2 .." 来创建它。

把这些添加写你的 .vimrc文件中:
        map _u :call ID_search()<Bar>execute "/\\<" . g:word . "\\>"<CR>
        map _n :n<Bar>execute "/\\<" . g:word . "\\>"<CR>

        function! ID_search()
          let g:word = expand("<cword>")
          let x = system("lid --key=none ". g:word)
          let x = substitute(x, "\n", " ", "g")
          execute "next " . x
        endfun

使用的时候,把光标放在一个单词上,敲入 "_u",然后 vim 会读入含有这个单词的文
件。使用 "n"查找这个单词在相同文件中下一次的出现的地方。使用 "_n" 可以跳转到下一个
文件。

这写操作已经使用 id-utils-3.2(这是位于距你最近的 gnu ftp 镜像服务器上的档案
名称)测试通过了。

[这个的想法来自于 Andreas Kutschera]


在 xterm 中切换屏幕                       *xterm-screens* *xterm-save-screen*

(来源:comp.editors, 作者:Juergen Weigert, 回答一个问题的时候)

:> 另一个问题就是退出 vim 后,屏幕内容就被留在那儿了,也就是说:我刚刚正在
:> 查看(或编辑)的内容就被留在屏幕上了。我此前执行的命令(例如:"ls")的输出
:> 就不见了,即在滚屏缓冲里不存在了。我知道有个办法可以在退出 vim 或其他类似
:> 于 vi 的编辑器的时候恢复屏幕内容,但是我不知道该如何操作。请帮助我,谢谢!
:
:我认为可能有人可以回答这个问题。我认为 vim 和 vi 在某个特定
:的 xterm 安装下和别的程序一样工作。

他们并不一定完全相同,因为这牵扯到一个 termcap 对 terminfo 的问题。你们应该知
道针对一种特定的终端,有2种数据库可以用来描述它的属性: termcap 和 terminfo。
当它们中的条目不同,而且以上问题中的一个程序使用 terminfo,另一个使用 termcap
时,两个程序会出现差异(请参见: |+terminfo|)。

在你的问题中,你可能需要以下的控制序列:^[[?47h 和 ^[[?47l。他们用来在 xterm
和主屏幕缓冲中轮流切换。如果你希望工作得更加有效率,如下的命令序列
        echo -n "^[[?47h"; vim ... ; echo -n "^[[?47l"
可能就是你所需要的。(我用符号 ^[ 表示 ESC 字符,往后你还会看到数据库使用 \E
来表示它)。

在启动的时候,vim 把 termcap 中变量 ti (terminfo:smcup)的值显示在终端上。退出
的时候,它显示 te (terminfo: rmcup) 的值。这样一来,这2个变量正好处于以上所述
的控制序列需要被执行的位置。

把你的 xterm termcap 条目(在 /etc/termcap 中)和 xterm terminfo 条目(用
/usr/5bin/infocmp -C xterm 得到)比较一下。两者应该都会有与下面类似的条目:
        :te=\E[2J\E[?47l\E8:ti=\E7\E[?47h:

附:如果你发现了任何差异,那么最好让某人(或许是你的系统管理员)彻底的检查
    一下 termcap 和 terminfo 的一致性。

备注 1: 如果你在 feature.h 中定义了 FEAT_XTERM_SAVE 之后又重新编译了 Vim,那
么内置的 xterm 会有上述的 "te" 和 "ti" 条目。

备注 2: 如果你希望禁止屏幕切换,并且不希望改变你的 termcap,你可以在你的
.vimrc 文件中加入这一行:
        :set t_ti= t_te=


在插入模式下滚屏                                        *scroll-insert*

如果你处于插入模式下并且希望看一些恰好在屏幕范围以外的东西,你可以使用 CTRL-X
CTRL-ECTRL-X CTRL-Y 来滚屏。
                                                |i_CTRL-X_CTRL-E|

为了使这个简单些,你可以用这些映射:
        :inoremap <C-E> <C-X><C-E>
        :inoremap <C-Y> <C-X><C-Y>
(逐字敲入这些字符,并确认 '<' 标志不在 'cpoptions' 选项中)。
不过这样你就不能使用从光标上一行/下一行拷贝文字的功能了 |i_CTRL-E|。

你还可以考虑把 'scrolloff' 设置得大一些,这样你就总能看到光标附近的上下文了。
如果 'scrolloff' 的值大于窗口高度的一半,在向上或向下移动光标时,文字会上下卷
动,但是光标会始终停留在屏幕中间的位置。


平滑的滚屏                                         *scroll-smooth*

如果你希望你的滚屏更加平滑一些,你可以使用以下的映射:
        :map <C-U> <C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y>
        :map <C-D> <C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E>

(逐字敲入这些字符,并确认 '<' 标志不在 'cpoptions' 选项中)。


纠正普通的录入错误                                     *type-mistakes*

如果有一些单词你总是敲错,你可以使用缩写来改正它们。例如:
        :ab teh the
        :ab fro for


统计单词,行数等                                        *count-items*

如果需要统计缓冲中一个模式(pattern)出现的频率,需要把 'report' 置为0,然后
用替换命令把模式替换成它自己。Vim 报告的已替换的数目就是模式出现的次数。例:

        :set report=0
        :%s/./&/g               characters
        :%s/\i\+/&/g            words
        :%s/^                   lines
        :%s/the/&/g             "the" anywhere
        :%s/\<the\>/&/g         "the" as a word

你可能希望复位 'hlsearch' 或者使用 ":nohlsearch"。

如果 'modifiable' 选项是关的,这些就失效了。另一种办法就是在 Visual 模式下使用
|v_g_CTRL-G|。

                                                        *count-bytes*
如果你想数字节数,你可以这样做:

        选中那些字符(块选择也可以)
        使用 "y" 来拷贝这些字符
        使用 strlen() 函数:
                :echo strlen(@")
一个换行符被当做一个字节。


恢复光标位置                                              *restore-position*

有时你希望写一个映射,让它在文件中的其他地方做一些修改然后恢复光标的位置
(不使当前的文本卷屏)。例如,修改一个文件中的日期标记:

   :map <F2> msHmtgg/Last [cC]hange:\s*/e+1<CR>"_D"=strftime("%Y %b %d")<CR>p'tzt`s

分解出保存位置的命令:
        ms      把光标位置存放在标记 's' 中
        H       跳转到当前屏幕的顶端
        mt      把这个位置存放在标记 't' 中

分解出恢复位置的命令:
        't      跳转到先前位于屏幕顶端的那一行
        zt      卷屏,使这一行位于屏幕的顶端
        `s      跳转到最初光标的位置


文件更名                                            *rename-files*

假如我有一个目录,里面有如下的文件(目录是随机选取的):

buffer.c
charset.c
digraph.c
...

现在我希望把 *.c 更名为 *.bla。我可以这样做:

        $ vim
        :r! ls *.c
        :%s/\(.*\).c/mv & \1.bla
        :w !sh
        :q!


加速外部命令的执行                                     *speed-up*

在一些情况下,外部命令执行起来非常的慢。这个还会减慢 Unix 下的通配符扩展。这
儿有一些建议可以加快速度:

如果你的 .cshrc 文件(根据你使用的 shell,文件名可能不同)非常的长,你应该把
它分割成2节:需要和用户交互的、无需和用户交互的(经常被称作二级 shell)。当你
在 Vim 中执行一个类似 ":!ls" 的命令时,你就不需要和用户交互的那一部分(例如:
设置提示符)。把那些不必要的部分放到这些行后面去:

        if ($?prompt == 0) then
                exit 0
        endif

另一个办法是在 'shell' 选项中包含 "-f" 参数,例如:

        :set shell=csh\ -f

(这儿的反斜杠是必须的,这样才能在选项中表示一个空格)。
这样就会使 csh 完全跳过 .cshrc 文件。不过这样可能会造成一些程序不能正常运行。


一些有用的映射                                   *useful-mappings*

这里有一些人们喜欢使用的映射。

                                                        *map-backtick*
        :map ' `
使得单引号和 ' 一样工作。把光标移动到一个标记的第一列,而不是那一行的第一个非
空白字符。

                                                        *emacs-keys*
要在命令行上实现 Emacs 风格的编辑操作:
        " start of line
        :cnoremap <C-A>         <Home>
        " back one character
        :cnoremap <C-B>         <Left>
        " delete character under cursor
        :cnoremap <C-D>         <Del>
        " end of line
        :cnoremap <C-E>         <End>
        " forward one character
        :cnoremap <C-F>         <Right>
        " recall newer command-line
        :cnoremap <C-N>         <Down>
        " recall previous (older) command-line
        :cnoremap <C-P>         <Up>
        " back one word
        :cnoremap <Esc><C-B>    <S-Left>
        " forward one word
        :cnoremap <Esc><C-F>    <S-Right>

备注: 前提条件是 '<' 标志不在 'cpoptions' 选项中。 |<>|

                                                        *format-bullet-list*
这个映射可以格式化任何 bullet 列表,不过它需要在每一个条目的上下都各有一
个空行。这些表达式命令可以用来对映射的部分进行注释:

        :let m =     ":map _f  :set ai<CR>"   " 需要设置 'autoindent'
        :let m = m . "{O<Esc>"                " 在项目上面加入空行
        :let m = m . "}{)^W"                  " 跳转到 bullet 之后的文本
        :let m = m . "i     <CR>     <Esc>"   " 为缩进加空格
        :let m = m . "gq}"                    " 格式化 bullet 之后的文本
        :let m = m . "{dd"                    " 删除空行
        :let m = m . "5lDJ"                   " 把文本放到 bullet 之后
        :execute m                            |" 定义这个 mapping

(符号 <> |<>|。  注意 这些必须按照字面逐个输入。  ^W 是 "^" 和 "W",而不是
CTRL-W。 如果 '<' 标志不在 'cpoptions' 选项中,你可以把这些拷贝/粘贴到 Vim 中
去)

注意 最后一个注释以 |" 开始,因为 ":execute" 不能直接识别一个注释。

你还需要把 'textwidth' 设置成一个非0值,例如:
        :set tw=70

以下这个映射可以达到同样的效果,不过它从第一行获得列表的缩进(备注: 这个
映射是在同一行中输入的,其中有很多空格):
        :map _f :set ai<CR>}{a                                                          <Esc>WWmmkD`mi<CR><Esc>kkddpJgq}'mJO<Esc>j

                                                        *collapse*
这2个映射可以把一连串的空行 (;b) 或空白行 (;n) 压缩到一行
    :map ;b   GoZ<Esc>:g/^$/.,/./-j<CR>Gdd
    :map ;n   GoZ<Esc>:g/^[ <Tab>]*$/.,/[^ <Tab>]/-j<CR>Gdd


压缩帮助文件                                              *gzip-helpfile*

对于那些磁盘空间极度紧张的人来说,你们可以压缩帮助文件。这样会使得查看帮助文
件时稍微慢一点,并且需要 "gzip" 这个程序的支持。

(1) 压缩所有帮助文件: "gzip doc/*.txt"。

(2) 编辑文件 "doc/tags",用 ".txt.gz" 替换 ".txt":
        :%s=\(\t.*\.txt\)\t=\1.gz\t=

(3) 把这一行加入到你的 vimrc 文件中:
        set helpfile={dirname}/help.txt.gz

这儿 {dirname} 是存放帮助文件的目录。|gzip| 这个插件会负责解压缩这些文件的。
你必须肯定已经把 $VIMRUNTIME 设置成存放 Vim 文件的目录了,如果两者不一致的
话。参见: |$VIMRUNTIME|。


在一个窗口中执行 shell 命令                           *shell-window*

很多人都询问能不能在 Vim 中的一个窗口内执行 shell 命令。答案是:不行!加入这
个功能会给 Vim 增加很多代码量,这也是为什么我们没有这样做的一个很好的理由。毕
竟,Vim 只是一个编辑器,它本身并不是用来做那些非编辑类工作的。然而,要达到这
样的目的,你可以把你的终端屏幕拆分开或者使者用 "splitvt" 程序来显示你的屏幕。
在一些 ftp 服务器上你可以找到这个工具。Sam Lantinga <slouken@cs.ucdavis.edu>
对此了解颇多。另一种办法就是使用最早在 BSD Unix 上出现的 "window" 命令,它可
以支持很多重叠的窗口。或者使用最先出现在 www.uni-erlangen.de 上的 "screen" 程
序,它支持把多个窗口存入堆栈中。


十六进制编辑                                      *hex-editing* *using-xxd*

请看用户手册的第 |23.4| 节。

如果你用一个专门的扩展名用来命令二进制文件(诸如 exe,bin等等),你们会发现以
下使用在 <.vimrc> 文件中的一些命令在自动处理这些文件时非常有用。你可以用你希
望编辑的文件扩展名(用逗号分隔)替换以下的 "*.bin":

        " vim -b : edit binary using xxd-format!
        augroup Binary
          au!
          au BufReadPre  *.bin let &bin=1
          au BufReadPost *.bin if &bin | %!xxd
          au BufReadPost *.bin set ft=xxd | endif
          au BufWritePre *.bin if &bin | %!xxd -r
          au BufWritePre *.bin endif
          au BufWritePost *.bin if &bin | %!xxd
          au BufWritePost *.bin set nomod | endif
        augroup END


在自动命令中使用符号 <>                                       *autocmd-<>*

在一个自动命令的参数中,符号 <> 是不能被识别的。为避免使用用特殊的字符,你可
以使用一个可以自我毁灭的映射来得到符号 <>,然后从自动命令中调用这个映射。
举例如下:

                                                *map-self-destroy*
 " 这个将会自动的把文件名加入到菜单列表中
 " 它使用了一个可以自我毁灭的 mapping!
 " 1. 用缓冲中的一行把文件名中的点('dots')转换成 \ 。
 " 2. 把那个存放在寄存器 '"' 中
 " 3. 把那个名字存在缓冲菜单列表中
 " 注意:这个有一些副作用,比如:覆盖当前的寄存器内容和
 " 删除任何对 "i" 命令的mapping。
 "
 autocmd BufNewFile,BufReadPre * nmap i :nunmap i<CR>O<C-R>%<Esc>:.g/\./s/\./\\./g<CR>0"9y$u:menu Buffers.<C-R>9 :buffer <C-R>%<C-V><CR><CR>
 autocmd BufNewFile,BufReadPre * normal i

另一个或许更好一些的办法就是使用 ":execute" 命令。在字符串中,你可以通过在符
号 <> 前面加一个反斜杠的方法来使用它。别忘了重复反斜杠以及在'"'前面放一个反斜
杠。

  autocmd BufNewFile,BufReadPre * exe "normal O\<C-R>%\<Esc>:.g/\\./s/\\./\\\\./g\<CR>0\"9y$u:menu Buffers.\<C-R>9 :buffer \<C-R>%\<C-V>\<CR>\<CR>"

为了建立一个真实的缓冲菜单,需要用到用户函数(参见 |:function|),不过那里不
使用符号 <> , 所以失去了在这里举例的意义。

 vim:tw=78:ts=8:ft=help:norl:

Generated by vim2html on Wed Mar 2 23:06:29 GMT 2005