Vim 實用技術,第 3 部分: 定製 Vim

3.1. Vim 腳本基礎

在 .vimrc 文件中,和在第二章提到的插件和語法文件中,使用的語言就是 Vim 腳本語言。這種腳本語言語法有點像 BASIC,表達式有點像 C,還是比較容易理解的。本章中並不打算對其作很系統的介紹(要完整了解的話,請參見“:help usr_41.txt”),而只是介紹一些基本知識,特別是,瞭解定製 .vimrc 所需要的基本知識。

Vim 腳本相當於可直接在命令模式下執行的命令,只是不需要輸入前面的冒號(如果用了冒號也不會出錯)。因此,像設置選項、創建鍵盤映射這樣的命令是直接可用的。當然,作爲一種腳本語言,除了普通鍵盤上會輸入的命令外,我們還需要一些更復雜的功能,特別是:變量,表達式,條件和循環語句,函數。

3.1.1. 變量

Vim 中使用如下的語法對變量進行賦值(創建變量):

let 變量名 = 數值

變量類型有兩種,整數和字符串,在第一次賦值之前都不能使用。變量名除了可使用常規的字母、下劃線和數字外,還可以使用幾種特殊的前綴:

  • “b:”——只對當前緩衝區(buffer)有效的變量;
  • “w:”——只對當前編輯窗口(window)有效的變量。
  • “g:”——全局變量(在函數中訪問全局變量必須使用該前綴,不加前綴的話則認爲是函數內的局部變量);
  • “s:”——變量名只在當前腳本中有效;
  • “a:”——函數的參數;
  • “v:”——Vim 內部預定義的特殊變量(參見“:help vim-variable”)。

下面三個前綴用來訪問特殊的數值,由於行爲和變量較爲相似(可以讀取和修改),也放在這兒一起講:

  • “$”——訪問環境變量;
  • “&”——訪問 Vim 選項;
  • “@”——訪問寄存器。

當變量不再使用時,可以使用“unlet 變量名”刪除變量。

3.1.2. 表達式

和 C 非常類似,可以使用變量和常量,可以使用括號,可以調用函數(“函數名(...)”),支持加法(“+”)、減法(“-”)、乘法(“*”)、除法(“/”)和取模(“%”),支持邏輯操作(“&&”、“||”和“!”),支持三元條件表達式(“a ? b : c”)。字符串操作方面當然比 C 要強,可以使用“.”進行字符串拼接;可使用“==”、“<=”等進行字符串大小比較,可使用“=~”和“!~”進行正則表達式匹配,而且可以在比較操作符後面添加“#”或“?”來強制進行大小寫敏感或不敏感的比較(缺省受 Vim 選項 ignorecase 影響)。顯示一個表達式的結果,可以使用“:echo 表達式”顯示到狀態欄上,或者在插入模式下使用“Ctrl-R=表達式”插入到緩衝區的文本中。

和其它很多在 Unix 下成長起來的語言一樣,Vim 的字符串常量有雙引號和單引號兩種方式。使用單引號的話,單引號間的任何字符都是字符串的一部分,其中不能再包含單引號。使用雙引號的話,則可以使用“\”產生換碼序列(具體可參考“:help expr-quote”),如“\n”代表換行符,“\"”代表雙引號,“\\”代表反斜槓本身,等等。

需要注意的話,雙引號除了可以表示字符串常量外,還可以表示註釋。行首的“"”,以及表達式中出現的成單的“"”,都表示“"”後面的部分全部是註釋。

3.1.3. 條件和循環語句

條件語句形式如下:


if 表達式
  語句
endif


if 表達式
  語句
else
  語句
endif


if 表達式
  語句
elseif 表達式
  語句
endif

循環語句形式如下:


while 表達式
  語句
endwhile

條件和循環語句都可以嵌套。這些比較簡單,就不多加說明了。

3.1.4. 函數

在表達式中使用函數時,就跟 C 裏面的方式類似,直接使用函數名加括號,括號裏寫上參數(可選)。在不需要返回值的情況下調用函數時,稍稍有些不同,要使用“call”命令,後面跟函數名和括號(括號裏面寫上可能有的參數)。

定義函數使用下面的語法:


function 函數名稱(參數列表)
  語句
endfunction

如果已有同名函數存在,Vim 會報錯,除非在“function”後面加上一個“!”。

如果參數中不包含“...”,那麼參數的數量是固定的,函數的調用者必須提供跟定義同樣多的參數(在函數定義中使用參數名之前加上“a:”進行訪問)。如果參數中包含“...”,那麼參數的數量不固定,除了可以使用參數名稱訪問傳遞過來的參數外,還可以使用“a:0”知道額外傳遞的參數數量,使用“a:1”、“a:2”等訪問這些額外傳遞的參數。

要在函數的中間返回,或者要返回數值的話,可以使用“return”語句。

Vim 內部定義了一百多個函數,詳細列表請參見“:help function-list”。

3.2. 我的 .vimrc

作爲一個 Vim 腳本的一個具體示例,我將講解一下最實用的情況,我的 .vimrc 文件。文件 .vimrc.html (請下載到本地打開) 是我的 .vimrc 文件通過以下步驟生成的 HTML 文件:

1. 在 Vim 中打開 .vimrc 文件;

2. 執行命令“:colorscheme koehler”(缺省配色可能在瀏覽器中效果不佳)

3. 執行命令“:%!nl -w4 -s' '”(1.11 節)

4. 執行命令“:TOhtml”(1.13 節)

5. 執行命令“:w”

可以把瀏覽器中的文本內容粘貼到 Vim 中,然後使用下面這個替換命令“:%s/^ \+[0-9]\+ //”刪除前面的行號,來恢復出最初的 .vimrc 文件。

下面逐行進行講解,幷包含理解其內容所需的資料的鏈接。建議大家直接閱讀 .vimrc 文件的內容,並在有疑問時查閱下面的解釋。

第 1 行:註釋(3.1.2 節末段),其中包含一個模式行(1.4 節和 1.5 節)。

第 2 行:首先判斷系統是否具有“自動命令”(autocmd)的支持,有的話才執行第3到第六行的內容(1.1 節、“:help has”和“:help feature-list”)。

第 3 行:純註釋(後面我將跳過註釋行不再說明)。

第 4 行:清除所有的自動命令(“:help autocmd-remove”),以方便調試,可以使用“source ~/.vimrc”查看一些修改後的效果(“:help source”)。

第 6 行:對於後綴爲“.asm”的文件,認爲其是微軟的 Macro Assembler 格式(“:help masm-syntax”)。

第 7 行:與第 2 行的 if 語句配對。

第 8-10 行:當使用了圖形界面時(“:help feature-list”),確保所有的文件類型會在菜單“語法”(“Syntax”)下出現,而不是出現一個菜單項“Show filetypes in menu”。缺省行爲可以讓 Vim 啓動得更快一點點。

第 11-13 行:當使用了圖形界面,並且環境變量 LANG 中不含“.”(即沒有規定編碼)時,把 Vim 的內部編碼設爲 UTF-8。

第 14 行:不需要保持和 vi 非常兼容(“:help 'compatible'”)。

第 15 行:執行 Vim 缺省提供的 .vimrc 文件的示例,包含了打開語法加亮顯示等最常用的功能。

第 16 行:打開自動縮進(1.4 節)。

第 17 行:缺省不產生備份文件(“:help 'backup'”)。

第 18 行:在輸入括號時光標會短暫地跳到與之相匹配的括號處,不影響輸入(“:help 'showmatch'”)。

第 19 行:正確地處理中文字符的折行和拼接(1.12 節)。

第 20 行:可自動識別的文件類型爲帶 BOM 字符的 Unicode 文件、UTF-8 編碼的文件和 GBK 編碼的文件。

第 21 行:設置狀態行,使其能額外顯示文件的編碼信息,如圖 2 中的“gbk”和“big5”(“:help 'statusline'”)。


圖 2
圖2

第 22-24 行:如果該 Vim 支持鼠標,則啓用鼠標支持(1.3 節)。

第 25-29 行:判斷 Vim 是否包含多字節語言支持(multi_byte 特性),並且版本號(“:help v:version”)大於 6.1(包含 ambiwidth 選項)。

第 26-28 行:如果 Vim 的語言(“:help v:lang”;受環境變量 LANG 影響)是中文(zh)、日文(ja)或韓文(ko)的話,將模糊寬度的 Unicode 字符的寬度(ambiwidth選項,1.2 節)設爲雙寬度(double)。

第 31-36 行:改變上、下方向鍵行爲方式:通常情況下這些鍵的作用範圍是邏輯行,所以如果行很長的話光標的移動可能會不太方便;這些鍵盤映射把這些鍵的作用範圍改成屏幕行(“help gk”),還爲習慣使用“j”、“k”的人增加了映射“Ctrl-j”和“Ctrl-k”作用於屏幕行。前面四個映射使用的命令是“noremap”,作用於正常模式、可視模式和命令執行時;後面兩個映射使用的命令是“inoremap”,僅作用於插入模式,其中使用“Ctrl-O”臨時執行一個普通模式的命令(“:help i_CTRL-O”)。

第 38-41 行:在 Vim 中的插入模式中可以使用“Ctrl-R =”計算整數表達式的數值,但 Vim 本身沒有計算浮點表達式的能力。這四個映射提供了浮點表達式的計算能力:使用“\ma”(假設 Leader 字符爲缺省的“\”,參見“:help <Leader>”)可將計算的結果放到下一行上(待計算的表達式爲當前行或在可視模式選中的內容),使用“\mr”則用計算的結果替換待計算的表達式(同樣爲當前行或在可視模式選中的內容)。這些映射假設有一個命令“calcu”可用來計算一個表達式的內容。該命令可用下面的 shell 腳本簡單實現:


#! /bin/sh
echo "$*" | sed -e $'s/\r$//' -e 's/sin *(/s(/g' -e 's/cos *(/c(/g' -e 's/atan * 
        (/a(/g' -e 's/log *(/l(/g' 
-e 's/exp *(/e(/g' | bc -l

該腳本把表達式轉換成 bc [1] 能接受的形式(把“sin(x)”轉換成“s(x)”,等等),並通過標準輸出送到 bc 的標準輸入。

該映射較爲複雜,此處不詳加解釋了——其中心思想都是選取待計算的表達式,放到無名寄存器中,然後使用“Ctrl-R"”粘貼到命令行上,使用 calcu 進行計算,再把結果粘貼回正在編輯的緩衝區中;最後一個最複雜,因爲爲了替換原先的表達式,還需要記住原先被選中的內容的起始和結束位置,你可能希望看一下“:help gv”、“:help v_o”、“:help m”、“:help `”,並複習節 1.11。可以注意一下,在映射中使用了“<silent>”(“:help map-<silent>”),這會防止命令行上回顯執行的內容。

第 43-44 行:允許用戶使用 F2 來取消搜索/替換的加亮顯示。此處一個映射用於正常模式(nmap),一個用於插入模式(imap)。上面已經提過一次,“Ctrl-O”可以在插入模式中執行一個正常模式的命令。

第 46-47 行:這兩個映射用於 taglist 插件,使用 F9 直接打開(或關閉)taglist 的窗口。

第 49-50 行:方便快速修訂窗口(1.10 節)的使用,可使用 F11(和 F12)查看下一個(上一個)錯誤(或 grep 項等)。

第 52-65 行:一些適用於文本模式運行的 Vim 的設定;詳見下面的具體說明。

第 54-56 行:將變量 Tlist_Inc_Winwidth 的值設爲 0,防止 taglist 插件改變終端窗口的大小(有些情況下會引起系統不穩定)。使用“has('eval')”是讓該語句僅在功能較爲完整、至少支持表達式的 Vim 版本中運行。

第 58-64 行:在系統支持 wildmenu 特性(“:help 'wildmenu'”)啓用文本模式的菜單。

第 59 行:打開 wildmenu 選項,啓動具有菜單項提示的命令行自動完成。

第 60 行:確保字符序列“<C-Z>”被理解爲 Ctrl-Z 而不是分開的五個字符(“:help 'cpoptions'”)。

第 61 行:設置使用 Ctrl-Z 激活自動完成提示。

第 62-63 行:把正常模式和插入模式下的 F10 映射成執行菜單項,並自動提示菜單內容。注意缺省菜單仍不會自動載入,我使用該特性的主要目的是在文本模式的 Vim 中使用 CVS 菜單。圖 16 是按 F10 鍵後再按 Tab 鍵的結果。


圖 16
圖16

第 66-161 行:使用自動命令(autocmd)特性的設置。使用“has”來防止該部分內容在不支持自動命令的 Vim 版本中運行。

第 67-129 行:定義了若干個下面的自動命令會用到的函數,具體在下面的自動命令中講。請注意在每個“function”之後都用了一個“!”(“:help E122”):這也是爲了方便調試,讓“source ~/.vimrc”能正確運行而不會報告函數已定義的錯誤。

第 131-133 行:只要沒有將環境變量 VIM_HATE_SPACE_ERRORS 的值設爲零,則把變量 c_space_errors 的值設爲 1——效果是在 C/C++ 代碼中“不正確”的空白字符(行尾的空白字符和緊接在製表符之前的空格字符)將會被高亮顯示。圖 17所示的代碼中,第 3 行的行尾多了兩個空格,第 5 行的第一個製表符之前多了個空格。Vim 提示#935 裏有一些額外的說明。同時請參看對第 160 行的說明。


圖 17
圖 17

第 135 行:使用的英文拼寫變體爲加拿大風格,即:使用拼寫“abridgement”(而不是“abridgment”)、“colour”(而不是“color”)、“realize”(而不是“realise”)、“theatre”(而不是“theater”)等,比較符合中國人一般的英語教科書中的拼寫方式,也比較適合於寫“國際”英語。

第 138 行:使用鍵盤映射“\a”來查看光標下字符的屬性,主要用於調試 Vim 的語法文件。圖 18顯示了光標下的字符所屬的語法“組”爲 vimOption,使用配色方案中的 PreProc(預處理符號)項,前景色爲紫色(RGB:#a020f0)。有興趣可查看 Vim 腳本#383 的具體內容。


圖 18
圖18

第 140 行:在函數找不到時(“:help FuncUndefined”),自動在運行環境(Linux 下一般爲 ~/.vim)的 autoload 目錄下讀入與函數名同名的 .vim 文件。這是腳本#383 的建議安裝方式(SyntaxAttr.vim 文件放在 autoload 目錄下,僅在執行時載入)。

第 142 行:設置適用於 C/C++ 文件的選項(1.4 節)。

第 143 行:把補丁文件的縮進和製表符寬度設定設成和 C/C++ 文件相同(1.4 節)。

第 144 行:取消 Vim 對 HTML 標記自動產生的縮進,但打開自動縮進選項(1.4 節)。

第 145 行:對於變更日誌類型的文件,設置行寬爲 76 個字符(1.12 節)。

第 147 行:當文件後綴爲“.gb”時,認爲這是一個 GBK 編碼的文件,在讀入文件之前(“:help BufReadPre”)調用函數 SetFileEncodings 把原先的 fileencodings 選項的內容保存在本緩衝區的一個變量中(3.1.1 節),然後把 fileencodings 設成 gbk,即只嘗試對文件內容作爲 GBK 字符序列來解釋。

第 148 行:類似於上面把“.big5”後綴的文件當作 Big5 編碼的文件,在讀入文件之前把 fileencodings 設成 big5,只嘗試對文件內容作爲 Big5 字符序列來解釋。

第 149 行:類似於上面把“.nfo”後綴的文件當作 CP437 編碼(即英文 DOS 的 OEM 字符集編碼)的文件。效果可參看圖 19。


圖 19
圖19

第 150 行:在讀入 .gb、.big5 或 .nfo 文件之後(“:help BufReadPost”),調用函數 RestoreFileEncodings 恢復保存起來的 fileencodings 原數值。

第 151 行:對於 .txt 後綴的文件,在顯示文件時(“:help BufWinEnter”,確保在模式行被執行之後)調用函數 CheckFileEncoding 檢查文件是否已修改並且 fileencoding 設有數值。條件滿足的話說明該文件在模式行中修改了 fileencoding,因而使用該編碼(“:help ++enc”)重新強制(“!”)讀入該文件以保證文件被正確解碼。Vim 提示#911 裏有一些額外的說明。

第 153 行:在遇到 HTML 文件時,如果 Vim 判斷出的編碼類型和 HTML 代碼中使用“<meta http-equiv="Content-Type" content="text/html; charset=編碼">”規定的編碼不一致,將使用網頁中規定的編碼重新讀入該文件。函數 ConvertHtmlEncoding 會把一些網頁中使用的編碼名稱轉換成 Vim 能夠正確處理的編碼名稱;函數 DetectHtmlEncoding 在判斷文件類型確實是 HTML 之後,會記下當前的光標位置,並搜索上面這樣的 HTML 代碼行,找出字符集編碼後,在編碼不等於當前文件編碼(fileencoding)時且當前文件編碼爲空或等於系統判斷出的文件編碼時,使用該編碼強制重新讀入文件,忽略任何錯誤(“silent!”)。該自動命令寫成是可嵌套執行的(“:help autocmd-nested”),目的是保證語法高亮顯示有效,且上次打開文件的光標位置能夠正確保持。Vim 提示#1074 裏有一些額外的說明。

第 155-156 行:確保把 /usr/include/c++ 和 /usr/include/g++-3 目錄下的所有文件都當成 C++ 類型的文件,不管 Vim 原先認定這些文件類型是什麼(“:help BufEnter”)。C++ 的很多標準頭文件(如“algorithm”)沒有文件後綴,缺省情況下不會被 Vim 當作 C++ 文件。

第 158 行:第 142 行把 C/C++ 文件的製表符寬度設成了 4(個人設置),但系統的源代碼一般使用 GNU 編碼規範,製表符寬度爲 8。該行設置所有 /usr 目錄下的文件都使用 GNU 編碼規範(1.4 節)。

第 160 行:在寫文件之前(“:help BufWritePre”),調用函數 RemoveTrailingSpace:只要沒有將環境變量 VIM_HATE_SPACE_ERRORS 的值設爲零,則對於文件類型爲 C、C++、Vim 腳本類型的文件,自動悄悄清除所有的行尾空白字符;“normal m`”記憶當前的光標位置,“normal ``”恢復記憶下來的光標位置。

至此爲此,我已經介紹了 Vim 的基本知識、很多實用技巧和一些最常用的 Vim 插件,並通過定製 .vimrc 文件介紹了腳本的基本知識。如果有需要進一步深入學習 Vim 或是想提什麼關於 Vim 的特定問題的話,不妨參加從 Vim 的網站上參加 Vim 的郵件討論列表,應該會獲益良多。而作者也希望本文至此也已經完成了引導讀者學習、瞭解 Vim 的高級特性的任務。

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