目标

  • 使用PCRE正则表达式在当前目录下递归搜索
  • 将选定内容自动转换成正则表达式
  • 有较高的搜索速度

依賴

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
" 在当前缓冲区中查找與替換
nmap <leader>ff yiw/\<<C-R>"\>\C
vmap <leader>ff y/<C-R>=XEscapeRegex(@")<CR>\C
nmap <leader>rr yiw:%s/\<<C-R>"\>\C//g<LEFT><LEFT>
vmap <leader>rr y:%s/<C-R>=XEscapeRegex(@")<CR>\C//g<LEFT><LEFT>
nmap <leader>rl yiw:s/\<<C-R>"\>\C//g<LEFT><LEFT>
vmap <leader>rl y:s/<C-R>=XEscapeRegex(@")<CR>\C//g<LEFT><LEFT>
" 在当前目录下递归搜索和替换
let g:FerretExecutable='ag'
nmap <leader>ak <Plug>(FerretAck)
nmap <leader>lak <Plug>(FerretLack)
nmap <leader>aw <Plug>(FerretAckWord)
nmap <leader>as <Plug>(FerretAcks)
vmap <leader>ak y:Ack <C-R>=XEscapeRegex(@", 2)<CR>
vmap <leader>lak y:Lack <C-R>=XEscapeRegex(@", 2)<CR>
nnoremap <leader>a :set operatorfunc=GrepOperator<CR>g@
vnoremap <leader>a :<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
exec "Ack ".XEscapeRegex(@@, 2)
" exec "Grep ".XEscapeRegex(@@, 2)
endfunction"}}}
" 转义正则表达式特殊字符,以便在正则表达式中使用
" a:1 是否转义为vimgrep的pattern格式,1,2
" a:2 是否用shellescape()转义,1是转义,2是转义并去掉两侧单引号
function! XEscapeRegex(str, ...)"{{{
let pattern = a:str
let pattern = escape(pattern, '/\.*$^~[]"')
if a:0 && a:1
let pattern = escape(pattern, '()+?')
if a:1 == 2
let pattern = escape(pattern, '\')
endif
endif
if a:0 > 1 && a:2
let pattern = shellescape(pattern)
if a:2 == 2
let pattern = pattern[1:-2]
endif
endif
let whitespacePattern = a:0 && a:1 ? '\\s\+' : '\\s\\+'
let pattern = substitute(pattern, '\s\+', whitespacePattern, 'g')
return pattern
endfunction"}}}

Ferret

Ferret是我用過的vim搜索擴展里最接近理想的一個,最大的優點是不用引號包裹搜索內容,且較大程度地支持PCRE正則表達式。

EasyGrep的缺點是正則表達式必須是shellescape()過的,手寫不方便,也不直觀。Ferret在底層做了shellescape(),所以比EasyGrep簡單一些。不過Ferret對反斜槓的處理仍然不直觀,例如搜索App\Link,正則表達式是App\\Link,而Ferret里只能用App\\\\Link。原因是從輸入到執行,存在著三層轉義:Vim命令行、Shell、grep/ag。所以對於EasyGrep,要搜索一個\,必須輸入\\\\\\\\。對於Ferret,由於底層做了shellescape(),只需要兩層轉義,即\\\\。當然這樣也不方便,所以我用自定義函數XEscapeRegex()對選擇的內容做這個事,不過最完美的方法當然是Ferret自己支持,或者自己再封裝一下Ferret的命令,這樣輸入的正則表達式可讀性就正常了。

The Silver Searcher

ag是我用過的搜索工具里對速度和功能兼顧得最好的。

搜索運算符

GrepOperator是從Learn Vimscript the Hard Way里扒出來的,可以實現自動搜索指定範圍的內容:

  • <leader>aiw:搜索光標下的詞
  • <leader>a$:搜索從光標到行末的內容
  • <leader>at;:搜索從光標到下一個分號的內容

轉換選定內容到正則表達式

XEscapeRegex()根據參數轉換字符串到不同格式的正則表達式。主要用途為轉換成給搜索當前緩衝區的Vim格式的正則,或給Ferret使用的PCRE格式。