Operators和movements所包含的理念是Vim中的一個非常重要的概念,也是Vim之所以這么高效的最大原因所在。在這一章我們會在這一塊做更多的實踐,這會讓Vim變得更強(qiáng)大。
假設(shè)你現(xiàn)在在往Markdown中寫入一些文字。如果你沒有用過Markdown,不要緊,對于我們現(xiàn)在要做的事情而言,它很簡單。把下面的文字輸入到一個文件中:
Topic One
=========
This is some text about topic one.
It has multiple paragraphs.
Topic Two
=========
This is some text about topic two. It has only one paragraph.
使用=
作為“下劃線”的行會被Markdown當(dāng)作標(biāo)題。我們現(xiàn)在創(chuàng)建一些映射,這些映射可以讓我們通過movements定位到標(biāo)題。運(yùn)行下面的命令:
:onoremap ih :<c-u>execute "normal! ?^==\\+$\r:nohlsearch\rkvg_"<cr>
這個映射有些復(fù)雜。現(xiàn)在把你的光標(biāo)放到文本中的某個位置(不要放到標(biāo)題上),然后敲擊cih
。Vim會刪除光標(biāo)所在章節(jié)的標(biāo)題,然后保持在插入模式,這可以稱為"修改所在的標(biāo)題(change inside heading)"。
這里使用了一些我們之前從來沒有見過的東西,所以我們有必要單獨(dú)分析下每一部分的含義。這個映射的第一部分,:onoremap ih
是映射命令,這個我們很熟悉了,無需多言。<c-u>
上一章講過,我們現(xiàn)在也不深究。
接著看看剩下的部分:
:execute "normal! ?^==\\+$\r:nohlsearch\rkvg_"<cr>
:normal
命令的后面會跟著一串字符,無論這些字符表示什么含義,:normal
命令都會執(zhí)行它們,就像是在常用模式下敲擊這些字符一樣。我們會在后面的章節(jié)中談?wù)撽P(guān)于:normal
的更多細(xì)節(jié),由于這個它已經(jīng)出現(xiàn)多次,所以我覺得有必要現(xiàn)在做一個簡單的說明,算是淺嘗輒止吧。執(zhí)行下面的命令:
:normal gg
Vim會將光標(biāo)跳轉(zhuǎn)到文件的頂部?,F(xiàn)在執(zhí)行下面的命令:
:normal >>
Vim將縮進(jìn)當(dāng)前行。
那normal
后面的!
是干啥的呢?先別管,以后再說。
execute
命令后面會跟著一個Vim腳本字符串(以后會深究它的細(xì)節(jié)),然后把這個字符串當(dāng)作一個命令執(zhí)行。執(zhí)行下面的命令:
:execute "write"
Vim會寫文件,就像你已經(jīng)輸入了:write<cr>
一樣?,F(xiàn)在執(zhí)行下面的命令:
:execute "normal! gg"
Vim會執(zhí)行:normal! gg
,這個會將光標(biāo)跳轉(zhuǎn)到文件的頂部,跟之前一樣。現(xiàn)在問題來了,我們?yōu)槭裁匆愕眠@么蛋疼,又是execute
,又是normal!
,直接執(zhí)行normal!
不就可以搞定么?
看看下面的命令,猜猜它會干啥:
:normal! gg/a<cr>
這個命令似乎會做下面的一些事情:
你自己執(zhí)行一下,Vim會將光標(biāo)跳轉(zhuǎn)到了文件頂部,然后。。沒有然后了!
之所以會這樣是由于normal!
的一個問題,這問題是normal!
不能識別“特殊字符”,例如這里的<cr>
。這個問題有很多辦法可以搞定,最簡單的就是使用execute
,另外使用execute
也會讓腳本更易讀。
當(dāng)execute
碰到任何你想讓它執(zhí)行的字符串的時候。它會先替換這個字符串中的所有特殊字符。在這個示例中,\r
是一個轉(zhuǎn)義字符,它表示的是"回車符(carriage return)"。兩個反斜線也是一個轉(zhuǎn)義字符,它會將一個反斜線當(dāng)作一般字符放到字符串中。
如果我們按照上面的分析替換這個映射中的特殊字符,然后就可以搞清楚這個映射會怎么執(zhí)行:
:normal! ?^==\+$<cr>:nohlsearch<cr>kvg_
^^^^ ^^^^
|| ||
這里的<cr>實際上是一個回車符,而不是由4個字符——“左尖括號”,“c“,”r“和“右尖括號”組成的字符串。
所以現(xiàn)在normal!
會執(zhí)行這些字符,如同我們是在常用模式下敲擊了它們一樣。我們以回車符對這些字符進(jìn)行拆分,然后看看它們是怎么執(zhí)行的:
?^==\+$
:nohlsearch
kvg_
第一部分?^==\+$
會向后搜索任何由兩個或多個等號組成的行,這些行不會包含除等號外的任何其他字符。這個命令執(zhí)行后會讓光標(biāo)停留在符合搜索條件的行的行首。
之所以使用向后搜索,是因為當(dāng)你想“修改所在的標(biāo)題(change inside heading)”的時候,而當(dāng)前光標(biāo)是位于某一節(jié)的文本上,那么你最可能想做的是修改_這_一節(jié)的標(biāo)題,而不是下一節(jié)的標(biāo)題。
第二部分是:nohlsearch
命令。這個命令只是簡單的清除之前的搜索結(jié)果的高亮顯示,防止這些高亮顯示分散我們的注意。
最后一部分是三個常用模式下的命令的序列:
k
:向上移動一行。第一部分已經(jīng)將光標(biāo)定位到了等號符號組成的行的第一個字符,所以執(zhí)行這個命令后光標(biāo)就會被定位到標(biāo)題的第一個字符上。v
:進(jìn)入可視模式(characterwise)。g_
:移動到當(dāng)前行的最后一個非空字符上。這里沒有使用$
,是因為$
會選中換行符號,這不是我們所想要的。這個映射做了很多工作,所以看起來有些復(fù)雜,不過我們已經(jīng)搞清楚了這個映射中的每個部分?,F(xiàn)在來概括一下:
execute
和normal!
這兩個命令來執(zhí)行我們用于選擇標(biāo)題的常用命令,這讓我們可以在這些命令中使用特殊字符。再來看一個映射。執(zhí)行下面的命令:
:onoremap ah :<c-u>execute "normal! ?^==\\+\r:nohlsearch\rg_vk0"<cr>
把光標(biāo)放到某一節(jié)的文字上,然后敲擊cah
試試看。這一次Vim不僅會刪除這一節(jié)的標(biāo)題,而且還會刪除跟這個標(biāo)題相連的等號組成的行。你可以把這個movement當(dāng)作是“_環(huán)繞_這一節(jié)的標(biāo)題(around?this section's heading)“。
這個映射有什么不同呢?讓我們對照之前的映射看一下:
:onoremap ih :<c-u>execute "normal! ?^==\\+$\r:nohlsearch\rkvg_"<cr>
:onoremap ah :<c-u>execute "normal! ?^==\\+$\r:nohlsearch\rg_vk0"<cr>
唯一的不同是映射的后面用于選擇文本的部分:
inside heading: kvg_
around heading: g_vk0
其他的部分都是一模一樣的,所以我們現(xiàn)在從將光標(biāo)定位到等號組成的行的第一個字符的那個部分開始進(jìn)行講解:
g_
:移動到當(dāng)前行(譯注:等號組成的行)的最后一個非空字符。v
:進(jìn)入可視模式(characterwise)。k
:向上移動一行。這會將光標(biāo)移動到包含標(biāo)題文字的行上。0
:移動到這一行(譯注:標(biāo)題行)的第一個字符。這一系列命令的執(zhí)行結(jié)果就是在可視模式下同時選中標(biāo)題的文字和等號組成的行,然后Vim會在這兩行上執(zhí)行相應(yīng)的操作。
Markdown也可以用-
字符來限定標(biāo)題。調(diào)整上面的正則表達(dá)式使得這些映射可以工作在不同類型的標(biāo)題上。你可能想查看:help pattern-overview
。記住正則表達(dá)是在一個字符串中,所以反斜線需要進(jìn)行轉(zhuǎn)義。
添加兩個創(chuàng)建這些映射的自動命令到你的~/.vimrc
文件中。確保只對合適的緩沖區(qū)使用這些映射,并且確保使用自動命令組來防止每次加載~/.vimrc
的時候創(chuàng)建這些自動命令的副本。
閱讀?:help normal
。
閱讀?:help execute
。
閱讀?:help expr-quote
了解你可以在字符串中使用的轉(zhuǎn)義序列。
創(chuàng)建一個“在下一個郵件地址內(nèi)(inside next email address)”的operator-pending映射,然后你就可以使用“修改在下一個郵件地址內(nèi)(change inside next email address)”。將in@
作為映射的按鍵是個不錯的選擇。你可能還需要將這個按鍵映射為/...some regex...<cr>
。
更多建議: