tips

tips.txt 适用于 Vim 9.0 版本。 最近更新: 2022年7月 VIM 参考手册 by Bram Moolenaar 译者: iCrazy <icrazy@ustc.edu>、tocer 使用 Vim 的技巧 tips 这里只是一小部分我们认为会对很多用户有用的技巧。更多的技巧都在 wiki 上。URL 可 以从这里找到: http://www.vim.org 别忘记浏览用户手册,里面也有很多实用的技巧 usr_toc.txt 。 编辑 C 程序 C-editing 查找使用标识符的地方 ident-search 在 xterm 中切换屏幕 xterm-screens 在插入模式下滚屏 scroll-insert 平滑的滚屏 scroll-smooth 纠正普通的录入错误 type-mistakes 统计单词数、行数等 count-items 恢复光标位置 restore-position 文件更名 rename-files 更改多个文件中的一个名字 change-name 加速外部命令的执行 speed-up 一些有用的映射 useful-mappings 压缩帮助文件 gzip-helpfile 在一个窗口中执行外壳命令 shell-window 十六进制编辑 hex-editing 在自动命令中使用 <> 记法 autocmd-<> 高亮匹配括号 match-parens 在当前窗口打开帮助 help-curwin

编辑 C 程序 C-editing

Vim 里面有不少功能可以帮助你们编辑 C 程序。以下是一个概括,你们可以使用标签跳 转到具体的内容中去: usr_29.txt 用户手册中关于在程序的不同部分间移动的内容。 usr_30.txt 用户手册中关于编辑程序的内容。 C-indenting 输入时自动设置每行的缩进。 = 重新缩进一些行。 format-comments 对注释进行排版。 :checkpath 显示所有被 (嵌套) 包含的文件。 [i 在当前和被包含的文件中查找光标所在的的标识符。 [_CTRL-I 跳到 "[i" 的匹配。 [I 列出在当前和被包含的文件中匹配光标所在的标识符的行。 [d 在当前和被包含的文件中查找光标所在的标识符的宏定义。 CTRL-] 跳到光标所在的标签处 (例如: 函数的定义)。 CTRL-T 跳回执行 CTRL-] 命令前的地方。 :tselect 从一连串匹配的标签中选出一个。 gd 跳到光标所在的局部变量的声明处。 gD 跳到光标所在的全局变量的声明处。 gf 跳到光标所在的文件名表示的文件。 % 跳到匹配的 ()、{}、[]、/* */、#if、#else、#endif 处。 [/ 跳到上一个注释开始的位置。 ]/ 跳到下一个注释结束的位置。 [# 反向到未闭合的 #if、#ifdef 或 #else 处。 ]# 正向到未闭合的 #else 或 #endif 处。 [( 反向到未闭合的 '(' 处。 ]) 正向到未闭合的 ')' 处。 [{ 反向到未闭合的 '{' 处。 ]} 正向到未闭合的 '}' 处。 v_ab 选中 "一个块" ("a block"),从 "[(" 至 "])",含括号 v_ib 选中 "内含块" ("inner block"),从 "[(" 至 "])" v_aB 选中 "一个大块" ("a Block"),从 "[{" 至 "]}",含括号 v_iB 选中 "内含大块" ("inner Block"),从 "[{" 至 "]}"

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

你应该已经知道 tags 可以被用来跳转到定义函数和变量的地方。但是有时你希望跳转 到使用函数和变量的地方。可以用以下两种方法实现: 1. 使用 :grep 命令。这个应该可以在大多数 Unix 系统上工作,但是速度会比较慢 (因为它读取所有的文件),并且只能在一个目录中搜索。 2. 使用 ID 工具集。这个速度比较快而且可以搜索多个目录。它使用一个数据库来存放 定位信息。你需要一些额外的程序来使它得以工作,并且你必须使数据库不断保持更新。 使用 GNU id 工具集 (id-tools): 你所需要的: - 安装 GNU id 工具集 (mkid 是用来创建 ID 的,lid 是用来运行宏的) - 一个在当前目录下名为 "ID" 的标识符数据库。你可以用外壳命令 "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 的问题。你们应该知 道针对一种特定的终端,有两种数据库可以用来描述它的属性: 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) 的值。这样一来,这两个变量正好处于以上所述 的控制序列应该执行的位置。 把你的 xterm termcap 条目 (在 /etc/termcap 中) 和 xterm terminfo 条目 (用 "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) 出现的频率,使用 substitute 命令并加入 'n' 标志位以避免实际的替代。Vim 报告的已替代的数目就是模式出现的次数。例: :%s/./&/gn 字符数 :%s/\i\+/&/gn 单词数 :%s/^//n 行数 :%s/the/&/gn 任何地方出现的 "the" :%s/\<the\>/&/gn 作为单词出现的 "the" 此时你可能希望复位 'hlsearch' 或者使用 ":nohlsearch"。 如果你希望没有匹配的时候不报错,加上标志位 'e'。 另一种办法就是在可视模式下使用 v_g_CTRL-G 。 如果你想在多个文件中查找匹配,用 :vimgrep 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 跳转到最初光标的位置 更高级的方法见 winsaveview()winrestview()

文件更名 rename-files

假如我有一个目录,里面有如下的文件 (目录是随机选取的): buffer.c charset.c digraph.c ... 现在我希望把 *.c 更名为 *.bla。我可以这样做: $ vim :r !ls *.c :%s/\(.*\).c/mv & \1.bla :w !sh :q!

更改多个文件中的一个名字 change-name

使用脚本文件在若干文件中更改一个名字的示例: 创建文件 "subs.vim",包含替代命令和 :update 命令: :%s/Jones/Smith/g :%s/Allen/Peter/g :update 在所有你要改动的文件上执行 Vim,然后为每个参数执行该脚本: vim *.let argdo source subs.vim 参见 :argdo

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

在某些情况下,外部命令执行起来非常地慢。而且还会减慢 Unix 上的通配符扩展。这儿 有一些可以加快速度的建议: 如果你的 .cshrc 文件 (根据你使用的外壳,文件名可能不同) 非常地长,你应该把它分 割成两节: 需要和用户交互的、无需和用户交互的 (经常被称作二级外壳)。当你在 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 风格的编辑操作: " 至行首 :cnoremap <C-A> <Home> " 后退一个字符 :cnoremap <C-B> <Left> " 删除光标所在的字符 :cnoremap <C-D> <Del> " 至行尾 :cnoremap <C-E> <End> " 前进一个字符 :cnoremap <C-F> <Right> " 取回较新的命令行 :cnoremap <C-N> <Down> " 取回以前 (较旧的) 命令行 :cnoremap <C-P> <Up> " 后退一个单词 :cnoremap <Esc><C-B> <S-Left> " 前进一个单词 :cnoremap <Esc><C-F> <S-Right> 备注: 前提条件是 '<' 标志位不在 'cpoptions' 选项中。 <> format-bullet-list 这个映射可以排版任何带符号的 (bullet) 列表,不过它需要在每一个条目的上下都各有 一个空行。使用表达式命令,以便对映射的部分进行注释: :let m = ":map _f :set ai<CR>" " 需要置位 'autoindent' :let m ..= "{O<Esc>" " 在项目上面加入空行 :let m ..= "}{)^W" " 跳转到 bullet 之后的文本 :let m ..= "i <CR> <Esc>" " 为缩进加空格 :let m ..= "gq}" " 排版 bullet 之后的文本 :let m ..= "{dd" " 删除空行 :let 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 这两个映射可以把一连串的空行 (;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 插件会负责解压这些文件。 如果其它 Vim 文件和存放压缩帮助的 "doc" 目录所在的位置不一致,要确保 $VIMRUNTIME 设为存放那些 Vim 文件的目录。参见: $VIMRUNTIME

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

terminal 。 另一个方法是用 "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 " 此命令自动把文件名加入到菜单列表中。 " 它使用了一个可以自我毁灭的映射! " 1. 用缓冲区中的一行把文件名中的点 ('dots') 转换成 \. " 2. 把该行存放在寄存器 '"' 中 " 3. 把该名字加入缓冲区菜单列表 " 警 告: 这会有些副作用,比如: 覆盖当前的寄存器内容和删除任何 "i" 命令的映射 " 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 ),不过那里不 使用 <> 记法,所以失去了在这里举例的意义。

高亮匹配括号 match-parens

本例演示一些高级技巧的用法: - 使用 CursorMoved 自动命令事件 - 使用 searchpairpos() 查找匹配括号 - 使用 synID() 检测光标是否在字符串或注释里 - 使用 :match 高亮一些内容 - 使用 pattern 匹配文件的特定位置。 它应该被放在 Vim 脚本里,因为使用了局部于脚本的变量。除非光标起始于字符串或者 注释的内部,它会跳过字符串或注释里的匹配。这需要语法高亮的支持。 matchparen 插件使用的是一个稍稍改进的版本。 let s:paren_hl_on = 0 function s:Highlight_Matching_Paren() if s:paren_hl_on match none let s:paren_hl_on = 0 endif let c_lnum = line('.') let c_col = col('.') let c = getline(c_lnum)[c_col - 1] let plist = split(&matchpairs, ':\|,') let i = index(plist, c) if i < 0 return endif if i % 2 == 0 let s_flags = 'nW' let c2 = plist[i + 1] else let s_flags = 'nbW' let c2 = c let c = plist[i - 1] endif if c == '[' let c = '\[' let c2 = '\]' endif let s_skip ='synIDattr(synID(line("."), col("."), 0), "name") ' .. \ '=~? "string\\|comment"' execute 'if' s_skip '| let s_skip = 0 | endif' let [m_lnum, m_col] = searchpairpos(c, '', c2, s_flags, s_skip) if m_lnum > 0 && m_lnum >= line('w0') && m_lnum <= line('w$') exe 'match Search /\(\%' .. c_lnum .. 'l\%' .. c_col .. \ 'c\)\|\(\%' .. m_lnum .. 'l\%' .. m_col .. 'c\)/' let s:paren_hl_on = 1 endif endfunction autocmd CursorMoved,CursorMovedI * call s:Highlight_Matching_Paren() autocmd InsertEnter * match none

在当前窗口打开帮助 help-curwin

缺省在分割窗口打开帮助。如果喜欢在当前窗口打开帮助,可用下面的定制命令 :HelpCurwin : command -bar -nargs=? -complete=help HelpCurwin execute s:HelpCurwin(<q-args>) let s:did_open_help = v:false function s:HelpCurwin(subject) abort let mods = 'silent noautocmd keepalt' if !s:did_open_help execute mods .. ' help' execute mods .. ' helpclose' let s:did_open_help = v:true endif if !getcompletion(a:subject, 'help')->empty() execute mods .. ' edit ' .. &helpfile set buftype=help endif return 'help ' .. a:subject endfunction vim:tw=78:ts=8:noet:ft=help:norl: