歡迎您光臨本站 註冊首頁

使用腳本編寫 Vim 編輯器,第 1 部分: 變數、值和表達式

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  
Vimscript 是一種用於改造和擴展 Vim 編輯器的機制。使用腳本可以創建新工具、簡化常見任務,甚至重新設計並取代已有的編輯特性。本文是本系列文章的第一篇,介紹了 Vimscript 編程語言的基本元素:值、變數、表達式、語句、函數和命令。將通過一系列簡單的示例演示和解釋這些特性。

優秀的文本編輯器

有這樣一則老笑話:如果 Emacs 擁有一款優良的文本編輯器,那麼它將是一個優秀的操作系統,相反,如果 vi 擁有一個不錯的操作系統,那麼它將是一款非常出色的文本編輯器。這個笑話反映了 Emacs 有一個一直比 vi 好的策略優勢:一種嵌入式擴展編程語言。實際上,Emacs 用戶一直樂於忍受引入了 RSI 的控制問題,並且願意在 Lisp 中編寫他們的擴展,這個事實展示了內置擴展語言的強大優勢。

現在,vi 程序員不再需要向 Emacs 的插入式腳本語言投去嫉妒的眼光。我們最喜歡的編輯器現在也可以使用腳本進行更改 — 並且比 Emacs 更友好。

在本系列文章中,我們將考查最受歡迎的 vi 新版本,即 Vim 編輯器,以及 Vim 提供的簡單但功能極其強大的腳本語言。第一篇文章將探究 Vim 腳本的基本構建塊:變數、值、表達式、簡單流控制以及 Vim 為數眾多的實用函數。

我將假設您已經接觸過 Vim 並且熟悉它的互動式特性。如果還沒有這些基礎的話,Vim 網站和各種在線資源以及一些書籍都是非常不錯的學習起點,或者只需在 Vim 內部輸入 :help 獲得幫助。請查看 參考資料 小節中的鏈接。

如沒有特別說明,本系列文章的所有示例將假設您使用的是 Vim 7.2 版本或更高版本。如果要檢查所使用的 Vim 的版本,只需要像下面這樣調用編輯器:

vim --version

或者在 Vim 內輸入 :version。如果使用的是舊版本的 Vim,強烈建議您升級到最新版本,因為早期版本並不支持我們將要討論的大部分 Vimscript 特性。參考資料 小節提供了下載和升級 Vim 的鏈接。





Vimscript

Vim 的腳本語言被稱為 Vimscript,是典型的動態命令式語言,提供了大多數常見的語言特性:變數、表達式、控制結構、內置函數、用戶定義函數、一級字元串、高級數據結構(列表和字典)、終端和文件 I/O、正則表達式模式匹配、異常和集成調試器。

如果要通過內置的幫助系統讀取 Vim 自帶的 Vimscript 文檔,在任何 Vim 會話內部輸入下面的內容即可:

:help vim-script-intro

然後閱讀這些文檔。

運行 Vim 腳本

有許多種方法可以執行 Vim 腳本命令。最簡單的方法就是將命令放入到一個文件(通常使用 .vim 作為擴展名),然後執行該文件,方法為在一個 Vim 會話內部對文件執行 :source:

:source /full/path/to/the/scriptfile.vim

或者,可以在 Vim 命令行中,在冒號後面直接輸入腳本命令。例如:

:call MyBackupFunc(expand('%'), { 'all':1, 'save':'recent'})

但很少有人這樣做。畢竟,腳本的一個重要方面就是減少 輸入的內容。因此,調用 Vim 腳本的最普通的方法就是創建新的鍵盤映射,如下所示:

:nmap ;s :source /full/path/to/the/scriptfile.vim<CR>

:nmap \b :call MyBackupFunc(expand('%'), { 'all': 1 })<CR>

這些命令通常被放在主目錄的 .vimrc 初始化文件中。隨後,當處於 Normal 模式(就是說不能插入文本)時,關鍵序列 ;s 將執行指定的腳本文件,而 \b 序列將調用 MyBackupFunc() 函數(該函數大概也是在 .vimrc 文件的某個位置中定義的)。

本文中的所有 Vimscript 示例使用各種類型的鍵映射作為觸發器。在後續文章中,我們將探究另外兩個常見調用技巧:從 Vim 命令行中以冒號形式運行腳本,並使用編輯器事件自動觸髮腳本。





語法示例

Vim 提供有非常複雜的語法突出顯示功能,該特性可以通過內置 :syntax enable 命令啟用,並使用 :syntax off 命令關閉。

但是,在每次需要啟用或關閉語法突出顯示功能時,都需要輸入 10 個以上的字元,這一點令人很惱火。相反,您可以在自己的 .vimrc 文件中添加以下 Vimscript 代碼行:


清單 1. 啟用或關閉語法突出顯示功能
				  function! ToggleSyntax()     if exists("g:syntax_on")        syntax off     else        syntax enable     endif  endfunction    nmap <silent>  ;s  :call ToggleSyntax()<CR>  

這些代碼的作用是:在 Normal 模式下,每當輸入命令時,;s 序列將開啟或關閉語法突出顯示功能。讓我們看看這段腳本的每個部分。

顯然,第一個代碼塊是一個函數聲明,定義了名為 ToggleSyntax() 的函數,它沒有包含任何參數。這個用戶定義函數首先調用名為 exists() 的 Vim 函數,為它傳遞一個字元串。exists() 函數確定是否定義了一個具有由字元串定義的名稱的變數(在本例中為全局變數 g:syntax_on)。

如果是的話,if 語句將執行一個 syntax off 命令;否則它將執行 syntax enable 命令。由於 syntax enable 定義了 g:syntax_on 變數,而 syntax off 解除了變數定義,反覆調用 ToggleSyntax() 函數將不停地啟用和禁用語法突出顯示。

剩下的工作就是設置一個關鍵的序列(本例中為 ;s)來調用 ToggleSyntax() 函數:

nmap <silent> ;s :call ToggleSyntax()<CR>

nmap 表示 “normal-mode key mapping”。nmap 之後的 <silent> 選項使得映射不能回傳它所執行的任何命令,確保新的 ;s 命令默默地完成它的工作。它的職責就是執行下面的命令:

:call ToggleSyntax()<CR>

這就是希望忽略返回值時,在 Vimscript 中調用函數的方式。

注意,最後的 <CR> 序列是字元 <、C、R 和 > 的字母序列。Vimscript 將它與回車符等同起來。事實上,Vimscript 理解非列印字元的許多其他類似的表示。例如,可以創建一個鍵盤映射,使空格鍵起到下翻鍵的功能(和在大多數 Web 瀏覽器中一樣),如下所示:

:nmap <Space> <PageDown>

通過在 Vim 內輸入 :help keycodes,可以看到這些特殊符號的完整列表。

還需注意,ToggleSyntax() 能夠直接調用內置 syntax 命令。這是因為 Vim 中的每個內置冒號命令都自動成為 Vimscript 中的一個語句。例如,要為使用 Vim 編寫的文檔更方便地創建中心標題,可以創建一個函數,將當前行中的每個字母大寫,將整個行集中在一起,然後跳到下一行,如下所示:


清單 2. 創建中心標題
				  function! CapitalizeCenterAndMoveDown()     s/\<./\u&/g   "Built-in substitution capitalizes each word     center        "Built-in center command centers entire line     +1            "Built-in relative motion (+1 line down)  endfunction    nmap <silent>  \C  :call CapitalizeCenterAndMoveDown()<CR>  

Vimscript 語句

如此前的例子所示,Vimscript 中的所有語句都以換行符結尾(和 shell 腳本或 Python 一樣)。如果需要跨多個行運行一條語句,只需要使用反斜杠作為續行符。在表示續行時,反斜杠很少被放在行的末尾,而是放在延續行的開始處:


清單 3. 使用反斜杠作為續行符
				  call SetName(  \             first_name,  \             middle_initial,  \             family_name  \           )  

通過使用豎線做分隔符,可以將兩個或更多個語句放在單個行中:

echo "Starting..." | call Phase(1) | call Phase(2) | echo "Done"

也就是說,Vimscript 中的豎線相當於其他大多數編程語言中的分號。不幸的是,Vim 不能使用分號,因為這個符號在命令的開始處已經有了其他含義(它表示 “從當前行到……”,是命令的行範圍的一部分)。

註釋

豎線作為語句分隔符的一個重要用途體現在註釋中。Vimscript 註釋以一個雙引號開始並持續到行末,如下所示:


清單 4. Vimscript 中的註釋
				  if exists("g:syntax_on")     syntax off      "Not 'syntax clear' (which does something else)  else     syntax enable   "Not 'syntax on' (which overrides colorscheme)  endif  

不幸的是,Vimscript 字元串也可以以雙引號開頭並且始終優先於註釋。這意味著不能把註釋放到可能需要使用字元串的位置,因為註釋將始終被解釋為字元串:

echo "> " "Print generic prompt

echo 命令要求一個或多個字元串,因此此行代碼將生成一個錯誤,指出第二個字元串缺少一個右引號(Vim 是這樣認為的)。

然而,可以始終將註釋放在語句的最前頭,這樣,通過在開始註釋之前使用豎線顯式地開始一條新語句,就可以解決上面的問題,如下所示:

echo "> " |"Print generic prompt

值和變數

Vimscript 中的變數分配需要一個特殊的關鍵字,let:


清單 5. 使用 let 關鍵字
				  let name = "Damian"    let height = 165    let interests = [ 'Cinema', 'Literature', 'World Domination', 101 ]    let phone     = { 'cell':5551017346, 'home':5558038728, 'work':'?' }  

注意,字元串可以使用雙引號或單引號作為分隔符進行指定,雙引號字元串用於特殊的 “轉義序列”,比如 "\n"(用於換行符)、"\t"(用於製表符)、"\u263A"(用於 Unicode 笑臉標誌)或者 "\<ESC>"(用於轉義字元)。相比之下,使用單引號分隔的字元串將包括在其中的所有內容視為文字字元 — 兩個連續的單引號是一個例外,它們被單純地當作單引號。

Vimscript 的值通常為以下三種類型的其中之一:

  • scalar:一個單個值,比如字元串或數字。例如:"Damian" 或 165
  • list:使用方括弧分隔的有序值序列,並帶有以 0 開頭的隱式整數索引。例如:['Cinema', 'Literature', 'World Domination', 101]
  • dictionary:使用花括弧分隔的一組無序值集合,帶有顯式的的字元串鍵。例如:{'cell':5551017346, 'home':5558038728, 'work':'?'}

注意,列表或字典中的值不一定必須是相同的類型;可以混合使用字元串、數字,甚至是嵌套的列表或字典,如果需要的話。

和值不同的是,變數沒有固有類型。相反,它們使用分配得到的第一個值的類型。因此,在前面的例子中,name 和 height 變數現在都是標量(就是說,它們現在只能存儲字元串或數字),interests 現在是一個 list 變數(就是說,它只能存儲列表),而 phone 現在是一個 dictionary 變數(並且只能存儲字典)。變數類型一旦分配后,就會保持不變並在運行時嚴格遵守:

let interests = 'unknown' " Error: variable type mismatch

默認情況下,變數的範圍是變數第一次被分配到的函數的內部,如果它的第一次分配出現在任何函數的外部,那麼它就是全局變數。然而,也可以顯式地將變數聲明為屬於其他範圍,可以使用各種前綴進行聲明,表 1 進行了總結。


表 1. Vimscript 變數範圍
前綴 含義
g: varname 變數為全局變數
s: varname 變數的範圍為當前的腳本文件
w: varname 變數的範圍為當前的編輯器窗口
t: varname 變數的範圍為當前的編輯器選項卡
b: varname 變數的範圍為當前的編輯器緩衝區
l: varname 變數的範圍為當前的函數
a: varname 變數是當前函數的一個參數
v: varname 變數是 Vim 的預定義變數

還有一些偽變數(pseudovariables),腳本可以使用它們訪問 Vim 提供的其他類型的值容器。表 2 對此進行了總結。


表 2. Vimscript 偽變數
前綴 含義
& varname 一個 Vim 選項(如果指定的話,則為本地選項,否則為全局選項)
&l: varname 本地 Vim 選項
&g: varname 全局 Vim 選項
@ varname 一個 Vim 註冊器
$ varname 一個環境變數

“option” 偽變數非常有用。例如,可以設置兩個鍵映射(key-map)來增加或減小當前的表空間,如下所示:

nmap <silent> ]] :let &tabstop += 1<CR>

nmap <silent> [[ :let &tabstop -= &tabstop > 1 ? 1 : 0<CR>

表達式

注意,上例中的 [[ 鍵映射使用了一個包含類似 C 的 “三元表達式” 的表達式:

&tabstop > 1 ? 1 : 0

這可以阻止鍵映射將當前表空間遞減到最小值 1 以下。如這個例子所示,Vimscript 中的表達式由大多數其他現代腳本語言中使用的相同的基本運算符組成,並且使用基本相同的語法。可用的運算符(按照優先權分組)在表 3 中做了總結。


表 3. Vimscript 運算符優先表
運算 運算符語法
賦值
數值相加並賦值
數值相減並賦值
字元串連接並賦值
let var = expr
let var += expr
let var -= expr
let var .= expr
三元運算符 bool ? expr-if-true : expr-if-false
邏輯 OR bool || bool
邏輯 AND bool && bool
數值或字元串相等
數值或字元串不相等
數值或字元串大於
數值或字元串大於等於
數值或字元串小於
數值或字元串小於等於
expr == expr
expr != expr
expr > expr
expr >= expr
expr < expr
expr <= expr
數值相加
數值相減
字元串連接
num + num
num - num
str . str
數值相乘
數值相除
數值係數
num * num
num / num
num % num
轉換為數值
求負數
邏輯 NOT
+ num
- num
! bool
括弧優先 ( expr )

邏輯說明

和 C 一樣,在 Vimscript 中,對於布爾值來說,只有數值 0 為假;任何非 0 數值 — 不管是正數還是負數 — 都為真。然而,所有邏輯和比較運算符都全部返回值 1,表示真。

當將字元串作為布爾值使用時,將首先把字元串轉換為整數,然後再計算真(非 0)或假(0)。這意味著大部分字元串 — 包括大部分非空字元串 — 將被計算為 false。一個典型的錯誤是像下面這樣測試空字元串:


清單 6. 錯誤的空字元串測試
				  let result_string = GetResult();    if !result_string     echo "No result"  endif  

問題在於,在 result_string 被分配了一個空字元串后,上面的代碼就不能正常工作,如果 result_string 包含諸如 "I am NOT an empty string" 之類的字元串,它仍然表示 "No result",因為該字元串首先將被轉換為一個數字(0),然後再轉換為布爾值(假)。

正確的做法是顯式地測試字元串是否為空,使用相應的內置函數:


清單 7. 正確地測試空字元串
				  if empty(result_string)     echo "No result"  endif  

比較函數說明

在 Vimscript 中,比較函數始終執行數字比較,除非兩個運算對象都是字元串。特別是,如果一個運算對象是字元串,另一個是數字,那麼字元串將被轉換為數字,然後再對兩個數字進行數值比較。這樣做會導致一個難以發現的錯誤:

let ident = 'Vim'

if ident == 0 "Always true (string 'Vim' converted to number 0)

在這種情況下,一種更健壯的解決方案是:

if ident == '0'   "Uses string equality if ident contains string  				"but numeri                    c equality if ident contains number  			

字元串比較通常使用 Vim 的 ignorecase 選項的本地設置,但是任何字元串比較函數都可以被顯式地標記為大小寫敏感(通過附加一個 #)或大小寫不敏感(通過附加一個 ?):


清單 8. 將字元串比較函數設置為大小寫敏感或大小寫不敏感
				  if name ==? 'Batman'         |"Equality always case insensitive     echo "I'm Batman"  elseif name <# 'ee cummings' |"Less-than always case sensitive     echo "the sky was can dy lu minous"  endif  

強烈推薦對所有字元串比較使用 “顯式設置大小寫的” 運算對象,因為這樣可以確保無論用戶選項設置如何變化,腳本都可以可靠地執行。

算術說明

在使用算術表達式時,還需要記住一點,在版本 7.2 之前,Vim 只支持整數運算。早期版本中的一個普遍錯誤是編寫類似下面的代碼:


清單 9. 整數算術的問題
				  				"Step through each file...  for filenum in range(filecount)     " Show progress...     echo (filenum / filecount * 100) . '% done'  				" Make progress...     call process_file(filenum)  endfor  

由於 filenum 始終小於 filecount,整數除法 filenum/filecount 將始終生成 0,因此每次迭代循環都將生成:

Now 0% done

即使對於版本 7.2,如果其中一個運算對象被明確聲明為浮點類型,那麼 Vim 只支持浮點算術:

let filecount = 234    echo filecount/100   |" echoes 2  echo filecount/100.0 |" echoes 2.34  			





另一個切換示例

可以採用前面所示的語法切換示例輕鬆地創建更多有用工具。例如,如果您經常會拼錯或錯誤使用一組單詞,那麼可以向 .vimrc 添加一個腳本來激活 Vim 的匹配機制並在檢查文本時突出顯示錯誤的單詞。

比如,可以創建一個鍵映射(比如 ;p),能夠按照下面的方式在 Vim 中顯式文本(比如前一個段落):

It's easy to adapt the syntax-toggling script shown earlier to create other useful tools. For example, if there is a set of words that you frequently misspell or misapply, you could add a script to your .vimrc to activate Vim's match mechanism and highlight problematic words when you're proofreading text.

這段腳本可能類似清單 10。


清單 10. 突出顯示經常拼錯的單詞
				  				"Create a text highlighting style that always stands out...  |-------10--------20--------30--------40--------50--------60--------70--------80--------9|  |-------- XML error:  The previous line is longer than the max of 90 characters ---------|  highlight STANDOUT term=bold cterm=bold gui=bold    "List of troublesome words...  let s:words = [               \ "it's",  "its",               \ "your",  "you're",               \ "were",  "we're",   "where",               \ "their", "they're", "there",               \ "to",    "too",     "two"               \ ]    "Build a Vim command to match troublesome words...  let s:words_matcher  \ = 'match STANDOUT /\c\<\(' . join(s:words, '\|') . '\)\>/'    "Toggle word checking on or off...  function! WordCheck ()     "Toggle the flag (or set it if it doesn't yet exist)...     let w:check_words = exists('w:check_words') ? !w:check_words : 1       "Turn match mechanism on/off, according to new state of flag...     if w:check_words        exec s:words_matcher     else        match none     endif  endfunction    "Use ;p to toggle checking...  nmap <silent>  ;p  :call WordCheck()<CR>  

變數 w:check_words 被用作一個布爾標記,用來啟用或關閉單詞檢查。WordCheck() 函數的第一行代碼檢查標記是否已經存在,如果存在的話,變數分配僅僅啟用變數的布爾值:

let w:check_words = exists('w:check_words') ? !w:check_words : 1

如果 w:check_words 不存在,那麼將通過向它分配值 1 來創建:

let w:check_words = exists('w:check_words') ? !w:check_words : 1

注意使用了 w: 作為前綴,這表示標記變數對於當前窗口來說始終是本地變數。這將允許在每個編輯器窗口中獨立啟用單詞檢查(這與 match 命令的行為是一致的,後者的作用也只對當前窗口有效)。

單詞檢查是通過設置 Vim 的 match 命令啟用的。match 要求一個文本突出顯示說明(本例中為 STANDOUT),緊接著是一個正則表達式,指定要突出顯示哪段文本。在本例中,正則表達式的構建方式是對腳本 s:words list 變數中指定的所有單詞執行 OR 運算(即 join(s:words, '\|'))。這組待選對象隨後使用大小寫不敏感的單詞邊界括起(\c\<\(...\)\>),確保只有完整的單詞被匹配,而不需要關心大小寫。

WordCheck() 函數隨後將結果字元串轉換為一個 Vim 命令並執行它(exec s:words_matcher),從而啟用匹配功能。當 w:check_words 被關閉后,函數將執行 match none 命令,解除特殊匹配。





在 Insert 模式下使用腳本

Vimscripting 絕不僅限於 Normal 模式。還可以使用 imap 或 iabbrev 命令設置鍵映射或縮寫詞,可以在插入文本時使用。例如:

imap <silent> <C-D><C-D> <C-R>=strftime("%e %b %Y")<CR>

imap <silent> <C-T><C-T> <C-R>=strftime("%l:%M %p")<CR>

在 .vimrc 中有了這些映射后,在 Insert 模式下輸入兩次 CTRL-D 將促使 Vim 調用它的內置 strftime() 函數並插入生成的日期,同樣,兩次按下 CTRL-T 將插入當前時間。

可以使用相同的通用模式,讓插入模式或縮寫詞執行任何 可編寫腳本的操作。只需要將相應的 Vimscript 表達式或函數調用放到第一個 <C-R>=(告訴 Vim 插入後面內容的計算結果)和最後一個 <CR>(告訴 Vim 實際地執行前面的表達式)之間。但是,要注意,<C-R>(Vim 對 CTRL-R 使用的縮寫)和 <CR>(Vim 對回車使用的縮寫)是不同的。

比如,可以使用 Vim 的內置 getcwd() 函數為當前的工作目錄創建縮寫,如下所示:

iabbrev <silent> CWD <C-R>=getcwd()<CR>

或者,可以嵌入一個簡單的計算器,在文本插入期間輸入 CTRL-C 便可調用該計算器:

imap <silent> <C-C> <C-R>=string(eval(input("Calculate: ")))<CR>

下面這個表達式:

string( eval( input("Calculate: ") ) )

首先調用內置的 input() 函數來請求用戶輸入他們的計算,隨後 input() 作為一個字元串返回。該輸入字元串被傳遞到內置的 eval() 中,後者將它作為一個 Vimscript 表達式進行計算並返回結果。接下來,內置 string() 函數將數值結果轉換回一個字元串,鍵映射的 <C-R>= 序列將能夠插入該字元串。

更加複雜的 Insert 模式的腳本

插入映射所涉及的腳本要比前面的例子更加複雜。在這種情況下,通常最好將代碼重構為一個用戶定義的函數,鍵映射隨後就可以調用該函數。

例如,可以在插入期間改變 CTRL-Y 的行為。通常,CTRL-Y 在 Insert 模式下會執行 “縱向複製(vertical copy)”。也就是說,它將從緊挨著游標的上一行中複製同一列中的字元。例如,在下面的情形中,將在游標處插入一個 “m”:

Glib jocks quiz nymph to vex dwarf
Glib jocks quiz ny_

然而,您可能希望忽略中間的空行,並複製插入點上方的第一個非空 行的同一列中的字元。這將意味著,舉例說,下面情形中的 CTRL-Y 也將插入一個 “m”,即使緊挨著它的上一行是空行:

Glib jocks quiz nymph to vex dwarf

Glib jocks quiz ny_

通過在 .vimrc 文件中添加以下代碼,您就可以實現這種增強的行為:


清單 11. 改進縱向複製,忽略空行
				  				"Locate and return character "above" current cursor position...  |-------10--------20--------30--------40--------50--------60--------70--------80--------9|  |-------- XML error:  The previous line is longer than the max of 90 characters ---------|  function! LookUpwards()     "Locate current column and preceding line from which to copy...     let column_num      = virtcol('.')     let target_pattern  = '\%' . column_num . 'v.'     let target_line_num = search(target_pattern . '*\S', 'bnW')       "If target line found, return vertically copied character...     if !target_line_num        return ""     else        return matchstr(getline(target_line_num), target_pattern)     endif  endfunction    "Reimplement CTRL-Y within insert mode...  imap <silent>  <C-Y>  <C-R><C-R>=LookUpwards()<CR>  

LookUpwards() 函數首先判斷插入點目前位於屏幕上的哪個列(即 “虛擬列”),使用內置的 virtcol() 函數可以做到這點。'.' 參數指定當前游標位置的列編號:

let column_num = virtcol('.')

LookUpwards() 隨後使用內置 search() 函數從當前的游標位置倒退著瀏覽文件:

let target_pattern = '\%' . column_num . 'v.'
let target_line_num = search(target_pattern . '*\S', 'bnW')

搜索使用了一個特殊的目標模式(即 \%column_numv.*\S)來查找最近的一個行,這個行在游標所處的這一列(\%column_numv)或后一列(.*)應當具有一個非空字元(\S)。search() 的第二個參數是配置字元串 bnW,它告訴函數向後搜索,而不是移動游標,也不是封裝搜索。如果搜索成功,search() 將返回相應行的行號;如果搜索失敗,它將返回 0。

if 語句將判斷哪個字元 — 如果有的話 — 將被複制到插入點。如果沒有找到符合條件的行,target_line_num 將被分配一個 0 值,因此,第一個返回語句被執行並返回空字元串(表示 “沒有插入任何內容”)。

但是,如果找到符合條件的行,那麼將執行第二個返回語句。它首先從當前編輯器緩衝區中獲得前面行的副本:

return matchstr(getline(target_line_num), target_pattern)

然後查找並返回之前調用 search() 並成功匹配的只包含一個字元的字元串:

return matchstr(getline(target_line_num), target_pattern)

在 LookUpwards() 內部實現了這個新的縱向複製行為後,剩下的工作就是在 Insert 模式中覆蓋標準的 CTRL-Y 命令,使用下面的 imap:

imap <silent> <C-Y> <C-R><C-R>=LookUpwards()<CR>

注意,考慮到前面的 imap 示例都使用 <C-R>= 調用 Vimscript 函數調用,因此本例使用 <C-R><C-R>=。使用 single-CTRL-R 表單插入後面的表達式的結果,就好象是直接輸入的一樣,這意味著結果中的任何特殊字元將保留它們的特殊含義和行為。另一方面,double-CTRL-R 表單將按原樣逐字插入結果,不做任何處理。

在本例中更適合使用逐字插入,因為這裡的目標是精確地複製游標上方的文本。如果鍵映射使用 <C-R>=,那麼從前面的行中複製字母轉義字元就相當於輸入字元,而這將會造成編輯器立刻退出 Insert 模式。

學習 Vim 的內置函數

從前面的每一個示例中可以看到,Vimscript 的大部分功能都來源於它提供的廣泛的內置函數,數目大約超過 200 個。可以通過輸入下面的內容開始學習這些函數:

:help functions

或者,可以訪問一個(更加有用的)分類列表:

:help function-list





結束語

Vimscript 是一種用來重組和擴展 Vim 編輯器的機制。通過腳本能夠創建新的工具(比如突出顯示拼錯的單詞)並簡化常見任務的執行(比如修改表空間或者插入時間和日期信息,或者啟用語法突出顯示功能),甚至能夠完全重新設計已有的編輯器特性(比如增強 CTRL-Y 的 “複製上一行” 的行為)。

對於許多人,學習任何一種新語言的最簡單方法就是參考示例。為此,您可以在 Vim Tips wiki 中找到大量的樣例 Vimscripts — 大多數樣例本身就是非常有用的工具。或者,要獲得更廣泛的樣例 Vim 腳本,可以參考 Vim 腳本歸檔中 2000 多個更大型的項目。參考資料 小節中給出了資源鏈接。

如果您已經熟悉使用 Perl、Python、Ruby、PHP、Lua、Awk、Tcl 或任何 shell 語言,那麼 Vimscript 會讓您覺得既熟悉又陌生,它具有與上述語言類似的通用方法和概念,但是又具有特殊的語義特性。為了克服這些衝突並掌握 Vimscript,您將需要花一些時間來實際體驗、研究並使用這種語言。那麼,為什麼不研究一下 Vim 目前的工作原理並嘗試編寫一個更好的解決方案呢?

本文僅僅描述了 Vimscript 的基本變數、值、表達式和函數。您使用這些內容所能構建的 “更好的解決方案” 顯然是十分有限的。因此,在未來的幾期文章中,我們將查看更高級的 Vimscript 工具和技巧:數據結構、流控制、用戶定義命令、事件驅動腳本、構建 Vim 模塊,並使用其他腳本語言擴展 Vim。特別是,本系列的下一篇文章將重點考察 Vimscript 的用戶定義函數的特性,以及它們如何改善您的 Vim 體驗。(責任編輯:A6)



[火星人 ] 使用腳本編寫 Vim 編輯器,第 1 部分: 變數、值和表達式已經有998次圍觀

http://coctec.com/docs/linux/show-post-68869.html