Anda di halaman 1dari 8

此文章由 LinuxHall.org 發放 (www.linuxhall.

org)

《用 vim 寫程式快 n 倍》
作者:Anthony <ypwong@ypwong.org>

或許你一見到標題就想罵筆者:「你沒有問題吧,在 Linux 雜誌教 vi?」,又或是「pico/emacs 才是我的至


愛喔,學 vi 來幹什麼?」。其實我實不太應該在這裡教大家使用 vi 的,但 vi 在 Unix 世界中幾乎是無處不在,
懂得用 vi 有如身份象徵一樣,不曉得用 vi 就少了一招絕招了,學不學就好好想清楚啦,嘿嘿 >:D。身為
programmer 的你就更加要學 vim 了,你將會親身體驗寫 program 時如入無人之境的快感(甚麼?要學 emacs?
我不想開罵戰呢,或許你考慮寫篇《用 emacs 寫程式比用 vim 快 n 倍》那還可以 :))。

Learn vi in 3 minutes...

即使你一丁點兒 vi 也不懂也不要緊,我將會在 3 分鐘內教曉你。

vi 與眾不同的地方是它有兩個模式,分別為 normal mode 和 insert mode,在使用 vi 的過程中你會時常轉換


於這兩個模式之間。在 normal mode 的時候你不能如常地輸入文字到你的文件中,你只能輸入指令,如移動浮
標,存檔等系統工作,或刪除文字等。Insert mode 則是用來輸入文字時的狀態,換句話說一般的編輯器只有
insert mode 而沒有 normal mode。為了方便解釋,我暫且把 normal mode 和 insert mode 譯作指令和輸入模式。

在 command line 輸入 vi <file> 即表示要編輯 <file> 這個檔案(在餘下篇幅中的 vi 都可以由 vim 所取代)。


一進入 vi 後的模式為指令模式,按 i (insert) 或 a (append) 鍵來進入輸入模式,此時你可鍵入任何文字,輸入
完畢後即按 ESC 返回指令模式,並按入 :wq 來存檔並離開程式。

* 「指令」與「輸入模式」之切換:

* 存檔並離開程式:

你會發覺在輸入模式中是無法任意移動到文件的其他位置,而且亦只能用 Backscape 或 Delete 鍵刪除浮標


前的字元,若要執行這些動作則必需回到指令模式下進行。你只需記著 5 個鍵:k, j, h, l, x。前四個鍵用來作上
下左右的移動,x 用來刪除浮標下的字元。(因為 vi 由來已久,以前的 Unix 工作站的鍵盤未必有方向鍵的,
但近來的 vi 程式都可以用方向鍵來代替 hjkl,而且就連在輸入模式中也可以使用,確實方便了不少初學者過渡。
不過我絕不建議大家使用方向鍵,因為這樣會令你的輸入速度減慢─你必須把你的右手移到方向鍵所在的位置
嘛)

此文章以 Creative Commons 件授權証發表


(http://creativecommons.org/licenses/by/2.0/)
1/8 頁
此文章由 LinuxHall.org 發放 (www.linuxhall.org)

* 於指令模式移動:

* 刪去浮標上之字元:

已經學完 vi 了,就是這麼簡單。

vi 強勁的地方在於指令能組合使用使得變化多端,令編輯文件時的速度加快,試試這些指令吧:0, $, 9j, 9k,


5x, cw, d0, dw, G, yy100p,還有更多有趣的,不過就要你「自修」了,進入 vim 後輸入 :help,或看看有沒有安裝
vimtutor 程式吧!

Programmer 的 vim

vi 由當初一個簡單的編輯器慢慢發展,不斷加入新元素,不過在過程中亦無可避免地產生了一些由 vi 分支出
來的程式,例如 elvis, levee, nvi 及 vim 等,而 vim (Vi IMproved) 亦名副其實地成為最出色的 vi 繼承者。vim 的
開發工作一直也很熱鬧,新功能還不斷地在增加。好吧!現在我就假設你是個已經用了 vim 一段時間的用者,
知道甚麼是 .vimrc 和 vim 的 option,以下我就會讓你目睹 vim 用在編寫程式時的威力,好讓大家都能靈活運用
它。

1 自動對位

編寫程式時我們會做對位 (indentation) 的工作來使程式易於閱讀,vim 的 smartindent 選項能夠做自動對位,


省卻自己親手輸入 tab 或 space。這個選項適用於程式碼結構像 C 的語言,即是一個個 block 的那種,它能自
動將下一行對齊上一行。而且如果上一行的結尾是 '{',又或者是由 if, while, for 等保留字 (在 cinwords 選項中
定義)開始的話,那麼當你按 Enter 後,下一行便會自動向右移適當的空位。當你輸入 '}' 符號來關閉一個
block 時,vi 亦會尋找與它相對應的 '{' 與它對齊。若在一行中第一個輸入的字是 '#' 的話,那行會立刻被移至
最左,這是用來方便輸入 C preprocessor 的 directives,如 #ifdef。

cindent 選項比 smartindent 更先進更聰明﹐可改變的參數亦較多,因此亦複雜得多。如果你對 indentation


的要求不太高的話大可忘記 cindent 的存在,我建議你先試試 smartindent 能不能滿足你,不夠用時才看
cindent 也不遲。

cindent 的表現主要由 cinwords, cinoptions 及 cinkeys 三個參數操控。它們有自己本身的預設值,我認為已

此文章以 Creative Commons 件授權証發表


(http://creativecommons.org/licenses/by/2.0/)
2/8 頁
此文章由 LinuxHall.org 發放 (www.linuxhall.org)

經定得不錯的了,所以你也可以不用改動它們而直接使用 cindent。

‧cinwords 上面已經提過了,用途是當上一行的第一個字是 cinwords 中的其中一個時,下面那行便會加入


indentation。預設值是 "if,else,while,do,for,switch"。你可以因應使用的程式語言修改 cinwords,譬如加入
begin。

‧cinoptions 控制 vim 怎樣做 indentation,例如一個 block 內的句子右移多少(或左移多少,不過向左移就


怪怪的),'{' 和 '}' 符號做多少 indentation 等等。cinoptions 中的每個項目分兩部份,第一部份是有待控制
的 indentation 種類,然後是用於 indentation 時的空格數目。例如 ">" 代表一般的 indent,因此 ">4" 表示在
一般的情況下的 indent 是 4 個空格。

舉多一個例子:假如 cinoptions 中有一個項目為 ":0.5s",當中的 ':' 代表 switch() block 內的 case


label,":0.5s" 就解作那些 case label 的 indent 是右移 0.5 個 s,而 s 是 shiftwidth 選項中定的值之代碼。如果
shiftwidth 的值是 4,我們將得到以下的結果(留意預設值是 1s):

+-----------+ +----------------+
|cinoptions=| |cinoptions=:0.5s|
+-----------+ +----------------+
switch (x) switch(x)
{ {
case 1: case 1:
a = b; a = b;
default: default:
} }

cinoptions 總共有 19 個參數,太多了,因此未能盡錄它們的功用,help file 裡有詳盡的介紹。

‧cinkeys 定義當甚麼鍵被按下時 vim 便會執行自動對位的動作,任何鍵也可以用於 cinkeys 的。例如把 ';' 字


元加到 cinkeys,就表示每逢 ';' 鍵被按下,那一行便會立刻被對齊。特別的字元,例如方向鍵等,要用 <> 括
著,如 <Up>。而 control 字元則用 ^ 表示,如 ^F。

cinkeys 的預設值是 "0{,0},:,0#,!^F,o,O,e",其中的 'o', 'O' 和 'e' 有特別的意思的。'o' 和 'O' 表示每當你在指令


模式下按 'o' 和 'O' 來開新行時,浮標會走到對位適當的位置;而 'e' 字元則表示當你鍵入 'else' 的最後一個字
母 e 時那行便會對齊。

另外,在字元前可以加上一個特別字元來改變它的意義:'0' 表示跟著的那個字元必需為那行的第一個字,例
如 '0#' 表示如果 '#' 是在行中第一個被輸入的字,那行便會被對齊;'!' 表示隨後跟著的那個字元不會被輸入到
文件裡的,例如 '!^F' 代表 Control-F 不會被加進文件裡。

'=' 也是個很有用的功能鍵,它是用於需要強制對位的時候。按 '==' 就會把浮標所在的那行對齊。'=' 和其他 vi

此文章以 Creative Commons 件授權証發表


(http://creativecommons.org/licenses/by/2.0/)
3/8 頁
此文章由 LinuxHall.org 發放 (www.linuxhall.org)

功能一樣,能配合其它鍵一起使用,例如 '=k', '=j', '=gg', '=G',以及用於 visual mode(v, V 和 Control-v)。另


外,'>' 和 '<' 則可用來把一整行作左移及右移,用法與 '=' 大同小異,如 '<<' 及 '>>'。

2 顏色顯示

有了顏色顯示 (syntax highlighting) 編寫程式時也會倍感方便,程式碼更易閱讀和容易偵錯。vim 5.7 為 215


種檔案類型提供顏色顯示,包括 C, Java, PHP, HTML, Perl, shell scripts, assembly 等常用語言,以至於 xpm,
gnuplot, fvwm 設定檔等也支援,可說是應有盡有,相信已經相當夠用的了。
開啟顏色顯示非常簡單,只需在指令模式下鍵入

:syntax on

或把它寫到 ~/.vimrc 裡,這樣便不需要每次使用 vim 時都用人手 set 了。但當然啦,如果你是在 xterm 中開


啟 vim,請務必確定 xterm 能夠顯示彩色,不過現在幾乎所有 distribution 的 xterm 都支援彩色的了。如果你
像我一樣習慣了把 xterm 的背景顏色 set 做黑色的話,那便最好亦把 background 選項設為 dark,這樣做顏色
會較好看,否則把它設做 light,記緊把 background 設定好才 turn on syntax:

:set background=dark
:syntax on

vim 是從檔案名字的 extension 及檔案中的第一行,如 #!/bin/sh 來決定檔案為何種類型和使用哪種 syntax。


但假若 extension 未能反映檔案的種類時,你便需要用人手去強制設定它。舉個例子,很多人也喜歡把 include
入 PHP 檔的檔案檔尾設成 .inc,如 header.inc。當你一開啟 header.inc 後便心知不妙,因為顏色怪怪的,vim
一定是用了別的 syntax 檔來顯示顏色了。你可以在指令模式下輸入

:set syntax

來查詢 vim 正在使用哪種 syntax。啊,原來是用了 povray 檔的顏色,怪不得了。在當 vim 無法辨認此文件為


PHP 檔時,你便需要在指令模式下輸入

:set syntax=php3

來逼使 vim 使用 php3 的 syntax,之後顏色也立刻變得正常了。你亦可把它用 modeline 的方式寫到檔案裡,例


如:

<?php
// vim: filetype=php3
?>

那麼便不需每次都設定 syntax 的值了。

此文章以 Creative Commons 件授權証發表


(http://creativecommons.org/licenses/by/2.0/)
4/8 頁
此文章由 LinuxHall.org 發放 (www.linuxhall.org)

3 與編譯器整合

很多時候我們會開啟兩個視窗,一個用來編輯程式碼,一個用來編譯程式並觀看來自編譯器的錯誤訊息,但
這個做法太過不方便了。我們當然可以在 vim 裡用 shell escape 的方法去編譯程式而不用脫離 vim,但這樣做又
不便於回顧編譯時的錯誤訊息。vim 配備兩個極為有用的選項:makeprg 和 errorformat,它們的功用是把程式
編譯的步驟整合到 vim。vim 更會把編譯時的錯誤和警告記下,之後你便可逐個錯誤檢查,vim 能夠跳至發生錯
誤的那一行,方便你立刻作出修改。

‧makeprg 選項的功能是定義編譯所用的指令,makeprg 的預設值為 make。當你在指令模式輸入 :make 後,


makeprg 代表的指令就會被執行。例如你可以設定 makeprg 為

:set makeprg=make\ %<

當中的 %< 為正在編輯中的檔案名字除卻檔尾部份,並留意空格必需用 \ 號來 escape。假設現在正編輯


wap.c,當鍵入 :make 後,vim 就會立刻執行 make wap。編譯完成後更會跳至出現第一個錯誤的那一行。

‧vim 未必懂得如何處理由 makeprg 指令發出的訊息,因為用戶可以任意定義 makeprg 的。此時我們需要用


errorformat 來教導 vim 閱讀由 makeprg 所發出的訊息。errorformat 的格式很像 scanf,例如:

:set errorformat=%f:%l:\ %m

其中的 %f, %l 及 %m 分別解作檔案名稱,行數及錯誤訊式,errorformat 還接受很多其它的參數。以上的


errorformat 參數值能捕捉到 gcc 發出的一般錯誤訊式,如:

wap.c:16: parse error before `}'

之後 vim 便會懂得錯誤是發生在哪個檔案,檔案的哪一行,和相對應的訊息。vim 的 errorformat 預設值


(可用 :set efm 觀看)較適合與 GNU make 配合使用,若要與其他編譯器配合使用,請看 help file 的 quickfix
部份。

如果編譯完成後沒有任何錯誤,就真是可喜可賀!不過通常都沒有那麼完美的,編譯完畢後我們的首要任務
便是檢查錯誤的出處,然後除錯。用於觀察各個錯誤的指令有下列幾個:

‧:cl:列引所有錯誤,包括錯誤代碼、發生錯誤的檔案、行號和錯誤訊息。例如:

2 wap.c:934: `fp' undeclared (first use in this function)

第一個數字為錯誤代號,可以用於 :cc 指令。

此文章以 Creative Commons 件授權証發表


(http://creativecommons.org/licenses/by/2.0/)
5/8 頁
此文章由 LinuxHall.org 發放 (www.linuxhall.org)

‧:cn:跳到下一個錯誤。
‧:cp:跳到上一個錯誤。
‧:cc:跳回最近觀察過的那個錯誤,如果在 cc 後附有數字,則會跳到該錯誤。
‧:cr:跳回第一個錯誤。
‧:cla:跳到最後的錯誤。

4 穿梭於檔案之間

Project 愈大,所涉及的檔案數量愈多,而程式碼之間千絲萬縷的關係就愈複雜,使得工作困難起來,因為經
常於程式檔案間反複查閱變數和函數的定義實在是非常煩嫌的。

vim 能配合 ctags 程式一起使用,令到來回翻查程式碼的步驟簡化。ctags 並非 vim 的一部份,它能為所有程


式碼製作一個參考檔,名字通常叫 tags,它記載著程式碼中曾出現過的變數、函式、macro、enum、struct、
typedef、class 等關鍵字被定義時所處的位置。ctags 的用法非常簡單,一般都是用 "ctags *" 就可以了,之
後 ctags 就會產生一個名為 tags 的檔案,vim 起動時便會匯入它,之後你便可以用以下介紹的方法去翻查那些
關鍵字。ctags 程式也又好幾種,與 vim 一起發布的 ctags 程式為 Exuberant ctags,它支援的語言包括 C, C++,
Eiffel, Fortran 和 Java。

以下是檢索用的指令:

‧Control-]:vim 會檢索浮標上的關鍵字,並跳到該關鍵字被定義時的位置。當你想查出某個函式或 struct


的內容時就非常方便。

‧Control-T:差不多是 Ctrl-] 相反,當用了 Ctrl-] 後你可以用 Ctrl-t 來返回之前的位置。

‧:tag:除了使用 Ctrl-] 來查找關鍵字定義的位置外,你亦可使用 ':tag <keyword>',這樣便不需要把浮標移


到關鍵字之上亦能去到它的所在位置。

‧:tags:有否在檢查程式碼時不停地從一個檔案跳到另一個檔案,最終還是被搞到頭昏的經驗?特別是在看
別人的程式的時候啊!不過如果你是用 Ctrl-] 或 :tag 檢查關鍵字的話,你便可以用 :tags 來翻查 tag stack,
回顧自己曾到過的檔案,多方便!

除了使用 ctags 之外,還有幾個在編寫 C/C++ 程式都很有用的搜尋指令,它們能夠一起搜尋被 #include 在程


式碼中的檔案,通常是 header 檔,這對於搜尋 macro 及 struct 的定義尤其有用。程式語言不是 C 或 C++ 也可
以用這些指令,但需要修改一下有關選項,例如 'include' 和 'define' 選項。

‧[D:列出浮標上的 macro 定義的所有位置。例如在 NULL 這個字上按 '[D' 便得到以下的結果:

此文章以 Creative Commons 件授權証發表


(http://creativecommons.org/licenses/by/2.0/)
6/8 頁
此文章由 LinuxHall.org 發放 (www.linuxhall.org)

/usr/include/libio.h
1: 90 # define NULL (__null)
2: 93 # define NULL ((void*)0)
3: 95 # define NULL (0)

意思為 NULL 在 /usr/include/libio.h 裡有三處地方被定義過,分別是第 90 行,第 93 行和第 95 行。

‧[ Control-D:跳到浮標上的 macro 第一次被定義的地方,沿用上例則是跳到 /usr/include/libio.h 的第 90 行。


你亦可以在指令前加上一個數字,代表選擇跳到第幾個定義,例如按 '3[Ctrl-D' 便會跳到第 95 行。可惜的是,
用了這個指令後,你便無法返回原來的檔案那裡去。所以不太好用,我建議大家用下面這個指令取代它。

‧Control-W d 或 Control-W Control-D:這個指令與 '[Ctrl-D' 類似,但 vim 會先開啟一個新「視窗」,才在那裡


讀入將要跳至的檔案,然後把浮標移到適當的位置。那個所謂的「視窗」並非一個如 X window 的東西,它是將
vim 的範圍上下一分為二,上面的範圍是新開啟的檔案,而下面的是原有的編輯區域。在同一時間你只可以控
制其中一個「視窗」,你可以用 'Ctrl-W j', 'Ctrl-W k', 'Ctrl-W Ctrl-W' 等鍵在各「視窗」之間走動,關閉「視窗」
則仍舊使用 ':q", 'q!' 等就可以了。而「視窗」的大小是利用 'Ctrl-W +', 'Ctrl-W -' 和 'Ctrl-W =' 來控制。'Ctrl-W d'
的好處在於能夠在檢視 macro 完畢後把視窗關掉,然後繼續在原本的檔案工作。像 '[Ctrl-D' 一樣,你可以在
'Ctrl-W d' 之前加入一個數字,用來代表要跳到第幾個定義。

‧[I:與 [D 很類似,但它不單只檢索 macro,而是被 include 的檔案的所有地方。例如在 'struct dirent' 的


'dirent' 上按 '[I' 便得到以下結果:

/usr/include/dirent.h
1: 20 * POSIX Standard: 5.1.2 Directory Operations <dirent.h>
2: 47 /* This file defines `struct dirent'.
/usr/include/bits/dirent.h
3: 20 # error "Never use <bits/dirent.h> directly; include <dirent.h> instea
d."
4: 23 struct dirent
/usr/include/dirent.h
5: 68 /* These macros extract size information from a `struct dirent *'.
6: 137 dirent' describing the entry, or NULL for EOF or error. The
7: 144 extern struct dirent *readdir __P ( (DIR *__dirp));

似乎第 4 點最像 struct dirent 被 declare 的地方,我們便可以用 '4Ctrl-W i' 或 '4[ Ctrl-I' 來檢視它。

‧[ Control-I:[ Control-D 的非 macro 版。


‧Control-W i 或 Control-W Control-I:Control-W d 的非 macro 版。

此文章以 Creative Commons 件授權証發表


(http://creativecommons.org/licenses/by/2.0/)
7/8 頁
此文章由 LinuxHall.org 發放 (www.linuxhall.org)

5 除此之外……

在我的 ~/.vimrc 檔中亦 set 了以下這些 option:

‧ruler
‧showmatch
‧shiftwidth=4
‧tabstop=4
‧expandtab
‧nocompatible

ruler 會把浮標的坐標印在右下方,方便自己知道現在身處第幾行。

showmatch 會在當你輸入 ')', ']' 及 '}' 時搜尋相對的 '(', '[' 和 '{',找到後便會在那字符上閃一下,有助你發現


相對的括號正不正確。除此之外,檢查括號對稱常用的方法是用 '%' 鍵。按 '%' 鍵能使浮標來回於開關的括號之
間,這樣能確保括號沒有被放錯位置,'%' 亦適用於 C 的 #if...#else...#endif preprocessor directive 。

shiftwidth 控制 indentation 用多少空格,有些人喜歡 8 格,有些人則喜歡 4 格或 2 格。

tabstop 控制 <Tab> 鍵所用的空格數目,我習慣了把它設定成與 shiftwidth 的一樣。

expandtab 會使 Tab 字符化成空格,因為我不喜歡用 Tab 來做 indent,當我把程式給別人看時,別人又未必


知道我的一個 Tab 代表多少個空格,所以我較喜歡用空格完全代替。不過寫 Makefile 時要小心,因為 Makefile
的某些地方必須用 Tab 字符作開頭的,那時便需要用 :set noet 來 unset expandtab。

最後的 nocompatible 是叫 vim 不要使用與 vi 兼容的模式。畢竟我們是在用 vim 嘛,與 vi 兼容的話很多新功能


就不能用的了,所以務必要 :set nocompatible。

最後還有個小 tips,就是 :set fileencoding=taiwan,用它來編輯 Big5 文件看看!

小結
----
希望今後大家用 vim 寫程式時都能更得心應手,如果你們有甚麼心得的話,不妨 email 我或 post 到 HKLUG 的
newsgroup 上和大家分享!

此文章以 Creative Commons 件授權証發表


(http://creativecommons.org/licenses/by/2.0/)
8/8 頁