vim + ctags 用法(转)
2007-08-01 15:35:03
/ 个人分类:嵌入式
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":
http://ctags.sf.net
现在你可以使用下面的命令跳转到一个函数定义的地方:
: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 < cnt; ++i) | | write_line(s[i]); | |} | | +-----------|-------------------------+ | CTRL-] | | +----------------------------+ +--> |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 ]
如果指定了计数参数,新窗口将包含指定的那么多行。
多 个 标 记 文 件
如果在
*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; | ]} | | | } <-+ | | ][ +-- foobar(a) | | } <-+ | } <-+
当编写 C++ 或 Java 代码时,外部代码块是类,而下一级的 {} 是方法。在类内部用 "[m" 可以找到前一个方法的开始。"]m" 会找到下一个方法的开始。
另外,"[]" 向后移动到一个函数的结尾,"]]" 向前移动到一个函数的结尾。函数的结尾 指的是处在第一列的 "}"。
int func1(void) { return 1; +----------> } | [] | int func2(void) | +-> { | [[ | if (flag) start +-- +-- return flag; | ][ | return 2; | +-> } ]] | | int func3(void) +----------> { return 3; }
不要忘了你还可以用 "%" 在匹配的 (), {} 和 [] 间移动。这在它们相距很多行时仍然 适用。
在 括 号 内 移 动
"[(" 和 "])" 命令"[}" 和 "]}" 类似,只不过它们适用于 () 对而不是 {} 对。
[( <-------------------------------- <------- if (a == b && (c == d || (e > f)) && x > y) --------------> --------------------------------> ])
在 注 释 间 移 动
移动到一个注释的开始用 "[/";向前移动到注释的结尾用 "]/"。这只对 /* - */ 注释 有效。
+-> +-> /* | [/ | * A comment about --+ [/ | +-- * wonderful life. | ]/ | */ <-+ | +-- foo = bar * 3; --+ | ]/ /* a short comment */ <-+
*29.4* 查找全局标识符
你在编辑一个 C 程序,想要知道一个变量是被声明为 "int" 还是 "unsigned"。一个快 速的方法是使用 "[I" 命令来查找。 假设光标在单词 "column" 处。输入:
[I
Vim 会列出它所找出的匹配行,不仅在当前文件内查找,还会在所有的包含文件中查找。 结果如下所示:
structs.h 1: 29 unsigned column; /* column number */
相对使用标签文件或预览窗口的好处是包含文件也被搜索。大多数情况下都能找到正确 的声明。即使标签文件已经过期或者你没有为包含文件建立标签也不会影响结果。 但是一些准备工作是必要的,否则 "[I" 就没法工作。首先,‘include‘ 选项必须指 定文件是如何被包含的。省缺值适用于 C 和 C++。对其它的语言,你需要自己设定。
定 位 包 含 文 件
Vim 会找到 ‘path‘ 选项指定路径中的包含文件。如果缺少某个目录,一些包含文件 将不会被找到。你可以用这个命令来查看:
:checkpath
它会列出不能找到的包含文件,以及被找到的包含文件。一个输出样例:
--- Included files not found in path --- <io.h> vim.h --> <functions.h> <clib/exec_protos.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)" 而不再重新显示一遍。
跳 转 到 匹 配
"[I" 产生一个每项只有一行文本的列表。如果你想要进一步的查看第一项,你可以这个 命令来跳转:
[<Tab>
你也可以使用 "[ CTRL-I", 因为 CTRL-I 和按 <Tab> 效果一样。
"[I" 产生的列表在每行的开头都有一个序号。如果你要跳转到第一项外的其它项,首先 输入序号:
3[<Tab>
会跳转到列表中的第三项。记住你可以用 CTRL-O 跳回到原来的地方。
相 关 命 令
[i 只列出第一项匹配 ]I 只列出光标下面的项目 ]i 只列出光标下面的第一项匹配
查 找 宏 定 义 标 识 符
"[I" 命令查找任何标识符。只查找 "#define" 定义的宏,用:
[D
同样,这会在所有的包含文件中查找。 ‘define‘ 选项指定 "[D" 所查找的预定义样式。 你需要改变它值来适用于 C 或 C++ 以外的语言。 "[D" 相关命令:
[d 只列出第一项匹配 ]D 只列出光标下面的项目 ]d 只列出光标下面的第一项匹配
*29.5* 查找局部标识符
"[I" 命令查找所有的包含文件。要在当前文件中查找并跳转到光标处单词被首次使用的 地方,用:
gD
提示:Goto Definition。这个命令对查找局部(C 语言中的 "static") 声明的变量或函 数很有用。例如(光标在 "counter" 处):
+-> static int counter = 0; | | int get_counter(void) gD | { | ++counter; +-- return counter; }
要进一步的缩小查找范围,只在当前函数内查找,用这个命令:
gd
这会回到当前函数的开始处寻找光标处单词首次出现的地方。实际上,它是向后找到一个 在第一列 ‘{‘ 上面的空行,然后再从那里向前查找标识符。例如(光标位于 idx 上):
int find_entry(char *name) { +-> int idx; | gd | for (idx = 0; idx < table_len; ++idx) | if (strcmp(table[idx].name, name) == 0) +-- return idx; }
|