使用脚本编写 Vim 编辑器,第 2 部分: 用户定义函数
用戶定義函數
Haskell 或 Scheme 程序員會告訴您,函數對于任何嚴肅的編程語言來說都是最重要的特性。對于 C 或 Perl 程序員,他們也會告訴您完全相同的觀點。
函數為嚴肅的程序員提供了兩個基本優勢:
Vimscript 是一種嚴肅的編程語言,因此它天生就支持創建用戶定義函數。事實上,它確實提供了比 Scheme、C 或 Perl 更加優秀的?用戶定義函數支持。本文探究了 Vimscript 函數的各種特性,并展示了如何使用這些函數以可維護的方式增強并擴展 Vim 的內置函數。
聲明函數
Vimscript 中的函數使用?function?關鍵字定義,后跟函數名,然后是參數列表(這是強制的,即使該函數沒有參數)。函數體然后從下一行開始,一直連續下去,直到遇到一個匹配的?endfunction?關鍵字。例如:
清單 1. 具有正確結構的函數
functionExpurgateText (text)let expurgated_text = a:textfor expletive in [ 'cagal', 'frak', 'gorram', 'mebs', 'zarking']let expurgated_text\ = substitute(expurgated_text, expletive, '[DELETED]', 'g')endforreturn expurgated_text endfunction函數返回值使用?return?語句指定。可以根據需要指定任意數量的單獨?return?語句。如果函數被用作一個過程,并且沒有任何有用的返回值,那么可以不包含?return?語句。然而,Vimscript 函數始終?返回一個值,因此如果沒有指定任何?return,那么函數將自動返回 0。
Vimscript 中的函數名必須以大寫字母開頭:
清單 2. 以大寫字母開頭的函數名
function SaveBackup ()let b:backup_count = exists('b:backup_count') ? b:backup_count+1 : 1return writefile(getline(1,'$'), bufname('%') . '_' . b:backup_count) endfunctionnmap <silent> <C-B> :call SaveBackup()<CR>這個例子定義了一個函數,它將遞增當前緩沖區的?b:backup_count?變量的值(或初始化為 1,如果尚不存在的話)。函數隨后獲取當前文件(getline(1,'$'))中的每一行并調用內置的?writefile()?函數來將它們寫入到磁盤中。writefile()?的第二個參數是將要寫入的新文件的名稱;在本例中,為當前文件(bufname('%'))的名稱附加上計數器的新值。返回的值為對?writefile()?調用的 success/failure 值。最后,nmap?設置 CTRL-B 以調用函數來創建對當前文件的有限備份。
Vimscript 函數沒有使用前導大寫字母,相反,可以使用顯式的范圍前綴聲明函數(類似變量,如?第 1 部分?所述)。最常見的選擇是?s:,它表示函數對于當前腳本文件是本地函數。如果函數使用這種方式確定范圍,那么它的名稱就不需要以大寫開頭;它可以是任意有效標識符。然而,顯式確定范圍的函數必須始終使用范圍前綴進行調用。比如:
清單 3. 使用范圍前綴調用函數
" Function scoped to current script file... function s:save_backup ()let b:backup_count = exists('b:backup_count') ? b:backup_count+1 : 1return writefile(getline(1,'$'), bufname('%') . '_' . b:backup_count) endfunctionnmap <silent> <C-B> :call s:save_backup()<CR>可重新聲明的函數
Vimscript 中的函數聲明為運行時語句,因此如果一個腳本被加載兩次,那么該腳本中的任何函數聲明都將被執行兩次,因此將重新創建相應的函數。
重新聲明函數被看作一種致命的錯誤(這樣做是為了防止發生兩個不同腳本同時聲明函數的沖突)。這使得很難在需要反復加載的腳本中創建函數,比如自定義的語法突出顯示腳本。
因此 Vimscript 提供了一個關鍵字修飾符(function!),允許在需要時指出某個函數聲明可以被安全地重載:
清單 4. 表示某個函數聲明可以被安全地重載
function! s:save_backup ()let b:backup_count = exists('b:backup_count') ? b:backup_count+1 : 1return writefile(getline(1,'$'), bufname('%') . '_' . b:backup_count) endfunction對于使用這個修飾過的關鍵字定義的函數,沒有執行任何重新聲明檢查,因此非常適合用于顯式確定范圍的函數(在這種情況下,范圍已經確保函數不會和其他腳本中的函數發生沖突)。
調用函數
要調用函數并使用它的返回值作為語言表達式的一部分,只需要命名它并附加一個使用圓括號括起的參數列表:
清單 5. 使用函數的返回值
"Clean up the current line... let success = setline('.', ExpurgateText(getline('.')) )但是要注意,與 C 或 Perl 不同,Vimscript?并不?允許您在未使用的情況下拋出函數的返回值。因此,如果打算使用函數作為過程或子例程并忽略它的返回值,那么必須使用?call?命令為調用添加前綴:
清單 6. 在未使用返回值的情況下使用函數
"Checkpoint the text... call SaveBackup()否則,Vimscript 將假設該函數調用實際上是一個內置的 Vim 命令,并且很可能會發出報警,指出并不存在這類命令。我們將在本系列的后續文章中查看函數和命令之間的區別。
參數列表
Vimscript 允許您定義顯式參數?和可變參數列表,甚至可以將兩者結合起來。
在聲明了子例程的名稱后,您可以立即指定最多 20 個顯式命名的參數。指定參數后,通過將?a:?前綴添加到參數名,可以在函數內部訪問當前調用的相應參數值:
清單 7. 在函數內部訪問參數值
function PrintDetails(name, title, email)echo 'Name: ' a:title a:nameecho 'Contact:' a:email endfunction如果您不清楚一個函數具有多少個參數,那么可以指定一個可變的參數列表,使用省略號(...)而不是命名參數。在本例中,函數可以使用任意數量的參數調用,并且這些值被收集到一個單一變量中:一個名為?a:000?的數組。為單個參數也提供了位置參數名:a:1、a:2、a:3,等等。參數的數量可以是?a:0。例如:
清單 8. 指定并使用一個可變的參數列表
function Average(...)let sum = 0.0for nextval in a:000"a:000 is the list of argumentslet sum += nextvalendforreturn sum / a:0"a:0 is the number of arguments endfunction注意,在本例中,sum?必須被初始化為一個顯式的浮點值;否則,所有后續計算都將使用整數運算計算。
結合命名參數和可變參數
可以在同一個函數中同時使用命名參數和可變參數,只需要將可變參數的省略號放在命名參數列表之后。
例如,假設您希望創建一個?CommentBlock()?函數,它將接收一個字符串并針對不同的編程語言將其格式化為相應的注釋塊。這類函數始終需要調用者為其提供一個字符串來進行格式化,因此應當使用命名參數。但是,您可能希望注釋導入器(introducer)、“boxing” 字符和注釋的寬度全部是可選的(在被省略時具有合理的默認值)。那么應當像下面這樣調用:
清單 9. 一個簡單的 CommentBlock 函數調用
call CommentBlock("This is a comment")并且將返回一個多行字符串包含:
清單 10. CommentBlock 返回
//******************* // This is a comment //*******************然而,如果提供額外的參數,那么將為注釋導入器、“boxing” 字符和注釋寬度指定非默認值。因此這個調用將為:
清單 11. 更加復雜的 CommentBlock 函數調用
call CommentBlock("This is a comment", '#', '=', 40)would return the string:
清單 12. CommentBlock 返回
#======================================== # This is a comment #========================================這類函數的可能的實現方式為:
清單 13. CommentBlock 實現
function CommentBlock(comment, ...)"If 1 or more optional args, first optional arg is introducer...let introducer = a:0 >= 1 ? a:1 : "//""If 2 or more optional args, second optional arg is boxing character...let box_char = a:0 >= 2 ? a:2 : "*""If 3 or more optional args, third optional arg is comment width...let width = a:0 >= 3 ? a:3 : strlen(a:comment) + 2" Build the comment box and put the comment inside it...return introducer . repeat(box_char,width) . "\<CR>"\ . introducer . " " . a:comment . "\<CR>"\ . introducer . repeat(box_char,width) . "\<CR>" endfunction如果至少有一個可選參數(a:0 >= 1),那么導入器參數將指定給第一個選項(即?a:1);否則,將指定一個默認值?"//"。類似地,如果有兩個或多個可選參數(a:0 >= 2),那么?box_char?變量被分配給第二個選項(a:2),或一個默認值?"*"。如果提供了三個或多個可選參數,那么第三個選項被分配給?width?變量。如果沒有提供寬度參數,那么將自動根據注釋參數本身計算相應的寬度(strlen(a:comment)+2)。
最后,將所有參數值解析后,將構建注釋框的頂部和底部行:首先是一個注釋導入器,后跟 boxing 字符的重復次數(repeat(box_char,width)),在這兩者之間是注釋文本本身。
當然,要使用這個函數,需要以某種方式調用它。最理想的方法可能是使用一個插入映射:
清單 14. 使用一個插入映射調用函數
"C++/Java/PHP comment... imap <silent> /// <C-R>=CommentBlock(input("Enter comment: "))<CR>"Ada/Applescript/Eiffel comment... imap <silent> --- <C-R>=CommentBlock(input("Enter comment: "),'--')<CR>"Perl/Python/Shell comment... imap <silent> ### <C-R>=CommentBlock(input("Enter comment: "),'#','#')<CR>對于每一個映射,將首先調用內置的?input()?函數來請求注釋文本中的用戶類型。CommentBlock()?函數隨后被調用,以將文本轉換為一個注釋塊。最后,前導?<C-R>=?插入結果字符串。
注意,第一個映射僅僅傳遞一個單一參數,因此默認使用?//?作為其注釋標記。第二個和第三個映射傳遞第二個參數來指定?#?或?--?作為它們各自的注釋導入器。最后一個映射傳遞第三個參數,使得 “boxing” 字符匹配它的注釋導入器。
回頁首
函數和行范圍
可以使用一個初始的行范圍調用任何標準的 Vim 命令(包括?call),這將針對范圍中的每一行重復一次命令:
"Delete every line from the current line (.) to the end-of-file ($)...
:.,$delete
"Replace "foo" with "bar" everywhere in lines 1 to 10
:1,10s/foo/bar/
"Center every line from five above the current line to five below it...
:-5,+5center
可以在任何 Vim 會話中輸入?:help cmdline-ranges?來了解更多有關此工具的內容。
對于?call?命令,指定范圍將致使所請求的函數被反復調用:對范圍中的每一行調用一次。要了解這樣做的原因,考慮一下如何編寫一個函數來將當前行中的任何 “原始的” & 符號轉換為適當的 XML?&?實體,但是這樣做也足夠靈巧,可以忽略任何已經存在于其他實體中的 & 符號。這個功能的實現方式類似如下所示:
清單 15. 轉換 & 符號的函數
function DeAmperfy()"Get current line...let curr_line = getline('.')"Replace raw ampersands...let replacement = substitute(curr_line,'&\(\w\+;\)\@!','&','g')"Update current line...call setline('.', replacement) endfunctionDeAmperfy()?中的第一行代碼從編輯器緩沖區獲取當前行(getline('.'))。第二行代碼從當前行中查找其后未?跟隨標識符和冒號的?&,使用了否定先行(negative lookahead)模式?'&\(\w\+;\)\@!'(參見?:help \@!?獲得更多細節)。substitute()?調用隨后使用 XML&?實體替換所有此類 “原始” & 符號。最后,DeAmperfy()?中的第三行代碼使用修改后的文本更新當前行。
如果從命令行調用該函數:
:call DeAmperfy()
將只對當前行執行替換。但是如果在?call?之前指定了一個范圍:
:1,$call DeAmperfy()
那么將針對范圍內的每一行調用一次函數(在本例中,指的是文件中的每一行)。
內部化函數行范圍
這種針對每一行反復調用函數?的行為是一種方便的默認行為。然而,有時希望指定一個范圍,但是只調用一次函數,然后在函數內部處理范圍語義。這對于 Vimscript 也很簡單。只需要將一個特殊修飾符(range)附加到函數聲明之后:
清單 16. 函數內部的范圍語義
function DeAmperfyAll() range"Step through each line in the range...for linenum in range(a:firstline, a:lastline)"Replace loose ampersands (as in DeAmperfy())...let curr_line = getline(linenum)let replacement = substitute(curr_line,'&\(\w\+;\)\@!','&','g')call setline(linenum, replacement)endfor"Report what was done...if a:lastline > a:firstlineecho "DeAmperfied" (a:lastline - a:firstline + 1) "lines"endif endfunction在參數列表之后指定了?range?修飾符后,使用如下范圍調用?DeAmperfyAll()?時:
:1,$call DeAmperfyAll()
將只對函數執行一次調用,而兩個特殊參數?a:firstline?和?a:lastline?被設置為范圍的第一個行號和最后一個行號。如果未指定任何范圍,那么?a:firstline?和?a:lastline?都將被設置為當前行號。
函數首先構建一個包含所有相關行號的列表(range(a:firstline, a:lastline))。注意,對內置?range()?函數的調用與在函數聲明中使用?range?修飾符一點關系也沒有。range()?函數只是一個 list 構造函數,非常類似于 Python 中的?range()?函數,或者是 Haskell 或 Perl 中的?..?運算符。
確定了將要處理的行號列表后,函數使用?for?循環來遍歷每個行號:
for linenum in range(a:firstline, a:lastline)
然后相應地更新每一行(正如最初的?DeAmperfy()?所做的那樣)。
最后,如果范圍涵蓋了多個行(即?a:lastline > a:firstline),函數將報告被更新的行的數量。
可視范圍
一旦擁有了一個可以操作行范圍的函數調用后,一個特別有用的技巧就是通過 Visual 模式(參見?:help Visual-mode?獲得更多細節)調用該函數。
例如,如果游標位于文本塊的某個位置,那么可以使用下面的代碼在周圍的段落中編碼所有 & 號:
Vip:call DeAmperfyAll()
在 Normal 模式下輸入?V?將切換到 Visual 模式。ip?隨后將使 Visual 模式突出顯示您正位于其中的整個段落。之后,:?將您切換到 Command 模式并自動將命令范圍設置為剛剛從 Visual 模式選擇的行的范圍。此時,調用?DeAmperfyAll()?對所有行執行 deamperfy 操作。
注意,在這個實例中,可以使用下面的代碼獲得相同的效果:
Vip:call DeAmperfy()
惟一的不同之處在于?DeAmperfy()?函數將被反復調用:針對 Visual 模式下?Vip?中突出顯示的每一行調用一次。
回頁首
用于編碼的函數
Vimscript 中的大多數用戶定義函數只需要很少的參數,并且通常情況下根本不需要參數。這是因為它們常常直接從當前編輯器緩沖區和上下文信息(比如當前游標位置、當前段落大小、當前窗口大小或當前行的內容)中獲得數據。
此外,如果函數通過上下文而不是參數列表包含數據,那么往往更加有用和方便。例如,維護源代碼的一個常見問題就是賦值運算符在聚集起來后無法對齊,這將損害代碼的可讀性:
清單 16. 無法對齊的賦值運算符
let applicants_name = 'Luke' let mothers_maiden_name = 'Amidala' let closest_relative = 'sister' let fathers_occupation = 'Sith'在每次添加新語句時手動重新對齊它們將十分費力:
清單 17. 手動重新對齊賦值運算符
let applicants_name = 'Luke' let mothers_maiden_name = 'Amidala' let closest_relative = 'sister' let fathers_occupation = 'Sith'要讓日常編程任務沒那么乏味,可以創建一個鍵映射(比如?;=),它可以選擇當前代碼塊、定位具有賦值運算符的任何行,并自動對齊這些運算符。如下所示:
清單 18. 對齊賦值運算符的函數
function AlignAssignments ()"Patterns needed to locate assignment operators...let ASSIGN_OP = '[-+*/%|&]\?=\@<!=[=~]\@!'let ASSIGN_LINE = '^\(.\{-}\)\s*\(' . ASSIGN_OP . '\)'"Locate block of code to be considered (same indentation, no blanks)let indent_pat = '^' . matchstr(getline('.'), '^\s*') . '\S'let firstline = search('^\%('. indent_pat . '\)\@!','bnW') + 1let lastline = search('^\%('. indent_pat . '\)\@!', 'nW') - 1if lastline < 0let lastline = line('$')endif"Find the column at which the operators should be aligned...let max_align_col = 0let max_op_width = 0for linetext in getline(firstline, lastline)"Does this line have an assignment in it?let left_width = match(linetext, '\s*' . ASSIGN_OP)"If so, track the maximal assignment column and operator width...if left_width >= 0let max_align_col = max([max_align_col, left_width])let op_width = strlen(matchstr(linetext, ASSIGN_OP))let max_op_width = max([max_op_width, op_width+1])endifendfor"Code needed to reformat lines so as to align operators...let FORMATTER = '\=printf("%-*s%*s", max_align_col, submatch(1),\ max_op_width, submatch(2))'" Reformat lines with operators aligned in the appropriate column...for linenum in range(firstline, lastline)let oldline = getline(linenum)let newline = substitute(oldline, ASSIGN_LINE, FORMATTER, "")call setline(linenum, newline)endfor endfunctionnmap <silent> ;= :call AlignAssignments()<CR>AlignAssignments()?函數首先創建兩個正則表達式(參見?:help pattern?獲得有關 Vim 正則表達式語法的必要細節):
let ASSIGN_OP = '[-+*/%|&]\?=\@<!=[=~]\@!' let ASSIGN_LINE = '^\(.\{-}\)\s*\(' . ASSIGN_OP . '\)'ASSIGN_OP?中的模式匹配任何標準的賦值運算符:=、+=、-=、*=,等等,但是注意不要匹配其他包含?=?的運算符,比如?==?和?=~。如果您喜歡的語言中包含其他賦值運算符(比如?.=?或?||=?或?^=),那么可以擴展?ASSIGN_OP?正則表達式來識別這些運算符。另一種選擇是,可以重新定義?ASSIGN_OP?來識別其他 “可對齊的” 類型,比如注釋導入器或列表及,并對齊它們。
ASSIGN_LINE?中的模式只在行的起始部分(^)開始匹配,首先匹配最小字符數(.\{-}),然后匹配任何空白(\s*),最后匹配賦值運算符。
注意,最初的 “最小字符數” 子模式和運算符子模式都在捕捉圓括號內進行了指定:\(...\)。這兩個正則表達式組件捕獲的子字符串稍后將通過調用內置?submatch()?函數來提取;具體來講,通過調用?submatch(1)?來提取運算符前面的所有內容,然后調用?submatch(2)?來提取運算符本身。
AlignAssignments()?隨后查找它將對其進行操作的行范圍:
let indent_pat = '^' . matchstr(getline('.'), '^\s*') . '\S' let firstline = search('^\%('. indent_pat . '\)\@!','bnW') + 1 let lastline = search('^\%('. indent_pat . '\)\@!', 'nW') - 1 if lastline < 0let lastline = line('$') endif在此前的例子中,函數依賴于一個顯式的命令范圍或一個 Visual 模式選擇來確定要進行處理的行,但是這個函數則直接計算它自己的行范圍。具體來講,它首先調用內置?matchstr()?函數來確定出現在當前行(getline('.'))起始部分的前導空白('^\s*')。隨后在indent_pat?中構建一個新的正則表達式,精確匹配任何非空行的起始處的相同序列的空白(即拖尾?'\S')。
AlignAssignments()?隨后調用內置?search()?函數向上搜索(使用標記?'bnW')并定位位于游標上方的第一個不?具有相同縮進的行。向此行號加 1 將得出感興趣的范圍的起始行號,也就是說,具有相同縮進的第一個相鄰行就作為當前行。
第二個?search()?調用隨后向下搜索('nW')來判斷?lastline:具有相同縮進的最后一個相鄰行。對于這種情況,搜索可能會到達文件的末尾,并且沒有找到具有不同縮進的行,這種情況下?search()?將返回?-1。要正確地處理這種情況,隨后的?if?語句需要顯式地將?lastline?設置為文件末端的行號(即設置為由?line('$')?返回的行號)。
這兩個搜索的結果將使?AlignAssignments()?知道緊鄰著當前行的上方或下方、具有與當前行完全相同的縮進的完整行范圍。它使用這些信息來確保只對位于同一代碼塊中相同范圍級別的賦值語句執行對齊。當然,如果代碼的縮進不能正確反映它的范圍,那么這種情況下必須進行重新格式化。
AlignAssignments()?中的第一個?for?循環判斷其中的賦值運算符應當對齊的列。實現方法是遍歷所選范圍內的行列表(由getline(firstline, lastline)?取回的行)并檢查每個行是否包含賦值運算符(運算符的前面可能包含空格):
let left_width = match(linetext, '\s*' . ASSIGN_OP)如果該行中沒有運算符,那么內置?match()?函數將無法找到匹配,因此將返回?-1。對于這種情況,循環將直接跳到下一行。如果存在?運算符,那么?match()?將返回在其中顯示運算符的(正)指數。if?語句隨后使用內置?max()?函數判斷這個最近的列位置是否比此前找到的運算符更靠右,從而跟蹤所需的最大列位置來對齊范圍內的所有賦值運算符:
let max_align_col = max([max_align_col, left_width])if?中剩下的兩行代碼使用內置?matchstr()?函數檢索實際的運算符,然后使用內置?strlen()?函數判斷行的長度("="?的長度為 1,'+='、'-='?的長度為 2,等等)。max_op_width?變量隨后被用來跟蹤所需的最大寬度,以對范圍內的各種運算符執行對齊:
let op_width = strlen(matchstr(linetext, ASSIGN_OP)) let max_op_width = max([max_op_width, op_width+1])一旦確定了賦值區域的位置和寬度,剩下的就是遍歷范圍中的行并相應地執行重新格式化。要執行重新格式化,函數將使用內置的?printf()函數。這個函數十分有用,但是它的命名非常糟糕。它與 C、Perl 或 PHP 中的?printf?函數不同。實際上,它類似于以上這些語言中的sprintf?函數。也就是說,在 Vimscript 中,printf?并不會輸出其數據參數列表的格式化后的版本;它會返回一個字符串,其中包含了數據參數列表的格式化后的版本。
理想情況下,要重新格式化每一行,AlignAssignments()?將使用內置的?substitute()?函數,并使用經過?printf?重新整理后的文本替換運算符之前的所有內容。不幸的是,substitute()?要求使用固定的字符串作為它的替代值,而不是一個函數調用。
因此,要使用?printf()?來重新格式化每個替換文本,需要使用特殊的嵌入式替換形式:"\=expr"。替換字符串中的前導?\=?要求substitute()?對隨后的表達式求值并使用結果作為替換文本。注意,這類似于 Insert 模式下的?<C-R>=?機制,惟一不同的是這種奇妙的行為只針對內置?substitute()?函數的替換字符串(或在標準?:s/.../.../?Vim 命令中)。
在本例中,特殊替換形式對于每一行來說都將是相同的?printf?,因此它將在第二個?for?循環開始之前被預先存儲到?FORMATTER?變量中:
let FORMATTER = '\=printf("%-*s%*s", max_align_col, submatch(1), \ max_op_width, submatch(2))'當最終被?substitute()?調用時,這個內嵌的?printf()?將把運算符左側的所有內容(submatch(1))靠左對齊(使用?%-*s?占位符)并將結果放到字符寬度為?max_align_col?的字段中。隨后將運算符本身(submatch(2))右對齊(使用?%*s)到第二個字段,其字符寬度為max_op_width。參考?:help printf(),了解?-?和?*?選項如何修改這里使用的兩個?%s?格式說明符(specifier)。
有了這個格式化程序后,第二個?for?循環就可以遍歷完整的行號范圍,每次取回一行相應的文本緩沖內容:
for linenum in range(firstline, lastline)let oldline = getline(linenum)循環隨后使用?substitute()?來轉換這些內容,方法是匹配位于任何賦值運算符之前并包括運算符在內的所有內容(使用?ASSIGN_LINE?中的模式)并使用?printf()?調用的結果替換文本(如?FORMATTER?中指定的那樣):
let newline = substitute(oldline, ASSIGN_LINE, FORMATTER, "")call setline(linenum, newline) endfor當?for?循環遍歷了所有行之后,這些行中的賦值運算符將被正確對齊。剩余的工作是創建一個鍵映射來調用?AlignAssignments(),如下所示:
nmap <silent> ;= :call AlignAssignments()<CR>回頁首
結束語
為了處理真實?Vim?編程任務的復雜性,需要將應用程序分解為正確的、可維護的組件,而函數是實現這個過程的基本工具。
Vimscript 允許您使用固定的或可變的參數列表來定義函數,并使它們通過自動的或用戶控制的方式與編輯器的文本緩沖中的行范圍進行交互。函數可以回調到 Vim 的內置特性(比如,回調到?search()?或?substitute()?文本),并且它們可以直接訪問編輯器狀態信息(比如通過line('.')?確定游標所在的當前行)或者與當前進行編輯的任何文本緩沖進行交互(通過?getline()?和?setline())。
這無疑提供了非常強大的功能,但是通過編程的方式操作狀態和內容始終受限于數據表示的整潔性和準確性,我們的代碼將對這些數據進行處理。到目前為止,該?系列文章?一直關注單個標量函數(數值、字符串和布爾值)的使用。在接下來兩篇文章中,我們將探討更強大、更方便的數據結構的應用:有序列表和隨機訪問字典。
參考資料
學習
- 閱讀 “使用腳本編寫 Vim 編輯器,第 1 部分:變量、值和表達式”(developerWorks,2009 年 5 月),了解 Vimscript,這是用于擴展 Vim 編輯器的嵌入式語言。創建新工具、簡化常見任務,并重新設計和替換現有編輯器特性。
- 要了解有關 Vim 編輯器及其眾多特性的更多內容,請訪問:
- Vim 主頁
- 在線圖書?A Byte of Vim
- 各種有關 Vim 的圖書
- Vim 手冊
- Steve Oualline 的?Vim Cookbook
- 要獲得大量 Vimscripting 示例,請參考:
- Vim Tips wiki
- Vim 腳本歸檔
- 在?可愛的 Python:將 XML 和 Python 結合起來 介紹 Python 的 XML 工具,找到更多面向 Linux 開發人員的資源,并瀏覽我們的?最受歡迎的文章和教程。
- 在 developerWorks 上查閱所有?Linux 技巧?和?Linux 教程。
- 隨時關注 developerWorks?技術活動和網絡廣播。
總結
以上是生活随笔為你收集整理的使用脚本编写 Vim 编辑器,第 2 部分: 用户定义函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Vim 实用技术,第 3 部分: 定制
- 下一篇: 使用脚本编写 Vim 编辑器,第 4 部