第 140 期 維基百科‧一刀未剪版─自由軟體鑄造場電子報─智邦公益電子報
enews.url.com.tw · February 07,2012■ [技術專欄] Vim - 自動補齊
Cornelius/文 2009/12/12
自動補齊 (Auto-Completion) 一直是每個整合環境編輯器 (IDE) 都必備的一項功能,因此 Vim 也不例外的支援了自動補齊 (Auto-Completion) 的功能。
其中最大的不同就是使用者可以撰寫或設定自己的補齊函式 (Completion Function),其中還包含了使用者定義補齊 (User-defined completion)、全補齊 (Omni-completion)、關鍵字補齊 (Keyword completion)、檔案名稱補齊 (Filename completion)、字典補齊 (Dictionary completion)、詞庫補齊 (Thesaurus completion) 以及 Vim 指令補齊 (Vim command completion ) 等等各種的自動補完方式。
其中,使用者定義補齊 (User-defined completion) 以及全補齊 (Omni-completion)是可以由使用者定義函式 (Function)來延展補齊的精確性以及廣度。而字典檔補齊 (Dictionary completion) 以及詞庫補齊 (Thesaurus)則採用讀取外部文字檔案來做字典來源的補齊。
我們接下來將一一介紹基本的補齊操作以及各種補齊方式的使用,再進階進入撰寫自己的自動補齊函式。自動補齊主要在插入模式 (Insert Mode) 以 Ctrl-x 鍵作為補齊觸發鍵,再接上一個補齊類型的觸發鍵。
以下是內建的補齊列表:
C-x C-l 行補齊
C-x C-n 以目前檔案做關鍵字補齊
C-x C-k 由字典補齊
C-x C-t 由分類詞庫補齊
C-x C-i 從目前以及被引入的檔案為來源做關鍵字補齊
C-x C-] 標籤 (tags) 補齊
C-x C-f 檔案名稱補齊
C-x C-d 定義或巨集補齊
C-x C-v Vim 指令補齊
C-x C-u 使用者定義函式補齊
C-x C-o 全補齊函式做補齊
C-x s 拼字校正補齊
C-n 關鍵字補齊,不過依據 `complete` 選項來決定關鍵字來源
接下來對每種補齊方式一一做說明。
※ Ctrl-x Ctrl-l 行補齊 (Line completion):以往前搜尋行,找出與目前行相同開始的行做補齊項目 (Completion Item)。並且以 `complete`選項來決定來源的項目。行補齊將忽略縮排。使用行補齊 (Ctrl-x Ctrl-l)之後,可以再次按下 Ctrl-x Ctrl-l,這樣就會將當前行的補齊結果作為下一行的補齊。
※ Ctrl-x Ctrl-n 以目前檔案內容做關鍵字補齊 (Keyword completion):可使用 Ctrl-x Ctrl-n 往後搜尋關鍵字做補齊,Ctrl-x Ctrl-p 往前搜尋關鍵字補齊。可參考 `iskeyword` 選項來定義關鍵字的字元。
※ Ctrl-x Ctrl-k 字典補齊 (Dictionary completion):使用 `dictionary`選項所設定的字典檔案來做自動補齊。將使用字典檔案內的關鍵字來做自動補齊。舉例來說,新增字典檔案到目前的 `dictionary` 選項可用 set dictionary+=/path/to/you_dictionary 那麼 Ctrl-x Ctrl-k 時,便可以以這個字典檔作為補齊。
※ Ctrl-x Ctrl-t 分類詞庫補齊 (Thesaurus completion):其實和字典補齊 (Dictionary completion) 很類似,差異在 Thesaurus completion 所作的是在分類詞庫裡頭找到符合的關鍵字之後,相同一行的其他關鍵字會成為補齊的選項。
※ Ctrl-x Ctrl-I 由引入檔做關鍵字補齊常在撰寫程式碼時用到,舉例來說,在 C/C++ 程式碼我們寫:
#include “stdio.h”
那麼在 Ctrl-x Ctrl-i 做補齊的時候,Vim 會自動去 stdio.h 裡面搜尋符合的關鍵字。但是要注意的是 `path` 選項,記得將 C 語言或任何其他語言的引入檔路徑加入 `path`。以 C 語言為例:
set path+=/usr/share/include/glib/
如此 Vim 才會在相關路徑內找到你的標頭檔。
※ Ctrl-x Ctrl-] 標籤補齊 (Tag completion)由標籤檔 (tags file) 來取得關鍵字來做自動補齊。標籤檔通常由 `ctags` 所產生。
※ Ctrl-x Ctrl-f 檔案名稱補齊 (Filename completion)此種補齊用以補齊檔案名稱或者路徑。(可使用 `isfname` 選項來決定檔案名稱的樣式 (Pattern))
※ Ctrl-x Ctrl-d 定義或巨集補齊 (Definitions and Macros completion)在目前檔案以及引入檔案的內容搜尋定義以及巨集名稱做補齊。
※ Ctrl-x Ctrl-v Vim 命令補齊 (Vim command completion)Vim 命令補齊,撰寫 Vim script 時非常有用。
※ Ctrl-x Ctrl-u 使用者定義補齊 (User-defined completion)由使用者定義的函式來補齊,使用者可撰寫自己的函式來客製化補齊,可透過 `completefunc` 選項來設置補齊函式。
※ Ctrl-x Ctrl-o 全補齊 (Omni completion)全補齊類似使用者定義函式補齊,不過通常為特定檔案類型做補齊。可透過 `omnifunc` 選項定義函式名稱。
※ Ctrl-x s 拼字校正補齊 (Spelling suggestions completion)批字校正補齊會在游標前或是游標上的字上提供建議的單字作為取代。
由於 Ctrl-s 會使得某些 Terminal 造成 scroll lock,所以建議直接以 Ctrl-x s 來呼叫拼字補齊。
※ Ctrl-n 關鍵字補齊 (由 `complete` 選項決定關鍵字來源)這種補齊由 `complete` 選項決定關鍵字補齊。
◎ 簡單介紹 `complete` 選項:
`complete` 為一字串,選項由逗號分別,預設的 `complete` 值為 “.,w,b,u,t,i”
各個選項的說明如下:
. 掃描目前 Buffer。
w 掃描其他視窗的 Buffer
b 掃描其他在清單中的 Buffer
u 掃描已經卸載的 Buffer(但仍然在 Buffer 清單中)
U 掃描不在 Buffer 清單內的 Buffer。也就是 Unlisted Buffer ,被隱藏起來的 Buffer。
k 掃描 `dictionary` 指定的字典做補齊。
kspell 使用目前的拼字檢查做補齊。
k{dict} 掃描檔案 {dict} ,可以同時有多個 k 旗標設置,來掃描不同的字典檔。
譬如說
:set cpt=k/usr/dict/*,k~/spanish
s 使用 `thesaurus` 選項做分類詞庫補齊。
s{tsr} 掃描 {tsr} 檔案做分類詞庫補齊。
i 掃描目前的檔案以及引入的檔案。
d 掃描目前的檔案以及引入的檔案,並且做定義 (defined name) 或巨集(macro) 的補齊。
] 使用標籤補齊 (tag completion)
t 同 “]”
所以預設的選項 ".,w,b,u,t,i" 意思就是掃描:
1. 目前的 Buffer
2. 其他視窗的 Buffer
3. 其他載入的 Buffer
4. 其他卸載的 Buffer
5. 標籤 (tags)
6. 引入檔
◎ 使用者定義補齊 (User-defined Completion) 以及全補齊 (Omni Completion):
介紹如何定義補齊函式 (Completion Function)
Completion Function 的運作方式:
第一次被呼叫必須先找到補齊的定位點,函式必須回傳補齊定位點的位置。(為整數)
第二次被呼叫時,函式會被給予已輸入字串作為參數,Completion function 再
利用此字串搜尋需補齊的項目,並且回傳。
基本上補齊函式的原型為:
function! CompletionFunc(findstart,base)
endfunction
其中 CompletionFunc 為你的補齊函式名稱,這個函式必須取得兩個參數
a:findstart 以及 a:base。
a:findstart 參數第一次被呼叫時,會被設定為 1 ,此時我們必須找到補齊的定位點,
並且回傳補齊定位點的位置。
第二次被呼叫時,a:findstart 會為 0 ,a:base 會被設定為游標位置至補齊定位點的字串
假設我們輸入字串至:
how comple
^
cursor
那麼按下 C-x C-o 或 C-x C-u 呼叫函式補齊時,我們第一次會接到 a:findstart 為 1
我們回傳 5 (c 的位置) ,第二次時,我們會接到 a:base 回 "comple"
,接著我們可回傳 一串列作為可補齊的項目,如:
[ "complete" , "completion" , "completefunc" ]
假設我們只回傳
["completion"]
那麼就會自動補齊至 "completion"。
所以 Completion Function 的範本基本上可以寫成:
function! Func(findstart,base)
if a:findstart
" 尋找補齊定位點
return
else
" 使用 a:base 尋找補齊項目
return
endif
endfunction
那麼一個簡單的月份補齊函式可以這樣寫:(取自 vim help 文件 complete-functions 條目)
fun! CompleteMonths(findstart, base)
if a:findstart
" locate the start of the word
let line = getline('.')
let start = col('.') - 1
while start > 0 && line[start - 1] =~ '\a'
let start -= 1
endwhile
return start
else
" find months matching with "a:base"
let res = []
for m in split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec")
if m =~ '^' . a:base
call add(res, m)
endif
endfor
return res
endif
endfun
set completefunc=CompleteMonths
在回傳補齊項目的串列時,我們也可以是回傳 Dictionary
的串列,換而言之就是由雜湊 (hash) 組成的串列,這個雜湊可以有以下索引值:
word 補齊字串
abbr 補齊字串的縮寫,當不為空值時,abbr 會成為補齊選單
用以顯示補齊字串的字串。
menu 標注的文字
用以顯示在補齊項目後面。
info 額外資訊,當不為空時,額外資訊會被顯示在預覽視窗中。
kind 該項目的補齊類型。(為單個字元)
icase 忽略大小寫,為 1 時將忽略大小寫。
同樣的補齊項目會被視為同一個項目。
dup 決定重複的字是否加入補齊選單
譬如說:
let list = []
for item in items
if item.text =~ '^'.a:base
cal add(list, { 'word': item.text , 'abbr' : item.abbr , 'menu' : item.file })
endif
endfor
return list
上述寫法是將所有結果處完之後才回傳
有時補齊項目需要花比較久的時間搜尋,那麼我們可以利用 complete_add() 以及
complete_check() 函式來即時插入補齊項目,而不需要等待所有搜尋完成:
for item in items
if item.text =~ '^'.a:base
cal complete_add({ 'word': item.text , 'abbr' : item.abbr , 'menu' : item.file })
endif
if complete_check()
break
endif
endfor
◎ 特定檔案類型的全補齊 (Omni completion)
可參考 *compl-omni-filetypes* 條目,
此章節針對各種檔案類型的補齊做了說明。
Vim 已經內建了大部份主要程式語言的全補齊函式 (Omni completion
function),相關程式碼可以參考 $VIMRUNTIME/autoload/*complete.vim
◎ 相關 Plugin
Takeshi Nishida 寫的 AutoComplPop (ACP)
可以自動呼叫補齊函式,不需另外按快捷鍵呼叫,ACP 會在已定義的 樣式 (Pattern)
呼叫相對應的補齊模式。
使用 Vimana 可直接安裝
$ vimana install autocomplpop
由於多數補齊函式運作繁複且花費許多時間剖析字串,所以
日本的 Shougo Matsushita 寫了 NeoComplCache 來做 Completion 的快取。
使用 Vimana 可直接安裝
$ vimana install neocomplcache
◎作者簡介
Cornelius,目前在 AIINK(愛印網),以 Perl 語言開發的 Jifty web framework 從事網站開發相關工作。於 CPAN - Perl 模組典藏網維護多個 Perl 模組,參與 Jifty, SD 等 Perl 相關開放原始碼專案 。主要以 Vim 做為開發工具,著有 cpan.vim , perl-completion.vim , perldoc.vim 等多個 vim 相關 Plugin。
Github
Twitter
Plurk
Blog
Google group
[開放原力] 維基百科‧一刀未剪版
Jedi/文 2009/12/10
「當寫作成為閱讀的手段」,這並不是虛無的想像,而是此刻正在發生的現實;對於維基百科以及網路上成千上萬運用圍紀來製作百科、辭典、文件的計畫來說,最完整的閱讀體驗,少不了參與書寫的部份。並不是每個網路使用者都能體認到這一點,就連維基百科這麼知名的計畫,也不是每個使用者都會參與書寫─事實上參與貢獻的使用者比例並不高,許多使用者僅閱讀網頁上最膚淺的文字部分,沒有進一步地去發掘這些表象背後的實質內容。僅閱讀維基百科網頁表面的內容而不深究,往往距離真相最為遙遠。
維基百科以「編撰一部百科全書」作為出發點、作為概念上的目標,讓許多對資訊技術較不敏感的使用者也能迅速掌握整個計畫的輪廓,這部份固然是功不可沒,但是隨著數年過去,許多使用者很容易受限於傳統對「百科全書」的認知與想像,這就很不妙了。維基百科跟傳統百科全書之間有著許多差異,本質上可以說是完全不同的兩回事:
百科全書係經過寡頭權威式的編輯作業,編輯修改的過程並不公開,一旦面世後若非必要則甚少更動,若出現謬誤則出版社及編輯需負全責;維基百科採用鄉民參與式的編輯作業,儘管使用者的權限等級可以有所不同,但技術上任何使用者隨時都可以編輯、修改、刪除、還原,這些編輯過程會公開地保留下來,內容時時刻刻均有不同,甚至每一秒都可能與前一秒相異,一旦內容有謬誤,沒有別人會負責─誰發現錯誤,誰就有責任去改正。
由於這些本質上的差異,當需要在權威體系下(亦即現今多數的學術系統、政經體系中)正式引用資料或數據時,維基百科就會是最不適合的引用來源之一,因為維基百科的內容常常會處於未經同儕審閱且尚未編輯、校對的狀態,而且通常引用時的維基百科內容,與後來讀者查閱時必會有所出入,使得原本力求穩步徐行的研究方法,完全派不上用場。更甚而者,未經紮實編輯的維基百科內容,所發生的種種謬誤,諸如將愚人節笑話當成正經的消息來源,同一個人名在同一句話中出現兩種不同譯法,或者前一段所言與後一段南轅北轍,屢見不鮮;尤其在維基百科開始將若干語言合併為一後,語言及文化差異造成的編寫阻礙,更是嚴重。
雖然維基百科在編輯、校訂,以及學術價值的權威性上遠遠不及傳統的百科全書,但是另一方面,這也是維基百科的過人之處。任何使用者,不需要先經歷傳統學術權威體系的制約,就可以參與內容變動;這個特性使得維基百科有機會避免過於單一的詮釋角度,讓維基百科的內容不會輕易流於學究或布爾喬亞觀點。也因為參與門檻遠比傳統百科全書低得多,維基百科也能更有彈性地反映出最新近的流行文化,因此對於文化事件以及次文化領域的闡釋,維基百科總能更迅速地收錄有關內容。
儘管維基百科有一整套的守則與方針,也有企圖堅守的立場,現實上還是不可能讓所有參與的人都有共同的信念及認知;任何維基百科的內容,尤其是重要的內容,必然歷經頻繁反覆的編修競爭,這也是維基百科追尋聖杯的必經之途。這些演變的歷程,自成一部集體文化認知的發展史,正是所有其他的百科全書所缺乏的部份;這些演變的歷程才真正是維基百科的價值所在,每一次的內容變化,像是為什麼要如何更動、刪減了些什麼、添加了何種潤飾等,或多或少都有些「故事」可說。這些故事可不是八卦而已,而是見證內容存廢的重要註腳,使用者可以從這些故事體認維基百科的立場定位、運作模式,掌握寫作風格,並有效減少再三反覆的爭議或推敲。
當德國的出版社要將維基百科印刷出版成冊、或者當維基百科的「離線閱讀套件」逐一釋出,都只是在體現維基百科的「內容自由」而已;這些都是合理的發展沒錯,可是這些產物充其量是維基百科的驚鴻一瞥而已,如果過分重視或強調這類利用,反倒是在抹煞維基百科最珍貴的部份了。當然也有針對內容沿革進行研究的計畫,但這並不容易,可以說是維基百科發展至今難以有所突破的瓶頸之一:要如何輕易、明確地描述這些沿革呢?或者說,要如何才能讓維基百科的尋常使用者,也能便利地取用這些資訊呢?
維基百科每一則內容都有「修訂歷史」可查,但是這部份的操作介面十年來沒什麼成長,大致上就是按照時間順序列出由新到舊的修訂版、修訂的使用者、修訂摘要,並且讓使用者能夠任選兩個修訂版來比對內容差異。說實話,這樣的介面設計非常的技客導向,任何尋常的使用者幾乎都會略過這部份的功能不顧。如果維基百科想讓使用者能夠善用修訂沿革的資訊,接下來的發展方向應該是把這些資訊跟內容緊密結合在一起,亦即讓使用者在檢視模式下,就可以利用滑鼠選單或其他機制,即時操作動態的修訂歷史資訊,而且這些修訂歷史資訊也需要進一步地分類、整理,例如按照修改的模式、修改的幅度、修改的範圍等不同的條件,以程式化或人工的方式來處理,也可以試著用心智圖來呈現這些整理過的資訊。
用說的當然是很簡單,實際上困難重重:當一份內容有著上萬使用者參與、經歷數十萬次的修訂,是否還適合這樣子表達?再加上前段所描述的介面,也勢必得用上最新的網頁技術來重新開發,但這樣的工作遲早都必須進行,因為唯有幫助使用者融入內容脈絡,才能繼續降低參與貢獻的門檻,讓使用者能參與內容編修,並藉此探索維基百科真正的內容與價值。
到了那一天,我們才能說維基百科「一刀未剪」吧。