vi中关于tag的使用
1、使用tags
tag是什么? 一个位置。记录了关于一个标识符在哪里被定义的信息。 比如verilog代码中的一个module的定义。这种tag聚集在一起被放入一个tags文件。这个文件可以让Vim能够从任何位置起跳达到tag所指示的位置--标识符被定义的位置。
2、生成tags文件
在shell下运行ctags生成当前目录下的文件的tags文件,使用选项-R包含当前目录的所有子目录。例:ctags -R。若只生成当前目录.v文件的tags,则用:ctags *.v。若没有安装ctags程序,可在 下载安装。
3、在vi(vim)中查找关键字
在当前目录启动vi,不是当前目录需要为vi指定路径。例:
:set tags=/home/careful/grx/rtl/tags。若定义了一个module为grx,查找时输入:
:tag grx。需要查找其下的子模块时,不用打开文件,直接在vi中输入:
:tag grx_wr_ctrl即可跳往子模块的定义行。
在当前光标停留的关键字处按ctrl+]即可跳到当前光标所在的关键字定义处。按ctrl+t回溯到前一关键字。
4、预览窗口
当你在写一个module调用时, 往往需要获知这个module的断口列表--查看module的定义。tag所提供的机制正为此用。最好是函数的定义可以显示在另一窗口中以免影响当前的编辑. 这可以由预览窗口提供。要打开一个预览窗口显示module"write_char"的定义使用命令:
:ptag write_char
Vim会打开一个窗口, 并将找到的"write char"函数的定义显示其中。然后光标仍保持在你的当前编辑位置。切换窗口使用CTRL+w+k即可继续工作。
要关闭该预览窗口, 使用命令:
:pclose
[/home/brimmer/src]$ ctags -R
"-R"表示递归创建,也就包括源代码根目录下的所有子目录下的源程序。"tags"文件中包括这些对象的列表:
l 用#define定义的宏
l 枚举型变量的值
l 函数的定义、原型和声明
l 名字空间(namespace)
l 类型定义(typedefs)
l 变量(包括定义和声明)
l 类(class)、结构(struct)、枚举类型(enum)和联合(union)
l 类、结构和联合中成员变量或函数
VIM用这个"tags"文件来定位上面这些做了标记的对象,下面介绍一下定位这些对象的方法:
1) 用命令行。在运行vim的时候加上"-t"参数,例如:
[/home/brimmer/src]$ vim -t foo_bar
这个命令将打开定义"foo_bar"(变量或函数或其它)的文件,并把光标定位到这一行。
2) 在vim编辑器内用":ta"命令,例如:
:ta foo_bar
3) 最方便的方法是把光标移到变量名或函数名上,然后按下"Ctrl-]"。用"Ctrl-o"退回原来的地方。
注意:运行vim的时候,必须在"tags"文件所在的目录下运行。否则,运行vim的时候还要用":set tags="命令设定"tags"文件的路径,这样vim才能找到"tags"文件。
在函数中移动光标
[{ 转到上一个位于第一列的"{"
}] 转到下一个位于第一列的"{"
{ 转到上一个空行
} 转到下一个空行
gd 转到当前光标所指的局部变量的定义
* 转到当前光标所指的单词下一次出现的地方
# 转到当前光标所指的单词上一次出现的地方
Vim 的创造者是一名计算机程序员,因此这就不奇怪 Vim 中有许多帮助编写程序的功能:
跳转到标识符被定义和使用的地方;在另一个窗口中预览有关的声明等等。在下一章中还
会介绍更多的功能。
|29.1| 使用标签
|29.2| 预览窗口
|29.3| 在代码间移动
|29.4| 查找全局标识符
|29.5| 查找局部标识符
下一章:|usr_30.txt| 编辑程序
前一章:|usr_28.txt| 折叠
目录:|usr_toc.txt|
*29.1* 使用标签
什么是标签?标签就是一个标识符被定义的地方。一个例子就是 C 或者 C++ 程序中的函
数定义。标签列表可以保存在一个标签文件中。Vim 可以通过它来从任何地方跳转到该标签,
也就是一个标识符被定义的地方。
在当前目录下为所有的 C 文件生成标签文件,使用下面的这个命令:
ctags *.c
"ctags" 是一个独立的程序。大多数 Unix 系统上都已经安装了它。如果你还没有安装,
可以在这里找到 "Exuberant ctags":
现在你可以使用下面的命令跳转到一个函数定义的地方:
:tag startlist
这个命令会找到函数 "startlist",即使该函数是在另一个文件中。
CTRL-] 命令会跳转到当前光标下单词的标签。这样浏览毫无头绪的 C 代码会变得更容
些易。举个例子,假设你在函数 "write_block" 中。你可以看到它调用了函数
"write_line"。但 "write_line" 做了什么呢?将光标置于调用 "write_line" 的地方然
后按 CTRL-],你就跳转到了这个函数的定义的地方了。
"write_line" 函数调用了 "write_char"。你需要知道它做了什么。将光标定位到调
用 "write_char" 的地方然后按 CTRL-],你就到了定义"write_char" 的地方。
+-------------------------------------+
|void write_block(char **s; int cnt) |
|{ |
| int i; |
| for (i = 0; i |void write_line(char *s) |
|{ |
| while (*s != 0) |
| write_char(*s++); |
|} | |
+--------|-------------------+
|
CTRL-] |
| +------------------------------------+
+--> |void write_char(char c) |
|{ |
| putchar((int)(unsigned char)c); |
|} |
+------------------------------------+
":tags" 命令显示你经过的标签列表:
:tags
# TO tag FROM line in file/text
1 1 write_line 8 write_block.c
2 1 write_char 7 write_line.c
>
现在介绍向回跳转。 CTRL-T 命令跳转到上一个标签。在上例中,你会回到 "write_line"
函数调用 "write_char" 的地方。
这个命令接受一个计数参数,用来表示跳转回去的标签个数。你已经向前跳转,现在
又跳转了回去。现在我们再一次向前跳转。下面的命令跳转到标签列表中最上面的标签:
:tag
你可以在前面加上要向前跳转的标签个数。比如:":3tag"。 CTRL-T 同样可以加上一个
计数参数。
通过这些命令,你可以用 CTRL-] 延着调用树向前跳转, 用 CTRL-T 向回跳转,用
":tags" 命令显示当前位置。
分 割 窗 口
":tag" 命令会将当前窗口的文件替换为包含新函数的文件。怎样才能同时查看两个文件
呢?你可以使用 ":split" 命令将窗口分开然后再用 ":tag" 命令。Vim 有个缩写命令可
以做到这些:
:stag tagname
使用下面的命令可以分割当前窗口并跳转到光标下的标签:
CTRL-W ]
如果指定了计数参数,新窗口将包含指定的那么多行。
多 个 标 记 文 件
如果在多个目录中都有文件,你可以在每一个目录下创建一个标签文件。Vim 只能跳转到
那个目录下的标签。
通过设定 'tags' 选项,你可以使用多个相关的标签文件。 比如:
:set tags=./tags,./../tags,./*/tags
这会使 Vim 找到当前文件所在目录及其父目录和所有子目录下的标签文件。
这已经是不少的标签文件了,但也许仍不够。比如,当编辑 "~/proj/src" 目录下的
一个文件时,你无法找到 "~/proj/sub/tags" 目录下的标签文件。对这种情况,Vim
提供了一个查找整个目录树下标签文件的方法,比如:
:set tags=~/proj/**/tags
单 个 标 记 文 件
当 Vim 在多个地方查找标签文件时,你会听到硬盘在格格作响。这样会有点慢。在这种
情况下,你最好将这些时间花在生成一个大的标签文件上。你可以要等一会儿。
这得借助上面提到的 "Exuberant ctags" 程序。它有一个选项可以搜索整个目录树:
cd ~/proj
ctags -R .
这样做的好处是 "Exuberant ctags" 可以识别多种文件类型,它不仅适用于 C 和 C++
程序,还适用于 Eiffel 甚至 Vim 脚本。请参考 ctags 文档进行调整所用参数。
现在你只需要告诉 Vim 你的标签文件在何处:
:set tags=~/proj/tags
多 个 匹 配
当一个函数(或类中的方法)被定义多次, ":tags" 命令会跳转到第一处。如果在当前
文件中存在匹配,那它将会被首先使用。
你现在可以跳转到同一个标签的其它匹配处:
:tnext
重复执行这个命令可以找到更多的匹配。如果存在很多匹配,你可以选择要跳转到哪一
个:
:tselect tagname
Vim 会为你展示一个选择列表:
# pri kind tag file
1 F f mch_init os_amiga.c
mch_init()
2 F f mch_init os_mac.c
mch_init()
3 F f mch_init os_msdos.c
mch_init(void)
4 F f mch_init os_riscos.c
mch_init()
Enter nr of choice ( to abort):
你现在可以输入要跳转到的匹配代号(在第一列)。其它列的信息可以让你知道匹配在何
处被定义。
可以用这些命令在各匹配的标签间移动:
:tfirst 到第一个匹配
:[count]tprevious 向前 [count] 个匹配
:[count]tnext 向后 [count] 个匹配
:tlast 到最后一个匹配
如果没有指定,[count] 省缺为一。
猜 测 标 签 名
命令行补全是避免输入长标签名的好办法。只需输入开始的一部分然后按 :
:tag write_
你会得到第一个匹配。如果这不是你想要的,重复输入 直到你找到正确的匹配。
有时你只知道一个函数名的一部分,或是你有很多以相同字符串开头而结尾不同的标
记。这时你可以告诉 Vim 使用一个模式来查找标签。
假设你要跳转到一个包含 "block" 的标签。首先输入:
:tag /block
现在再利用命令行补全功能:输入 。Vim 会找到所有包含 "block" 的标签并使用
第一个匹配。
标签名前面的 "/" 告诉 Vim 这不是一个确定的标签名而是一个模式。你可以利用有
关查找模式的所有特性。举个列子,假设你要选择所有以 "write_" 开头的标签:
:tselect /^write_
"^" 指定标签以 "write_" 开头,否则在中间含有 "write_" 的标签名也会被找到。类似
地,"___FCKpd___0quot; 指定标签名结尾处的匹配。
标 签 浏 览 器
CTRL-] 可以让你跳转到光标所在标识符的定义处,因此你可以利用标识符的列表来形成
一个目录。这里给出一个例子。
首先生成一个标识符列表(需要 Exuberant ctags):
ctags --c-types=f -f functions *.c
现在打开 Vim 并在一个垂直分割窗口中编辑这个文件:
vim
:vsplit functions
窗口中包含一个所有函数的列表。其它的东西可以被忽略。用 ":setlocal ts=99" 命令
使其显示得更清晰些。
在这个窗口中,定义一个 mapping:
:nnoremap 0yew:tag "
移动光标至要跳转到函数的所在行,输入 。Vim 会在另一个窗口中跳转到所选择
的函数定义处。
相 关 杂 项
你可以设定 'ignorecase' 选项来忽略标签名里的大小写。
'tagbsearch' 选项标明标签文件是否经过排序。省缺是假定为标签文件已排序,这样会
使查找更快,但如果文件没有被排序是无法工作的。
'taglength' 选项可用来告诉 Vim 一个标签的有效字符个数。
当你使用 SNiFF+ 程序时,你可以利用 Vim 的有关接口 |sniff| 。SNiFF+ 是一个商业
软件。
Cscope 是一个自由软件。它不仅可以找到一个标识符被声明的地方,还可以找到标识符
被使用的地方。 请参考 |cscope|。
*29.2* 预览窗口
当编辑含有函数调用的代码时,你需要使用正确的调用参数。要获知所要传递的值,你可以
查看这个函数是如何定义的。标签机制对此十分适用。如果定义可在另一个窗口内显示那
就更好了。对此我们可以利用预览窗口。
打开一个预览窗口来显示函数 "write_char":
:ptag write_char
Vim 会打开一个窗口,跳转到 "write_char" 标签。然后它会回到原来的位置。这样你可
以继续输入而不必使用 CTRL-W 命令。
如果函数名出现在文本中,你可以用下面的命令在预览窗口中得到其定义:
CTRL-W }
有一个脚本可以自动显示光标处的标签定义。请参考 |CursorHold-example| 。
用下面的命令关闭预览窗口:
:pclose
要在预览窗口中编辑一个指定的文件,用 ":pedit" 。这在编辑头文件时很有用,比如:
:pedit defs.h
最后, "psearch" 可用来查找当前文件和任何包含文件中的单词并在预览窗口中显示匹
配。这在使用没有标签文件的库函数时十分有用。例如:
:psearch popen
这会在预览窗口中显示含有 popen() 原型的 "stdio.h" 文件:
FILE *popen __P((const char *, const char *));
你可以用 'previewheight' 选项指定预览窗口打开时的高度。
*29.3* 在代码间移动
因为程序代码是结构化的,Vim 可以识别其中的有关项目。一些特定的命令可用来完成相
关的移动。
C 程序中经常包含类似下面的代码:
#ifdef USE_POPEN
fd = popen("ls", "r")
#else
fd = fopen("tmp", "w")
#endif
有时会更长,也许还有套嵌。将光标置于 "#ifdef" 处按 %。Vim 会跳转到"#else"。继
续按 % 会跳转到 "#endif"。再次按下 % 又回到原来的 "#ifdef"。
当代码套嵌时,Vim 会找到相匹配的项目。这是检查你是否忘记了一个 "#endif" 的
好办法。
当你在一个 "#ifdef" - "#endif" 块内的某个位置,你可以用下面的命令回到开始
处:
[#
如果你的位置不是在 "#if" 或 "#ifdef" 之后, Vim 会鸣音。用下面命令可以跳转到下
一个 "#else" 或 "#endif":
]#
这两个命令会跳过它所经过的 "#if" - "#endif" 块。
例如:
#if defined(HAS_INC_H)
a = a + inc();
# ifdef USE_THEME
a += 3;
# endif
set_width(a);
如果光标在最后一行,"[#" 会移动到第一行。中间的 "#ifdef" - "#endif" 块被跳过。
在 代 码 块 内 移 动
C 代码块包含在 {} 中,有时一个代码会很长。要跳转到外部代码块的开始处,用 "[["
命令。用 "][" 找到结尾处。(前提是 "{" 和 "}" 都在第一列。)
"[{" 命令跳转到当前代码块的开始处。它会跳过同一级别的 {} 对。"]}" 跳转到结尾。
一点概述:
function(int a)
+-> {
| if (a)
| +-> {
[[ | | for (;;) --+
| | +-> { |
| [{ | | foo(32); | --+
| | [{ | if (bar(a)) --+ | ]} |
+-- | +-- break; | ]} | |
| } }
|
[] | int func2(void)
| +-> {
| [[ | if (flag)
start +-- +-- return flag;
| ][ | return 2;
| +-> }
]] |
| int func3(void)
+----------> {
return 3;
}
不要忘了你还可以用 "%" 在匹配的 (), {} 和 [] 间移动。这在它们相距很多行时仍然
适用。
在 括 号 内 移 动
"[(" 和 "])" 命令"[}" 和 "]}" 类似,只不过它们适用于 () 对而不是 {} 对。
[(
f)) && x > y)
-------------->
-------------------------------->
])
在 注 释 间 移 动
移动到一个注释的开始用 "[/";向前移动到注释的结尾用 "]/"。这只对 /* - */ 注释
有效。
+-> +-> /*
| [/ | * A comment about --+
[/ | +-- * wonderful life. | ]/
| */
vim.h -->
文件 "io.h" 被当前文件包含但无法找到。"vim.h" 可以找到,这样 ":checkpath" 跟进
这个文件并检查其中的包含文件。结果显示无法找到 "vim.h" 包含的 "functions.h" 和
"clib/exec_protos.h" 文件。
Note:
Vim 不是一个编译器。它无法识别 "#ifdef" 语句。这就是说所有的
"#include" 语句都会被使用,即使它在 "#if NEVER" 之后。
给 'path' 选项增加一个目录可以修正无法找到文件的错误。一个好得参考是 Makefile。
注意那些包括 "-I" 的条目,比如 "-I/usr/local/X11"。要增加这个目录,用:
:set path+=/usr/local/X11
如果有很多的子目录,你可以用 "*" 通配符。例如:
:set path+=/usr/*/include
这会找到 "/usr/local/include" 以及 "/usr/X11/include" 目录下的文件。
如果你的工程项目的包含文件都在一个套嵌的目录树下,"**" 就非常有用。它会搜索所
有的子目录。例如:
:set path+=/projects/invent/**/include
这会找到这些目录下的文件:
/projects/invent/include
/projects/invent/main/include
/projects/invent/main/os/include
etc.
还有其它的可能性。更多信息,请查看 'path' 选项。
如果你想查看找到的包含文件,用这个命令:
:checkpath!
你会得到一个(很长)的包含文件列表。为使它更短些,Vim 会对已经找到的文件显示
"(Already listed)" 而不再重新显示一遍。
阅读(783) | 评论(0) | 转发(0) |