目前為止,我們已經(jīng)完成了一個(gè)原型,是時(shí)候擴(kuò)充它,讓它更加強(qiáng)大。
記?。何覀兂跏寄繕?biāo)是創(chuàng)建"grep運(yùn)算符"。我們還需要做一大堆新的東西來達(dá)成目標(biāo), 但要像前一章的過程一樣:從簡(jiǎn)單的東西開始,并逐步改進(jìn)直到它滿足我們的需求。
在開始之前,注釋掉`~/.vimrc`中在前一章創(chuàng)建的映射。我們還要用同樣的快捷鍵來映射新的運(yùn)算符。
## 新建一個(gè)文件
創(chuàng)建一個(gè)新的運(yùn)算符需要許多命令,把它們手工打出來將很快變成一種折磨。 你可以把它附加到`~/.vimrc`,但讓我們?yōu)檫@個(gè)運(yùn)算符創(chuàng)建一個(gè)獨(dú)立的文件。我們有足夠的必要這么做。
首先,找到你的Vim`plugin`文件夾。在Linux或OS X,這將會(huì)是`~/.vim/plugin`。 如果你是Windows用戶,它將位于你的主目錄下的`vimfiles`文件夾。(如果你找不到,在Vim里使用`:echo $HOME命令) 如果這個(gè)文件夾不存在,創(chuàng)建一個(gè)。
在`plugin/`下新建文件`grep-operator.vim`。這就是你放置新運(yùn)算符的代碼的地方。 一旦文件被修改,你可以執(zhí)行`:source %`來重新加載代碼。 每次你打開Vim,這個(gè)文件也會(huì)被重新加載,就像`~/.vimrc`。
不要忘了,在你source之前,你_必須_先保存文件,這樣才能看到變化!
## 骨架(Skeleton)
要?jiǎng)?chuàng)建一個(gè)新的Vim運(yùn)算符,你需要從兩個(gè)組件開始:一個(gè)函數(shù)還有一個(gè)映射。 先添加下面的代碼到`grep-operator.vim`:
~~~
nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
function! GrepOperator(type)
echom "Test"
endfunction
~~~
保存文件并用`:source %`source它。嘗試通過按下`<leader>giw`來執(zhí)行"grep整個(gè)詞"。 Vim將在接受`iw`動(dòng)作(motion)后,輸出`Test`,意味著我們已經(jīng)搭起了骨架。
函數(shù)部分是簡(jiǎn)單的,沒有什么是我們沒講過的。不過映射部分比較復(fù)雜。 我們首先對(duì)函數(shù)設(shè)置了`operatorfunc`選項(xiàng),然后執(zhí)行`g@`來以運(yùn)算符的方式調(diào)用這個(gè)函數(shù)。 看起來這有點(diǎn)繞,不過這就是Vim工作的原理。
暫時(shí)把這個(gè)映射看作黑魔法吧。稍后你可以到文檔里一探究竟。
## 可視模式
我們已經(jīng)在normal模式下加入了這個(gè)運(yùn)算符,但還想要在visual模式下用到它。 在之前的映射下面添加多一個(gè):
~~~
vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>
~~~
保存并source文件?,F(xiàn)在在visual模式下選擇一些東西并按下`<leader>g`。 什么也沒發(fā)生,但Vim確實(shí)輸出了`Test`,所以我們的函數(shù)已經(jīng)運(yùn)行了。
之前我們就見過`<c-u>`,但是還沒有解釋它是做什么的。試一下在可視模式下選中一些文本并按下`:`。 Vim將打開一個(gè)命令行就像平時(shí)按下了`:`一樣,但是命令行的開頭自動(dòng)添加了`'<,'>`!
Vim為了提高效率,插入了這些文本來讓你的命令在被選擇的范圍內(nèi)執(zhí)行。 但是這次,我們不需要它添倒忙。我們用`<c-u>`來執(zhí)行"從光標(biāo)所在處刪除到行首的內(nèi)容",移除多余文本。 最后剩下一個(gè)孤零零的`:`,為調(diào)用`call`命令作準(zhǔn)備。
我們傳遞過去的`visualMode()`參數(shù)還沒有講過呢。 這個(gè)函數(shù)是Vim的內(nèi)置函數(shù),它返回一個(gè)單字符的字符串來表示visual模式的類型:?`"v"`代表字符寬度(characterwise),`"V"`代表行寬度(linewise),`Ctrl-v`代表塊寬度(blockwise)。
## 動(dòng)作類型
我們定義的函數(shù)接受一個(gè)`type`參數(shù)。我們知道在visual模式下它將會(huì)是`visualmode()`的返回值, 但是在normal模式下呢?
編輯函數(shù)體部分,讓代碼像這樣:
~~~
nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>
function! GrepOperator(type)
echom a:type
endfunction
~~~
Source文件,然后繼續(xù)并用多種的方式測(cè)試它。你可能會(huì)得到類似下面的結(jié)果:
* 按下`viw<leader>g`顯示`v`,因?yàn)槲覀兲幱谧址麑挾鹊膙isual模式。
* 按下`Vjj<leader>g`顯示`V`,因?yàn)槲覀兲幱谛袑挾鹊膙isual模式。
* 按下`<leader>giw`顯示`char`,因?yàn)槲覀冊(cè)谧址麑挾鹊膭?dòng)作(characterwise motion)中使用該運(yùn)算符。
* 按下`<leader>gG`顯示`line`,因?yàn)槲覀冊(cè)谛袑挾鹊膭?dòng)作(linewise motion)中使用該運(yùn)算符。
現(xiàn)在我們已經(jīng)知道怎么區(qū)分不同種類的動(dòng)作,這對(duì)于我們選擇需要搜索的詞是很重要的。
## 復(fù)制文本
我們的函數(shù)將需要獲取用戶想要搜索的文本,而這樣做最簡(jiǎn)單的方法就是復(fù)制它。 把函數(shù)修改成這樣:
~~~
nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>
function! GrepOperator(type)
if a:type ==# 'v'
execute "normal! `<v`>y"
elseif a:type ==# 'char'
execute "normal! `[v`]y"
else
return
endif
echom @@
endfunction
~~~
哇。好多新的東西啊。試試按下`<leader>giw`,`<leader>g2e`和`vi(<leader>g`看看。 每次Vim都會(huì)輸出動(dòng)作所包括的文本,顯然我們已經(jīng)走上正道了!
讓我們把這段代碼一步步分開來看。首先我們用`if`語句檢查`a:type`參數(shù)。如果是`'v'`, 它就是使用在字符寬度的visual模式下,所以我們復(fù)制了可視模式下的選中文本。
注意我們使用大小寫敏感比較`==#`。如果我們只用了`==`而用戶設(shè)置`ignorecase`,?`"V"`也會(huì)是匹配的,結(jié)果_不會(huì)_如我們所愿。重視防御性編程!
`if`語句的第二個(gè)分支則會(huì)攔住normal模式下使用字符寬度的動(dòng)作。
剩下的情況只是默默地退出。我們直接忽略行寬度/塊寬度的visual模式和對(duì)應(yīng)的動(dòng)作類型。 Grep默認(rèn)情況下不會(huì)搜索多行文本,所以在搜索內(nèi)容中夾雜著換行符是毫無意義的。
我們每一個(gè)`if`分支都會(huì)執(zhí)行`normal!`命令來做兩件事:
* 在可視狀態(tài)下選中我們想要的文本范圍:
* 先移動(dòng)到范圍開頭,并標(biāo)記
* 進(jìn)入字符寬度的visual模式
* 移動(dòng)到范圍結(jié)尾的標(biāo)記
* 復(fù)制可視狀態(tài)下選中的文本。
先不要糾結(jié)于特殊標(biāo)記方式。你將會(huì)在完成本章結(jié)尾的練習(xí)時(shí)學(xué)到為什么它們會(huì)不一樣。
函數(shù)的最后一行輸出變量`@@`。不要忘了以`@`開頭的變量是寄存器。`@@`是"未命名"(unnamed)寄存器: 如果你在刪除或復(fù)制文本時(shí)沒有指定一個(gè)寄存器,Vim就會(huì)把文本放在這里。
簡(jiǎn)明扼要地說:我們選中要搜索的文本,復(fù)制它,然后輸出被復(fù)制的文本。
## 轉(zhuǎn)義搜索文本
既然得到了Vim字符串形式的需要的文本,我們可以像前一章一樣將它轉(zhuǎn)義。修改`echom`命令成這樣:
~~~
nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>
function! GrepOperator(type)
if a:type ==# 'v'
normal! `<v`>y
elseif a:type ==# 'char'
normal! `[v`]y
else
return
endif
echom shellescape(@@)
endfunction
~~~
保存并source文件,然后在可視模式下選中帶特殊字符的文本,按下`<leader>g`。 Vim顯示一個(gè)被轉(zhuǎn)義了的能安全地傳遞給shell命令的文本。
## 執(zhí)行Grep
我們終于可以加上`grep!`命令來實(shí)現(xiàn)真正的搜索。替換掉`echom`那一行,代碼看起來就像這樣:
~~~
nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>
function! GrepOperator(type)
if a:type ==# 'v'
normal! `<v`>y
elseif a:type ==# 'char'
normal! `[v`]y
else
return
endif
silent execute "grep! -R " . shellescape(@@) . " ."
copen
endfunction
~~~
看起來眼熟吧。我們簡(jiǎn)單地執(zhí)行上一章得到的`silent execute "grep! ..."`命令。 由于我們不再把所有的代碼塞進(jìn)單個(gè)`nnoremap`命令里,現(xiàn)在代碼甚至更加清晰易懂了!
保存并source文件,然后嘗試一下,享受自己辛勤勞動(dòng)的成果吧!
因?yàn)槎x了一個(gè)全新的Vim運(yùn)算符,現(xiàn)在我們可以在許多場(chǎng)景下使用它了,比如:
* `viw<leader>g`: 可視模式下選中一個(gè)詞,然后grep它。
* `<leader>g4w`: Grep接下來的四個(gè)詞。
* `<leader>gt;`: Grep到分號(hào)為止的文本。
* `<leader>gi[`: Grep方括號(hào)里的文本.
這里彰顯了Vim的優(yōu)越性:它的編輯命令就像一門語言。當(dāng)你加入新的動(dòng)詞,它會(huì)自動(dòng)地跟(大多數(shù))現(xiàn)存的名詞和形容詞搭配起來。
## 練習(xí)
閱讀`:help visualmode()`。
閱讀`:help c_ctrl-u`。
閱讀`:help operatorfunc`。
閱讀`:help map-operator`。
- 前言
- 鳴謝
- 預(yù)備知識(shí)
- 打印信息
- 設(shè)置選項(xiàng)
- 基本映射
- 模式映射
- 精確映射
- Leaders
- 編輯你的Vimrc文件
- Abbreviations
- 更多的Mappings
- 鍛煉你的手指
- 本地緩沖區(qū)的選項(xiàng)設(shè)置和映射
- 自動(dòng)命令
- 本地緩沖區(qū)縮寫
- 自動(dòng)命令組
- Operator-Pending映射
- 更多Operator-Pending映射
- 狀態(tài)條
- 負(fù)責(zé)任的編碼
- 變量
- 變量作用域
- 條件語句
- 比較
- 函數(shù)
- 函數(shù)參數(shù)
- 數(shù)字
- 字符串
- 字符串函數(shù)
- Execute命令
- Normal命令
- 執(zhí)行normal!
- 基本的正則表達(dá)式
- 實(shí)例研究:Grep 運(yùn)算符(Operator),第一部分
- 實(shí)例研究:Grep運(yùn)算符(Operator),第二部分
- 實(shí)例研究:Grep運(yùn)算符(Operator),第三部分
- 列表
- 循環(huán)
- 字典
- 切換
- 函數(shù)式編程
- 路徑
- 創(chuàng)建一個(gè)完整的插件
- 舊社會(huì)下的插件配置方式
- 新希望:用Pathogen配置插件
- 檢測(cè)文件類型
- 基本語法高亮
- 高級(jí)語法高亮
- 更高級(jí)的語法高亮
- 基本折疊
- 高級(jí)折疊
- 段移動(dòng)原理
- Potion段移動(dòng)
- 外部命令
- 自動(dòng)加載
- 文檔
- 發(fā)布
- 還剩下什么?
