ctags -R
"-R"表示遞歸創(chuàng)建,也就包括源代碼根目錄下的所有子目錄下的源程序。"tags"文件中包括這些對(duì)象的列表:
l 用#define定義的宏
l 枚舉型變量的值
l 函數(shù)的定義、原型和聲明
l 名字空間(namespace)
l 類型定義(typedefs)
l 變量(包括定義和聲明)
l 類(class)、結(jié)構(gòu)(struct)、枚舉類型(enum)和聯(lián)合(union)
l 類、結(jié)構(gòu)和聯(lián)合中成員變量或函數(shù)
VIM用這個(gè)"tags"文件來定位上面這些做了標(biāo)記的對(duì)象,下面介紹一下定位這些對(duì)象的方法:
1) 用命令行。在運(yùn)行vim的時(shí)候加上"-t"參數(shù),例如:
[/home/brimmer/src]$ vim -t foo_bar
這個(gè)命令將打開定義"foo_bar"(變量或函數(shù)或其它)的文件,并把光標(biāo)定位到這一行。
2) 在vim編輯器內(nèi)用":ta"命令,例如:
:ta foo_bar
3) 最方便的方法是把光標(biāo)移到變量名或函數(shù)名上,然后按下"Ctrl-]"。用"Ctrl-o"退回原來的地方。
注意:運(yùn)行vim的時(shí)候,必須在"tags"文件所在的目錄下運(yùn)行。否則,運(yùn)行vim的時(shí)候還要用":set tags="命令設(shè)定"tags"文件的路徑,這樣vim才能找到"tags"文件。
在函數(shù)中移動(dòng)光標(biāo)
[{ 轉(zhuǎn)到上一個(gè)位于第一列的"{"
}] 轉(zhuǎn)到下一個(gè)位于第一列的"{"
{ 轉(zhuǎn)到上一個(gè)空行
} 轉(zhuǎn)到下一個(gè)空行
gd 轉(zhuǎn)到當(dāng)前光標(biāo)所指的局部變量的定義
* 轉(zhuǎn)到當(dāng)前光標(biāo)所指的單詞下一次出現(xiàn)的地方
# 轉(zhuǎn)到當(dāng)前光標(biāo)所指的單詞上一次出現(xiàn)的地方
Vim 的創(chuàng)造者是一名計(jì)算機(jī)程序員,因此這就不奇怪 Vim 中有許多幫助編寫程序的功能:
跳轉(zhuǎn)到標(biāo)識(shí)符被定義和使用的地方;在另一個(gè)窗口中預(yù)覽有關(guān)的聲明等等。在下一章中還
會(huì)介紹更多的功能。
|29.1| 使用標(biāo)簽
|29.2| 預(yù)覽窗口
|29.3| 在代碼間移動(dòng)
|29.4| 查找全局標(biāo)識(shí)符
|29.5| 查找局部標(biāo)識(shí)符
下一章:|usr_30.txt| 編輯程序
前一章:|usr_28.txt| 折疊
目錄:|usr_toc.txt|
*29.1* 使用標(biāo)簽
什么是標(biāo)簽?標(biāo)簽就是一個(gè)標(biāo)識(shí)符被定義的地方。一個(gè)例子就是 C 或者 C++ 程序中的函
數(shù)定義。標(biāo)簽列表可以保存在一個(gè)標(biāo)簽文件中。Vim 可以通過它來從任何地方跳轉(zhuǎn)到該標(biāo)簽,
也就是一個(gè)標(biāo)識(shí)符被定義的地方。
在當(dāng)前目錄下為所有的 C 文件生成標(biāo)簽文件,使用下面的這個(gè)命令:
ctags *.c
"ctags" 是一個(gè)獨(dú)立的程序。大多數(shù) Unix 系統(tǒng)上都已經(jīng)安裝了它。如果你還沒有安裝,
可以在這里找到 "Exuberant ctags":
http://ctags.sf.net
現(xiàn)在你可以使用下面的命令跳轉(zhuǎn)到一個(gè)函數(shù)定義的地方:
:tag startlist
這個(gè)命令會(huì)找到函數(shù) "startlist",即使該函數(shù)是在另一個(gè)文件中。
CTRL-] 命令會(huì)跳轉(zhuǎn)到當(dāng)前光標(biāo)下單詞的標(biāo)簽。這樣瀏覽毫無頭緒的 C 代碼會(huì)變得更容
些易。舉個(gè)例子,假設(shè)你在函數(shù) "write_block" 中。你可以看到它調(diào)用了函數(shù)
"write_line"。但 "write_line" 做了什么呢?將光標(biāo)置于調(diào)用 "write_line" 的地方然
后按 CTRL-],你就跳轉(zhuǎn)到了這個(gè)函數(shù)的定義的地方了。
"write_line" 函數(shù)調(diào)用了 "write_char"。你需要知道它做了什么。將光標(biāo)定位到調(diào)
用 "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" 命令顯示你經(jīng)過的標(biāo)簽列表:
:tags
# TO tag FROM line in file/text
1 1 write_line 8 write_block.c
2 1 write_char 7 write_line.c
>
現(xiàn)在介紹向回跳轉(zhuǎn)。 CTRL-T 命令跳轉(zhuǎn)到上一個(gè)標(biāo)簽。在上例中,你會(huì)回到 "write_line"
函數(shù)調(diào)用 "write_char" 的地方。
這個(gè)命令接受一個(gè)計(jì)數(shù)參數(shù),用來表示跳轉(zhuǎn)回去的標(biāo)簽個(gè)數(shù)。你已經(jīng)向前跳轉(zhuǎn),現(xiàn)在
又跳轉(zhuǎn)了回去。現(xiàn)在我們?cè)僖淮蜗蚯疤D(zhuǎn)。下面的命令跳轉(zhuǎn)到標(biāo)簽列表中最上面的標(biāo)簽:
:tag
你可以在前面加上要向前跳轉(zhuǎn)的標(biāo)簽個(gè)數(shù)。比如:":3tag"。 CTRL-T 同樣可以加上一個(gè)
計(jì)數(shù)參數(shù)。
通過這些命令,你可以用 CTRL-] 延著調(diào)用樹向前跳轉(zhuǎn), 用 CTRL-T 向回跳轉(zhuǎn),用
":tags" 命令顯示當(dāng)前位置。
分 割 窗 口
":tag" 命令會(huì)將當(dāng)前窗口的文件替換為包含新函數(shù)的文件。怎樣才能同時(shí)查看兩個(gè)文件
呢?你可以使用 ":split" 命令將窗口分開然后再用 ":tag" 命令。Vim 有個(gè)縮寫命令可
以做到這些:
:stag tagname
使用下面的命令可以分割當(dāng)前窗口并跳轉(zhuǎn)到光標(biāo)下的標(biāo)簽:
CTRL-W ]
如果指定了計(jì)數(shù)參數(shù),新窗口將包含指定的那么多行。
多 個(gè) 標(biāo) 記 文 件
如果在
*29.2* 預(yù)覽窗口
當(dāng)編輯含有函數(shù)調(diào)用的代碼時(shí),你需要使用正確的調(diào)用參數(shù)。要獲知所要傳遞的值,你可以
查看這個(gè)函數(shù)是如何定義的。標(biāo)簽機(jī)制對(duì)此十分適用。如果定義可在另一個(gè)窗口內(nèi)顯示那
就更好了。對(duì)此我們可以利用預(yù)覽窗口。
打開一個(gè)預(yù)覽窗口來顯示函數(shù) "write_char":
:ptag write_char
Vim 會(huì)打開一個(gè)窗口,跳轉(zhuǎn)到 "write_char" 標(biāo)簽。然后它會(huì)回到原來的位置。這樣你可
以繼續(xù)輸入而不必使用 CTRL-W 命令。
如果函數(shù)名出現(xiàn)在文本中,你可以用下面的命令在預(yù)覽窗口中得到其定義:
CTRL-W }
有一個(gè)腳本可以自動(dòng)顯示光標(biāo)處的標(biāo)簽定義。請(qǐng)參考 |CursorHold-example| 。
用下面的命令關(guān)閉預(yù)覽窗口:
:pclose
要在預(yù)覽窗口中編輯一個(gè)指定的文件,用 ":pedit" 。這在編輯頭文件時(shí)很有用,比如:
:pedit defs.h
最后, "psearch" 可用來查找當(dāng)前文件和任何包含文件中的單詞并在預(yù)覽窗口中顯示匹
配。這在使用沒有標(biāo)簽文件的庫函數(shù)時(shí)十分有用。例如:
:psearch popen
這會(huì)在預(yù)覽窗口中顯示含有 popen() 原型的 "stdio.h" 文件:
FILE *popen __P((const char *, const char *));
你可以用 ‘previewheight‘ 選項(xiàng)指定預(yù)覽窗口打開時(shí)的高度。
*29.3* 在代碼間移動(dòng)
因?yàn)槌绦虼a是結(jié)構(gòu)化的,Vim 可以識(shí)別其中的有關(guān)項(xiàng)目。一些特定的命令可用來完成相
關(guān)的移動(dòng)。
C 程序中經(jīng)常包含類似下面的代碼:
#ifdef USE_POPEN
fd = popen("ls", "r")
#else
fd = fopen("tmp", "w")
#endif
有時(shí)會(huì)更長,也許還有套嵌。將光標(biāo)置于 "#ifdef" 處按 %。Vim 會(huì)跳轉(zhuǎn)到"#else"。繼
續(xù)按 % 會(huì)跳轉(zhuǎn)到 "#endif"。再次按下 % 又回到原來的 "#ifdef"。
當(dāng)代碼套嵌時(shí),Vim 會(huì)找到相匹配的項(xiàng)目。這是檢查你是否忘記了一個(gè) "#endif" 的
好辦法。
當(dāng)你在一個(gè) "#ifdef" - "#endif" 塊內(nèi)的某個(gè)位置,你可以用下面的命令回到開始
處:
[#
如果你的位置不是在 "#if" 或 "#ifdef" 之后, Vim 會(huì)鳴音。用下面命令可以跳轉(zhuǎn)到下
一個(gè) "#else" 或 "#endif":
]#
這兩個(gè)命令會(huì)跳過它所經(jīng)過的 "#if" - "#endif" 塊。
例如:
#if defined(HAS_INC_H)
a = a + inc();
# ifdef USE_THEME
a += 3;
# endif
set_width(a);
如果光標(biāo)在最后一行,"[#" 會(huì)移動(dòng)到第一行。中間的 "#ifdef" - "#endif" 塊被跳過。
在 代 碼 塊 內(nèi) 移 動(dòng)
C 代碼塊包含在 {} 中,有時(shí)一個(gè)代碼會(huì)很長。要跳轉(zhuǎn)到外部代碼塊的開始處,用 "[["
命令。用 "][" 找到結(jié)尾處。(前提是 "{" 和 "}" 都在第一列。)
"[{" 命令跳轉(zhuǎn)到當(dāng)前代碼塊的開始處。它會(huì)跳過同一級(jí)別的 {} 對(duì)。"]}" 跳轉(zhuǎn)到結(jié)尾。
一點(diǎn)概述:
function(int a)
+-> {
| if (a)
| +-> {
[[ | | for (;;) --+
| | +-> { |
| [{ | | foo(32); | --+
| | [{ | if (bar(a)) --+ | ]} |
+-- | +-- break; | ]} | |
| } <-+ | | ][
+-- foobar(a) | |
} <-+ |
} <-+
當(dāng)編寫 C++ 或 Java 代碼時(shí),外部代碼塊是類,而下一級(jí)的 {} 是方法。在類內(nèi)部用
"[m" 可以找到前一個(gè)方法的開始。"]m" 會(huì)找到下一個(gè)方法的開始。
另外,"[]" 向后移動(dòng)到一個(gè)函數(shù)的結(jié)尾,"]]" 向前移動(dòng)到一個(gè)函數(shù)的結(jié)尾。函數(shù)的結(jié)尾
指的是處在第一列的 "}"。
int func1(void)
{
return 1;
+----------> }
|
[] | int func2(void)
| +-> {
| [[ | if (flag)
start +-- +-- return flag;
| ][ | return 2;
| +-> }
]] |
| int func3(void)
+----------> {
return 3;
}
不要忘了你還可以用 "%" 在匹配的 (), {} 和 [] 間移動(dòng)。這在它們相距很多行時(shí)仍然
適用。
在 括 號(hào) 內(nèi) 移 動(dòng)
"[(" 和 "])" 命令"[}" 和 "]}" 類似,只不過它們適用于 () 對(duì)而不是 {} 對(duì)。
[(
<--------------------------------
<-------
if (a == b && (c == d || (e > f)) && x > y)
-------------->
-------------------------------->
])
在 注 釋 間 移 動(dòng)
移動(dòng)到一個(gè)注釋的開始用 "[/";向前移動(dòng)到注釋的結(jié)尾用 "]/"。這只對(duì) /* - */ 注釋
有效。
+-> +-> /*
| [/ | * A comment about --+
[/ | +-- * wonderful life. | ]/
| */ <-+
|
+-- foo = bar * 3; --+
| ]/
/* a short comment */ <-+
*29.4* 查找全局標(biāo)識(shí)符
你在編輯一個(gè) C 程序,想要知道一個(gè)變量是被聲明為 "int" 還是 "unsigned"。一個(gè)快
速的方法是使用 "[I" 命令來查找。
假設(shè)光標(biāo)在單詞 "column" 處。輸入:
[I
Vim 會(huì)列出它所找出的匹配行,不僅在當(dāng)前文件內(nèi)查找,還會(huì)在所有的包含文件中查找。
結(jié)果如下所示:
structs.h
1: 29 unsigned column; /* column number */
相對(duì)使用標(biāo)簽文件或預(yù)覽窗口的好處是包含文件也被搜索。大多數(shù)情況下都能找到正確
的聲明。即使標(biāo)簽文件已經(jīng)過期或者你沒有為包含文件建立標(biāo)簽也不會(huì)影響結(jié)果。
但是一些準(zhǔn)備工作是必要的,否則 "[I" 就沒法工作。首先,‘include‘ 選項(xiàng)必須指
定文件是如何被包含的。省缺值適用于 C 和 C++。對(duì)其它的語言,你需要自己設(shè)定。
定 位 包 含 文 件
Vim 會(huì)找到 ‘path‘ 選項(xiàng)指定路徑中的包含文件。如果缺少某個(gè)目錄,一些包含文件
將不會(huì)被找到。你可以用這個(gè)命令來查看:
:checkpath
它會(huì)列出不能找到的包含文件,以及被找到的包含文件。一個(gè)輸出樣例:
--- Included files not found in path ---
<io.h>
vim.h -->
<functions.h>
<clib/exec_protos.h>
文件 "io.h" 被當(dāng)前文件包含但無法找到。"vim.h" 可以找到,這樣 ":checkpath" 跟進(jìn)
這個(gè)文件并檢查其中的包含文件。結(jié)果顯示無法找到 "vim.h" 包含的 "functions.h" 和
"clib/exec_protos.h" 文件。
Note:
Vim 不是一個(gè)編譯器。它無法識(shí)別 "#ifdef" 語句。這就是說所有的
"#include" 語句都會(huì)被使用,即使它在 "#if NEVER" 之后。
給 ‘path‘ 選項(xiàng)增加一個(gè)目錄可以修正無法找到文件的錯(cuò)誤。一個(gè)好得參考是 Makefile。
注意那些包括 "-I" 的條目,比如 "-I/usr/local/X11"。要增加這個(gè)目錄,用:
:set path+=/usr/local/X11
如果有很多的子目錄,你可以用 "*" 通配符。例如:
:set path+=/usr/*/include
這會(huì)找到 "/usr/local/include" 以及 "/usr/X11/include" 目錄下的文件。
如果你的工程項(xiàng)目的包含文件都在一個(gè)套嵌的目錄樹下,"**" 就非常有用。它會(huì)搜索所
有的子目錄。例如:
:set path+=/projects/invent/**/include
這會(huì)找到這些目錄下的文件:
/projects/invent/include
/projects/invent/main/include
/projects/invent/main/os/include
etc.
還有其它的可能性。更多信息,請(qǐng)查看 ‘path‘ 選項(xiàng)。
如果你想查看找到的包含文件,用這個(gè)命令:
:checkpath!
你會(huì)得到一個(gè)(很長)的包含文件列表。為使它更短些,Vim 會(huì)對(duì)已經(jīng)找到的文件顯示
"(Already listed)" 而不再重新顯示一遍。
跳 轉(zhuǎn) 到 匹 配
"[I" 產(chǎn)生一個(gè)每項(xiàng)只有一行文本的列表。如果你想要進(jìn)一步的查看第一項(xiàng),你可以這個(gè)
命令來跳轉(zhuǎn):
[<Tab>
你也可以使用 "[ CTRL-I", 因?yàn)?CTRL-I 和按 <Tab> 效果一樣。
"[I" 產(chǎn)生的列表在每行的開頭都有一個(gè)序號(hào)。如果你要跳轉(zhuǎn)到第一項(xiàng)外的其它項(xiàng),首先
輸入序號(hào):
3[<Tab>
會(huì)跳轉(zhuǎn)到列表中的第三項(xiàng)。記住你可以用 CTRL-O 跳回到原來的地方。
相 關(guān) 命 令
[i 只列出第一項(xiàng)匹配
]I 只列出光標(biāo)下面的項(xiàng)目
]i 只列出光標(biāo)下面的第一項(xiàng)匹配
查 找 宏 定 義 標(biāo) 識(shí) 符
"[I" 命令查找任何標(biāo)識(shí)符。只查找 "#define" 定義的宏,用:
[D
同樣,這會(huì)在所有的包含文件中查找。 ‘define‘ 選項(xiàng)指定 "[D" 所查找的預(yù)定義樣式。
你需要改變它值來適用于 C 或 C++ 以外的語言。
"[D" 相關(guān)命令:
[d 只列出第一項(xiàng)匹配
]D 只列出光標(biāo)下面的項(xiàng)目
]d 只列出光標(biāo)下面的第一項(xiàng)匹配
*29.5* 查找局部標(biāo)識(shí)符
"[I" 命令查找所有的包含文件。要在當(dāng)前文件中查找并跳轉(zhuǎn)到光標(biāo)處單詞被首次使用的
地方,用:
gD
提示:Goto Definition。這個(gè)命令對(duì)查找局部(C 語言中的 "static") 聲明的變量或函
數(shù)很有用。例如(光標(biāo)在 "counter" 處):
+-> static int counter = 0;
|
| int get_counter(void)
gD | {
| ++counter;
+-- return counter;
}
要進(jìn)一步的縮小查找范圍,只在當(dāng)前函數(shù)內(nèi)查找,用這個(gè)命令:
gd
這會(huì)回到當(dāng)前函數(shù)的開始處尋找光標(biāo)處單詞首次出現(xiàn)的地方。實(shí)際上,它是向后找到一個(gè)
在第一列 ‘{‘ 上面的空行,然后再從那里向前查找標(biāo)識(shí)符。例如(光標(biāo)位于 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;
}