Vim,一個開放原始碼的文字編輯器

來源:互聯網
上載者:User

標籤:

Vim,http://linux.21ds.net/2002/03/13/0268dc26fd9c725c23dae68d797935f3/

作者:Bram Moolenaar
翻譯:slimzhao

開放源碼的自由軟體-VIM的主創者在本文中講述了 vim的開發內幕和發展方向. 同時提出慈善軟體的概念並解釋了Bram為何將這一概念用之於vim. 本文也深入程式內部的函數和資料結構, 作者講述了vim這一複雜的程式是如何工作的, 並且討論了在vim最新版6.0中的新功能.

介紹
不太可能你還從來沒有聽 說過vim吧. 它作為標準的Vi編輯器存在於Linux的幾乎每一種發行版裡. 在很多系統裡如FreeBSD和Sun Solaris 8它作為一個便於安裝的獨立軟體包, 在其它系統(MS-Windows, Macintosh, OS/2等等)上的原始碼和已編譯好的可執行程式在網際網路上很多地方都可以下載

Vim是一個類似於Vi的文字編輯器, 不過在Vi的基礎上增加了很多新的特性, Vim普遍被推崇為類Vi編輯器中最好的一個, 事實上真正的勁敵來自Emacs的不同變體. 1999 年Emacs被選為Linuxworld文本編輯分類的優勝者, Vim屈居第二, 但在2000年2月 Vim贏得了Slashdot Beanie的最佳開放原始碼文字編輯器大獎, 又將Emacs推至二線, 總的來看, Vim和Emacs同樣都是非常優秀的文字編輯器.

Vim(和Vi)一個最大的優勢在於, 它最常用的命令都是簡單的字元, 這比起使用複雜的控制按鍵組合要快得多, 而且也解放了手指的大量工作, 學習使用這些命令的時間很快就能從由此帶給你的高效率中得到回報. 另外, 與Vi不同, Vim也支援在插入模式下使用上下方向鍵, 這使初學者可以很容易上手.

與其它的類Vi編輯器相比, Vim擁有眾多的特性: 對180多種語言的文法高亮功能, 對C語言的自動縮排, 以及一個功能強大的內建指令碼語言. 對每個人來說, Vim總有讓他們喜歡的東西.

Vim的開發仍然在繼續進行, 寫作本文時, Vim6.0版的工作已經於9月份(2001年 )完成了, 在這之後, vim的主要目標是更穩定, 更實用, 而不是再去增加更多的新功能了, 完善已有功能和修改bug的工作已經夠人忙的了. 比起Vim, 原始版的Vi自1985年並沒有多大的變化.

是GTK版Vim的一個快照, 圖片上顯示正在編輯的C原始碼, 代碼的不同部分以相應的文法高亮顯示, 被尋找的字元模式”msg_didany”以黃色的背景高亮顯示. 圖上還顯示了一個可視化的選擇(灰色背景), 綠色的閃爍游標反白這一模式. 在底行”– VISUAL –”表明當前所處的工作模式, 右邊是當前游標在檔案中的位置資訊.

Vim曆史
很久以前我 自己有一台Amiga電腦. 因為我過去一直使用Vi, 所以我想找一個 Amiga版的類似Vi的編輯器. 我找到一些叫”複製”的版本. 但沒一個中意的. 所以我就挑了其中最好的一個來改進它. 最初的目標是達到所有Vi的功能. 後來我逐漸增加了一些Vi所沒有的額外功能, 如多層復原.

當它看起來已經運行得不錯時, 我發布了一個Vim(意思是Vi IMitation[譯註] Vi的仿造品)放在由Fred Fish搞的一個公用磁碟上. 接下來開始有人發給了我一些補丁, 還有一些人努力把Vim移植到其它平台上, 如MS-DOS和Unix. 我又增加了更多的一些功能讓它看起來更好用. 此時, Vim可以名正言順地叫”Vi IMProved”( [譯註]增加版Vi)了. 後來整個原始碼被重新設計並且擴充了很多, 幾乎再也找不到原始的”複製”版原始碼的痕迹了.

起初 我在Vim所作的工作完全是為了方便自己. 後來我意識到Vim同樣可以方便他人, 於是我就把它公之於眾. 從此我就不斷地致力於讓它成為一個更好的工具以服務於它的大量使用者. 創造一個對他人有用的東西是很有趣的. 同時, 還有很多優秀的合作小組和使用者鼓舞著我的工作.

下面是Vim的發展簡曆:

————————————————————————-
1991 Nov 2 - Vim 1.14: 第一版 (在Fred Fish的第591號磁碟上).
1992 - Vim 1.22: 移植到Unix上. Vim開始挑戰Vi
1994 Aug 12 - Vim 3.0: 支援多視窗多緩衝區
1996 May 29 - Vim 4.0: 支援GUI(圖形化使用者介面) (主要是Robert Webb的努力).
1998 Feb 19 - Vim 5.0: 顏色支援和文法高亮
2001 Sep 26 - Vim 6.0: 折行, 外掛程式, 垂直視窗分隔
————————————————————————-

Vim中的IMproved(增強)到底意味著什麼?
1.22版的Vim比之Vi已經多了很多功能. 我決定把”Vi IMitation”改為”Vi IMproved”. 隨著時間的推移兩者之間的差距越來越大, 現在, 幾乎沒有理由去使用Vi而不是Vim. 我建議所有的Vi使用者轉而使用Vim. 下面是其大量優點的部分摘錄:

- 對文本行的長度沒有限制, 允許ASCII碼為0的字元; 可以編輯任意類型的檔案
, 包括二進位檔案.
- 多級的撤消和重做功能: 當你的轉換大小寫鍵盤燈不慎燈亮起來時, 無須擔
心誤操作會毀了你的檔案.
- 多視窗和多緩衝區: 同時可以編輯多個檔案, 並且可以在它們之間複製內容.
- 文法高亮: 讓你快速把握整個文檔的結構, 迅速發現錯誤所在.
- 命令列記錄和命令補全: 更正打字失誤, 快速重複曆史命令, 快速鍵入
長檔名.
- 可以刪除和添加矩形的文本地區: 用於編輯表格
- 錯誤資訊解析: 在Vim中運行編譯器並且能根據編譯產生的錯誤資訊迅速定位
到原始碼中發生錯誤的地方([譯註]對程式員尤其有用)
- 帶有超連結([譯註]不同於WEB瀏覽器中的連結, 但功能類似)的線上協助: 讓
你快速找到關於任何命令的詳細協助, 閱讀相關說明主題.
- 一個功能強大的指令碼語言: 可以增加你自己的擴充功能.

Vim的開發
幾年的辛苦工作之後Vim6.0版終於完成了, 核心的工作由我親自操刀, 另外有很多人也加入了工作, 有時他們新加一項功能然後發給我一個補丁, 以便我把他們的新功能加進去, 但是多數時間這些發來的補丁還要做一些整合的工作, 很少有人能去把握Vim的各個部分是如何協同工作的. 因為整個代碼實在是太複雜了. 比如說, 有人發過來一個跨行搜尋字元模式的補丁. 這表明這一功能可以如何?以及原始碼中哪個地方需要做哪些改動. 不幸的是補丁的作者使用指標操作每個文本行, 他並沒有意識到這些指標可能是非法的. 我必需總覽整個改過的新代碼來改正它. 雖然融合這些補丁很麻煩, 但這確實能幫我把握正確的方向.

使用者們在Vim郵件清單上的問題往往能指明哪些是最經常碰到的問 題. 有時也有人直接發補丁給我, 或者要求增加一些新功能. 我認為這種與使用者的互動和合作開發是vim的主要優勢. 使用者與開發人員能直接地開誠布公地交流. 這也正是開源軟體比之商業軟體更好的原因.

發行
Vim可以自由發行, 不過, 也有少許的限制. 下面一段文字是與Vim一起發行的:

————————————————————————-
摘要
Vim是福利軟體. 你可以自由使用和複製, 但是我們希望你能關注一下烏幹達的
孤兒. 參見下面的|iccf|節.
如果你將Vim在CD-ROM中發行, 我很高興能收到一份. 只是想知道世界上又有人
在這樣發行Vim(也可以向我的朋友們展示:-)).
本文
對Vim不加修改的複製和發行都沒有限制, Vim也可以部分發發行, 但是這段文字
必需包括在內. 你也可以在發行版中包括你自己從一個未被修改的原始碼中編譯
的可執行檔, 或者你自己的使用例子, 指令碼.
如果你要發行一個修改過的Vim版本, 我們(維護者)希望收到一份你的包括原始碼
的修訂版. 或者讓維護者可以通過ftp來下載; 如果所作的改動很小,(比如只是
更改了Makefile) 僅僅把不同之處EMAIL給當前維護者也可以, 但無論如何如果維
護者要求的話, 你必需提供原始碼給他.
Vim的維護者保留將這些改進包括到Vim正式版中的權利. 不過仍有商量餘地, 但
不允許在你不準備把你的原始碼提供給維護人員的情況下發行你的修訂版Vim.
當前的維護者是Bram Moolenaar([email protected]), 如有變動, 會在適當的地方公開聲明(主要是
如www.vim.org 網站上或comp.editors這樣的新聞群組). 如果實在沒法聯絡維護者,
當然你也就不再有義務把你的修訂版原始碼發給他.
不允許從Vim原始碼發行版或部分發行版中刪除本文, 本文的聲明也適用於Vim
的前期版本以代替當時的版本聲明.
————————————————————————-

我更希望給使用者在使用原始碼方面更大的自由, 之所以加上如上的限制是為了避免Elvis(vi的一個複製版)過去遭遇過的事情: 有人弄到了Elvis原始碼, 加了一些諸如Windows的GUI特性的東西, 然後開始拿去賣錢. 修改之後的Elvis不公開源碼而且整個軟體的核心代碼仍然是最初的Elvis, 這實在太不公平了. 不光是因為有人拿他人的勞動去賺錢, 更重要的是他不公開源碼, 從而拒絕別人對該軟體進行進一步的改進. 正因為這個我才加了如上限制以保證別人對原始碼做的修改必需要讓我知道. 雖然這些限制仍留有餘地, 比如某公司做了一個Vim的修訂版然後跟我協商能否不把他們所做出的修改公之於眾, 但畢竟讓我保留了對自己創造的軟體的決定權.

為何不用GNU的GPL(Gnu公用許可證)?
GNU的公用許可證有更嚴 格的限制, 雖然它聲明保護軟體自由, 但實際上限制了你所能做出的改變. 你可以做出修改, 但如果你要發行修訂版的話你必需將你的所有改動公之於眾, 這樣人們就失去了自己持有他所修改過的原始碼的權利, 我想這實際上限制了你的自由. 另一方面, 如果允許任何人都可以任意修改然後作為自己的私藏, 甚至他並未作出修改就從Vim的某部分受益, 也並不公平, 所以我決定加上這一條件: 所有對原始碼的修改必需讓我知道. 我可據此決定這些改動中哪些確實是對人們普遍有益的, 從而把它添加到Vim中. 而哪些只擁有少量的使用者群, 所以允許某個公司從中賺點錢, 畢竟, 如果一個軟體的原始碼是公開的話, 很難再去要使用者為此付錢.

同時我也並不認為所有的軟體都應該是免費的或是都應該是開源項目, 我所知道的所有為開源軟體工作的人也都為商業軟體工作謀生, 或者是全職工作為其工作, 或者是正準備找, 如果沒有商業軟體,那這些軟體業的人又能如何謀生呢? 我認為自由軟體, 開源軟體和商業軟體將會共存, 很多商業軟體不可能公開原始碼, 因為這樣公司就會失去競爭優勢. 軟體原始碼的創造代價昂貴, 所以商業軟體公司可不想別人能坐享其成. 由於軟體的專利和著作權保護力實際上是很微弱的, 不予公開在很多情況下仍然是最好的保護措施. 一個不幸的後果就是, 你無法從中學習這些商業軟體是如何?的, 也無法自己去修改你花錢買來的軟體裡面的BUG, 解決之道可以是公開一個軟體的大部分原始碼, 但保留其中核心的部分.

開源軟體的前景
什麼軟體會是開源項目而什麼軟體又不會是開源項目? 這一問題無從回答, 對於某一個應用領域, 是否有人願意為此花費大量心力去創造並維護一個軟體, 很大程度上依賴於一個個人的動機因素: 又沒錢得, 又要花費自己的大量勞動. 這一點無法控制, 而且後果莫測. 不久以前人們還認為只有小的軟體項目才可能是開放原始碼的, 而大型的軟體項目由於項目周期和資金方面的問題必需由一個商業軟體公司來承擔, 但Linux的發展打破了這想法, 而且Linux並不是個別的例外, 還有不少其它的大型的開源軟體項目也都成功了, 如KDE.

現實中我發現多數人只會對自己要使用的軟體感興趣. 這正是Vim當時的情況: 我每天都用它. 這幾乎成了一種制約, 因為越來越多的人開始學寫程式. 這確實限制了致力於某個專用程式的人數, 理論上可以按如下公式去計算有多少人可能為開源軟體工作:

人數 = 感興趣的人數 x 能力 x 動機

其中:

人數 為某個開源項目工作的人數
感興趣的人數 有興趣使用該程式的人數
能力 這其中有能力寫程式的人所佔的百分比
動機 某個人有志於從事這個項目的比率

值得注意的是, 有興趣使用某個程式的人數也依賴於已有程式的可用性, 如果沒有足夠好的可替代程式或者太貴了, 人數就會升高, 如果已經有一個又便宜又好的程式. 人數就會下降.

不是說隨便一個人都能寫軟體, 而且寫不同的程式所需要的技巧差別也很大. 如果目標程式是軟體工程方面的, 可能就有很多人投身其中, 這其中不乏確實有足夠的編程能力的人, 如果程式是處理稀有鳥類的資料, 可能感興趣的人數就很少.

整個公式中動機是一個最難以把握的因素, 在有能力寫程式的人中又有多少人願意去寫? 這一問題聽起來也蠻有趣, 看看是否這個因素也是可以估算出來的.

對這一公式的修正要引入另一因素:有多少人願意為了別人來寫程式. 尤其是為傷殘人士. 不過, 我想人數很少.

假設人數最終還是被算出來了, 剩下的一個大問題就是: 事實上真的會有人這麼做嗎. 或者情況好一點: 就算會, 什麼時間? 只要有足夠的時間, 我相信每個程式都需要有人去做. 應該有一個公式也可以計算出某個程式今年是否會被寫出來. 這個就留給讀者們去考慮吧…

總的來說, 這個公式中有太多的不確定因素, 可以說難以預測將來會有多少開源軟體.

福利軟體
由於Vim是一個開源軟體並且免費發布, 所以使用者不需要為使用它而付錢. 即使這樣, 還是有一些經常使用Vim的人表示他們想通過某種方式褒獎一下我為此作出的工作. 我自己其實不需要額外的錢財, 並且我也不喜歡人們因為使用一個自由軟體而付錢給我. 當時我開始考慮”福利軟體”的概念. 主要意思是使用Vim的人要向福利事業捐贈. 這樣使用Vim還是免費的, 但是如果你認為值的話, 可以為一個更好的原因付錢.

我怎麼會選擇了福利? 是這樣的, 我曾經作為一個志願者為一個項目在烏幹達南部工作過一年. 這一地區被愛滋病嚴重侵害. 估計有10%到30%的成人感染有HIV. 很多身為父母的人都死了, 留下他們的孩子孤苦伶仃. 這個項目通過幾種方式協助這些嗷嗷待哺的孩子. 為他們找一個新家, 保證他們可以上學, 或給予他們醫學方面的關心, 等等.

從烏幹達回來之後, 我仍然心裡想著那裡. 我決定至少我可以通過給他們一些錢來繼續支援這個項目. 與Vim關聯起來是一件順理成章的事. 所以現在我要求Vim的使用者們考慮為烏幹達的孤兒們捐款. 你可以在財政上收養一個孩子, 這會使他得到長期穩定的協助, 對孩子來說這是最好的. 由於我們只是跟志願者們一直工作並且錢是直接送到該項目去的, 所以絕大多數錢確實能真正用於烏幹達.

你可以在http://iccf-holland.org找到更多的相關資訊.

Nabasagi Morine是荷蘭ICCF贊助的孩子之一.

福利軟體真的行得通嗎?
通過福利軟體, 我確實收到不少給烏幹達孤兒的捐贈品. 情況不定, 有時一個月也沒有一次, 有時一次就有好幾筆捐贈. 一些數目很少, 也有的數目很大. 我說不準具體的數目, 因為不是所有的捐贈都是經我之手, 也搞不清楚到底是不是因為Vim. 大概在1997年共收到2000美元, 1998年有4000美元. 相當多的一部分來自德國.

不光是錢的問題, 福利軟體也讓人知道了其他人的需要. 如果我不這樣做, 就不會有那麼多人知道烏幹達的這個項目, 對很多人來說很難想象生命中除了掙錢和關心自己還有其它很多有價值的事情. 我也接觸到一些人, 他們無力捐贈, 但是被這一概念深深觸動. 在某些方面福利軟體確實能改變人.

福利軟體也適用於其它項目嗎?
檢查以下幾點以確定它是否適用於你的項目:

你自己不需要錢. 如果你把錢用在比鄰居更大的汽車上, 它不適合你.
你正在做一個共用軟體, 但所獲不多, 因為人們不願把錢給你.
你目前提供的軟體完全免費, 但你認為它值得人們為它付出點什麼.
何時你會決定做福利軟體, 你會使用什麼理由? 最好的一個是通過這個你可以親自接觸一些人. 使用者會更好地理解你的動機, 另外, 找一個你可以信任的需要錢和人們注意的組織. 我不主張支援大的組織, 尤其是他們本來就有路子聯絡到贊助者的組織.

福利軟體的變體
我不強制Vim的使用者向烏幹達的項目捐贈. 也沒有數目上的限定, 最大的自由留給使用者自己, 他也可以看完烏幹達愛滋病的事然後就忘了它.

另一種做法, 使用者被告知他們必需要捐贈, 並且還要指定一個最小限額. 聲明必需放在一個不可能被使用者忽略的地方. 但願這一做法會帶來更多的捐贈, 但是注意不要把使用者給惹火了. 你必需設定一個最小限額, 否則這種強制將毫無意義. 不過確定這個最小限額到底是多少又是一個難題.

另一個更具強制性的做法是限制一些或全部的功能, 直到收到使用者的捐贈. 這不是一個好辦法, 因為這不合乎仁道; 這樣窮人就不能用你的程式了!

Vim內幕
Vim已變為一個很大的項目. 這裡是一個關於程式主要部分和原始碼分布的概覽. 我把每個檔案(針對5.6版)的大小也統計進去, 以便你知道程式的各個部分到底需要多少的代碼量

程式啟動代碼, 主要的命令迴圈 main.c 43538
普通命令模式的命令處理 normal.c 140320
執行命令列命令 ops.c 109742
在螢幕上顯示文本 screen.c 165136
多視窗處理 window.c 52168
多緩衝區處理 buffer.c 63743
命令預修飾符,鍵映射和縮寫 getchar.c 76964
終端輸入輸出 term.c 109869
檔案讀寫 fileio.c 122685
分頁檔管理 memfile.c 31516
緩衝行管理 memline.c 114193
撤消與重做 undo.c 29014
插入模式 edit.c 142302
保留檔案中的標記 mark.c 25582
命令列編輯 ex_getln.c 89509
執行Ex命令 ex_docmd.c 176724
組合Ex命令 ex_cmds.c 100549
快速改錯命令 quickfix.c 31760
Regex匹配 regexp.c 73771
搜尋命令 search.c 93721
標記命令 tag.c 69337
選項設定 option.c 148884
vim指令碼命令,運算式求值 eval.c 122346
與具體系統相關的代碼 os_*.c up to 94640
通用的GUI代碼 gui.c 73468
Motif的GUI代碼 gui_motif.c 26963
Athena的GUI代碼 gui_athena.c 25220
一般的Motif和Athena GUI 代碼 gui_x11.c 62913
GTK的GUI代碼 gui_gtk*.c 158289
MS-Windows的GUI代碼 gui_w32.c 141809
Macintosh的GUI代碼 gui_mac.c 82600
菜單處理 menu.c 36164
Perl介面 if_perl.c 21246
Python介面 if_python.c 51814
Sniff介面 if_sniff.c 25035

每個檔案一般都很大, 因為我喜歡把功能上相關的程式段放在一起, 也可以使用多個的小檔案, 對於一個版本控制系統這樣做尤其適用, 但對我來說目前這樣就蠻好, 似乎沒理由把它改成其它樣子. 所有的檔案都放在同一個目錄下, 這樣做最大的好處就是一個簡單的grep命令都可以方便地找出某個標識符來.

上面的列表中有幾處值得注意. “撤消”功能的代碼量相對較少, 主要是因為它使用了一種`well thought-out’機制, 這種機制簡潔有力. 另一方面, 你也可以看到GUI相關的檔案一般都很大, 為GUI編碼確實費勁.

Michael Godfrey教授就Vim的體繫結構和開發做了一些研究, 你可以在 http://plg.uwaterloo.ca/~migod/找到相關論述.([譯註]我沒找到, 如果你找到了, 歡迎給我一個準確的URL)

下面是一些你可能感興趣的特殊問題.

可移植性
Vim被設計成可以工作於不同的作業系統上, 做到這一點並不輕鬆. 僅僅是支援多數主流的Unix版本工作量已經相當大. 增加對MS-DOS和MS-Windows的支援又帶來了些另外的問題, 如在檔案名稱中使用反斜線. 象Amiga和Macintosh這樣的機器都有自己特有的一套作業系統, 對它們需要另搞一套辦法.

一個首要的選擇是在Vim的整個代碼中使用著名的也是”老”的K&R C. 這使得代碼可以在幾乎任何一種系統上編譯. 使用ANSI C外加一個前置處理器也可收到同樣效果. 比如, 象下面這樣的函數原型:

void ml_open_file __ARGS((BUF *buf));

對非ANSI標準的編譯器”__ARGS(())”部分被處理為”()”. 這些原型都是由 cproto程式產生. 使用工具自動產生不光是省去了手工鍵入這些原型, 同時也避免了錯誤的可能. 使用__ARGS結構帶來一些額外的工作, 不過它可以使Vim可以在一些古舊的系統上編譯, 而現代的編譯器又可以檢查原型, 以避免很多的麻煩.

為了讓Vim得以在不 同的系統上順利編譯, 對每種系統都有一個系統相關的檔案, 如”os_unix.c”或”os_amiga.c”. 這些檔案包含了很多系統相關的代碼. 但是, 這並不解決所有問題, 在一般檔案裡還有大量的”#ifdef”這樣的先行編譯器控制選項來解決與不同編譯器, 不同作業系統, 不同體繫結構相關的問題. 有時我會清除這些東西以避免代碼的混亂. 這就要在每個系統相關檔案中另建一個如”mch_xxx()”這樣的函數 , 從每個一般源檔案裡調用它. 因為我不可能擁有所有這些不同的系統, 所以還得靠合作的開發人員們去測試這些改動.

對Unix來說”autoconf”命令可 以用於檢驗一個系統中各個不同的東西. 它會找到每個被”include”的標頭檔在哪裡, 庫檔案在哪裡等等. 它使用一個叫 “configure.in”的描述檔案來列出哪些東西需要”探測”. autoconf命令處理這個檔案並產生一個叫”configure”的SHELL指令碼, autoconf會照顧到位讓這個指令碼可以在各個Unix裡都能吃得開. 雖然使用autoconf並不容易, 我還是向大家推薦它. 如果你要使自己的程式在各個不同的Unix上都能四通八達, autoconf正是管這事的.

對使用者來說標準的做法是運行” configure”命令, 它會產生一個名為”Makefile” 的檔案. 對Vim來說, 情況有所不同, 因為在此之前, 已經有了一個”Makefile”, 第一次運行”make”時, 它會自動為你調用”configure”, 使用預設的參數, 這導致產生一個叫”config.mk”的檔案, 該檔案被包含在Makefile中, 這種略有不同的方式的一個主要好處是Makefile檔案可以包含configure參數的一些例子, 你可以注釋掉這些參數. 另外你也可以通過運行”configure –help”找到所有這些可用參數, 檢查這一命令的輸出, 找到你要小心對付的參數, 對Vim來說, 你可以手工編輯Makefile檔案, 讀它的注釋說明, 或者刪除或者插入一些”#”字元, 這很好做. 傳統的運行configure也可以, 所以這一額外的辦法並沒有帶來負作用.

Storing text
文本儲存
一個文字編輯器的主要工作就是讀取一個檔案, 修改, 然後寫這個檔案. 文本要以何種形式儲存在編輯器內部要考慮以下幾個因素:

- 讀寫檔案要快.
- 對檔案大小, 字元集, 文本行的長度沒有限制.
- 處理檔案的代碼不能太多.
- 允許撤消誤操作.
- 系統崩潰時, 必需能恢複未被儲存的改動.

沒有省事的辦法能輕而易舉地滿足所有這些要求. 首先, 不可能把所有文本都放在記憶體裡, 一些系統根本沒多少記憶體, 對另一些系統而言使用大量記憶體是非常低效的, 這正是我使用一個分頁檔的原因. 檔案存取的操作很慢, 所以必需有一些文本是通過一種緩衝機制放在記憶體裡的. 由於使用固定大小的幾K位元組的資料區塊時檔案I/O操作很快, 所以最好不要把單個的文本行寫回到分頁檔中. 考慮幾種替代方案後, 我決定使用一種結構來存放一定數量的文本行在資料區塊中. 這些資料區塊通過一種類似BSD系統上”db”函數的方式操縱, 但是根據Vim使用文本的方式進行了一番剪裁.

可以在檔案” memfile.c”中發現處理存放文本行的資料區塊的代碼. 它通過維護一個緩衝區來減少磁碟I/O的負擔. 工作原理跟所有的緩衝系統一樣: 資料區塊使用太多記憶體時就寫向分頁檔, 或者是儲存使用者所作出的修改. 這也使得系統意外宕機時可以從分頁檔中恢複未儲存的內容.

將文本行打包成資料區塊的代碼在”memline.c”檔案中. 因為資料區塊大小是固定的, 所以能放入的文本行的行數也各不相同. 這需要不少代碼來處理, 如何能夠插入, 刪除和修改文本行. 複雜性主要來自這樣一種情況: 插入一個文本行時, 容納它的資料區塊放不下, 這導致這個資料區塊必需被一分為二. 此外還有一種特殊情況: 一個文本行長到一整個資料區塊連它一行都容它不下. 這就需要建立一個非常規的足夠大的資料區塊來存放這一行的內容. 這就做就可以對文本行的長度不加限制, 同時又不影響處理短的文本行的效率.

存放在分頁檔中的資料區塊是沒有特定順序的, 如果要排序的話, 那麼往中間插入一個資料區塊就要求它後面的所有資料區塊都要往後推移, 這太慢了. 為了通過行號找到一個文本行, 需要使用資料區塊索引. 資料區塊索引包含了一個資料區塊中所含文本行行號的列表, 如果檔案太大, 這個列表可能在一個單個的資料區塊裡放不下. 也需要分成多個資料區塊, 問題來了, 這就需要另有一個索引資料區塊來”索引”這些因資料增長而必需分而置之的資料區塊.([譯註]這有點象C語言中指標的指標). 這組成了一個索引塊的平衡樹, 文字區塊是葉子節點. 實踐證明這種結構穩定而高效.

文法高亮
分析一個檔案的結構本身已 經夠複雜了. 另外還要考慮這種解析可能要以一個檔案中的任意位置為開始, 兩個問題一起來, 必死無疑. 幸運的是Vim的設計把整個任務分為兩部分: 核心的文法引擎, 用C語言實現, 通過字元模式指定不同語言自己的文法項. 這使Vim無需重新編譯即可支援一種新的文法.

基本思路是用一個Regex來指定一種語言的一個文法項. Vim試圖在每行中匹配這個模式. 如果匹配到了, 就會相應的醒目提示反白匹配到的字串. 對雩都在一行中的文法項這很容易實現. 但對跨行的文法項來說情況就複雜多了, 在Vim中你需要指定一個字元模式來規定一個文法項如何開始又怎麼樣才算結束. 比如, 對於C語言中的注釋, 發乎”/*”而止於”*/”. 但Vim碰到一個半截的注釋行時會怎麼樣呢? 此行中沒有”/*”, 這樣它就無以確定它是否是一個注釋了. 這需要Vim回頭做一些搜尋以確定當前行是否是一個注釋的一部分. 這叫同步.

另一種結構是嵌套. 當你想以另一種不同於注釋的顏色定義顯示包含於注釋中的”TODO”時就碰到了這種情況. 所以在注釋文法項中又有一種”TODO”文法項的子項. 還有一種, 如果在一個字串中發現了”/*”字串, 顯然它不標誌著一個注釋的開頭, 這就讓字串文法項要多個心眼, 防著其中的”/*”或”*/”混淆視聽. 所有這些情況歸之為”可包含的文法項”, 對每個文法項使用者可以指定它可以再包含哪些子文法項. 這一處理適用於大多數語言的結構.

處理文法項的代碼維護一個嵌套文法項的棧. 對於檔案中的任何位置, Vim會去尋找可以包含在當前文法項中的子文法項, 所以這個文法項的棧也代表著文法高亮引擎的目前狀態. 為了加速文法高亮的處理, 這個狀態被儲存下來並在需要顯示同一行內容時被重用. 文本行的內容發行改變時儲存的文法狀態也因之失效. 一行的改變也可能引起它後續行的文法狀態隨之改變, 比如: 在一行中插入一個”/*”來注釋掉部分代碼時, 直到找到匹配的”*/”, 所有後來的文本都被作為注釋處理. 程式知道位於”*/”之後的文法狀態與此前的舊的狀態一樣, 所以此後的所有文法狀態仍然有效, 對文法狀態的重新洗牌就止於”*/”.

資料結構
Vim使用不同的資料結構來儲存不同的內容. 折衷考慮通用的資料結構和針對具體問題量體裁衣的特殊資料結構. 比如, 一個字串可以被儲存在棧中一個固定大小的字元數組中, 也可以放在一個動態分配的固定大小的記憶體區, 或動態分配的大小不定的記憶體區, 具體用哪一種取決於要儲存的字串的類型.

對於一個已知其長度的上限的字串, 使用棧中一個字元數組就無需進行 malloc()和free()的調用. 這通常用來處理檔案名稱, 但Vim為避免對使用者的限制. 其它的字串可以是任意長度, 所以對於其它的字串長度未定的情況棧並不適用.
分配固定大小的記憶體區可用之於不經常發生變動的記憶體需求. 比如, 用於 選項值, 這不會浪費什麼記憶體, 而malloc()/free()帶來的開銷也不成問題.
對 於經常改變的字串, 調用malloc()/free()會花費很多時間. 對於那 些字串和列表的尺寸要經常增加的情況尤其如此, 在Vim中一種叫”結構化 的增長數組”的動態記憶體分配結構專門對付這種情況. 記憶體配置的步長很大, 所以可以無需進行重新分配即可容納更大的資料.
在Vim中建立這些資料結構真要了我的命. 缺點是這太花時間, 也增加了複雜性, 維護起來也更加困難. 優點是帶給Vim更高的效率. 對Emacs來說, 使用者最大的抱怨就是說它太慢了, 太耗資源了. 我的印象是Emacs使用了常規的資料結構並且寄希望於速度更快的硬體. 在我看來我的選擇是正確的. 一個文字編輯器是要被很多人每天都用的, 值得讓作者花些時間和精力來讓它更加高效.

特性列表
1998年11月在Vim使用者中展開了一項調查以投票決定Vim要作出哪些改變. 調查結果最好地說明了使用者最想要的東西是什麼, 下面是熱門排行榜上的前6名:

1. 折行功能(只顯示一段文本中被選出來的部分文本行)
2. 垂直分隔視窗
3. 增加對很多語言的可配置的自動縮排
4. 修改大大小小的BUGS, 讓它更健壯.
5. 增加與PERL相容的搜尋模式
6. 跨行搜尋模式字元

前3個和第6個已經在Vim6.0中實現了, 對BUG的修改跟往常一樣, 從來就沒停過! 對模式字元的搜尋已經被擴充為包容了PERL的所有相關特性, 但是並不是直接與Perl相容的寫法, 因為這會帶來Vim的向後不相容性.

折行
最先加到Vim6.0中的特性是折行. 這是一種隱藏部分文本行的機制, 得以讓整個文本的結構凸現出來. 看起來就象折起來的紙. 這個改動很大. 也影響了其它的代碼 - 還不知道使用者會對不同的檔案如何使用這些功能. 我花了很多時間研究如何讓使用折行的使用者介面更加友好, 不同的使用者有不同的需求. 我設定了如下幾種折行模式:

手工 手工選擇哪些行要摺疊起來.
根據縮排 文本行的縮排作為哪些行要摺疊的依據, 比如, 所有縮排量超
過8個空格的行要摺疊.
根據運算式 就象根據縮排一樣, 但是依據是一個運算式.
文法 文法高亮的項決定摺疊地區
標記 用標記來指示文本中從哪裡到哪裡要摺疊起來.

很多折行方法都不要求改變檔案. 這對於那些只需要查看檔案內容的人來說是必要的, 或者是那些與別人共用檔案的人. 但這些方法不允許任意指定一個摺疊應該如何開始如何結束. 所以增加了一種”標記”方法. 這種方法允許你在希望開始摺疊的地方插入一個標記, 比如, 如果你在函數定義之前有一些注釋行, 就可以把注釋標記放在這些注釋行的上面, 以使它們與函數屬於同一個摺疊, 也可以指定一些額外的文字來說明摺疊的內容, 比如”local declarations” 或者是函數名附帶一個簡短的說明. 這種折行定義的缺點是檔案本身因這些插入的標記而被修改.

這幅圖展現了使用了折行功能的C原始碼. ‘foldlevel’選項被設定為對每個函數顯示一行. 這些折行也被定義了標記, 象”{{{1″, 附帶著對被摺疊內容的簡述. 函數”lineFolded()”的摺疊被開啟以查看該函數.

多行搜尋模式字元
如前所述, 我增加了跨行搜尋模式字元的功能, 這項新功能在文檔中的描述很簡短, 因為很容易說明白, 就是一個新的模式可以匹配到一個行尾. 但這項功能對整個原始碼的影響很大, 主要是問題是一個緩衝區中最少的底限是保留一行內容在記憶體中, 開始另一行時, 前一行所佔的空間可能已被釋放以騰出供其它行使用. 這限制了記憶體的使用. 但調用模式比對的代碼總是假設它的指標都是可用的. 但現在它要取出緩衝中的其它行以檢查一個模式比對是否符合, 所以它的假設並非總是成立的, 解決的辦法是使用行號和列的位置來代替使用指標.

我把跨行模式比對的補丁加入代碼中只花了一天, 但由此引發的改動卻花了足足一星期. 很容易低估它的影響. 真正的結果只有在實現它時和對新功能進行測試時才會明朗起來. 如果這種事發生在商業軟體的開發中你就會錯過最後交貨期, 預算也變得緊張, 總之麻煩大了, 所幸, 對於自由軟體的開發這並不是頂頂重要的, 你不過要多花一些時間, 軟體發布延遲一些, 所以通常不值得因此引起計劃延遲. 這是自由軟體的巨大優勢.

UTF-8
另一個加到6.0版中的是國際化支援. Vim已經支援雙位元組的檔案. 這叫做多位元組支援, 不過實際上對很多雙位元組它並不能正常工作, 因為大家都在轉向Unicode, 那才是我加入的東西. 曆史上Vim在內部使用位元組, 所以自然而然地使用UTF-8編碼(Unicode字元集編碼作為一個8位位元組的序列). 另一個辦法是使用寬字元, 但這需要改變所有的文本處理代碼, 因為大多數字元都還是7位的ASCII 碼, 所以這樣可能會帶來效率問題.

增加對UTF -8的支援時需要確定幾件事:是Vim處理的文本都以UTF-8編碼, 還是說使用一個選項在ASCII和UTF-8甚至雙位元組之間切換? 把所有東西都按UTF-8處理有一個好處, 就是代碼很簡單, 因為它只需要處理一種類型的編碼. 但是鍵入字元, 顯示文本和與其它應用程式之間的複製粘貼卻要使用另一種編碼, 這需要在UTF-8和其它規範之間互相轉換, 這可能會引起overhead並且使文本發生改變. 後者是絕對不可接受並要竭力避免的. 如果你用Vim讀入一個檔案並且在沒有作任何修改的情況下寫迴文件, 這應該讓你得到完全一樣的一個檔案才對.

所以主要問題在於檔案讀入Vim時是要把它轉換為UTF-8編碼還是保持其原來的編碼方式:

在內部以UTF-8儲存所有文本.
讀入檔案時, 把它轉為UTF-8, 寫迴文件時轉換為檔案原來的模式, 鍵入的文字也需要轉換, 如果IME不支援UTF-8格式, 或是螢幕顯示使用另一種格式, 就也需要轉換.

保持檔案原來的編碼方式.
鍵入的文字和送到螢幕的字元只在他們使用另一種格式時才需要轉換. 所以處理文本的函數都必需知道所有可能的格式. 在一個視窗中複製文本到另一個使用不同編碼格式的視窗中時也需要轉換.

兩種方法都各有利弊. 一番思索之後, 我決定使用兩者的混合. 內部格式可以選擇, 或是ASCII, UTF-8, 或者是雙位元組編碼甚至是其它的什麼編碼方式. 一般來說這應該與環境相符合(當前的locale設定). 可能的話, 檔案格式和內部表現格式之間要發生轉換. 當轉換不可能時(不可用或有非法字元)就跳過去. 這可能導致錯誤地顯示文本, 但至少文本寫回去時不致於發生錯誤

結論
Vim已經成長為一個大的開源項目. 它不光是一個有用的流行軟體, 同時也是一個開源軟體開發的成功典範. 我希望Vim能啟發和協助其它的開源軟體作者.

如果你想使用Vim, 可以在很多地方找到它: 如果你裝有Linux, FreeBSD 或Solaris 8的發行版, 找一找一個你可以安裝的Vim軟體包, 安裝很容易, 對 Linux來說Vi命令會啟動一個Vim的簡裝版, 如果你想使用更多的特性就要自己去安裝Vim軟體包.

關於如何下載Vim, 參看下面的URL: http://vim.sf.net/download.php.

關於Vim的大量資訊可以從http://www.vim.org獲得. 很幸運 Sven Guckes在維護這個網站, 使我可以把精力放在Vim的開發上. Vim的技巧提示和外掛程式可以在http://vim.sf.net上找到. 這些內容都由Vim的使用者們創造, 上傳.

如果你有什麼問題, BUG報告, 或是想協助Vim的開發, 可以加入Vim的郵件清單. 可參考http;//www.vim.org/mail.html

Bram Moolenaar

——————————————————————————–

Bram Moolenaar在Delft大學研究電子工程, 在1985年畢業時卻在搞Unix雙處理器結構, 現在他主要作軟體方面的工作, 不過他還記得怎麼用電烙鐵, 他生於荷蘭. 一個盛開鬱金香的地方, 現在他生活在荷蘭東部的Venlo, 他為Oce工作多年, 設計公司的第一個數字圖象複印機. 他目前從事自由職業, 花了很多時間在免費的開源軟體項目Vim(http://www.vim.org/)上, 他是Vim的主要作者. 做這項工作要處理大量的郵件, 與使用者和其它合作開發人員聯絡, 修改BUG和增加新的功能. Bram建立了荷蘭的ICCF基金會. 以支援烏幹達的兒童救治中心(Kibaale Children’s Centre), 這個項目通過教育上, 醫學上協助愛滋病受害者, 他的首頁在http://www.moolenaar.net/, email地址是: %[email protected]

Vim,一個開放原始碼的文字編輯器(轉)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.