2009/03/31

輕輕鬆鬆寫VIM plugin: 變數及表示式

在開始之前,必需先介紹一下如何建立一個測試環境,小弟本身是有用兩種開發方式,一是開發小功能時用的,另一則是真正在開發一個plugin時用的。

先來介紹第一種開發小功能的環境,一般小弟會先在_vimrc裡建一個函式,接著再用map指令將這個函式對應到一個快速鍵,舉例來說在_vimrc裡插入以下指令將會建立一個foo函式,並且當輸入rfa時會呼叫到它。

map rfa <ESC>:call Foo()<CR>
function! Foo()
echo "Hello world 1"
endfunction


藉此我們便可將小功能先放在_vimrc裡測試,等到功能成熟時再建立一個plugin。此外,若是在反覆測試這個函式時,不想每次做小更動就必需重開VIM讓它reload檔案,可以在更動完後用:source指令來使檔案重新生效。如下,將使_vimrc的設定重新讀取:

:source $VIM\_vimrc


第二種方式就較為複雜,這種方式會用在開發一個plugin時使用,因為它包含了一個plugin前後的一些readme、檢查、command或keymap,所以目前還先不介紹。等到這些基本的語法講解完後,準備踏入plugin開發時才會說明。接著將進入本篇的主題,說明時我儘可能用範例說,少說一些廢話。


常數

數字常數除了一般十進位外,16進位的prefix為0x,8進位的為0,請看以下例子。
:echo 0x7f 036
127 30
:echo 0x7f - 036
97


字串常數則有分單引號括住及雙引號,單引號的會乎略跳脫字元,VIM的跳脫字元有很下這些:
\t <Tab>
\n <NL>, line break
\r <CR>, <Enter>
\e <Esc>
\b <BS>, backspace
\" "
\\ \, backslash
\<Esc> <Esc>
\<C-W> CTRL-W



變數

變數的命令規則跟C很像,這個不再贅述,而VIM的變數根據類型及生存空間有以下幾種:
b:name variable local to a buffer
w:name variable local to a window
g:name global variable (also in a function)
v:name variable predefined by Vim
$NAME environment variable
&name option
@r register (list all by :reg)


變數的建立及刪除有檢查存在,可用2個指令及1個函式
let
unlet
exists("s:call_count")


請看以下例子:
:if !exists("s:call_count")
: let s:call_count = 0
:endif
:echo "called" s:call_count "times"


NOTE: 關於if判別式,若是字串型態則VIM會將它自動轉成數字型態,轉成數字型態的字串是根據字串開頭的數字當成轉型後結果。若是程式寫了如下的判別式
:if "true"

則會回傳0,表false,因true的字首為t,不為一個數字,故回傳0。


表示式

數字運算
a + b add
a - b subtract
a * b multiply
a / b divide
a % b modulo


字串串接
:echo "foo" . "bar"
foobar


執行表示式(:execute)用在用我們的命令需插入變數時使用,:execute的行為與eval是十分相似(或是一樣)的,請見以下例子。
:execute "normal " . normal_commands

:let optname = "path"
:let optval = eval('&' . optname)

:exe 'let optval = &' . optname

2009/03/26

VIM: 製作跟VIM help一樣能跳來跳去的文字檔

最近想要自己製作VIM的help,VIM的help就是輸入:help後會顯示的說明頁,這種說明頁的好處除了有原本VIM的優點外,他還可以讓你用Ctrl-]和Ctrl+t[跳來跳去]。本文就是介紹這種"跳來跳去"的非源碼頁面。

(題外話)
會想試試看原因有好多,最大原因是最近工作雖忙但都是做無聊的事沒學到什麼東西感到很悶,另外最近訂了一隻gphone(htc dream),整天在想要拿那隻手機做啥,沒什麼心情工作。最近常在想比起做韌體,我好像比較適合做軟體,做韌體有bug就是用一些bug tool不斷縮小縮圍找bug,之後看看腳位有沒有設對,釐清是誰的問題。才工作半年就對這份工作感到無趣會不會太快了點,相對於之前2份軟體的工作這份工作似乎太快就沒興趣了。

(恢復正題)
廢話不多說,馬上進入正題,我以範例來解說製作方式。現在我要製作2份文件分別為a.txt及b.txt,在a.txt內要有一個link連到b.txt的頭,而b.txt會有一個link連到a.txt的頭,以下就是他們的文件真實內容。

a.txt的內容如下
*a.txt*
|b.txt|
------------------------------------------------------------------------------
vim:tw=78:fo=tcq2:isk=!-~,^*,^\|,^\":ts=8:ft=help:norl:


b.txt的內容如下
*b.txt*
|a.txt|
vim:tw=78:fo=tcq2:isk=!-~,^*,^\|,^\":ts=8:ft=help:norl:


我們以a.txt與b.txt相似,我們以a.txt為解說例。首先第一行以*...*括起來的a.txt是表示它是一個錨(anchor)用作於連結目的端,錨名為a.txt。第2行以|...|括起來的則是一個link連結到名為b.txt的錨。接著我們必需讓VIM將這些資料做成cache,輸入":helptag ."便會在工作徑下產生tags檔。現在便可以使用Ctrl+]跳來跳去了。

大家看到這裡可能還會有個疑問,我在文件的最後一行輸入(vim:tw...)的是什麼東西,那行叫[modeline]是用來指示這個檔案的VIM設定,像是這個範例就表示textwidth=78, iskeyword有那些, filetype為help等設定。若將這些都設定上檔案開起來就會像這樣:



本文參考自:[Add your note files to Vim help]

VIM tip: 刪除搜尋到的行

剛好看到有人用sed來刪除比對到的字串行,VIM也有類似用法,有時這個功能還算滿有用的。若是想刪除出現Rickey is foolish的這些行可以輸入

:g/Rickey is foolish/d


開對的g是表所有的意思,尾巴的d則是刪除,中間被/包圍的則是要搜尋的字這是允許regex的。

這些資料可以在vim doc的[10.4 The global command]及[delete]找到,有時vim doc看一看,真覺得vim有一堆很真正去理解的命令。

2009/03/20

VIM 系列文章的下一步

最近在思考這個部落格接下來可以寫些什麼,之前[打造自己的VIM系列]文章是寫給那些已經會使用VIM的人看的,目的是希望能藉著這個系列文章能讓各位用起VIM能更得心應手。現在有2個大方向可以讓VIM這個主題繼續GO,一是寫簡單的VIM教學,另一則是寫更難的。如果有熟悉我的朋友,應該猜的到我的選擇,選擇比較困難的地方前進。所以下個主題我打算來寫如何寫VIM plugin。

學會寫plugin其實有很多好處。像之前我在用[easygrep]這個plugin時,它並不支援不分大小寫的搜尋,所以我就稍微看一下它的程式,再把我需要的功能加進去,最後將修正的地方寄給原作者好讓原作者merge起來,作者也很快將把我把我的功能加進去而且上面還有我的名字(Doro Wu)(羞)。這就是open source的好處,需要什麼功能自己動手加進去。

要會寫plugin的首要之務是要會寫VIM script,所以我會先對VIM script簡介並再介紹一些常用的內建函式,之後才會開始告訴各位如何將script組合成plugin。目前打算依照這樣介紹:
  • 變數及表示法
  • 判斷句
  • 函式
  • 例外
  • 事件 autocommand
  • syntax highlighting
  • window and buffer
  • 命令輸出擷取
  • plugin簡介
  • 輕輕鬆鬆寫個vim plugin

2009/03/14

打造自己的VIM: 源碼補齊 SuperTab OmniCppComplete Code_Complete

VIM的源碼補齊是我比較少用的部分(這就是身在系統廠的悲哀,幾乎沒什麼機會寫大一點的專案),但就我以前寫code的經驗,小弟認為我這次要介紹的3個plugins應該是能滿足大部分的需求。這3個plugins為[SuperTab]、[OmniCppComplete]及[Code_Complete]。

SuperTab

SuperTab是這3個源碼補齊我最喜歡的plugin,它使用不需要什麼設定,也不用產生tags檔案,裝好即用。它補齊的所使用的關鍵字是藉由搜尋所有被開啟的檔案內的字。而使用方式也很容易,當你打字打到一半需要它幫你補齊時就按下<tab>鍵,它就幫你把剩下的字補齊。但有時我們會希望是插入一個真正的Tab而不是要幫助補齊,這時需要按下Shift+Tab,它的使用就這樣而已,是不是很容易呀。以下是一張截圖,可以看到圖上有2個關鍵字AAAA及AABB,當我輸入AA後要它補齊,又有多個關鍵字被找到它會彈出一個popup menu讓你選擇。



OmniCppComplete

OmniCppComplete是針對C/C++程式所設計的源碼補齊方式,它需要tags檔案的幫忙,若是還不知道要怎麼產生tags檔案,可以參考我之前的文章,安裝:[打造自己的VIM: 函式列表 TagList],產生:[打造自己的VIM: 源碼追蹤 SrcExpl]。關於OmniCppComplete的截圖可以到[這裡]來看,那裡提供了很多圖,我就不再為各位截圖了。

OmniCppComplete的使用除了先產生tags檔案外,另外可以用的鍵就只有<c-x><c-o>,按下Ctrl+X及Ctrl+O後,它就會依據tags檔案及目前游標前的的字彈出相對應的popup menu供使用者選擇,就使用上也是很容易。

而在設定上,我自己是什麼都沒設,如果有覺得使用上有什麼不便,可以看看它裡頭的doc,那裡有所以它可以設定的option,我稍微看了一下大概就是調整popup menu的顯示設定或是自動選擇第一個item。

Code_Complete

不知道各位有沒有看過一些在mac上的demo,那些人有些人是寫RoR有些是寫HTML(PHP),他們只要輸入if或for等關鍵字,他們的文字編輯器就會幫他們把後面相關的文字補齊,一整個寫起程式來很帥,Code_Complete就是提供這種功能。相信有人還是不懂我在說什麼,我用下的套圖來說明,下面第一張圖是一個C的程式,一般include完檔案後會寫個main function,在我輸入完main再按下Alt+d如圖2,它便幫我把該補完的補完了,並且跳到要繼續寫程式的地方;接著我輸入if如圖3,再按下Alt+d,它又幫我把剩下補完。若是想跳到下一個`<...>`處,只要按下Alt+d它就會幫我們跳過去。









到這裡應該大家都知道基本用法,但是Code_Complete他是根據什麼來補齊呢?各位可以打開code_complete.vim,可以看到有一行寫著:

let g:template['c']['main'] = "int main(int argc, char \*argv\[\])\<cr>{\<cr>".g:rs."...".g:re."\<cr>}"


等於符號左邊的意思是遇到附檔名屬於c程式且游標前面是main時,一按下Alt+d便用等於符號右邊的字串補完。至於右邊字串的意思大家就自己跟main產生出來的東東比對一下大概就可以猜到它的意思。

2009/03/11

VIM tip: TabBar的密技

TabBar裡其實有一些設定很少被提到,但是卻很實用,若需要更多的說明可以自己打開tabar.vim來看看,大概在100出頭行的地方,亦或是直接在code裡的某處。我這裡提一些看起來比較有用到的。

g:Tb_MaxSize (Default = 1)
有時開的檔案太多,TabBar視窗會容不下那麼多檔案同時在一行內被顯示,但是使用者(我)會希望它可以分成2行被顯示,這時只要將這個值設定2就行。

g:Tb_TabWrap (Default = 0)
若開啟檔案數超過一行可顯示且g:Tb_MaxSize又設成大於1時,超過一行可顯示的字會折到下一行而造成最後一個檔案標籤斷了,若設成1則它不會讓標籤斷掉,會直接讓行尾標籤放到下一行行首。

g:Tb_SplitBelow (1 = below, 0 = above(Default))
將TabBar視窗放在上面or下面

g:Tb_VSplit (0:top, 1:left)
將TabBar放在上面or左邊


我自己是設定這些值
let g:Tb_MaxSize = 2
let g:Tb_TabWrap = 1


這篇文章已被收錄在原本[TabBar介紹]的文章裡

2009/03/10

VIM tip: 自動插入數字列號,使用巨集及VIM內建函式

在ptt上看到網友CyberFret問了一個問題:

[問題]vim如何作到ultraedit的自動插入數字列號
input  int[0]
input  int[1]
input  int[2]
input  int[3]
如上面的sample code
在ultraedit裡面, 可以用 "區塊-->插入數字列號" 來自動插入一整排的等差數列
如上面的 0 -3, 請問在vim裡面要如何辨到? 謝謝


換句話說就是只輸入上列第一行,如何快速產生下面幾行。這個問題其實google一下應該能找到相關討論,我是從這[]來的,當然也不完全是抄啦 ^^"

要達到這個功能方法有2:
  1. 使用巨集
  2. 使用VIM built-in function
先來說說第一種方法。請在輸入完第一列(到底是列還是行??)後,在數字處按造下面keyin來製作巨集:
qa
yy
p
Ctrl-a
q

上面第一個命令表開始錄製巨集a;第二,複製游標所在列;第三,貼上;第四,將游標所在處的數字加1;第五,結束巨集。其中若要改成數字減一則在第四個命令改成Ctrl-x,另外,因為在windows中Ctrl-a對應到全選,所以對於windows的vim使用者必需先下:nunmap <C-A>將全選的功能取消。

在錄製好巨集後,接著再輸入15@a便會重覆執行巨集a,15次。以上是第一個方法。

第二種方法為使用內建函式。先從一簡單的例子來看,以下命令:
:0put =range(11,15)

將輸出
11
12
13
14
15

以此類推,若要CyberFret的要求則輸入以下命令即可。
:for i in range(0,3) | put ='input int['.i.']' | endfor

2009/03/05

打造自己的VIM: 多檔字串搜尋 EasyGrep

在追蹤源碼時,除了最常用到的tags方式,另外搜尋也是不可少的功能之一,在Linux環境底下最常見的方式就是開個termianal用grep搜尋,再到自己的文字編輯器把相對應的檔案打開,當搜尋的機會變多,這樣的動作真是麻煩又囉嗦。為了縮短這些麻煩regular routine,有人寫了一個plugin [EasyGrep]。EasyGrep主要是把Grep的功能整合在VIM裡,它可以使用跨平台VIM內建的vimgrep或是外部的指令grep、findstr等指令,而它是將搜尋結果輸出到QuickFix視窗,更多的QuickFix使用可以參考小弟寫得[這篇文章];基本上因為輸出到QF視窗的關係,也使得這個套件更好用了。要直接看demo可以到[這裡看]。而底下是它的option截圖,大家可以看一下它提供那些功能。






安裝方式請至官網下載EasyGrep.vim and EasyGrepFileAssociations到你的vim plugin資料匣,其中EasyGrepFileAssociations為設定搜尋檔案關連性,像上面那張圖的第30行,我設定ASM檔包括*.s *.asm...等5種檔案,各位可以修改Easy...Associations來更改這個值。

基本上EasyGrep不需要什麼特別的配置,因為它提供的快速鍵就足夠使用了。以下我整理了我常用的指令(就4個而已):

<leader>vv- Grep for the word under the cursor
<leader>va - Like vv, but add to existing list
<leader>vo - Select the files to search in and set grep options
:Grep SearchString


<leader>預設值是對應到"\"。

大部分的調整是都在<leader>vo裡找的到,只是有少部分的預設值可以從vimrc裡修改,像我自己是加了底下這些預設值,這些預設值大都是for windows才需要的,除了第1個是為了很懶的我,連5個字都要建個mapping變只需打2個字,哈哈。
map f/ <esc>:Grep
let g:EasyGrepCommand = 1
let g:EasyGrepFileAssociations = "C:\\Program Files\\Vim\\vim72\\plugin\\EasyGrepFileAssociations"
let g:EasyGrepRecursive = 1
let g:EasyGrepHidden = 0
let g:EasyGrepExtraWarnings=0
let g:EasyGrepIgnoreCase= 1


2009/03/03

VIM tip: 搜尋不分大小寫及取代

:set ic


ic是ignorecase的縮寫,不分大小寫在windows上常用到,Linux上就不太常用到了。若是需要用原字串作為取代一部分,可以再用&符號或\1之類的表示。舉例來說,若想取代所有AAA, AAa, Aaa...分別取代成AAA123, AAa123, Aaa123,可以這樣輸入:

:set ic
:%s/AAA/&123/cg

Linux Tip: less從最後一行開始看

方法1:
less +G file_name

方法2:
執行less後,G到最後一行,gg到第一行。這跟VIM是不是很像呢?