Vim 学习手记(六) --- 程序员专用福利(上): 在程序中快速定位

 2010年01月22日 星期五 20:43 | 1963次浏览 | 0条评论

   Vim的作者就是一个程序员 .所以不奇怪Vim拥有大量辅助写程序的功能: 辗转于标识符的定义语句与引用语句. 在另一个窗口中预览程序中相应的声明部分, 等等.

//使用tags
    tag是什么? 一个位置. 记录了关于一个标识符在哪里被定义的信息. 比如C或C++程序中的一个函数定义.这种tag聚集在一起被放入一个tags文件.这个文件可以让Vim能够从任何位置起跳达到tag所指示的位置--标识符被定义的位置. 下面的命令可以为当前目录下的所有C程序文件生成对应的tags文件:
 ctags *.c

   "ctags"是一个独立的程序. 绝大多数Unix系统上都会预装这个程序,如果你的系统上还没有, 你可以在下面找到一个功能丰富的ctags:
http://ctags.sf.net
   现在你在Vim中要跳到一个函数的定义就可以用下面的命令:
:tag startlist
   这个命令会带你到函数"startlist"的定义处, 哪怕它是在另一个文件中.

  CTRL-]    取当前光标下的word作为tag的名字并直接跳转.
  ":tags"   列出现在你就已经到过哪些tag了:
  CTRL-T    跳到你前一次的tag处.
  CTRL-T    可以带一个命令记数, 以此作为往回跳的次数

下面的命令可以直接跳转到当前tag序列的最后:
:tag
你也可以给它一个前辍, 让它向前跳指定的步长.比如":3tag". CTRL-T也可以带一个前辍.


//分隔窗口
   ":tag"命令会在当前窗口中载入包含了目标函数定义的文件1 . 但假设你不仅要查看新的函数定义, 还要同时保留当前的上下文呢? 你可以在":tag"后使用一个分隔窗口命令":split". Vim还有一个一举两得的命令:
:stag tagname

要分隔当前窗口并跳转到光标下的tag:
CTRL-W ]
如果同时还指定了一个命令记数, 它会被当作新开窗口的行高.

如果你的源文件位于多个目录下, 你可以为每个目录都建一个tags文件. Vim会在使用某个目录下的tags文件进行跳转时只在那个目录下跳转
要使用更多tags文件, 可以通过改变'tags'选项的设置来引入更多的tags文件. 如:
:set tags=./tags,./../tags,./*/tags

这样可能会引入很多的tags文件, 但还有可能不敷其用. 比如说你 正在编辑"~/proj/src"下的一个文件, 但又想使用"~/proj/sub/tags"作
为tags文件. 对这种Vim情况提供了一种深度搜索目录的形式. 如下:
:set tags=~/proj/**/tags

Vim在搜索众多的tags文件时, 你可能会听到你的硬盘在咔嗒咔嗒拼命地叫. 显然这会降低速度. 如果这样还不如花点时间生成一个大一点的tags文件. 这可能要花一个通宵

这需要一个功能丰富的ctags程序, 比如上面提到的那个. 它有一个 参数可以搜索整个目录树:
cd ~/proj
ctags -R .


用一个功能更强的ctags的好处是它能处理多种类型的文件. 不光是C和C++源程序, 也能对付Eiffel或者是Vim脚本. 你可以参考ctags程序的文件调整自己的需要. 现在你只要告诉Vim你那一个tags文件在哪就行了:
:set tags=~/proj/tags

当一个函数被多次重载(或者几个类里都定义了一些同名的函数), ":tag"命令会跳转到第一个符合条件的. 如果当前文件中就有一个匹配的, 那又会优先使用它.  当然还得有办法跳转到其它符合条件的tag去:
:tnext

重复使用这个命令可以发现其余的同名tag.    如果实在太多, 还可以用下面的命令从中直接选取一个:
:tselect tagname

在多个匹配的tag之间移动, 可以使用下面这些命令:
:tfirst           go to first match
:[count]tprevious go to [count] previous match
:[count]tnext     go to [count] next match
:tlast            go to last match
如果没有指定[count], 默认是1.

有时候你只记得一个tag名的片段. 或者有几个tag开头相同. 这里你可以用一个模式匹配来告诉Vim你要找的tag. 首先键入命令 :
假设你想跳转到一个包含"block"的tag.
:tag /block

现在使用命令补齐: 按<Tab>.    Vim会找到所有包含"block"的tag并先提供给你第一个符合的.  "/"告诉Vim下面的名字不是一五一十的tag名, 而是一个搜索模式.

CTRL-]可以直接跳转到以当前光标下的word为tag名的地方去, 所以可以在一个tag列表中使用它. 下面是一个例子.
首先建立一个标识符的列表(这需要一个好的ctags):
ctags --c-types=f -f functions *.c

现在直接启动Vim, 以一个垂直分隔窗口的编辑命令打开生成的文件
:vsplit functions
这个窗口中包含所有函数名的列表. 可能会有很多内容, 但是你可以暂时忽略它. 用一个":setlocal ts=99"命令清理一下显示. 在该窗口中, 定义这样的一个映射:
:nnoremap <buffer> <CR> 0ye<C-W>w:tag <C-R>"<CR>



//预览窗口
   当你在写一个函数调用时, 往往需要获知这个函数的参数列表--查看函数的定义. tag所提供的机制正为此用. 最好是函数的定义可以显示在
另一窗口中以免影响当前的编辑. 这可以由预览窗口提供.

要打开一个预览窗口显示函数"write char"的定义使用命令:
:ptag write_char

如果一个函数名出现在当前文本中, 你还可以以下面的命令在预览窗口中打开它:
CTRL-W }

要关闭该预览窗口, 使用命令:
:pclose

要在预览窗口中编辑一个文件, 使用":pedit".
":psearch"可以搜索当前文件以及它所include的文件并显示匹配的行. 尤其是在使用以库的形式提供的函数时, 这时不会有对应
的tags文件. 比如:
:psearch popen
这会在预览窗口中显示"stdio.h"文件


//在程序中移动

C程序经常有如下的结构:

#ifdef USE_POPEN
    fd = popen("ls", "r")
#else
    fd = fopen("tmp", "w")
#endif

   但实际情况往往比这段代码长, 而且很可能含有此类结构的嵌套. 把光标置于"#ifdef"上按下%. Vim就会跳转到"#else".再按下%又会跳转到"#endif". 再按下%又会回到"#ifdef"上.
   当上述结构被嵌套使用时, Vim会准确找到匹配的对应元素. 这是检查是否遗漏"#endif"的好办法.

当你在"#if"-"#endif"结构的中间某个地方时, 可以使用这个命令跳转到此结构的开始元素 :
[#

如果你不是在"#if"或"#ifdef"的后面使用这个命令, Vim会发出蜂鸣声以示警告. 要向前跳转到下一个"#else"或"#endif"使用命令:
]#
这两个命令会跳过"#if"-"#endif"块的内容.


   在C代码中代码块以{}括起来. 代码块可长可短. 要移动到一个代码块的开头, 使用"[["命令. "]["可以到它的末尾, 这两个命令都假
设"{"和"}"字符位于第一列.

"[{"   跳转到当前代码块的开头, 它会跳过与它平级的代码块.
"]}"   跳转到当前代码块的末尾.

写C++或Java的源代码时1 , 外层的{}用于类的定义. 下一级的{}才是函数定义.
光标在类中时使用"[m"可以找到前一个函数的开始. "]m"到下一个函数的末尾.
另外, "[]"是向后查找一个函数的结尾, "]]"向前查找一个函数的开始, 函数的结尾以第一行出现"}"为标志.

括号内的移动
  "[("和"])"命令类似于"[和"]"", 只不过它工作于()的内部而不是{}.

向后移动到注释的开头用使用"[/".
 只对/* - */形式的注释有效.


//查找全局标识符
   你在写C程序时可能经常会想知道一个变量是被声明为"int"还是"unsigned".
解决这个问题的快速办法是使用"[I"命令.
假设你的光标置身于"column"上.键入:
[I

Vim会列出所有包含该标识符的行. 不光在当前文件中, 也查找当前文件所include的文件1 , 以及被include的文件所include的文件, 如此类
推.

定位include文件
  Vim会在由选项'path'指定的路径里查找include文件. 如果漏掉了某个路径, 可能就找不到一些include文件了. 下面的命令可以用于检查路
径是否正确:
:checkpath
它会列出所有能找到和不能找到的include文件.

要处理这些找不到的文件,可以向'path'选项中再添加一个搜索路径. 到Makefile里去找要添加的路径是一个好办法, 找到那些包含 了"-I"的行,比如"-I/usr/local/X11". 要把这个目录添加进去使用命令:
:set path+=/usr/local/X11

如果有多个子目录, 还可以使用通配符"*".如:
:set path+=/usr/*/include

如果你想知道实际找到了哪些include文件, 使用命令:
:checkpath!


跳转到匹配的目标
   "[I"生成的列表只含有一行上下文信息. 要仔细查看它找到的第一个匹配项, 可以使用
[<Tab>
   跳转到该项, 也可以用"[ CTRL-I", CTRL-I等同于<Tab>.

   "[I"命令列出的列表中每一行都有一个标号. 你要跳转到其它项时只
要先键入对应的标号:
3[<Tab>

   这会跳转到列表中的第3项. 记住CTRL-O可以把你再带回来.
相关命令
[i   只列出第一个匹配的
]I   只列出当前光标之后的匹配项
]i   只列出当前光标之后的第一个匹配项



//查找局部标识符
   "[I"命令会搜索include文件. 要使搜索限制在当前文件里并执行同样的功能, 以命令:
gD

要进一步限制查找的范围, 让它只在当前函数里查找, 使用命令:
gd

它会先往回找到当前函数的开始然后向下查找当前光标下的word的第一次出现. 实际上, 它只是往回查找一个第一列是"{"字符的行. 然后再从那个位置向前查找目标标识符.

评论

我的评论:

发表评论

请 登录 后发表评论。还没有在Zeuux哲思注册吗?现在 注册 !

暂时没有评论

Zeuux © 2024

京ICP备05028076号