2015年7月12日 星期日

VIM中的縮排(Tab)

程式設計師間對於縮排鍵(Tab)的使用真是百家爭鳴,
主要的問題是:
1. 一次縮排八個字元太多,縮排沒幾次code都看不見了。
2.在各家環境中,一個Tab鍵造成的縮排可能不同於你想像中的8個字元(Characters)長,而且tab鍵看不見,某些文字處理情境容易造成錯誤(例如弄混tab跟space之類)。

目前看到的多半都是讓一次的縮排使用4個字元,但是具體作法就百家爭鳴了,Vim提供以下幾種方式:
1. set tabstop=4
     改變Tab鍵為一次縮進4個字元,最好是別這樣改,因為其他程式不知道Vim改掉Tab的縮進值,格式怕跑掉,而且某些印表機吃到一個Tab鍵就是印八格,改到這種的印出來格式跑掉就糗了。
2. set softtabstop=4
     混用Tab與space,保留tabstop=8,改成每次按下Tab就插入4個Space,再度按下Tab時會先刪除原來的4個Space,改成插入1個Tab符號(^I)。
3. set smarttab
     混用Tab與space,用於縮排時每個縮排符號使用shiftwidth,而在第一個非空白字元後的Tab就真的使用(^I)
4. set expandtab
     不使用縮排符號(^I),改以Space替代,如果縮排寬度沒改的話就是8個Space,<BS>要按8次刪除

支持使用space的人認為就是直接把(^I)從文檔中全部拿掉,^I可能依系統不同造成佔用不同長度的空間,改成Space就是每個Space固定一個字元。

反方說法是保留其他文檔使用者自訂Tab寬度的權力,畢竟有的人愛縮排2,縮排3不一定,而且一個(^I)比八個Space佔用較少的空間。

我目前使用的是這樣,必要之惡:
set tabstop=8
set softtabstop=4
set expandtab

不管使用縮排符號與否,對於已經存在的文檔最好是統一一下,此時可以使用:%retab!
將文檔內縮排符號改為Space:
:set expandtab
:%retab!
將文檔內縮排符號改為^I:
:set noexpandtab
:%retab!

#其實set list可以讓Vim將Tab符號顯示為^I, 將行末顯示為$,只是Tab或是行數多的時候很醜,set nolist可以關掉。

VIM: Buffers vs Tabs

Vim在7.0開始的分頁(Tabs)功能對於依靠直覺使用工具的人來說應該不難理解,就像Firefox、Chrome、Ultraedit之類上的應用一般,開一個新檔案就開一個新頁籤(tab)去放,
 一個tab一個檔案,很清楚。

但是對於老用戶如我來說,新出現的分頁功能要我照Windows下的慣用用法來用實在是有夠彆扭,因為Vim本來就有運作良好的buffer機制可以進行多檔編輯,
交互參考還可以搭配vs(plit)開兩個視窗來做,chrome上的分頁用法套在這裡貌似不太適用。

事實上Vim的頁籤功能原本的設計意圖的確不是這樣用,開始說明之前先回顧一下Vim的buffer與window:

Vim所謂的buffer,指的就是編輯中的文檔(Text),有一個比較傳神的形容叫做instance of file,
當Vim打開一個檔案要編輯的時候,他事實上是將整個檔案讀到記憶體中的一塊空間,
我們編輯的是記憶體裡的副本,因此改完了要:w寫回去,
而當Vim同時編輯兩個檔案的時候,所作的就是將第二個檔案讀到另一塊記憶體的空間中,我們隨後可以使用bnext/bprev在兩管buffer中切換。

而 Vim中的window指的是一種顯示buffer內容的方法,是"任何"buffer,一次一個,
當我們正在編輯一個檔案,我們可以split開啟另一 個window,此時另一個window顯示的還是原本在編輯的buffer,
隨後我們bnext切換到另一個buffer,前面提到的交互參考就做出來 了。

以上是buffer與window,根據Vim的文件,buffer、window與tab的定義是這樣:
buffer: the in-memory text of a file.
window: viewpoint of a buffer.
tab: a collection of windows.

來 做個實驗,我們打開兩個檔案,使用垂直分割讓他們能同時顯示,然後:tabnew開一個新的tab,這次水平分割兩個window讓他們讀進方才的兩個檔 案,:tabnext來回切換tab, 
你會發現tab就真的只是讓window以不同形式layout,他們事實上還是共用同一組buffers。

具 體上tab的用法其實也就是這樣,讓你可以切換不同的layout,可以一個tab開啟.c與.h做交互參照,然後另外一個tab單開.c檔專心做編輯,
也可以一個tab編輯一個專案,另一個tab分開另一個專案,當然要一個tab開一個不同的檔案還是可以的,只是我會說那不是原本設計的意圖 (intention)。

2015年7月11日 星期六

VIM的剪貼簿(register)

關於在Vim裡的複製與貼上,help copy-move 可以得到詳細的說明。
較為特別之處是Vim提供了暫存器(register)的觀念,可以將他想成buffer的概念,
剪下來的東西分別以自動或手動的方式放置到不同的register,稍後就可以統一處理。

想像一個場景,用Windows的筆記本編輯一個檔案,你發現想要複製的內容分佈在檔案的不同部位,甚至是不同的檔案中,
通常的操作就是捲到目標處,複製,回來貼上,捲到下個目標處,回來再貼上,打開另一個檔案,重複本行的行為。

使用Vim的情景會是找到目標,複製到某暫存器,繼續找下一個目標,複製到另一個暫存器,打開別的檔案找到目標,複製到另一個暫存器,一直到暫存器用完或是全部複製作業結束,然後回來原先的編輯處按需要一個一個貼出來,以下是常用的register及功能介紹。

Vim總共有九種類型的暫存器, 共74個,
使用時按"接著要使用的暫存器,需要配合d、c、s、x、p等指令,
例如"a2x表示剪下游標後的兩個字元存入"a暫存器, "ap表示將a暫存器內容貼在游標處,
1. unmamed register ""
2. number register "0 ~ "9
3. small delete register "-
4. 26 named register "a ~ "z 或 "A ~ "Z
5. 4 read-only register ":  ".  "%  "#
6. expression register "=
7. selection and drop register "* "+ "~
8. black hold register "_
9. last search pattern register "/

1. unnamed register:
     Vim執行d, c, s, x, 等刪除行為, 或是複製行為y時就會將被剪下來或是複製下來的內容放進這個暫存器, 不管有沒有指定要使用那一個暫存器, 也可以說這個暫存器就是指向最後一個使用的暫存器, 在Vim內使用複製、剪下與貼上時,如果沒有指定使用哪個暫存器,東西就是存在unnamed register,順帶一題指定存入""的話,實際會寫入"0。

2. number register:
     number register也是屬於自動使用,沒指定暫存器的話,最近一次的複製(yank)內容會存到"0, 而最後一次的刪除(x|d)或變更(c)會將被去掉的內容放在"1 ,每次"1被換成新的內容時,前一次存著的內容就會推到"2,以此類推,最後的"9放的內容會自動丟掉。

3. small register:
     類似"1, 差別在少於一行的內容會放在"-, 大於一行的內容放在"1

4. named register:
     給你手動使用的register, 指定存到小寫暫存器("a ~ "z)時以新的內容置換掉原來暫存器的存放內容,指定存到大寫暫存器("A ~ "Z)時新的內容會被附加在原來內容的後面。

5. 4 read-only register:
     這四個暫存器值是Vim自己填的,你只能用p或P貼出去。
     ".     -     最後以i或o命令插入的文字, 打了1234abcd, ".裡面放的就是1234abcd
     "%     -     當前編輯的檔名
     "#     -     替代用檔名(alternate name)
     ":     -     最後執行的命令, 例如執行shell命令:! ls, ":裡放的就是! ls

6. expression register:
     read only register, 這個暫存器的用法有點不容易搞清楚,說明文字裡說的是讓我們評估Vim的表示式(expression),如果表示式的結果能被轉換成字串,字串就可以被插入當前游標所在地,很拗口的說法。
     實際上的使用可以當作一個簡易的計算機,在輸入模式時按下Ctrl+r及=,游標會顯示在命令行讓你鍵入命令,我們輸入一個算式例如4*2,按下enter後,結果8就會被插入文件中游標最後位置。
     所謂的表示式並不限於數學的運算式,只要是按下:後能在命令列輸入的命令就可以用,暫存器會存下執行的最後結果供你隨後使用。

7. selection and drop register:
     用在圖形界面中與其他應用程式共用剪貼板,一般來說會被WM或是Windows的內建功能接管剪貼簿,這個功能實驗不出來。

8. black hols register:
     像是/dev/null,寫進去之後就不見了,也貼不出東西,當不想打亂register的時候用

9. Last search register:
     這個register存放最後執行的search pattern,主要影響到n及hlsearch,意思是說當你想更換n及hlsearch的對象,但是又不想真的執行search時,使用:let設定這個register的值, ex: let@/='search_target'

#順道一提,:reg {arg}與:dis {arg}列出指定的register及值,:reg沒指定對象的話就列出全部register。