vim + ctags 用法(轉)
2007-08-01 15:35:03
/ 個人分類:嵌入式
ctags -R
"-R"表示遞歸創(chuàng)建,也就包括源代碼根目錄下的所有子目錄下的源程序。"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 的創(chuàng)造者是一名計算機程序員,因此這就不奇怪 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 系統(tǒng)上都已經安裝了它。如果你還沒有安裝, 可以在這里找到 "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" 標簽。然后它會回到原來的位置。這樣你可 以繼續(xù)輸入而不必使用 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"。繼 續(xù)按 % 會跳轉到 "#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; }
|