Vimscript 更多Operator-Pending映射

2018-02-24 16:02 更新

Operators和movements所包含的理念是Vim中的一個(gè)非常重要的概念,也是Vim之所以這么高效的最大原因所在。在這一章我們會(huì)在這一塊做更多的實(shí)踐,這會(huì)讓Vim變得更強(qiáng)大。

假設(shè)你現(xiàn)在在往Markdown中寫入一些文字。如果你沒有用過Markdown,不要緊,對(duì)于我們現(xiàn)在要做的事情而言,它很簡(jiǎn)單。把下面的文字輸入到一個(gè)文件中:

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.

使用=作為“下劃線”的行會(huì)被Markdown當(dāng)作標(biāo)題。我們現(xiàn)在創(chuàng)建一些映射,這些映射可以讓我們通過movements定位到標(biāo)題。運(yùn)行下面的命令:

:onoremap ih :<c-u>execute "normal! ?^==\\+$\r:nohlsearch\rkvg_"<cr>

這個(gè)映射有些復(fù)雜?,F(xiàn)在把你的光標(biāo)放到文本中的某個(gè)位置(不要放到標(biāo)題上),然后敲擊cih。Vim會(huì)刪除光標(biāo)所在章節(jié)的標(biāo)題,然后保持在插入模式,這可以稱為"修改所在的標(biāo)題(change inside heading)"。

這里使用了一些我們之前從來沒有見過的東西,所以我們有必要單獨(dú)分析下每一部分的含義。這個(gè)映射的第一部分,:onoremap ih是映射命令,這個(gè)我們很熟悉了,無需多言。<c-u>上一章講過,我們現(xiàn)在也不深究。

接著看看剩下的部分:

:execute "normal! ?^==\\+$\r:nohlsearch\rkvg_"<cr>

Normal

:normal命令的后面會(huì)跟著一串字符,無論這些字符表示什么含義,:normal命令都會(huì)執(zhí)行它們,就像是在常用模式下敲擊這些字符一樣。我們會(huì)在后面的章節(jié)中談?wù)撽P(guān)于:normal的更多細(xì)節(jié),由于這個(gè)它已經(jīng)出現(xiàn)多次,所以我覺得有必要現(xiàn)在做一個(gè)簡(jiǎn)單的說明,算是淺嘗輒止吧。執(zhí)行下面的命令:

:normal gg

Vim會(huì)將光標(biāo)跳轉(zhuǎn)到文件的頂部?,F(xiàn)在執(zhí)行下面的命令:

:normal >>

Vim將縮進(jìn)當(dāng)前行。

normal后面的!是干啥的呢?先別管,以后再說。

Execute

execute命令后面會(huì)跟著一個(gè)Vim腳本字符串(以后會(huì)深究它的細(xì)節(jié)),然后把這個(gè)字符串當(dāng)作一個(gè)命令執(zhí)行。執(zhí)行下面的命令:

:execute "write"

Vim會(huì)寫文件,就像你已經(jīng)輸入了:write<cr>一樣。現(xiàn)在執(zhí)行下面的命令:

:execute "normal! gg"

Vim會(huì)執(zhí)行:normal! gg,這個(gè)會(huì)將光標(biāo)跳轉(zhuǎn)到文件的頂部,跟之前一樣?,F(xiàn)在問題來了,我們?yōu)槭裁匆愕眠@么蛋疼,又是execute,又是normal!,直接執(zhí)行normal!不就可以搞定么?

看看下面的命令,猜猜它會(huì)干啥:

:normal! gg/a<cr>

這個(gè)命令似乎會(huì)做下面的一些事情:

  • 將光標(biāo)跳轉(zhuǎn)到文件的頂部。
  • 準(zhǔn)備搜索。
  • 把“a”當(dāng)作目標(biāo)字符串進(jìn)行搜索。
  • 按下return鍵執(zhí)行搜索。

你自己執(zhí)行一下,Vim會(huì)將光標(biāo)跳轉(zhuǎn)到了文件頂部,然后。。沒有然后了!

之所以會(huì)這樣是由于normal!的一個(gè)問題,這問題是normal!不能識(shí)別“特殊字符”,例如這里的<cr>。這個(gè)問題有很多辦法可以搞定,最簡(jiǎn)單的就是使用execute,另外使用execute也會(huì)讓腳本更易讀。

當(dāng)execute碰到任何你想讓它執(zhí)行的字符串的時(shí)候。它會(huì)先替換這個(gè)字符串中的所有特殊字符。在這個(gè)示例中,\r是一個(gè)轉(zhuǎn)義字符,它表示的是"回車符(carriage return)"。兩個(gè)反斜線也是一個(gè)轉(zhuǎn)義字符,它會(huì)將一個(gè)反斜線當(dāng)作一般字符放到字符串中。

如果我們按照上面的分析替換這個(gè)映射中的特殊字符,然后就可以搞清楚這個(gè)映射會(huì)怎么執(zhí)行:

:normal! ?^==\+$<cr>:nohlsearch<cr>kvg_
                ^^^^           ^^^^
                 ||             ||
這里的<cr>實(shí)際上是一個(gè)回車符,而不是由4個(gè)字符——“左尖括號(hào)”,“c“,”r“和“右尖括號(hào)”組成的字符串。

所以現(xiàn)在normal!會(huì)執(zhí)行這些字符,如同我們是在常用模式下敲擊了它們一樣。我們以回車符對(duì)這些字符進(jìn)行拆分,然后看看它們是怎么執(zhí)行的:

?^==\+$
:nohlsearch
kvg_

第一部分?^==\+$會(huì)向后搜索任何由兩個(gè)或多個(gè)等號(hào)組成的行,這些行不會(huì)包含除等號(hào)外的任何其他字符。這個(gè)命令執(zhí)行后會(huì)讓光標(biāo)停留在符合搜索條件的行的行首。

之所以使用向后搜索,是因?yàn)楫?dāng)你想“修改所在的標(biāo)題(change inside heading)”的時(shí)候,而當(dāng)前光標(biāo)是位于某一節(jié)的文本上,那么你最可能想做的是修改_這_一節(jié)的標(biāo)題,而不是下一節(jié)的標(biāo)題。

第二部分是:nohlsearch命令。這個(gè)命令只是簡(jiǎn)單的清除之前的搜索結(jié)果的高亮顯示,防止這些高亮顯示分散我們的注意。

最后一部分是三個(gè)常用模式下的命令的序列:

  • k:向上移動(dòng)一行。第一部分已經(jīng)將光標(biāo)定位到了等號(hào)符號(hào)組成的行的第一個(gè)字符,所以執(zhí)行這個(gè)命令后光標(biāo)就會(huì)被定位到標(biāo)題的第一個(gè)字符上。
  • v:進(jìn)入可視模式(characterwise)。
  • g_:移動(dòng)到當(dāng)前行的最后一個(gè)非空字符上。這里沒有使用$,是因?yàn)?code>$會(huì)選中換行符號(hào),這不是我們所想要的。

結(jié)果

這個(gè)映射做了很多工作,所以看起來有些復(fù)雜,不過我們已經(jīng)搞清楚了這個(gè)映射中的每個(gè)部分?,F(xiàn)在來概括一下:

  • 我們?yōu)椤八谡鹿?jié)的標(biāo)題內(nèi)(inside this section's heading)”創(chuàng)建了一個(gè)operator-pending的映射。
  • 我們使用了executenormal!這兩個(gè)命令來執(zhí)行我們用于選擇標(biāo)題的常用命令,這讓我們可以在這些命令中使用特殊字符。
  • 我們的映射會(huì)搜索由等號(hào)組成的行從而定位到一個(gè)標(biāo)題,然后在常用模式下選中標(biāo)題的文本。
  • Vim進(jìn)行剩下的處理標(biāo)題的工作。

再來看一個(gè)映射。執(zhí)行下面的命令:

:onoremap ah :<c-u>execute "normal! ?^==\\+\r:nohlsearch\rg_vk0"<cr>

把光標(biāo)放到某一節(jié)的文字上,然后敲擊cah試試看。這一次Vim不僅會(huì)刪除這一節(jié)的標(biāo)題,而且還會(huì)刪除跟這個(gè)標(biāo)題相連的等號(hào)組成的行。你可以把這個(gè)movement當(dāng)作是“_環(huán)繞_這一節(jié)的標(biāo)題(around?this section's heading)“。

這個(gè)映射有什么不同呢?讓我們對(duì)照之前的映射看一下:

: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)定位到等號(hào)組成的行的第一個(gè)字符的那個(gè)部分開始進(jìn)行講解:

  • g_:移動(dòng)到當(dāng)前行(譯注:等號(hào)組成的行)的最后一個(gè)非空字符。
  • v:進(jìn)入可視模式(characterwise)。
  • k:向上移動(dòng)一行。這會(huì)將光標(biāo)移動(dòng)到包含標(biāo)題文字的行上。
  • 0:移動(dòng)到這一行(譯注:標(biāo)題行)的第一個(gè)字符。

這一系列命令的執(zhí)行結(jié)果就是在可視模式下同時(shí)選中標(biāo)題的文字和等號(hào)組成的行,然后Vim會(huì)在這兩行上執(zhí)行相應(yīng)的操作。

練習(xí)

Markdown也可以用-字符來限定標(biāo)題。調(diào)整上面的正則表達(dá)式使得這些映射可以工作在不同類型的標(biāo)題上。你可能想查看:help pattern-overview。記住正則表達(dá)是在一個(gè)字符串中,所以反斜線需要進(jìn)行轉(zhuǎn)義。

添加兩個(gè)創(chuàng)建這些映射的自動(dòng)命令到你的~/.vimrc文件中。確保只對(duì)合適的緩沖區(qū)使用這些映射,并且確保使用自動(dòng)命令組來防止每次加載~/.vimrc的時(shí)候創(chuàng)建這些自動(dòng)命令的副本。

閱讀?:help normal。

閱讀?:help execute。

閱讀?:help expr-quote了解你可以在字符串中使用的轉(zhuǎn)義序列。

創(chuàng)建一個(gè)“在下一個(gè)郵件地址內(nèi)(inside next email address)”的operator-pending映射,然后你就可以使用“修改在下一個(gè)郵件地址內(nèi)(change inside next email address)”。將in@作為映射的按鍵是個(gè)不錯(cuò)的選擇。你可能還需要將這個(gè)按鍵映射為/...some regex...<cr>

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)