Vimscript 實例研究:Grep 運算符(Operator),第一部分

2018-02-24 16:02 更新

在本章和下一章中,我們將使用Vimscript來實現(xiàn)一個相當復雜的程序。我們將探討一些聞所未聞的東西, 也將在實戰(zhàn)中把之前學過的東西聯(lián)系起來。

在本實例研究中,遇到不熟悉的內容,你得用:help弄懂它。如果你只是走馬觀花,就將所獲無多。

Grep

如果你未曾用過:grep,現(xiàn)在你應該花費一分鐘讀讀:help :grep:help :make。 如果之前沒用過quickfix window,閱讀:help quickfix-window

簡明扼要地說::grep ...將用你給的參數(shù)來運行一個外部的grep程序,解析結果,填充quickfix列表, 這樣你就能在Vim里面跳轉到對應結果。

我們將會添加一個"grep運算符"到任意Vim的內置(或自定義!)的動作中,來選擇想要搜索的文本, 讓:grep更容易使用。

用法

在寫下每一個有意義的Vimscript程序的第一步,你需要思索一個問題:“它會被用戶怎么使用呢?”。 嘗試構思出一種優(yōu)雅,簡易,符合直覺的調用方法。

這次我會替你把這活干了:

  • 我們將創(chuàng)造一個"grep運算符"并綁定到<leader>g。
  • 它將表現(xiàn)得同其他任意Vim運算符一樣,還可以加入到組合鍵(比如wi{)中。
  • 它將立刻開始搜索并打開quickfix窗口展示結果。
  • 它將_不會_跳到第一個結果,因為當?shù)谝粋€結果不是你想要的時候,這樣做會困擾你。

一些你將怎么使用它的用例:

  • <leader>giw: Grep光標下的詞(word)。
  • <leader>giW: Grep光標下的詞的大寫形式(WORD)。
  • <leader>gi': Grep當前所在的單引號括住的詞。
  • viwe<leader>g: 可視狀態(tài)下選中一個詞并拓展選擇范圍到下一詞,然后Grep。

有很多,_很多_其他的方法可以用它。看上去它好像需要寫很多,很多代碼, 但事實上我們只需要實現(xiàn)"運算符"功能然后Vim就會完成剩下的工作。

一個原型

在埋頭寫下巨量(trickey bits)的Vimscript之前,有一個也許會幫上忙的方法是簡化你的目標并實現(xiàn), 來推測你最終解決方案可能的"外形"。

讓我們簡化我們的目標為"創(chuàng)造一個映射來搜索光標下的詞"。這有用而且應該更簡單,所以我們能更快得到可運行的成果。 目前我們將映射它到<leader>g。

我們從一個映射骨架開始并逐漸填補它。執(zhí)行這個命令:

:nnoremap <leader>g :grep -R something .<cr>

如果你閱讀過:help grep,你就能輕易理解這個命令。我們之前也看過許多映射,這里沒有什么是新的。

顯然我們還沒做什么,所以讓我們一步步打磨這個映射直到它符合我們的要求。

搜索部分

首先我們需要搜索光標下的詞,而不是something。執(zhí)行下面的命令:

:nnoremap <leader>g :grep -R <cword> .<cr>

現(xiàn)在試一下。<cword>是一個Vim的command-line模式的特殊變量, Vim會在執(zhí)行命令之前把它替換為"光標下面的那個詞"。

你可以使用<cWORD>來得到大寫形式(WORD)。執(zhí)行這個命令:

:nnoremap <leader>g :grep -R <cWORD> .<cr>

現(xiàn)在試試把光標放在諸如foo-bar的詞上面。Vim將grepfoo-bar而不是其中的一部分。

我們的搜索部分還有一個問題:如果這里面有什么特殊的shell字符,Vim會毫不猶豫地傳遞給外部的grep命令。 這樣會導致程序崩潰(或更糟:鑄成某些大錯)。

讓我們看看如何使它掛掉。輸入foo;ls并把光標放上去執(zhí)行映射。grep命令失敗了, 而Vim將執(zhí)行ls命令!這肯定糟透了,如果詞里包括比ls更危險的命令呢?

為了解決這個問題,我們將調用參數(shù)用引號括起來。執(zhí)行這個命令:

:nnoremap <leader>g :grep -R '<cWORD>' .<cr>

大多數(shù)shell把單引號括起來的內容當作(大體上)字面量,所以我們的映射現(xiàn)在更加健壯了。

轉義Shell命令參數(shù)

搜索部分還有一個問題。在that's上嘗試這個映射。它不會工作,因為詞里的單引號與grep命令的單引號發(fā)生了沖突!

為了解決問題,我們可以使用Vim的shellescape函數(shù)。 閱讀:help escape():help shellescape()來看它是怎樣工作的(真的很簡單)。

因為shellescape()要求Vim字符串,我們需要用execute動態(tài)創(chuàng)建命令。 首先執(zhí)行下面命令來轉換:grep映射到:execute "..."形式:

:nnoremap <leader>g :execute "grep -R '<cWORD>' ."<cr>

試一下并確信它可以工作。如果不行,找出拼寫錯誤并改正。 然后執(zhí)行下面的使用了shellescape的命令。

:nnoremap <leader>g :execute "grep -R " . shellescape("<cWORD>") . " ."<cr>

在一般的詞比如foo上執(zhí)行這個命令試試。它可以工作。再到一個帶單引號的詞,比如that's,上試試看。 它還是不行!為什么會這樣?

問題在于Vim在拓展命令行中的特殊變量,比如<cWORD>,的之前,就已經(jīng)執(zhí)行了shellescape()。 所以Vim shell-escaped了字面量字符串"<cWORD>"(什么都不做,除了給它添上一對單引號)并連接到我們的grep命令上。

通過執(zhí)行下面的命令,你可以親眼目睹這一切。

:echom shellescape("<cWORD>")

Vim將輸出'<cWORD>'。注意引號也是輸出字符串的一部分。Vim把它作為shell命令參數(shù)保護了起來。

為解決這個問題,我們將使用expand()函數(shù)來強制拓展<cWORD>為對應字符串, 搶在它被傳遞給shellescape之前

讓我們單獨看看這一部分是怎么工作的。把你的光標移到帶單引號的詞(比如that's)上去, 并執(zhí)行下面命令:

:echom expand("<cWORD>")

Vim輸出that's,因為expand("<cWORD>")以Vim字符串的形式返回當前光標下的詞。 是時候加入shellescape的部分了:

:echom shellescape(expand("<cWORD>"))

這次Vim輸出'that'\''s'。 如果覺得這看上去真可笑,你大概沒有感受過看透了各種shell轉義的瘋狂形式后的淡定吧。 目前,不用為此而糾結。就相信Vim接受了expand的輸出并正確地轉義了它。

目前我們已經(jīng)得到了光標下的詞的徹底轉義版本。是時候連接它到我們的映射了! 執(zhí)行下面的命令:

:nnoremap <leader>g :exe "grep -R " . shellescape(expand("<cWORD>")) . " ."<cr>

試一下。這個映射不再有問題,即使我們用它搜索帶古怪符號的詞。

"從簡單的Vimscript開始并一點點轉變它直到達成你的目標"這樣的工作方式將會被你一再取用。

整理整理

在完成映射之前,還要處理一些小問題。首先,我們說過我們不想自動跳到第一個結果, 所以要用grep!替換掉grep。執(zhí)行下面的命令:

:nnoremap <leader>g :execute "grep! -R " . shellescape(expand("<cWORD>")) . " ."<cr>

再一次試試,發(fā)現(xiàn)什么都沒發(fā)生。Vim用結果填充了quickfix窗口,我們卻無法打開。 執(zhí)行下面的命令:

:nnoremap <leader>g :execute "grep! -R " . shellescape(expand("<cWORD>")) . " ."<cr>:copen<cr>

現(xiàn)在試試這個映射,你將看到Vim自動打開了包含搜索結果的quickfix窗口。 我們所做的僅僅是在映射的結尾續(xù)上:copen<cr>。

最后一點,在搜索的時候,我們要移除Vim所有的grep輸出。執(zhí)行下面的命令:

:nnoremap <leader>g :silent execute "grep! -R " . shellescape(expand("<cWORD>")) . " ."<cr>:copen<cr>

我們完成了,試一試并犒勞一下自己吧!silent命令僅僅是在運行一個命令的同時隱藏它的正常輸出。

練習

把我們剛剛做出來的映射加入到你的~/.vimrc文件。

如果你未曾讀過:help :grep,去讀它。

閱讀:help cword

閱讀:help cnexthelp cprevious。修改你的grep映射,試一下它們。

設置:cnext:cprevious的映射,讓在匹配內容間的移動更加方便。

閱讀:help expand。

閱讀:help copen。

在我們創(chuàng)建的映射中加入height參數(shù)到:copen命令中,看看quickfix窗口能不能以指定的高度打開。

閱讀:help silent。

以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號