Vim 實用技術,第 1 部分: 實用技巧

0. Vim 簡介

作爲開源世界最重要的編輯器之一(另一個是 Emacs),Vim 以其強大的功能和可定製能力被衆多開發者所喜愛。不過,也許就是因爲 Vim 的功能太強大了,要真正用好 Vim 並不容易。本文作者在多年的實際使用中逐漸掌握了一些實用技術,在此介紹給大家。——本文並不企圖對 Vim 作全面而系統的介紹,但也絕非零星地點到即止;而是希望通過介紹一些重要特性和提供相關參考信息,引起大家的興趣,去深入挖掘其能力,真正把這一強大的工具用好。

下面首先對 Vim 做一下最基本的介紹,並給出一些參考信息,以方便對 Vim 不熟悉的讀者也能夠理解並自己查閱進一步信息。

與大部分其它編輯器不同,進入 Vim 後,缺省狀態下鍵入的字符並不會插入到所編輯的文件之中。Vim 的模式(mode,可以簡單地理解爲“狀態”)概念非常重要。需要知道,Vim 有以下幾個模式:

  • 正常(normal)模式,缺省的編輯模式;下面如果不加特殊說明,提到的命令都直接在正常模式下輸入;任何其它模式中都可以通過鍵盤上的 Esc 鍵回到正常模式。
  • 命令(command)模式,用於執行較長、較複雜的命令;在正常模式下輸入“:”(一般命令)、“/”(正向搜索)或“?”(反向搜索)即可進入該模式;命令模式下的命令要輸入回車鍵(Enter)纔算完成。
  • 插入(insert)模式,輸入文本時使用;在正常模式下鍵入“i”(insert)或“a”(append)即可進入插入模式(也有另外一些命令,如“c”,也可以進入插入模式,但這些命令有其它的作用)。
  • 可視(visual)模式,用於選定文本塊;可以在正常模式下輸入“v”(小寫)來按字符選定,輸入“V”(大寫)來按行選定,或輸入“Ctrl-V”來按方塊選定。
  • 選擇(select)模式,與普通的 Windows 編輯器較爲接近的選擇文本塊的方式;在以可視模式和選擇模式之一選定文本塊之後,可以使用“Ctrl-G”切換到另一模式——該模式很少在 Linux 上使用,本文中就不再介紹了。

Vim 帶有完整的幫助文檔。在當前的 Vim 6.4 的標準發佈中,有一百多章、近六十萬英文詞的幫助文件,進入 Vim 後輸入“:help”(命令模式中輸入的命令要敲回車鍵才結束輸入,下面不再說明這一點)即可訪問。本文在介紹特性時,對文檔中已經說明得很詳細的內容只會提綱挈領地加以簡短說明和提供應用範例,並提供訪問相應的 Vim 文檔的命令。

一般的發佈版中還常常帶有一個簡單的 30 分鐘的 Vim 教程,新手在操作系統的命令行上輸入“vimtutor”命令即可開始學習。除上面的簡單說明外,本文並不介紹最基本的 Vim 命令,Vim 的新手應該先通過教程熟悉一下 Vim,再繼續往下閱讀。

建議所有的 Vim 用戶經常訪問 Vim 的主站點 [1]。上面除了基本的發佈、安裝、下載等信息外,最有用的內容是用戶可以上傳自己寫的 Vim 腳本(script)和撰寫自己認爲有用的提示(tip),供其他 Vim 用戶使用。在寫這一段的時候,Vim 站點上已有一千三百多個腳本,提示數剛好超過了一千。對於序號爲 nn 的腳本,直接訪問的 URL 是 http://www.vim.org/scripts/script.php?script_id=nn;對於序號爲 nn 的提示,直接訪問的 URL 是 http://www.vim.org/tips/tip.php?tip_id=nn。

不另加說明的話,本文討論的內容適用於 Vim 版本 6(即從 6.0 到 6.4)。建議認真的 Vim 用戶升級到 Vim 6.4,最好是自己編譯升級所有的補丁包。相關信息網站上都有,此處不再贅述。

1.實用技巧

1.1 安裝

如果從 Linux 發佈版直接安裝 Vim,需要注意的一點是,缺省情況下系統並不一定爲你安裝了一個完整的 Vim。比如,在 Red Hat(以及後來的 Fedora Core)的發佈版中,Vim 被拆成了四個包:vim-common(公用部分),vim-minimal(最小安裝),vim-enhanced(除 X Window 支持外的完整安裝),和 vim-X11(X Window 圖形界面支持)。最小安裝不能完整展示 Vim 的優點,通常只是作爲 vi 的替代品出現,缺少很多重要的特性如多字節語言支持、鼠標支持和腳本支持。如果裝了 X Window 的話,圖形界面的 gvim 也比文本模式的 vim 具有更多的特性。建議大家儘可能安裝完全的 Vim。

如果願意稍稍費一點功夫,自己編譯 Vim 的話,可以更好地定製 Vim。——附帶的另一個好處是,你如果發現什麼錯誤的話,你就可以自己動手來修復這個錯誤,或至少找到錯誤所在的位置,讓 Bram(Vim 的作者)可以更快地解決問題。圖 1 是在 Vim 中執行“:version”的結果的一部分,可以看到 Vim 有很多不同的特性(feature)可在編譯時打開或關閉。如果自己編譯的話,就可以選擇打開需要的功能,關閉不需要的功能,來獲得一個既功能強大、又小巧快速的 Vim 定製版本。


圖 1
圖1

1.2 中文支持

Vim 支持世界上的主要語言,當然也包括中文。如果你用 Vim 編輯中文,而中文不能正確顯示,那有兩種可能性:一是使用的 Vim 不完整,不含多字節語言支持(multi_byte 特性);二是某個配置出了問題。

說到多語言支持,最基本的概念有兩個:一是文件的語言編碼,而是環境的內部編碼。在較老的操作系統中,不管 Linux 還是 Windows,這兩個編碼都是一樣的,也就意味着,一次只能處理一種編碼的文件:要麼只能處理西文編碼(Latin1,即 ISO-8859-1 [5]),要麼只能處理中文編碼(GB2312 [2])。而在新的操作系統中,這兩者可以是不一樣的。在 Linux 上,常見的情況是環境的內部編碼使用 UTF-8 [6],而 UTF-8 可以同任何一種語言編碼作無損轉換,這就保證了系統的多語言處理能力。Vim 這方面秉承了 Unix/Linux 的傳統,在內部編碼使 UTF-8 的時候,可以同時處理不同意語言編碼的文件。

以下列出了和語言編碼的相關的設置:

  • 環境變量 LANG(使用的語言);
  • 環境變量 LC_CTYPE(使用的內部編碼);
  • Vim 選項 encoding(Vim 的內部編碼);
  • Vim 選項 termencoding(Vim 在與屏幕/鍵盤交互時使用的編碼);
  • Vim 選項 fileencoding(Vim 當前編輯的文件在存儲時的編碼);
  • Vim 選項 fileencodings(Vim 打開文件時的嘗試使用的編碼);
  • Vim 選項 ambiwidth(對“不明寬度”字符的處理方式;Vim 6.1.455 後引入)。

如果你的環境只需要處理簡體中文的話,那麼,最簡單的方式就是所有的設定全部使用簡體中文。只需要:設定 LANG=zh_CN.GB2312,不設定 LC_CTYPE(默認跟 LANG 一樣),不設定與編碼相關的 Vim 選項(默認由 LANG 和 LC_CTYPE 決定),也無需設定 Vim 選項 ambiwidth。也就是說,我們把語言設定爲中國(CN)使用的中文(zh),編碼爲 GB2312(注意:Vim 內部並不識別國標 GB18030 [3],所以此處只能設 GB2312;參看下面關於 UTF-8 的討論)。

不過,如果按照目前 Linux 下的慣例,內部編碼一律使用 UTF-8 的話,會有一些額外的好處,其中之一就是在這種情況下 Vim 支持同時編輯多種不同編碼的文件,如簡體中文和繁體中文(參見圖 2);另外,此時 Vim 也可以通過編碼轉換支持 GBK [4] 和 GB18030了。這樣,衆多關於語言編碼的 Vim 選項就有了用武之地了。下面進一步說明一下這些選項和推薦設定(如果適用的話):


圖 2
圖2
  • encoding=utf-8:不管文件的編碼如何,不管如何顯示和輸入,Vim 內部使用的編碼是 UTF-8;這是國際化支持的基礎。
  • termencoding:取決於實際的終端或 X Window 的設定。舉例來說,如果選擇語言簡體中文登錄到 X Window,或者正在使用 CXTERM [10] 的話,那麼該選項應被設爲 GB2312;如果使用缺省的語言(LANG=en_US.UTF-8)登錄到 X Window,或者使用 PuTTY [11] 遠程訪問 Linux 機器、並且設定裏的字符編碼(配置中 Window-Translation)設爲 UTF-8 的話,該選項就應該設爲 utf-8。從 Windows 下使用 PuTTY 遠程連接 Linux 的請特別注意,測試表明,僅在使用 UTF-8 的情況下,PuTTY 才能可靠地支持中文的顯示和輸入(顯示字體必須設成中文字體)。
  • fileencoding:文件載入時,該選項被置爲 Vim 認定的文件編碼,因此,存儲時文件的編碼不會改變。此處和下面 fileencodings 可使用的編碼爲 libiconv 支持的所有幾百種編碼(如果編譯時包含了 iconv 特性的話),與中文相關的有 gb2312、gbk、gb18030、hz-gb-2312、iso-2022-cn、big5、cp936、cp950 等。如果創建新文件,你又不希望使用 UTF-8 作爲文件編碼時,那麼,你可能需要手工設定該選項,如“:set fileencoding=gb2312”。需要注意的一點是,使用“set”來設定該選項的話會改變以後新建文件的缺省編碼,而使用“setlocal”的話則隻影響當前文件(參考“:help setlocal”)。
  • fileencodings=ucs-bom,utf-8,chinese:Vim 會首先判斷文件的開頭是否是一個 Unicode [7] 的 BOM(byte order mark)字符 [8],是的話則把文件的其餘內容解釋成相應的 Unicode 序列;否的話再試圖把文件內容解釋成 UTF-8 的序列;再失敗的話,則把文件解釋爲簡體中文(chinese 是一個跨平臺的簡體中文字符集的別名,Linux 下相當於 gb2312 和 euc-cn;此處也可以根據需要以 gb2312、gbk 或 gb18030 等編碼替代)。需要注意的是,該順序不能顛倒,並且在後面再添加其它編碼如 big5、latin1 也是沒有意義的,因爲 Vim 不能識別 8 比特編碼中的錯誤,因此這些編碼後列的編碼永遠不會被用到。
  • ambiwidth=double:把所有的“不明寬度”字符 [9]——指的是在 Unicode 字符集中某些同時在東西方語言中使用的字符,如省略號、破折號、書名號和全角引號,在西方文字中通常字符寬度等同於普通 ASCII 字符,而在東方文字中通常字符寬度等同於兩倍的普通 ASCII 字符,因而其寬度“不明”——的寬度置爲雙倍字符寬度(中文字符寬度)。此數值只在 encoding 設爲 utf-8 或某一 Unicode 編碼時纔有效。需要額外注意的是,如果你通過終端使用 Vim 的話,需要令終端也將這些字符顯示爲雙寬度。比如,XTERM [12] 的情況下應該使用選項“-cjk”,即使用命令“uxterm -cjk”來啓動使用雙寬度顯示這些字符的 Unicode X 終端;使用 PuTTY 遠程連接的話則應在配置的 Window-Translation 中選中“Treat CJK ambiguous characters as wide”(參見圖 3)。

    圖 3
    圖3

需要設定的選項通常放在用戶的 Vim 資源配置文件中,即在 ~/.vimrc 文件中加入:


set encoding=utf-8
set fileencoding=chinese
set fileencodings=ucs-bom,utf-8,chinese
set ambiwidth=double

如果想進一步瞭解這些選項的話,可以使用“:help '選項'”查看幫助文檔中的相關(英文)信息。幫助中也可以查到這些選項(以及命令)的縮寫:本文中爲方便理解,除一些極少有人使用完整拼寫的命令如“:e(dit)”、“:s(ubstitute)”等之外,一般使用完整拼寫而不說明或使用縮寫。關於配置文件 .vimrc,可以使用“:help .vimrc”查看相關信息。

在使用內部編碼 UTF-8 的情況下,如需編輯 fileencodings 之外(其不能自動識別)的文件,則可以使用以下命令:“:e ++enc=編碼 文件名”。詳情可參考“:help ++enc”。

1.3. 鼠標支持

不管是文本界面還是圖形界面的 Vim,都支持鼠標。不過,在文本界面中,鼠標支持缺省沒有被激活;這就意味着,在終端上使用鼠標,所有的功能仍和沒有使用 Vim 時相同,並不受 Vim 影響。要激活文本界面中的鼠標支持也很容易,只需要執行一句“:set mouse=a”即可。

啓用了鼠標支持之後,Vim 主要支持的鼠標操作有:

  • 單擊移動光標到點擊的位置;
  • 在幫助的關鍵字上雙擊顯示該關鍵字相關的幫助信息;
  • 在普通文本上雙擊選中點擊位置的單詞;
  • 拖動鼠標選中文本;
  • 使用鼠標滾輪滾動當前緩衝區中的文本;
  • 多窗口編輯時可以拖動窗口分欄的位置。

進一步的信息可參看“:help 'mouse'”、“:help mouse-using”和“:help scroll-mouse-wheel”。

特別需要值得一提的是,在遠程訪問 Linux 系統時也是可以使用鼠標的。如果使用 X Window 系統,自然不必說;而使用 SSH 遠程連接時,大部分 Linux 下的終端客戶程序,如 XTERM、GNOME-Terminal [13]、較新版本的 Konsole [14],以及 Windows 下的 PuTTY,支持鼠標的使用:你只需簡單地啓動 Vim、執行一句“:set mouse=a”就可以了(當然,也可以把上面的語句去掉起始的冒號放到 .vimrc 文件中)。

1.4. 空格、製表符和縮進

對於編寫代碼,縮進是最基本的概念之一。至於縮進是使用空格還是製表符(Tab),或者縮進是否正好使用一個製表符來表示,很多程序員,特別是 Windows 開發出身的程序員,很容易混淆。幸好,Vim 對於這些概念有非常完整的支持,足以應付各種複雜的情況。以下是相關的主要 Vim 選項:

  • shiftwidth(縮進的空格數);
  • tabstop(製表符的寬度);
  • expandtab(是否在縮進和遇到 Tab 鍵時使用空格替代;使用 noexpandtab 取消設置);
  • softtabstop(軟製表符寬度,設置爲非零數值後使用 Tab 鍵和 Backspace 時光標移動的格數等於該數值,但實際插入的字符仍受 tabstop 和 expandtab 控制);
  • autoindent(自動縮進,即每行的縮進值與上一行相等;使用 noautoindent 取消設置);
  • cindent(使用 C 語言的縮進方式,根據特殊字符如“{”、“}”、“:”和語句是否結束等信息自動調整縮進;在編輯 C/C++ 等類型文件時會自動設定;使用 nocindent 取消設置);
  • cinoptions(C 語言縮進的具體方式,請參考“:help cinoptions-values”);
  • paste(粘貼模式,會取消所有上述選項的影響來保證後面的操作——通常是從剪貼板粘貼代碼——保持原有代碼的風格;使用 nopaste 取消設置)。

下面給出一些常用的組合:

  • shiftwidth=4 tabstop=4:很多 Windows 出身的程序員會習慣這樣的設置,讓縮進等於製表符寬度。
  • shiftwidth=4 tabstop=8:很多 Unix 程序員的設置,仍使用較常用的 4 格縮進,但製表符寬度爲標準的 8。
  • cinoptions=>4,n-2,{2,^-2,:2,=2,g0,h2,p5,t0,+2,(0,u0,w1,m1 shiftwidth=2 tabstop=8:標準的 GNU 編碼風格的設置,對 Vim 缺省的 C 縮進風格作了很多微調,比如,if 語句下的“{”、“}”要在“if”後縮進兩格,但函數定義部分“{”、“}”仍和函數名一行對齊。開源軟件經常使用該種縮進風格。

在編輯代碼時一個很有用的命令是調整代碼縮進,可以很方便地增加(或減少)若干級縮進,並自動根據選項設定使用正確的空格或製表符。只需要使用“V”選中你要調整的代碼行,然後鍵入“<”(或“>”)即可增加(或減少)一級縮進;在鍵入“<”(或“>”)之前鍵入數字則可以指定增加(或減少)的縮進級數。

我們要討論的最後一個相關的命令是“:retab”。在設定了 expandtab 選項時,該選項會把所有的製表符轉換成空格。在沒有設定 expandtab 選項時,使用“:retab!”可把空白字符轉換成製表符(可能誤轉換,慎用),使用“:retab n”可以把 tabstop 重置爲 n,並轉換含製表符的連續空白字符爲適當的製表符和空格的組合以保證含製表符的行看起來沒有任何變化。詳細信息請參看“:help :retab”。

1.5. 模式行(modeline)

沒人願意每次都手工輸入一大堆的 Tab 和縮進設定。可是,放在 .vimrc 文件中似乎也不是個好主意:如果我編輯的代碼不止一種風格呢?——考慮一下,如果你參加開源軟件項目,你能保證你參加的所有項目,還有你公司裏的軟件項目,代碼風格都一樣嗎?——Vim 是我用過的第一個支持在文件中記錄代碼風格設定的編輯器。這個特性在 Vim 中叫做模式行,實際上,它所做的是在打開文件時根據文件中的 Vim 指令設定相關的 Vim 選項。下面就是一個嵌在 C 源代碼中的模式行:


/* vim: set tabstop=4 shiftwidth=4 expandtab: */

模式行有好幾種形式。本文只介紹上面的這種形式(其它形式類似,請自行參考“:help modeline”):行首的“/*”和尾部的“*/”告訴 C 編譯器這是一行註釋,不是代碼的一部分;而 Vim 可通過後面的“vim:”識別出模式行的開始(必須出現在行首或前面有一個空白字符);後面則是“set”和空格間隔開的一串 Vim 選項;“:”表示模式行結束。

這種方式非常簡單,功能也非常強大。另外請注意,出於安全的考慮,模式行中的選項隻影響當前文件(“:help modeline-local”),也不能做任何設置選項以外的工作。

1.6. 寄存器

通常的編輯器有一個剪貼板,以存儲複製和剪切的內容。Vim 中的類似概念叫做寄存器(register)。除了有一個無名寄存器外,Vim 還有一大堆有名的寄存器,可以通過“"”(參見“:help "”)或“Ctrl-R”(參見“:help i_CTRL-R”和“:help c_CTRL-R”)加寄存器名(字母、數字和某些特殊字符,參見“:help registers”;“無名”寄存器的名字是“"”)來訪問。比如,你先使用“"ayy”複製了一行,然後使用“dd”刪掉了一行,然後移動光標到要複製到的位置,就可以使用“"aP”把先前複製的內容粘貼上去了。手工編輯是有名寄存器的作用還不是很大,但當你想讓 Vim 通過類似於宏的方式自動完成工作時,有名寄存器就變成不可缺少的重要功能了。下面我們還會用到。

在使用 X Window 系統時,有兩個特殊的寄存器是需要注意一下的:“"*”訪問的寄存器是 X 的主選擇區域(primary selection),“"+”訪問的寄存器是 X 的剪貼板(clipboard)。如果你要在 Vim 和其它的 X 應用程序之間複製文本內容,你可以試一下這兩個寄存器。

還有一個很特殊的“寄存器”:“=”。在插入模式或命令模式中,鍵入“Ctrl-R=”,Vim 會提示你輸入一個表達式,普通的整數運算在此完全有效。如果想要進行浮點運算,請參見第 3.2 節中的技巧。

1.7. 搜索、替換和正則表達式

大家應該都已經知道 Vim 裏使用“/模式”(或“?模式”)進行搜索,使用“:s/模式/字符串/標誌”進行替換,其中的“模式”是一個正則表達式。關於正則表達式,不熟悉的話可以邊用邊學,本節也不打算對 Vim 的正則表達式作完整的闡述(那可能可以專門寫一本小冊子了),而只拋磚引玉式地給出一些有用的例子加以說明,以及一些實用技巧。

先說一點點搜索。搜索裏最最有用的一個快捷方式是“*”(向下完整匹配光標下的單詞)。把光標移動到你要搜索的詞(變量名、函數名等)上,比如“test”,然後按“*”,Vim 將自動產生一個對“\<test\>”(參見“:help /\<”和“:help /\>”)的搜索,也就是說,搜索完整的單詞“test”。不要小看這個技巧,它經常可以大幅度地提高搜索的速度。事實上,這是 Vim 網站上公佈的第 1 號技巧,也是被評價最高的技巧。相似的技巧還有“#”(向上完整匹配光標下的單詞)、“g*”(向下部分匹配光標下的單詞)等,請自行查看(“:help #”等)。

Vim 在搜索和替換時會對匹配成功的文本進行加亮,在已經完成搜索和替換任務後,這種加亮有時反而會妨礙顯示。Vim 專門提供一個命令取消這種加亮(直到用戶再一次使用搜索或替換命令):“:nohlsearch”。建議用戶創建一個鍵盤映射(key mapping)加入到 .vimrc 中,如:


nmap <F2> :nohlsearch<CR>

以上命令表示,在正常模式下按 F2 鍵相當於輸入“:nohlsearch”後面跟一個回車,即取消搜索加亮顯示。

再看幾個搜索替換的實用例子。

  • 去掉所有的行尾空格:“:%s/\s\+$//”。“%”表示在整個文件範圍內進行替換,“\s”表示空白字符(空格和製表符),“\+”對前面的字符匹配一次或多次(越多越好),“$”匹配行尾(使用“\$”表示單純的“$”字符);被替換的內容爲空;由於一行最多隻需替換一次,不需要特殊標誌。這個還是比較簡單的。
  • 去掉所有的空白行:“:%s/\(\s*\n\)\+/\r/”。這回多了“\(”、“\)”、“\n”、“\r”和“*”。“*”代表對前面的字符(此處爲“\s”)匹配零次或多次(越多越好;使用“\*”表示單純的“*”字符),“\n”代表換行符,“\r”代表回車符,“\(”和“\)”對表達式進行分組,使其被視作一個不可分割的整體。因此,這個表達式的完整意義是,把連續的換行符(包含換行符前面可能有的連續空白字符)替換成爲一個單個的換行符。唯一很特殊的地方是,在模式中使用的是“\n”,而被替換的內容中卻不能使用“\n”,而只能使用“\r”。原因是歷史造成的,詳情如果有興趣的話可以查看“:help NL-used-for-Nul”。
  • 去掉所有的“//”註釋:“:%s!\s*//.*!!”。首先可以注意到,這兒分隔符改用了“!”,原因是在模式或字符串部分使用了“/”字符,不換用其他分隔符的話就得在每次使用“/”字符本身時寫成“\/”,上面的命令得寫成“:%s/\s*\/\/.*//”,可讀性較低。命令本身倒是相當簡單,用過正則表達式的人估計都知道“.”匹配表示除換行符之外的任何字符吧。
  • 去掉所有的“/* */”註釋:“:%s!\s*/\*\_.\{-}\*/\s*! !g”。這個略有點複雜了,用到了幾個不太常用的 Vim 正則表達式特性。“\_.”匹配包含換行在內的所有字符;“\{-}”表示前一個字符可出現零次或多次,但在整個正則表達式可以匹配成功的前提下,匹配的字符數越少越好;標誌“g”表示一行裏可以匹配和替換多次。替換的結果是個空格的目的是保證像“int/* space not necessary around comments */main()”這樣的表達式在替換之後仍然是合法的。

希望上面的這些簡單的例子能夠引起你使用 Vim 的正則表達式高效完成任務的興趣。進一步的信息可參考“:help regexp”。

1.8. 自動完成和路徑設定

Vim 支持單詞的自動完成。比如,你前面使用了一個很長的變量名,叫 aLongVariable,下面你在輸入時,就不用完整鍵入了。很可能,你只需要鍵入“aL”,然後按下“Ctrl-P”(向前搜索可匹配的單詞並完成)就可以得到完整的變量名(沒有得到想要的結果的話,多按幾下“Ctrl-P”;或者前面多輸入幾個字符,如“aLongV”)。類似的命令還有“Ctrl-N”(向後搜索可匹配的單詞並完成)、“Ctrl-X Ctrl-L”(搜索可匹配的行並完成)、“Ctrl-X Ctrl-F”(搜索可匹配的文件名並完成)等,具體可參看“:help ins-completion”。

如果你並不熟悉這些功能,但也並不覺得這有什麼稀奇的話,下面這個例子可能會讓你覺得吃驚。請嘗試打開一個空白的 C 文件(vim test.c),並輸入:


#include <stdio.h>
int main()
{
    pri

最後一行不要回車,直接在“pri”後面輸入“Ctrl-P”,你將看到“printf”出現。是的,雖然文件裏沒有“printf”,但 Vim 知道到哪裏去尋找它!在作關鍵字匹配完成時,如果當前文件和其它打開的文件中沒有想要的結果,Vim 會自動到“#include”的文件中進行進一步的搜索(爲什麼是“#include”呢?請查閱“:help 'include'”),搜尋的目錄則由選項 path 決定,其缺省值在 Unix(含 Linux)下爲“.,/usr/include,,”,代表搜索的目錄依次是文件所在目錄、/usr/include 和當前目錄。根據實際情況,你可能需要在 .vimrc 文件中設置該選項,加入項目相關的包含目錄,注意一般要保留最後的“,,”,除非你不需要在當前目錄下搜索。

設置了合適的 path 後,另外帶來的一個便利就是可以使用“gf”命令方便地跳轉到光標下的文件名所代表的文件中。在上面的例子中,把光標移到“stdio.h”的任一字符上,鍵入“gf”,則 Vim 會自動打開 /usr/include/stdio.h 文件。使用“Ctrl-O”(參見“:help CTRL-O”)可返回到原先的文件中。

1.9. 文件跳轉和 Tags

大家一般都知道,在 Vim 的幫助窗口中的關鍵字上雙擊鼠標或者鍵入“Ctrl-]”即可跳轉至該關鍵字相關的幫助主題。不過,“跳轉至匹配的關鍵字”這一功能並不僅僅侷限於幫助文件。只要有合適的 tags 文件(參見“:help tags-file-format”),我們同樣可以在源代碼中使用這個方便的功能,跳轉到與關鍵字匹配的“標記”處(通常是源代碼中某一函數、類型、變量或宏的定義位置)。

要產生 tags 文件,通常我們使用 Exuberant Ctags [15]。一般的 Linux 發佈版中均帶有這一工具。Ctags 帶有的選項數量極多,此處我們僅簡單介紹如何在一個典型的多文件、多層目錄的項目中使用其基本功能:我們只需在項目的根目錄處鍵入“ctags -R .”,Ctags 即可自動在文件中查找、識別支持的文件格式、生成 tags 文件。目前 Exuberant Ctags 支持多達 33 種編程語言 [16],包括了 Linux 下常用的 C、C++、Java、Perl、PHP 等。有了 tags 文件,以下的 Vim 命令就可以方便使用了(進一步的信息可參考“:help tags-and-searches”):

  • :tag 關鍵字(跳轉到與“關鍵字”匹配的標記處)
  • :tselect [關鍵字](顯示與“關鍵字”匹配的標記列表,輸入數字跳轉到指定的標記)
  • :tjump [關鍵字](類似於“:tselect”,但當匹配項只有一個時直接跳轉至標記處而不再顯示列表)
  • :tn(跳轉到下一個匹配的標記處)
  • :tp(跳轉到上一個匹配的標記處)
  • Ctrl-](跳轉到與光標下的關鍵字匹配的標記處;除“關鍵字”直接從光標位置自動獲得外,功能與“:tags”相同)
  • g](與“Ctrl-]”功能類似,但使用的命令是“:tselect”)
  • g Ctrl-](與“Ctrl-]”功能類似,但使用的命令是“:tjump”)
  • Ctrl-T(跳轉回上次使用以上命令跳轉前的位置)

當我們在項目的根目錄下工作時,上面這些命令工作得很好。但如果我們進到多層目錄的裏層再運行 Vim 打開文件時,這些命令的執行結果通常就變成了錯誤信息“E433: No tags file”。這是因爲缺省 Vim 只在文件所在目錄和當前目錄下尋找 tags 文件,而我們前面只在項目的根目錄下生成了 tags 文件,Vim 無法找到該文件。解決方法有好幾種,我認爲一般較簡單的做法是對每個項目都在 .vimrc 文件中增加一個路徑相關設定。假設我們有兩個項目,位置分別是 /home/my/proj1 和 /home/my/proj2,那我們可以使用:


au BufEnter /home/my/proj1/* setlocal tags+=/home/my/proj1/tags
au BufEnter /home/my/proj2/* setlocal tags+=/home/my/proj2/tags

Vim 選項 tags 用於控制檢查的 tags 文件,缺省值爲“./tags,tags”,即前面所說的文件所在目錄下和當前目錄下的 tags 文件。上面兩行自動命令告訴 Vim,在打開項目目錄下的文件時,tags 選項中的內容要增加項目的 tags 文件的路徑。進一步信息可參看“:help 'tags'”。

1.10. Make 和 grep

Make [17] 和grep [18] 應當算是 Unix 世界裏無人不曉的基本工具了吧。很自然的,Vim 對它們有着特殊的支持。該支持主要通過訪問一個特殊的快速修訂窗口(quickfix window)來實現。直接在 Vim 的命令模式裏輸入相應的 make 或 grep 命令(如“:grep foo *.c”)即可將命令的執行結果放入該窗口,同時根據返回的結果跳轉到第一個錯誤(make 的情況;在使用 grep 時是匹配成功之處)。以下是常用的“快速修訂”命令:

  • :cn(顯示下一個錯誤)
  • :cp(顯示上一個錯誤)
  • :cl(列出所有的錯誤及其編號)
  • :cc(跳轉到指定編號的錯誤)
  • :copen(打開快速修訂窗口,在其中顯示所有錯誤,可在錯誤上雙擊鼠標或按回車鍵跳轉至該錯誤;示例參見圖 4)

    圖 4
    圖4
  • :cclose(關閉快速修訂窗口)

Vim 的這個特性也可以與 make 和 grep 以外的程序一起工作(事實上,在 Windows XP 上,“:grep”命令一般調起的是“findstr /n”)。具體調用那個程序由選項 makeprg(Linux 下缺省爲“make”)和 grepprg(Linux 下缺省爲“grep -n $* /dev/null”)控制,而如何解析返回的內容則由選項 errorformat 和 grepformat 控制。鑑於在 Unix/Linux 下一般不需更改這些選項的內容,此處不再詳述。

1.11. 執行外部命令

在“:make”這樣的命令中,Vim 會自動調用外部的程序。用戶當然也可以自己執行外部的程序:估計很多的人都已經知道了用“:!命令”可以在 Vim 中執行一個外部命令。不過,估計大部分人都不知道,還有其它一些命令可以執行外部命令,並且,即使“:!”命令裏面也有一些技巧可以使用。

最正規的執行外部命令的方法,如前所述,就是“:!”。比如,我們想要顯示當前目錄下的所有文件,就可以直接執行:“:!ls”。Vim 會在一個終端窗口中進行文件列表,然後提示我們按鍵返回 Vim 中。事實上,這種方式對於“cp”、“rm”這樣基本不需要輸出的命令比較實用,而對於“ls”這樣關注於輸出的命令並不太適用。

如果想把外部命令執行的結果插入到當前編輯的緩衝區中,可以考慮使用“:r!”。比如,我們使用“:r!ls”,就可以把“ls”命令的執行結果插入到緩衝區中光標所在行下面。在使用宏時,這可能會特別有用。

Vim 的“:!”命令還有一個特別強大的技巧可以使用。拿一個實際例子,我們需要對在一個文件的每一行之前插入一個編號,該怎麼做呢?——用 Vim 的宏或者腳本可以完成這一工作,但這不是最高效、最靈活的工作方式。Linux 下一般帶有的 GNU 的 nl,可以用非常靈活的方式來完成這一任務——要對所有的非空行進行編號,只需要“:%!nl”;要對包含空行的所有行進行編號?OK,“:%!nl -ba”。

稍作一點解釋。當使用可視模式選中文本行後然後鍵入“:!”(命令行上將出現“:'<,'>!”,表示命令的範圍是選定的文本),或者使用“:%!”(表示命令的範圍是整個緩衝區中的文本),Vim 在執行後面的命令時,將把命令範圍裏的文本行作爲後面執行的命令標準輸入,並用命令執行後的標準輸出替換當前緩衝區中的這些文本行。這就是上面的命令行的工作原理。

1.12. 定寬文本排版

在傳統的 Unix 環境下,文本文件的定義是具有一定長度限制的文本行的組合 [19]。雖然 Vim 本身對行的長度沒有任何實際的限制,但有一些工具有這樣的限制。爲了最大程度的兼容性,也爲了在顯示、打印等處理上比較方便,一般推薦在郵件和源代碼中一般不要超出 72 列(最多不超出 80 列)。Vim 在處理定寬的文本方面具有特殊的支持能力。下面是一個在 Vim 中把行寬(使用選項 textwidth)設爲 40 後輸入 Harry Potter and the Half-Blood Prince 的第一句話的結果:


It was nearing midnight and the Prime
Minister was sitting alone in his
office, reading a long memo that was
slipping through his brain without
leaving the slightest trace of meaning
behind.

輸入時我只使用了英文字母和空格,換行符都是 Vim 自動插入的。如果在某一行加入或刪除了一些字符後行不就不齊了嗎,該如何處理?很簡單,把光標移到要重新格式化的文本開頭,使用“gq”命令後面跟一個光標移動命令確定重新格式化的範圍。比如“gq}”(格式化一段),“gq5j”(格式化 5 行),“gqG”(格式化至文件末尾)。

除了選項 textwidth 外,選項 formatoptions 確定了跟文本格式化有關的基本選項,常用的數值有:

  • t:根據 textwidth 自動折行;
  • c:在(程序源代碼中的)註釋中自動折行,插入合適的註釋起始字符;
  • r:插入模式下在註釋中鍵入回車時,插入合適的註釋起始字符;
  • q:允許使用“gq”命令對註釋進行格式化;
  • n:識別編號列表,編號行的下一行的縮進由數字後的空白決定(與“2”衝突,需要“autoindent”);
  • 2:使用一段的第二行的縮進來格式化文本;
  • l:在當前行長度超過 textwidth 時,不自動重新格式化;
  • m:在多字節字符處可以折行,對中文特別有效(否則只在空白字符處折行);
  • M:在拼接兩行時(重新格式化,或者是手工使用“J”命令),如果前一行的結尾或後一行的開頭是多字節字符,則不插入空格,非常適合中文

上面提到的註釋,可以是 C/C++ 中的“//”和“/*”,也可以是郵件中引用原文使用的“>”等字符(具體由 comments 選項控制;參見“:help 'comments'”)。Vim 在遇到這些字符時,能夠相當智能地進行處理,足以完成日常編輯源代碼和郵件的需要。在使用一些處理純文本不夠強大的郵件客戶端時,我通常使用 Vim 編輯郵件(特別是英文郵件),然後把結果貼回到郵件編輯窗口中進行發送。

Vim 中 formatoptions 的缺省值是“tcq”,一般我會在 .vimrc 文件中加入一行“set formatoptions+=mM”來確保 Vim 能在中文字符之間折行而不要求空格的存在,並且在大部分情況下可以正確地處理中文重新格式化。

1.13. 其它小技巧

也許你會覺得這些很有用:

  • %(跳轉到與之匹配的括號處)
  • .(重複上次的修改命令)
  • `.(跳轉到最近修改過的位置)
  • ZQ(無條件退出)
  • ZZ(存盤退出)
  • ga(顯示光標下的字符在當前使用的 encoding 下的內碼)
  • guw(光標下的單詞變爲小寫)
  • gUw(光標下的單詞變爲大寫)
  • :TOhtml(根據 Vim 的語法加亮的方式生成 HTML 代碼;在圖形界面中也可以使用菜單“Syntax—Convert to HTML”達到同樣效果)

無聊的時候,還可以試試(呵呵!):

  • :help!
  • :help 42
  • :help holy-grail
原文出自:http://www.ibm.com/developerworks/cn/linux/l-tip-vim1/index.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章