2006 年 3 月 22 日 本系列前面兩部分講的都是如何使用現(xiàn)有的 Vim 系統(tǒng),本部分則會(huì)通過(guò)實(shí)例來(lái)講如何定制 Vim 的行為。良好的定制可以讓使用 Vim 變得更為得心應(yīng)手;同時(shí),在掌握了基本的定制之后,也許你就會(huì)想進(jìn)一步寫(xiě)一些自己的 Vim 腳本,從而真正地成為一個(gè) Vim 專家。 在 .vimrc 文件中,和在第二章提到的插件和語(yǔ)法文件中,使用的語(yǔ)言就是 Vim 腳本語(yǔ)言。這種腳本語(yǔ)言語(yǔ)法有點(diǎn)像 BASIC,表達(dá)式有點(diǎn)像 C,還是比較容易理解的。本章中并不打算對(duì)其作很系統(tǒng)的介紹(要完整了解的話,請(qǐng)參見(jiàn)“:help usr_41.txt”),而只是介紹一些基本知識(shí),特別是,了解定制 .vimrc 所需要的基本知識(shí)。 Vim 腳本相當(dāng)于可直接在命令模式下執(zhí)行的命令,只是不需要輸入前面的冒號(hào)(如果用了冒號(hào)也不會(huì)出錯(cuò))。因此,像設(shè)置選項(xiàng)、創(chuàng)建鍵盤映射這樣的命令是直接可用的。當(dāng)然,作為一種腳本語(yǔ)言,除了普通鍵盤上會(huì)輸入的命令外,我們還需要一些更復(fù)雜的功能,特別是:變量,表達(dá)式,條件和循環(huán)語(yǔ)句,函數(shù)。 Vim 中使用如下的語(yǔ)法對(duì)變量進(jìn)行賦值(創(chuàng)建變量):
變量類型有兩種,整數(shù)和字符串,在第一次賦值之前都不能使用。變量名除了可使用常規(guī)的字母、下劃線和數(shù)字外,還可以使用幾種特殊的前綴:
下面三個(gè)前綴用來(lái)訪問(wèn)特殊的數(shù)值,由于行為和變量較為相似(可以讀取和修改),也放在這兒一起講:
當(dāng)變量不再使用時(shí),可以使用“unlet 變量名”刪除變量。 和 C 非常類似,可以使用變量和常量,可以使用括號(hào),可以調(diào)用函數(shù)(“函數(shù)名(...)”),支持加法(“+”)、減法(“-”)、乘法(“*”)、除法(“/”)和取模(“%”),支持邏輯操作(“&&”、“||”和“!”),支持三元條件表達(dá)式(“a ? b : c”)。字符串操作方面當(dāng)然比 C 要強(qiáng),可以使用“.”進(jìn)行字符串拼接;可使用“==”、“<=”等進(jìn)行字符串大小比較,可使用“=~”和“!~”進(jìn)行正則表達(dá)式匹配,而且可以在比較操作符后面添加“#”或“?”來(lái)強(qiáng)制進(jìn)行大小寫(xiě)敏感或不敏感的比較(缺省受 Vim 選項(xiàng) ignorecase 影響)。顯示一個(gè)表達(dá)式的結(jié)果,可以使用“:echo 表達(dá)式”顯示到狀態(tài)欄上,或者在插入模式下使用“Ctrl-R=表達(dá)式”插入到緩沖區(qū)的文本中。 和其它很多在 Unix 下成長(zhǎng)起來(lái)的語(yǔ)言一樣,Vim 的字符串常量有雙引號(hào)和單引號(hào)兩種方式。使用單引號(hào)的話,單引號(hào)間的任何字符都是字符串的一部分,其中不能再包含單引號(hào)。使用雙引號(hào)的話,則可以使用“\”產(chǎn)生換碼序列(具體可參考“:help expr-quote”),如“\n”代表?yè)Q行符,“\"”代表雙引號(hào),“\\”代表反斜杠本身,等等。 需要注意的話,雙引號(hào)除了可以表示字符串常量外,還可以表示注釋。行首的“"”,以及表達(dá)式中出現(xiàn)的成單的“"”,都表示“"”后面的部分全部是注釋。 條件語(yǔ)句形式如下:
或
或
循環(huán)語(yǔ)句形式如下:
條件和循環(huán)語(yǔ)句都可以嵌套。這些比較簡(jiǎn)單,就不多加說(shuō)明了。 在表達(dá)式中使用函數(shù)時(shí),就跟 C 里面的方式類似,直接使用函數(shù)名加括號(hào),括號(hào)里寫(xiě)上參數(shù)(可選)。在不需要返回值的情況下調(diào)用函數(shù)時(shí),稍稍有些不同,要使用“call”命令,后面跟函數(shù)名和括號(hào)(括號(hào)里面寫(xiě)上可能有的參數(shù))。 定義函數(shù)使用下面的語(yǔ)法:
如果已有同名函數(shù)存在,Vim 會(huì)報(bào)錯(cuò),除非在“function”后面加上一個(gè)“!”。 如果參數(shù)中不包含“...”,那么參數(shù)的數(shù)量是固定的,函數(shù)的調(diào)用者必須提供跟定義同樣多的參數(shù)(在函數(shù)定義中使用參數(shù)名之前加上“a:”進(jìn)行訪問(wèn))。如果參數(shù)中包含“...”,那么參數(shù)的數(shù)量不固定,除了可以使用參數(shù)名稱訪問(wèn)傳遞過(guò)來(lái)的參數(shù)外,還可以使用“a:0”知道額外傳遞的參數(shù)數(shù)量,使用“a:1”、“a:2”等訪問(wèn)這些額外傳遞的參數(shù)。 要在函數(shù)的中間返回,或者要返回?cái)?shù)值的話,可以使用“return”語(yǔ)句。 Vim 內(nèi)部定義了一百多個(gè)函數(shù),詳細(xì)列表請(qǐng)參見(jiàn)“:help function-list”。
作為一個(gè) Vim 腳本的一個(gè)具體示例,我將講解一下最實(shí)用的情況,我的 .vimrc 文件。文件 .vimrc.html (請(qǐng)下載到本地打開(kāi)) 是我的 .vimrc 文件通過(guò)以下步驟生成的 HTML 文件: 1. 在 Vim 中打開(kāi) .vimrc 文件; 2. 執(zhí)行命令“:colorscheme koehler”(缺省配色可能在瀏覽器中效果不佳) 3. 執(zhí)行命令“:%!nl -w4 -s‘ ‘”(1.11 節(jié)) 4. 執(zhí)行命令“:TOhtml”(1.13 節(jié)) 5. 執(zhí)行命令“:w” 可以把瀏覽器中的文本內(nèi)容粘貼到 Vim 中,然后使用下面這個(gè)替換命令“:%s/^ \+[0-9]\+ //”刪除前面的行號(hào),來(lái)恢復(fù)出最初的 .vimrc 文件。 下面逐行進(jìn)行講解,并包含理解其內(nèi)容所需的資料的鏈接。建議大家直接閱讀 .vimrc 文件的內(nèi)容,并在有疑問(wèn)時(shí)查閱下面的解釋。 第 1 行:注釋(3.1.2 節(jié)末段),其中包含一個(gè)模式行(1.4 節(jié)和 1.5 節(jié))。 第 2 行:首先判斷系統(tǒng)是否具有“自動(dòng)命令”(autocmd)的支持,有的話才執(zhí)行第3到第六行的內(nèi)容(1.1 節(jié)、“:help has”和“:help feature-list”)。 第 3 行:純注釋(后面我將跳過(guò)注釋行不再說(shuō)明)。 第 4 行:清除所有的自動(dòng)命令(“:help autocmd-remove”),以方便調(diào)試,可以使用“source ~/.vimrc”查看一些修改后的效果(“:help source”)。 第 6 行:對(duì)于后綴為“.asm”的文件,認(rèn)為其是微軟的 Macro Assembler 格式(“:help masm-syntax”)。 第 7 行:與第 2 行的 if 語(yǔ)句配對(duì)。 第 8-10 行:當(dāng)使用了圖形界面時(shí)(“:help feature-list”),確保所有的文件類型會(huì)在菜單“語(yǔ)法”(“Syntax”)下出現(xiàn),而不是出現(xiàn)一個(gè)菜單項(xiàng)“Show filetypes in menu”。缺省行為可以讓 Vim 啟動(dòng)得更快一點(diǎn)點(diǎn)。 第 11-13 行:當(dāng)使用了圖形界面,并且環(huán)境變量 LANG 中不含“.”(即沒(méi)有規(guī)定編碼)時(shí),把 Vim 的內(nèi)部編碼設(shè)為 UTF-8。 第 14 行:不需要保持和 vi 非常兼容(“:help ‘compatible‘”)。 第 15 行:執(zhí)行 Vim 缺省提供的 .vimrc 文件的示例,包含了打開(kāi)語(yǔ)法加亮顯示等最常用的功能。 第 16 行:打開(kāi)自動(dòng)縮進(jìn)(1.4 節(jié))。 第 17 行:缺省不產(chǎn)生備份文件(“:help ‘backup‘”)。 第 18 行:在輸入括號(hào)時(shí)光標(biāo)會(huì)短暫地跳到與之相匹配的括號(hào)處,不影響輸入(“:help ‘showmatch‘”)。 第 19 行:正確地處理中文字符的折行和拼接(1.12 節(jié))。 第 20 行:可自動(dòng)識(shí)別的文件類型為帶 BOM 字符的 Unicode 文件、UTF-8 編碼的文件和 GBK 編碼的文件。 第 21 行:設(shè)置狀態(tài)行,使其能額外顯示文件的編碼信息,如圖 2 中的“gbk”和“big5”(“:help ‘statusline‘”)。 圖 2 ![]() 第 22-24 行:如果該 Vim 支持鼠標(biāo),則啟用鼠標(biāo)支持(1.3 節(jié))。 第 25-29 行:判斷 Vim 是否包含多字節(jié)語(yǔ)言支持(multi_byte 特性),并且版本號(hào)(“:help v:version”)大于 6.1(包含 ambiwidth 選項(xiàng))。 第 26-28 行:如果 Vim 的語(yǔ)言(“:help v:lang”;受環(huán)境變量 LANG 影響)是中文(zh)、日文(ja)或韓文(ko)的話,將模糊寬度的 Unicode 字符的寬度(ambiwidth選項(xiàng),1.2 節(jié))設(shè)為雙寬度(double)。 第 31-36 行:改變上、下方向鍵行為方式:通常情況下這些鍵的作用范圍是邏輯行,所以如果行很長(zhǎng)的話光標(biāo)的移動(dòng)可能會(huì)不太方便;這些鍵盤映射把這些鍵的作用范圍改成屏幕行(“help gk”),還為習(xí)慣使用“j”、“k”的人增加了映射“Ctrl-j”和“Ctrl-k”作用于屏幕行。前面四個(gè)映射使用的命令是“noremap”,作用于正常模式、可視模式和命令執(zhí)行時(shí);后面兩個(gè)映射使用的命令是“inoremap”,僅作用于插入模式,其中使用“Ctrl-O”臨時(shí)執(zhí)行一個(gè)普通模式的命令(“:help i_CTRL-O”)。 第 38-41 行:在 Vim 中的插入模式中可以使用“Ctrl-R =”計(jì)算整數(shù)表達(dá)式的數(shù)值,但 Vim 本身沒(méi)有計(jì)算浮點(diǎn)表達(dá)式的能力。這四個(gè)映射提供了浮點(diǎn)表達(dá)式的計(jì)算能力:使用“\ma”(假設(shè) Leader 字符為缺省的“\”,參見(jiàn)“:help <Leader>”)可將計(jì)算的結(jié)果放到下一行上(待計(jì)算的表達(dá)式為當(dāng)前行或在可視模式選中的內(nèi)容),使用“\mr”則用計(jì)算的結(jié)果替換待計(jì)算的表達(dá)式(同樣為當(dāng)前行或在可視模式選中的內(nèi)容)。這些映射假設(shè)有一個(gè)命令“calcu”可用來(lái)計(jì)算一個(gè)表達(dá)式的內(nèi)容。該命令可用下面的 shell 腳本簡(jiǎn)單實(shí)現(xiàn):
該腳本把表達(dá)式轉(zhuǎn)換成 bc [1] 能接受的形式(把“sin(x)”轉(zhuǎn)換成“s(x)”,等等),并通過(guò)標(biāo)準(zhǔn)輸出送到 bc 的標(biāo)準(zhǔn)輸入。 該映射較為復(fù)雜,此處不詳加解釋了——其中心思想都是選取待計(jì)算的表達(dá)式,放到無(wú)名寄存器中,然后使用“Ctrl-R"”粘貼到命令行上,使用 calcu 進(jìn)行計(jì)算,再把結(jié)果粘貼回正在編輯的緩沖區(qū)中;最后一個(gè)最復(fù)雜,因?yàn)闉榱颂鎿Q原先的表達(dá)式,還需要記住原先被選中的內(nèi)容的起始和結(jié)束位置,你可能希望看一下“:help gv”、“:help v_o”、“:help m”、“:help `”,并復(fù)習(xí)節(jié) 1.11。可以注意一下,在映射中使用了“<silent>”(“:help map-<silent>”),這會(huì)防止命令行上回顯執(zhí)行的內(nèi)容。 第 43-44 行:允許用戶使用 F2 來(lái)取消搜索/替換的加亮顯示。此處一個(gè)映射用于正常模式(nmap),一個(gè)用于插入模式(imap)。上面已經(jīng)提過(guò)一次,“Ctrl-O”可以在插入模式中執(zhí)行一個(gè)正常模式的命令。 第 46-47 行:這兩個(gè)映射用于 taglist 插件,使用 F9 直接打開(kāi)(或關(guān)閉)taglist 的窗口。 第 49-50 行:方便快速修訂窗口(1.10 節(jié))的使用,可使用 F11(和 F12)查看下一個(gè)(上一個(gè))錯(cuò)誤(或 grep 項(xiàng)等)。 第 52-65 行:一些適用于文本模式運(yùn)行的 Vim 的設(shè)定;詳見(jiàn)下面的具體說(shuō)明。 第 54-56 行:將變量 Tlist_Inc_Winwidth 的值設(shè)為 0,防止 taglist 插件改變終端窗口的大?。ㄓ行┣闆r下會(huì)引起系統(tǒng)不穩(wěn)定)。使用“has(‘eval‘)”是讓該語(yǔ)句僅在功能較為完整、至少支持表達(dá)式的 Vim 版本中運(yùn)行。 第 58-64 行:在系統(tǒng)支持 wildmenu 特性(“:help ‘wildmenu‘”)啟用文本模式的菜單。 第 59 行:打開(kāi) wildmenu 選項(xiàng),啟動(dòng)具有菜單項(xiàng)提示的命令行自動(dòng)完成。 第 60 行:確保字符序列“<C-Z>”被理解為 Ctrl-Z 而不是分開(kāi)的五個(gè)字符(“:help ‘cpoptions‘”)。 第 61 行:設(shè)置使用 Ctrl-Z 激活自動(dòng)完成提示。 第 62-63 行:把正常模式和插入模式下的 F10 映射成執(zhí)行菜單項(xiàng),并自動(dòng)提示菜單內(nèi)容。注意缺省菜單仍不會(huì)自動(dòng)載入,我使用該特性的主要目的是在文本模式的 Vim 中使用 CVS 菜單。圖 16 是按 F10 鍵后再按 Tab 鍵的結(jié)果。 圖 16 ![]() 第 66-161 行:使用自動(dòng)命令(autocmd)特性的設(shè)置。使用“has”來(lái)防止該部分內(nèi)容在不支持自動(dòng)命令的 Vim 版本中運(yùn)行。 第 67-129 行:定義了若干個(gè)下面的自動(dòng)命令會(huì)用到的函數(shù),具體在下面的自動(dòng)命令中講。請(qǐng)注意在每個(gè)“function”之后都用了一個(gè)“!”(“:help E122”):這也是為了方便調(diào)試,讓“source ~/.vimrc”能正確運(yùn)行而不會(huì)報(bào)告函數(shù)已定義的錯(cuò)誤。 第 131-133 行:只要沒(méi)有將環(huán)境變量 VIM_HATE_SPACE_ERRORS 的值設(shè)為零,則把變量 c_space_errors 的值設(shè)為 1——效果是在 C/C++ 代碼中“不正確”的空白字符(行尾的空白字符和緊接在制表符之前的空格字符)將會(huì)被高亮顯示。圖 17所示的代碼中,第 3 行的行尾多了兩個(gè)空格,第 5 行的第一個(gè)制表符之前多了個(gè)空格。Vim 提示#935 里有一些額外的說(shuō)明。同時(shí)請(qǐng)參看對(duì)第 160 行的說(shuō)明。 圖 17 ![]() 第 135 行:使用的英文拼寫(xiě)變體為加拿大風(fēng)格,即:使用拼寫(xiě)“abridgement”(而不是“abridgment”)、“colour”(而不是“color”)、“realize”(而不是“realise”)、“theatre”(而不是“theater”)等,比較符合中國(guó)人一般的英語(yǔ)教科書(shū)中的拼寫(xiě)方式,也比較適合于寫(xiě)“國(guó)際”英語(yǔ)。 第 138 行:使用鍵盤映射“\a”來(lái)查看光標(biāo)下字符的屬性,主要用于調(diào)試 Vim 的語(yǔ)法文件。圖 18顯示了光標(biāo)下的字符所屬的語(yǔ)法“組”為 vimOption,使用配色方案中的 PreProc(預(yù)處理符號(hào))項(xiàng),前景色為紫色(RGB:#a020f0)。有興趣可查看 Vim 腳本#383 的具體內(nèi)容。 圖 18 ![]() 第 140 行:在函數(shù)找不到時(shí)(“:help FuncUndefined”),自動(dòng)在運(yùn)行環(huán)境(Linux 下一般為 ~/.vim)的 autoload 目錄下讀入與函數(shù)名同名的 .vim 文件。這是腳本#383 的建議安裝方式(SyntaxAttr.vim 文件放在 autoload 目錄下,僅在執(zhí)行時(shí)載入)。 第 142 行:設(shè)置適用于 C/C++ 文件的選項(xiàng)(1.4 節(jié))。 第 143 行:把補(bǔ)丁文件的縮進(jìn)和制表符寬度設(shè)定設(shè)成和 C/C++ 文件相同(1.4 節(jié))。 第 144 行:取消 Vim 對(duì) HTML 標(biāo)記自動(dòng)產(chǎn)生的縮進(jìn),但打開(kāi)自動(dòng)縮進(jìn)選項(xiàng)(1.4 節(jié))。 第 145 行:對(duì)于變更日志類型的文件,設(shè)置行寬為 76 個(gè)字符(1.12 節(jié))。 第 147 行:當(dāng)文件后綴為“.gb”時(shí),認(rèn)為這是一個(gè) GBK 編碼的文件,在讀入文件之前(“:help BufReadPre”)調(diào)用函數(shù) SetFileEncodings 把原先的 fileencodings 選項(xiàng)的內(nèi)容保存在本緩沖區(qū)的一個(gè)變量中(3.1.1 節(jié)),然后把 fileencodings 設(shè)成 gbk,即只嘗試對(duì)文件內(nèi)容作為 GBK 字符序列來(lái)解釋。 第 148 行:類似于上面把“.big5”后綴的文件當(dāng)作 Big5 編碼的文件,在讀入文件之前把 fileencodings 設(shè)成 big5,只嘗試對(duì)文件內(nèi)容作為 Big5 字符序列來(lái)解釋。 第 149 行:類似于上面把“.nfo”后綴的文件當(dāng)作 CP437 編碼(即英文 DOS 的 OEM 字符集編碼)的文件。效果可參看圖 19。 圖 19 ![]() 第 150 行:在讀入 .gb、.big5 或 .nfo 文件之后(“:help BufReadPost”),調(diào)用函數(shù) RestoreFileEncodings 恢復(fù)保存起來(lái)的 fileencodings 原數(shù)值。 第 151 行:對(duì)于 .txt 后綴的文件,在顯示文件時(shí)(“:help BufWinEnter”,確保在模式行被執(zhí)行之后)調(diào)用函數(shù) CheckFileEncoding 檢查文件是否已修改并且 fileencoding 設(shè)有數(shù)值。條件滿足的話說(shuō)明該文件在模式行中修改了 fileencoding,因而使用該編碼(“:help ++enc”)重新強(qiáng)制(“!”)讀入該文件以保證文件被正確解碼。Vim 提示#911 里有一些額外的說(shuō)明。 第 153 行:在遇到 HTML 文件時(shí),如果 Vim 判斷出的編碼類型和 HTML 代碼中使用“<meta http-equiv="Content-Type" content="text/html; charset=編碼">”規(guī)定的編碼不一致,將使用網(wǎng)頁(yè)中規(guī)定的編碼重新讀入該文件。函數(shù) ConvertHtmlEncoding 會(huì)把一些網(wǎng)頁(yè)中使用的編碼名稱轉(zhuǎn)換成 Vim 能夠正確處理的編碼名稱;函數(shù) DetectHtmlEncoding 在判斷文件類型確實(shí)是 HTML 之后,會(huì)記下當(dāng)前的光標(biāo)位置,并搜索上面這樣的 HTML 代碼行,找出字符集編碼后,在編碼不等于當(dāng)前文件編碼(fileencoding)時(shí)且當(dāng)前文件編碼為空或等于系統(tǒng)判斷出的文件編碼時(shí),使用該編碼強(qiáng)制重新讀入文件,忽略任何錯(cuò)誤(“silent!”)。該自動(dòng)命令寫(xiě)成是可嵌套執(zhí)行的(“:help autocmd-nested”),目的是保證語(yǔ)法高亮顯示有效,且上次打開(kāi)文件的光標(biāo)位置能夠正確保持。Vim 提示#1074 里有一些額外的說(shuō)明。 第 155-156 行:確保把 /usr/include/c++ 和 /usr/include/g++-3 目錄下的所有文件都當(dāng)成 C++ 類型的文件,不管 Vim 原先認(rèn)定這些文件類型是什么(“:help BufEnter”)。C++ 的很多標(biāo)準(zhǔn)頭文件(如“algorithm”)沒(méi)有文件后綴,缺省情況下不會(huì)被 Vim 當(dāng)作 C++ 文件。 第 158 行:第 142 行把 C/C++ 文件的制表符寬度設(shè)成了 4(個(gè)人設(shè)置),但系統(tǒng)的源代碼一般使用 GNU 編碼規(guī)范,制表符寬度為 8。該行設(shè)置所有 /usr 目錄下的文件都使用 GNU 編碼規(guī)范(1.4 節(jié))。 第 160 行:在寫(xiě)文件之前(“:help BufWritePre”),調(diào)用函數(shù) RemoveTrailingSpace:只要沒(méi)有將環(huán)境變量 VIM_HATE_SPACE_ERRORS 的值設(shè)為零,則對(duì)于文件類型為 C、C++、Vim 腳本類型的文件,自動(dòng)悄悄清除所有的行尾空白字符;“normal m`”記憶當(dāng)前的光標(biāo)位置,“normal ``”恢復(fù)記憶下來(lái)的光標(biāo)位置。 至此為此,我已經(jīng)介紹了 Vim 的基本知識(shí)、很多實(shí)用技巧和一些最常用的 Vim 插件,并通過(guò)定制 .vimrc 文件介紹了腳本的基本知識(shí)。如果有需要進(jìn)一步深入學(xué)習(xí) Vim 或是想提什么關(guān)于 Vim 的特定問(wèn)題的話,不妨參加從 Vim 的網(wǎng)站上參加 Vim 的郵件討論列表,應(yīng)該會(huì)獲益良多。而作者也希望本文至此也已經(jīng)完成了引導(dǎo)讀者學(xué)習(xí)、了解 Vim 的高級(jí)特性的任務(wù)。
[1] bc: http://www.answers.com/bc%20(Unix)
|
聯(lián)系客服