顯示具有 vim 標籤的文章。 顯示所有文章
顯示具有 vim 標籤的文章。 顯示所有文章

2017年6月5日 星期一

vim中的零寬度匹配

最近發現vim有一個功能
叫做「零寬度匹配」
還蠻繞口的
還搞不太懂,
先記錄一下吧~

參考資料:
[转]vim中的零宽度匹配 - [1.2 vim]
VIM REFERENCE MANUAL

2014年4月2日 星期三

vim - 複製

想要把第5行到第10行的內容貼到第12行之後,
要使用下面的指令
:5,10 co 12

參考資料:
[保留] 请问如何用vi 复制第5行到第10行并粘贴到第12行之后?

2014年3月13日 星期四

在linux下,使用ssh來操作vim同時trace code

這一次換到新的公司,
雖然工作環境看似windows,
但是,source code都必需要連到linux server操作,
因此必需要開始重新復習怎麼使用vim了。

要藉著使用ssh的方式來使用vim去trace code,
這個之前沒有完整用過,
以前至少是在本機端的QT IDE上操作,
現在要在遠端上使用,
必然會遇到很多問題,
不過,遇到問題是成長的過程,
這個是好的。

首先,如何在vim的編輯下,
跳出來shell,
並且在完成shell下的動作之後,
再回到vim哩?

:sh
暫時退出vim進入shell命令行,執行完後,ctrl+d退出重新進入vim編輯繼續

2012年8月2日 星期四

exVim

一直以來,都想要讓vim可以取代SourceInsight的目標,
但是一直沒有時間去做。

前一陣子發現同事有使用exVim,
這一個功能跟SourceInsight很類似,
現在暫時先研究如何使用~
之後就可以同時使用exVim與SourceInsight,
最後當然希望可以取代SourceInsight

先使用安裝檔的方式,讓它全部幫你設定完!
我的目標是建立一個portable的方式,
目前因為連操作方式都不熟,
還是先用最簡單的設定方式吧~

exVim主頁
exVim wiki

1. 安裝檔案
直接到exVim主頁選擇Download
選擇install去下載,
這一個安裝會全部的設定幫你設定好。

2. 建立一個Project的vimentry
到Project的根目錄資料夾,
點選右鍵,新增exvim vimentry file
新增完成之後,
直接點選這個檔案,
就可以開啟這一個視窗了

3. 掃描所有的檔案
當開啟新增的exvim vimentry file之後,
直接按\R

之後,直接點剛剛新增的vimentry的檔案,
就可以開啟這一個Project了

而以後若要重新掃描整個檔案的話,
則在指令列輸入
:Up[date] tag
:Up[date] symbol

4. 尋找tag
可以直接在目前鼠標的位置,按\]
或是在指令列輸入
:TS <tag-name>

When you are in tag select-window, you can press <enter> to jump to the definition, meanwhile the exTagSelect will record the position to the exJumpStack, you can use <leader>tt to check your stack result. And use <leader>tf, <leader>tb to move backward and forward from the next/last definition.
這個部分的tt, tf, tb不知道如何使用??



參考資料:
ExVim 试用手记 —— 编写代码之 移动光标
Think more... - exvim

2011年7月8日 星期五

在Source Insight直接透過vim打開目前的檔案

透過以下的設置,可以直接在Source Insight透過Vim打開目前已開啟的檔案。

我直接在Alt+a設定成呼叫Vim,並且跳到游標所在的行~

1. Source Insight工具列OptionsCustomer Command...Add...New Command name填入EditWithVim
2. 在Run的欄位填入: E:\Portable\PortableApps\gVimPortable\gVimPortable.exe --remote-tab-silent +%l %f
意思是說透過vim打開目前的檔案,並且跳到指定的行
%l指的是當前的行
%f為檔案名稱
3. Source Insight工具列OptionsKey Assignments
4. 在Command的欄位打入EditWithVim,並且按Assign New Key...→直接按Alt+a

大功告成了

[2012.08.02 補充]
設定Source Insight可以直接打開目前檔案的目錄
設定的流程跟上面類似

我直接在Ctrl+d設定成開啟目前瀏覽檔案的目錄~

1. Source Insight工具列OptionsCustomer Command...Add...New Command name填入Explore Project Folder
2. 在Run的欄位填入: explorer %d

意思是說開啟目前瀏覽檔案的目錄~
%j為the source directory of the current project
3. Source Insight工具列OptionsKey Assignments
4. 在Command的欄位打入Explore Project Folder,並且按Assign New Key...→直接按Ctrl+d

完成~~

[2014.03.31 補充]
開啟Project的根目錄,
而這一個要可以work,
在建立目錄時,必需要設定project的根目錄在哪裡。


在Options→Customer Commands→Add→Explore Project Root Folder→在Run裡面輸入「ShellExecute explore %j」→Close

Options→Key Assignments→選擇「Explore Project Root Folder」→按「Assign New Key...」→接著按你想輸入的key

參考資料:
鱼与熊掌可兼得: vim和source insight的配合使用
source Insight常用自定义命令和一些小技巧[z]
Source Insight Help - Custom Commands

2011年6月27日 星期一

vim file auto reload

read

2011年4月20日 星期三

vim function already exists

現在設定vim當修改了_vimrc(或.vimrc)後,會重新load新的vim設定。

在設定完後,會出現一個錯誤訊息,
vim function already exists

是說自定的function又被重覆定義了一次~
這個時候只需要在定義的function後面加入一個!
如下:

function! MySys ()
return "linux"
endfunction


這樣在reload _vimrc時,就不會出現重覆定義的錯誤訊息了

參考資料:
Suppressing function already exist warnings ?

2011年4月3日 星期日

VIM自定函式集合

判斷目前的Platform平台
" Platform
function! MySys()
if has("win32")
return "windows"
else
return "linux"
endif
endfunction

VIM的內建的變數

$VIMRUNTIME
表示vim的主程式的目錄

2010年10月29日 星期五

依檔名設定filetype

在vim中,會自動判斷副檔名或是檔案內容決定這是何種檔案~

但是,有時候會有一些新的副檔名沒有辦法被vim判斷出來!

這個時候,就要在filetype.vim修改,當什麼副檔名的時候,自動設定filetype特定的filetype。

例如要把*.dsc設定與*.c的配色相同,則在filetype.vim最下面加入以下的內容

augroup filetypedetect
au! BufRead,BufNewFile *.dsc setfiletype c
augroup END


如此一來,所有的dsc檔的配色都會與c的相同

[2014.05.08 補充]
若想知道目前vim的環境變數為何,
直接輸入
:set

而若想知道filetype這一個環境變數值為何,
直接輸入
: set filetype

參考資料:
Vim Tips

2010年10月10日 星期日

加入註解

好的編輯器送你上天堂,不好的編輯器送你住套房。

但是,有好的編輯器還是要有好的設定,才能送你上天堂。

同時註解多行
用virtual mode的方式選取多行
:'<,'>s/^/\/\//g

把暫存器a的內的東西貼出來
"ap

一般來說 :g 的運作方式是根據一行一行來做的,當你用下面指令的時候,
:g/pattern/y a
這代表的意思其實只是把每次比對到的那行都 yank 複製到"a這裡面去。所以這就會造成我們最後只會拿到最後一行的結果,而不是每個比對到的都會進入到"a去。所以就必須告訴 vim 我們指定的內容是要做附加而不是覆蓋:
:g/pattern/y A

刪除選取的行數,並且存到暫存器a
"aD

:'<,'>d a

取得目前檔案的目錄
:echo fnamemodify("%", ":p:h")
%表示current file name

取得目前檔案的名稱
:echo expand("%")

若執行的指令很可能會出現錯誤訊息,但仍不想要顯示錯誤訊息的話,可以使用silent,例:
:silent! exe "normal /path\<CR>"

:silent! /xxxfdafd

在目前位置加入時間
::map <F2> a<C-R>=strftime("%c")<CR><ESC>


流程
1. 把目前的時間存到暫存器a
//[-start-strftime("%y%m%d")-CCD9527-remove]//
SET @a=time()

2. 註解掉目前選取的所有行數
:'<,'>s/^/\/\//g

3. 把選取的部分附加到暫存器a
"AD

4. 把目前的時間存到暫存器a
//[-end-strftime("%y%m%d")-CCD9527-remove]//
SET @A=time()

5. 把暫存器a貼到目前的位置
"ap


line
append
cursor
getline
mode
confirm
repeat

一行一行讀出來(getline)
加工(substitute)
加到list(add)
刪除原本的資料

_vimrc_utils

" Decide comment symbol
function CommentSymbol ()
"
" Set the default comment
"
let markid_start='//'
let markid_end='//'

"
" Set the comment by the different file type
"
if exists("&filetype") != 0
if (&filetype == "asm") || (&filetype == "pov")
let markid_start=';'
let markid_end=';'
elseif (&filetype == "make") || (&filetype == "inform") || (&filetype == "conf")
let markid_start='#'
let markid_end='#'
elseif &filetype == "dosbatch"
let markid_start='rem '
let markid_end='//'
endif
endif

return [escape(markid_start, ' /'), markid_start, markid_end]
endfunction

"Mark from N to M
function MarkN2MbyComment (N, M, Comment)
let ln = a:N
while ln <= a:M
call MarkByLine (ln, a:Comment)
let ln = ln + 1
endwhile
let @/=''
endfunction

" Remove Mark from N to M
function RemoveMarkN2MbyComment (N, M, Comment)
let ln = a:N
while ln <= a:M
call RemoveMarkByLine (ln, a:Comment)
let ln = ln + 1
endwhile
let @/=''
endfunction

" Set UserID from user input
function SetUserID ()
if exists("g:UserID") == 0

let g:UserID = inputdialog("Set Comment Symbol : (EX:IB06920001)")

if g:UserID == ""
let g:UserID = "TempUser"
endif

endif
endfunction

" Clear UserID
function ClearUserID ()

if exists("g:UserID") != 0
unlet g:UserID
endif

endfunction

" Add ceiling and floor comment
function CeilFloorComment (lnL, lnF, markid_start, markid_end, type)

exe append(a:lnL, a:markid_start . "[-end-" . strftime("%y%m%d"). "-" . g:UserID . "-" . a:type . "]" . a:markid_end)
exe append(a:lnF - 1, a:markid_start . "[-start-" . strftime("%y%m%d"). "-" . g:UserID . "-" . a:type . "]" . a:markid_end)
exe "normal " . (a:lnL + 2) . "G"

endfunction

" AddCommentBlock (F9)
function AddCommentBlock (lnF, lnL)
if line(".") == a:lnL

call SetUserID ()

let [comment, markid_start, markid_end] = CommentSymbol ()

call CeilFloorComment (a:lnL, a:lnF, markid_start, markid_end, "add")

endif
endfunction

" ModifyCommentBlock (F10)
function ModifyCommentBlock (lnF, lnL)
if line(".") == a:lnL

call SetUserID ()

let [comment, markid_start, markid_end] = CommentSymbol ()

call CeilFloorComment (a:lnL, a:lnF, markid_start, markid_end, "modify")

endif
endfunction

" RemoveCommentBlock (F11)
function RemoveCommentBlock (lnF, lnL)
if line(".") == a:lnL

call SetUserID ()

let [comment, markid_start, markid_end] = CommentSymbol ()

call MarkN2MbyComment (a:lnF, a:lnL, comment)

call CeilFloorComment (a:lnL, a:lnF, markid_start, markid_end, "remove")

endif
endfunction

function VisualAddCommentBlock ()
let lnF = line("'<")
let lnL = line("'>")
call AddCommentBlock (lnF, lnL)
endfunction

function NormalAddCommentBlock ()
let lnF = line(".")
let lnL = line(".")
call AddCommentBlock (lnF, lnL)
endfunction

function VisualModifyCommentBlock ()
let lnF = line("'<")
let lnL = line("'>")
call ModifyCommentBlock (lnF, lnL)
endfunction

function NormalModifyCommentBlock ()
let lnF = line(".")
let lnL = line(".")
call ModifyCommentBlock (lnF, lnL)
endfunction

function VisualRemoveCommentBlock ()
let lnF = line("'<")
let lnL = line("'>")
call RemoveCommentBlock (lnF, lnL)
endfunction

function NormalRemoveCommentBlock ()
let lnF = line(".")
let lnL = line(".")
call RemoveCommentBlock (lnF, lnL)
endfunction

function VisualAddSelectionComment ()
let lnF = line("'<")
let lnL = line("'>")

call AddSelectionComment (lnF, lnL)

endfunction

function NormalAddSelectionComment ()

let lnF = line(".")
let lnL = line(".")

call AddSelectionComment (lnF, lnL)

endfunction

function AddSelectionComment (lnF, lnL)
if line(".") == a:lnL

let [comment, markid_start, markid_end] = CommentSymbol ()

call MarkN2MbyComment (a:lnF, a:lnL, comment)

endif
endfunction

function RemoveSelectionComment (lnF, lnL)
if line(".") == a:lnL

let [comment, markid_start, markid_end] = CommentSymbol ()

call RemoveMarkN2MbyComment (a:lnF, a:lnL, comment)

endif
endfunction

function NormalRemoveSelectionComment ()

let lnF = line(".")
let lnL = line(".")

call RemoveSelectionComment (lnF, lnL)

endfunction

function VisualRemoveSelectionComment ()

let lnF = line ("'<")
let lnL = line ("'>")

call RemoveSelectionComment (lnF, lnL)

endfunction

function MarkByLine (CurrentLine, comment)
let TempContent = getline(a:CurrentLine)
let TempContent = substitute (TempContent, '^', a:comment,'g')
exe a:CurrentLine . "d"
exe append (a:CurrentLine - 1, TempContent)
exe "normal " . a:CurrentLine . "G"
endfunction

function RemoveMarkByLine (CurrentLine, comment)
let TempContent = getline(a:CurrentLine)
let TempContent = substitute (TempContent, '^\(\s*\)' . a:comment, '\1', 'g')
exe a:CurrentLine . "d"
exe append (a:CurrentLine - 1, TempContent)
exe "normal " . a:CurrentLine . "G"
endfunction

"AddSelectionComment
nmap <F3> :call NormalAddSelectionComment ()<CR>
vmap <F3> :call VisualAddSelectionComment ()<CR>
"RemoveSelectionComment
nmap <F4> :call NormalRemoveSelectionComment ()<CR>
vmap <F4> :call VisualRemoveSelectionComment ()<CR>

" 移除每一行最後多餘的空白
nmap <F6> :silent! %s/ \+$//gc<CR>

" 把tab轉換成空白
nmap <F7> :silent! %s/\t/ /gc<CR>

nmap <F8> :call ClearUserID ()<CR>
vmap <F8> :call ClearUserID ()<CR>
vmap <F9> :call VisualAddCommentBlock ()<CR>
nmap <F9> :call NormalAddCommentBlock ()<CR>
vmap <F10> :call VisualModifyCommentBlock ()<CR>
nmap <F10> :call NormalModifyCommentBlock ()<CR>
vmap <F11> :call VisualRemoveCommentBlock ()<CR>
nmap <F11> :call NormalRemoveCommentBlock ()<CR>


參考資料:
vim 的選擇模式 (visual mode)
有多少種方式可以在vim中插入行號
我的 VIM 整理
vim 提供的函數
vim 選擇模式下的熱鍵( Key mapping )

2010年3月13日 星期六

vim自動補齊功能

在寫程式的時候,最好希望編輯器在我們打完了部分的字之後,它可以列出讓我們可以選擇的候選字。

像是Source Inside與Visual C++和QTCreate都有這一個功能~沒有錯~vim也可以有這一個功能~

開最一篇文章,是希望最後可以把這一篇文章補成完成像是Source Inside的功能(遠望~)

目前還沒有很多時間去研究,不過,其實,一開始它就有一部分很簡單的自動補齊功能啦~

自動補齊(Auto-completion) (在Insert Mode中使用)
Ctrl-N 自動補齊檔案內的下一個可能字(word)
Ctrl-P 自動補齊檔案內的上一個可能字(word)
Ctrl-X Ctrl-F 自動補齊檔名

參考資料:
給程式設計師的Vim入門圖解說明
在linux下用vim取代source insight (vim 6.4)
用vim替代source insight
使用了盜版source insight慣了,不知道linux有什麼好的編輯軟件
linux下有沒有像source insight 一樣的源程序查看工具阿?

2010年3月10日 星期三

vim的expression與substitute配合運作

vim強就強在可以對文字檔案本身,做有規則性的動作,像是同時在行首加入某一個特定的字串、找到某一個pattern,則執行某一個取代的動作。

另外,vim本身內建了很多function,透過expression與substitute結合起來,真的是天下無敵啊~可以不需要用到外部指令(當然有外部指令更好,就當作備用嘛~)

例如:
getline({lnum} [, {end}])
可以取得目前文件內容的第lnum行的資料。

input({prompt} [, {text} [, {completion}]])
可以讓使用者輸入資料

inputdialog({prompt} [, {text} [, {cancelreturn}]])
可以讓使用者透過跳出的視窗輸入資料

put
把字串放到文章中

line ({expr})
取得行數
Q: 取得目前指標的行數
line(".")
Q: 取得目前檔案最後一行的行數
line("$")
Q: 取得選取區塊的第一行行數
line("'<")
Q: 取得選取區塊的最後一行行數
line("'>")

submatch({nr})
而 submatch(0) 則是用來表示前面所尋找的整個字串(pattern),而submatch(1)的話,則用來表示第一個以 \(…\) 夾起來的子字串。

substitute({expr}, {pat}, {sub}, {flags})
在{expr}尋找{pat}並且用{sub}取代

假設這裡有一個文章,內容為
article.txt
I work hard,
I try to work smart.
Can I accomplish it?


Q: 如何在文章的內容每一行前面,加入行數
A: :%s/^/\=line(".") . "\. "/
解釋:
尋找^,也就是開頭字元,一般來說,這是看不到的!
要在substitute後面使用expression則必需使用\=
原文: When the substitute string starts with "\=" the remainder is interpreted as an
expression.
而line(".")上面說過了,就是表示目前指標的行數
而後面又有一個「.」我是把它想成連接的功用,與後面"\. "連接起來

Q: 如何把I改為You
A: %s/\<I\>/You/g
解釋:
\<I\>是表示我要找的就是一個單字I而已,而accomplish其中的i就不會被我取代掉,不然accomplish會被取代為accomplYoush

Q: 如何透過expression的方式,在I後面加上and you
A: %s/\<I\>/\=submatch(0) . " and you"/
解釋:
submatch(0)就是表示前面所找到的符合的字串
.是表示連接後面字串的動作的動作

Q: 如何同時在最前面加入行號,並且同時把I改為I and you
A: %s/\(.*\)\(\<I\>\)\(.*\)/\=line(".") . "\. " . submatch(1) . submatch(2) . " and you" . submatch(3)/

[2010.03.28 補充]
Q: 要如何行號補0,像是1就印001,2是002,而10就是010,這樣排版才會漂亮
A: :%s/^/\=printf("%03d ",line("."))/g

[2015.02.02 補充]
Q: 直接在14行到145加入index,
14是1.
15是2.
...
145行是132.
A: 14,145s/^/\=printf("%d. ",line(".") - 13)/g

參考資料:
vim 小技巧 : 行數的顯示與利用

2010年1月23日 星期六

vim 的 search 搜尋功能

若要在一個文件中,同時找到「肥仔魚」、「肥仔豬」、「肥仔貓」,有一個規則的字串,可以使用正規表示式。
/肥仔[魚豬貓」
就可以找到,[]表示的是一個字元,但是,可以被選擇的就是中間的那三個字元~

如果我們今天要找的是英文文件,想要搜尋的 lemon tree、lemon soup、lemon juice 的話,就必需使用「\|」這個「或(or)」運算
/lemon tree\|lemon soup\|lemon juice

想要搜尋的東西會是像這樣 1234- 1234-5 1234-56 1234-567 1234-789 1234-2399 1234349898。

我們大概可以分析一下,這個字串大約是以1234開頭,然後跟上或不跟上「-」之後,再接上一堆數字。先把指令給大家:

/1234[-]\{,1}[0-9]\{1,}

或是:

/1234-[0-9]\{1,}\|1234[0-9]*

我們可以先看第一個方法,在第一個方法裡面用了兩個特別的東西:\{1,}\{,1}。在前面我們剛剛說中括號後面如果不跟上任何東西就表示中括號裡面的某一字元要出現一次。而現在\{n,m}就是用來表示可以對中括號裡面的字元挑選幾次。(選幾次就會出現幾個字元)

\{1,}表示至少要出現一次(至少選一次),而\{,1}表示最多出現一次(最多選一次,不選的話就代表中括號裡面的字元都不出現)。當然我們也可以寫成\{3,9},這就表示可以在中括號裡面的字元挑選三到九次,也就是會出現三到九個中括號裡面出現的字元。

而在第二個方法裡面,則在中括號後面緊跟著一個「*」,這其實代表\{0,},也就是 0 個到任意個都可以的。而中括號裡面我們用了「0-9」這就表示 0123456789 都可以。同樣對於字母我們也可以用「a-z」表示所有小寫英文字母和「A-Z」用來表示所有的大寫字母。

儘管如此,我們可能還是不滿足。舉例來說,我可不可以只找以「傷寒」開頭的字串或是以「主之。」結尾的字串?

假如我們只想找以「傷寒」開頭的字串,可以這樣輸入搜尋命令:

/^傷寒

^」開頭就表示要找的東西是一行的開頭。

但是有時候為了排版需要,在「傷寒」前面可能會出現一些空白或是 tab ,如果我們只是用上面的指令就不夠。所以配合我們剛剛的中括號帶次數的方式就可以做到:

/^[\t <Space>]*傷寒

<Space>是為了網頁展示可能看不到所以用<Space>來表示空白。而「\t」則是 vim 中用來表示 tab ,所以上面的命令就代表一行的開頭有任意個空白或是 tab 的混合,而其後跟上「傷寒」兩個字。

那結尾呢?還記得我們怎麼把游標移到行尾嗎?就是用「$」這個符號。所以要找以「主之」結尾的字串就可以用:

/主之$

不過你可能還是不滿意,比方說 vim 可不可以幫我找出忘了在句末補上句號的地方?可以!我們先看指令:

/.*[^。]$

或是不要看到整句:

/[^。]$

中括號裡面的字元 X 如果前面出現「 ^ 」就表示除了 X 以外的字元都算是想要比對的字元。所以上面第一個的指令就表示搜尋句末沒有句號的行(這一行至少要有一個字元,因為用了[^。]$,而不是 [^。]*$)。第二個指令比較不一樣是只搜尋句末不是以句點結束的字元。其實看哪個指令對你來說比較「順眼」就用哪個。

有關這些特殊符號還有許多更 powerful 的功能,你可以在 vim 的指令模式下打 :h pattern 看更詳細的資料。或用 :h regular 看有關 regular expression 的詳細說明。

現在,你已經可以用 vim 編輯、刪除,也可以做搜尋了,基本的 vim 操作到這裡已經可以說是差不多了。

[2020.05.29 補充]
確認是否是數字
其中包含負數與小數的情況
^-?\s*?([\d\,]+(\.\d{1,})?|\.\d{1,})\s*$


參考資料:
vim 的 search 搜尋功能
javascript - 带小数位的数字,以允许在正则表达式验证中使用逗号

直接透過vim去取代文件內容

在之前,我要取代文件的內容,都是使用外部指令grep,來完成!

雖然,這樣也可以達成,不過,其實透過vim的內建功能就可以達到這一個功能啦~

Q: 選取特定幾行,執行取代的動作
A: 在之前我就要去算由第n行到第m行要執行取代的動作,那麼我就直接使用以下的動作
: n,ms/{pattern}/{string}/gc
而這裡還有另一個方法:
在你想置換部分的開頭按下大寫「V」然後用移動游標(我們之前講的 vim 移動游標的方法在選取模式下都可以用)到你想要的位置之後按「:」就會跳到輸入指令的狀態,如下圖最下面那行看到的一樣:


或者是如果你很確定要從現在游標所在行之後的所有行都置換,可以下成
:.,$s/vim/VIM/g
冒號一開頭的那一小點「 . 」就代表游標現在所在行,「$」則用來表示最後一行。

可以找到系統的相關說明
:h :s

假如我現在有一筆人名、電話的資料,由於是隨手記的,上面自然就是沒有排序過。那沒排序過對於想要在上面找資料的人就很麻煩。萬一人名記不太清楚,電話號碼也記得七七八八,雖然說有 vim 方便的 search 功能,但總是感覺不足。(當然這只是假設情況,因為實際上可能大家都已經建立某種方便搜尋的資料庫了)

我們先假設人名、電話的對應長成這樣:

趙大明  1235478982
錢小名 1223450012
王孫李 5938123812
周渚衛 1384914191
沈以情 2345934981


那我可不可以讓它變成這樣子?

1235478982   趙大明
1223450012 錢小名
5938123812 王孫李
1384914191 周渚衛
2345934981 沈以情


我可以下這樣的指令完成這個工作:

:%s/\([0-9]\{1,\}\)\([^0-9]\{1,\}\)/\2 \1/g

前面部分\([^0-9]\{1,\}\)\( \)括起來的,表示這是一個的單元,之後就可以依照它出現的順序而使用 \1, \2,… ,來代表它。所以我們可以看到[^0-9]就表示非數字的部分,後面的\{1,\}如果你還記得的話,就是代表出現至少一次。可以用「\+」來代表至少出現一次。你可以用 :h /multi 看到更多這些替代符號。

所以把 pattern用\( \)分開之後,我們就可以用 \1,\2 來把他們交換位置。

最短符合

之前提到的 "\{n,m}" 會盡可能的找最長符合字串。若是要找最短符合字串,要改用 "\{-n,m}" 。

譬如說:

/ab\{-1,3}

會與字串 "abbb" 中的 "ab" 符合。因為要求的是最短符合字串,所以只會比對到1個"b"。如果要強制它能比對到比下限多一些的話,還得要多加點東西。

同樣的規則也可以套用到去掉 "n" 或 "m" 的情形。甚至可以同時不寫 "m" 和 "n" ,只留下 "\{-}" 。這個指令的意思是符合零次以上,愈短愈好…所以一定會比對到符合零次。這些規則可以組合使用,譬如:

/a.\{-}b

會對 "axbxb" 中的 "axb" 符合。如果改用:

/a.*b

就會試著找最長符合字串,所以就會符合到整個 "axbxb" 。

[2010.02.27 補充]
把完整的目錄名稱,只留下檔案名稱
例如內容為
d:\work\project\main.c
d:\work\project\make.dxc
d:\work\main.h
可以使用
:%s/.*\\\(\w\+\.\w\+$\)/\1/g
尋找字串,必需滿足以下二個條件
1. 先說明\w\+\.\w\+$的部分,是以尋找符號.前,至少有一個非特殊字元且後面也是至少有一個特殊字元的字串,並且後面就是字尾啦,這樣會找到main.c、make.dxc與main.h;
而用\(...\)把它包起來,就是要把它當作pattern,等一下取代則可以使用。
2. .*\\則表示找到在\前面有任意字元的字串
當內容中,有符合以上的字串,就處理\1這一個動作,也就是把符合這一個字串的部分,整個替換成第一個pattern

現在又想到一個問題,Makefile是沒有副檔名的,所以沒有辦法使用「任意字串.任意字串」當作條件去尋找啦。
還好又想到正規表示式是貪婪的,如何在同一行符合的字串長度有兩個,則他會選則最長的那一個。例如:
d:\work\project\main.c
符合.*\\條件有d:\與d:\work\與d:\work\project\
則,它會選擇最長的部分也就是d:\work\project\
因此,在這裡使用
:%s/.*\\\(.*\)/file name is \1 end/g
就可以取出檔名,並且在前面加入一些字串和後面加一些字串

[Add-20100307-start]
全域性的指令
:[range]g/pattern/[cmd]
cmd 是 ed 可用的指令,預設是 p(print),您可查一下 man ed,就可以知道有什麼指令可用。
需注意的是,d 是行刪除指令,凡含 pattern 的整行都會被刪掉,而且 range 不指定的話,預設是全篇文章,因為 g 就是代表 globe。

Q: 刪除空白行
:g/^$/d
有些書中寫成:%s/^$//g可以刪除空白行,這是錯誤的,因為 :s 這個指令只更動一行裡的內容物,但不會做刪除一行的動作。
[End-20100307-start]

[2010.04.05 補充]
Q: Delete all lines containing which didn’t contain string
A: :v/string/d

Q: want to match all non-printable characters and delete them
A: /\p\@!.
上列後面那一個小數點的意思是說滿足這一個條件的是有幾個字元,因為,上述只有一個小數點,所以是一個字元

Q: 找到不滿足的字串(不含有"String")
A: /\(String\)\@!

Q: 重覆a這一個字元3次
A: /a\{3}

Q: 重覆abc這一個字串元5次
A: \(abc\){5}

[2010.09.22 補充]
Q: 移除每一行最後面多餘的空白
A: %s/ \+$//gc

[2012.02.20 補充]
Q: 尋找最短符合字串,如何把以下內容前面縮寫的部分移到最後,並且加括號
API Application Program Interface
BLDK Boot Loader Development Kit

A: %s/\(^[A-Z]\{-}\) \(.*$\)/\2 \(\1\)/gc

參考資料:
vim 的取代置換功能「s」
vim 的 search 搜尋功能
規則表示式的運用
100 Vim commands every programmer should know
help pattern-multi-items

2009年12月30日 星期三

解決在vim下可以讀取Unicode的檔案

在vim下要讀取Unicode的檔案,vim要做以下的設定
:set encoding=utf-8

但是,如此一來,在windows下的gvim選單就會出現亂碼
解決方法如下
source $VIMRUNTIME/delmenu.vim
source $VIMRUNTIME/menu.vim


砍掉目前選單,再重讀一次,選單就不會有亂碼了。

狀態列的中文訊息亂碼,則要加上下面這一行來解決

language messages zh_TW.UTF-8

若要把menu及訊息設定成英文,就可以設定為:
set langmenu=none
:lang mes en


參考資料:
讓 vim 在 Windows 下用 unicode 選單不亂碼的方法
vim簡介及如何使用Unicode或其他編碼
Linux 底下的編碼顯示問題
Linux作業系統Vim/Vi編程提速

2009年12月22日 星期二

vim 分割視窗功能

使用vim來比較檔案
因為比較後的結果是使用分割視窗來顯示,所以,把它放在這一個單元
% vim -d file1 file2

例子:
一開始的檔案before.txt內容為
aaaa
dddd
cccc
hhhh


而之後的檔案after.txt內容為
aaaa
xxxx
yyyy
cccc


最後,執行以下的指令
% vim -d before.txt after.txt
會出現以下的比較畫面

紅色的意思應該是說,把左邊第二行改為右邊第二行,算是屬於修改的部分!

而右邊第三行與左邊第三行表示右邊新增一個yyyy的內容

而第五行則表示邊新增一個hhhh的內容

[2010.03.13 補充]
:split 分割視窗(可加檔名順便開啟另一檔案)
:diffsplit xxx 以分割視窗和檔案xxx做比較(diff)
Ctrl-W p 跳到前一個分割視窗(在兩個分割窗來回切換)
Ctrl-W j 跳到下面的分割窗
Ctrl-W h 跳到左邊的分割窗
Ctrl-W k 跳到上面的分割窗
Ctrl-W l 跳到右邊的分割窗

參考資料:
給程式設計師的Vim入門圖解說明

vim tab功能

在vim下,可以使用類似firefox, kkman,在一個應用程式中,可以有多個畫面!
我主要分成兩大類!

一個是使用:split:vsplit,把一個畫面同時分成兩個以上的畫面,用這一個的好處就是,可以「同時」看到多個檔案開啟!

而另一個就是使用tab的方式,雖然一次只能看到一個畫面,但是,開啟多個檔案的時候,不會讓windows最下面的開啟檔案非常的複雜~而且,檔案之間,仍然可以使用vim內建的copy功能(像是yy, p...等等指令)~而這一個也是本篇文章要提到的主題~

一次 vi 數個檔案來作編輯,預設 vim (也就是 vi) 同時可開 10 個 tab (這可以透過 set tabpagemax= 的方式設定,將它加大或減少)。

也就是說,當你想同時 vim 三個檔案 (例如 file1、file2、file3),可以:

vim -p file1 file2 file3

還可以:set tabline來定義 tab 顯示方式

tab相關的功能為:
=========================================
:tabdo
可以同時對目前的所有已開啟的tabfile執行相同的動作,這一個動作對我非常有用
例如:對目前所有用tab開啟的檔案內容把originalstring轉換成newstring
:tabdo %s/originalstring/newstring/gc

另一個例子,把所有的檔案中,有Caspar-comment的行刪掉
:tabdo %!grep -v Caspar-comment
如此一來真的可以省下非常多的打指令的時間

[start-20100307-add]
可以直接使用vim內建的功能達到,而不需要使用到外部指令
:g/.*Caspar-comment.*/d
[end-20100307-add]
=========================================
:tabn(ext)
當有多個tab的時候,在execute mode下打:tabn就是移動到下一個tab
括號的部分可以打,若不打的話,就是簡寫
或是 gt, C-PageDown也可以達到相同的效果
=========================================
:tabp(revious)
當有多個tab的時候,在execute mode下打:tabp就是移動到上一個tab
或是 gT, C-PageUp也可以達到相同的效果
=========================================
:tabs
顯示目前的所有tab的情況
=========================================
:tabnew filename
開啟一個新的tab;後面可以接新的檔案名稱
=========================================
:tabe(dit) filename
開啟一個檔案,當檔案不存在的時候,直接開啟一個新的檔案
=========================================
:tabc(lose)
關掉目前正在使用的tab分頁
=========================================
:tabo
關閉其它tab分頁
=========================================
:tab(fi)r(st)
跳到編號為1號的tab分頁
=========================================
:tabfind filename
尋找名為filename的tab分頁
=========================================
:tabl(ast)
跳到編號為最後一號的tab分頁
=========================================
:tabm [N]
移動到編號為N的tab分頁
=========================================
在vimrc內容中,加入熱鍵
" 加入tabnew, tabprevious, tabnext的快速鍵
nmap <C-t> :tabnew<CR>
nmap <C-Left> :tabprev<CR>
nmap <C-Right> :tabnext<CR>

=========================================
在新的tab頁面顯示help頁面
:tab help
=========================================
參考資料:
Vim 的小玩意: tab 怎麼玩?
vim tab 功能
My editor ,Vim
Vim7 的 <C-Left> 與 <C-Right>
VIM Tab & Space
Vim Hacks

把Vim折疊(folding)

在我要去上免費課程vim之前,我把「大家來學 Vim一個歷久彌新的編輯器」看過一遍之後,
原本我認為vim的folding是不重要的!但是,在經過上次去上過免費課程vim之後,覺得似乎有那麼一點噱頭~
所以,就打算寫這一篇文章!

突然發現,vim的文章在我的blog越來越多了,所以,就新增一個vim的tag吧~

其實,大部分就是參考「大家來學 Vim一個歷久彌新的編輯器」的內容,新增這一篇文章是可以把在學習過程中使用到的相關資訊補充在這裡!

折疊的產生
zfap 將游標所在處的那個段落折疊成一行。
zf7G 自游標所在處至全文第 7 行折疊起來。
:3,9fold 將第 3 行至第 7 行折疊起來。
5zF 將游標所在處起算 5 行的內容折疊起來。

也可以將所要折疊的部份以 Shift+v 標記起來,然以按 zf 就會將標記的內容折疊起來。

折疊的操作

zo 將游標所在處的折疊打開。open。
zc 將游標所在處已打開的內容再度折疊起來。close。
zr 將全文的所有折疊依層次通通打開。reduce。
zm 將全文已打開的折疊依層次通通再折疊起來。more。
zR 作用和 zr 同,但會打開含巢狀折疊(折疊中又還有折疊)的所有折疊。
zM 作用和 zm 同,但對於巢狀折疊亦有作用。
zi 這是個切換,是折疊與不折疊指令間的切換。
zn 打開全文的所有折疊。fold none。
zN 這是 zn 的相對指令,回復所有的折疊。

折疊的記憶
我們可以使用:mkview把折疊的情形記憶起來,下一次載入同一個檔案時就可以下:loadview,記憶起以前折疊的情況。

另外,這裡要注意一點的是,在Linux下,若你的家目錄下沒有.vimfiles\的話,就出現以下錯誤
Cannot create directory: ~/.vimfiles/view
這個時候,只要在家目錄下,新增一個.vimfiles就可以了。

而在Windows下使用GVimportable的話,就必需要在App\vim\下建立一個vimfiles就可以了~

當完成上述的步驟之後,在到execute mode輸入:mkview就可以把目前的folding情況儲存起來了!而下次打開檔案的時候,在execute mode下輸入:loadview就可以把上次這一個檔案的folding載入進來~

自動折疊
以縮行為依據
:set foldmethod=indent 就會依縮行來折疊,本來預設是 :set foldmethod=normal。請注意,此時手動折疊的設定會被停止無法使用。

以特殊的表示法為依據
:set foldmethod=expr,另外還要設定 :set foldexpr=...,可參考線上使用手冊:h fold-expr的例子。

以語法為依據
這是在定義語法(syntax)檔時同時加入折疊的定義。然後,set foldmethod=syntax就會依照這個語法定義檔去折疊,請:h syn-fold

以是否更改過為依據
這在進入 vimdiff(即 vim -d file1 file2) 時會自動進入foldmethod=diff的模式,因此要 diff 設定項設在同一個螢幕顯示才行。以便整體的瀏覽。當然要自行更改預設值亦可。預設是:

setlocal diff foldmethod=diff scrollbind nowrap foldlevel=1

這樣一來,兩個檔案相同的部份會折疊起來,沒有折疊的部份就是有差異的地方。

以文件上的標誌為依據
這是跟據文章中的標誌(marker)來做折疊。這些標誌,起於{{{,止於}}},這中間的部份會折疊起來。通常這些標誌是藏在註解欄裡頭,例如 C 程式碼的/**/之間,shell script 的#之後,Vim script 的"之後,得視程式語言的不同而定,因此一般的文章就不適合使用了。

這些預設的標誌也可以由 foldmarker 來更改,但為了文件的流通性,建議使用預設值就可以了。

當然,一些程式碼載入時再來設定:set foldmethod=marker就太麻煩了,這個設定可以設在文件內,例如:
#!/bin/sh
# 這是一個 sh script
# {{{
……
這裡是 script 內容,由 vim 打開這
個檔時,這個部份會自動折疊起來。
……
# }}}
# vim:fdm=marker:ts=2

最後一項不是很懂,留到以後再說吧~

參考資料:
把 Vim 折疊(folding)?

2009年12月19日 星期六

Vim script

按鍵對應

[實例] :map <F5> bi{<Esc>ea}<Esc>
這樣一來,只要在英文單字任意處按 F5,這個英文單字的前後就會加上大括號。

說明如下:
bi{<Esc> 按 b 移至英文單字的第一個字母處,並按 i 進入插入模式,寫入 { 這個字元,並按 Esc 鍵,回復常態模式。
e 移動至這個英文單字的最後一個字母處。
a}<Esc> 進入插入 append 模式,並輸入 } 這個字元,再回到常態模式。

需注意的是,在命令列模式前頭會有個冒號,因為在 vim 裡頭,要按個冒號才會進入命令列模式。但如果是設在 Vim script 檔裡頭當然就沒有冒號了。以上的例子也是可以對應至一些較容易記憶的按鍵,例如:

map ,b bi{<Esc>ea}<Esc>

這樣按 ``,b'' 就會有同樣的作用,但 ``,'' 及 ``b'' 之間不能相隔太久的時間,這樣 Vim 才會知道這兩個按鍵是結合在一起的。而 b 在此就代表 braces(大括號)。

如果要知道目前的按鍵對應的情形,可 :map 就會列出所有的目前按鍵對應,前面標示的記號就是各種模式的代表字母,例如 n 代表 normal,i 代表 insert。

縮寫對應
縮寫對應是把一長串的字串對應到簡單的幾個代表性字串。縮寫的對應只能用在插入模式、取代模式及命令列模式。主要用於輸入時節省時間及避免拼錯。這裡利用例子來做說明:

[實例] :ab gl GNU Linux
把 gl 這個按鍵對應成 GNU Linux。

ab 是 abbreviate 的縮寫,這樣一來,在插入模式時,只要輸入 gl,再按 Sapce 鍵或 Ctril+],這個 gl 就會變成 GNU Linux 這個字串。SapceCtril+] 的區別在於前者會多留個空白,方便繼續輸入其他文字;而後者則不會多留個空白。要注意的是,這在取代模式、命令列模式也是可以作用,如果是使用 :ia[bbrev],那只會在插入模式及取代模式有作用。

以下是一些常會用到的指令:

:ab[breviate] 不接任何參數,這會列出目前所有的縮寫對應。其中標有 `i' 的,代表作用於插入模式;標有 `c' 的,代表作用於命令列模式;標有 `!' 的,則兩種模式皆有作用。
:ab gl 這會列出 gl 是對應成什麼字串。
:una[bbreviate] gl 取消 gl 這個對應。
:ia[bbrev] 和 :ab 的定義一樣,但只作用於插入模式。
:cb[bbrev] 和 :ab 的定義一樣,但只作用於命令列模式。
:norea[bbrev] 用於防止重對應已有對應的字串。
:abc[lear] 取消所有由 :ab 所定義的對應。
:iabc[lear] 取消所有由 :ia 所定義的對應。
:cabc[lear] 取消所有由 :ca 所定義的對應。

" 設定加入函式註解的快速鍵(F5)
nmap <F5> o<Home>/*++^M^M<Home>Routine Description:^M<Home>  Description context^M^M<Home>Arguments:^M<Home>  Variable                  - the description for variable^M^M<Home>Returns:^M<Home>  EFI_SUCCESS               - operation successfully^M^M<Home>--*/<Esc>
其中^M是表示Enter動作,需要用Ctrl+v+M產生出來,其中nmap是表示只有在normal才會有作用,另外,要注意的是,上面的所有資料都是在同一行

[2010.10.10 補充]
設定在visual mode時有效的map
vunmap

參考資料:
Vim script 簡介