【neovim 技巧系列】quickfix 与文本处理
quickfix 與文本處理
本文最新發布在 github 上,不保證 CSDN 上的內容最新。
大部分內容整理自:Advanced Vim topics, tips and tricks (by Mark McDonnell)
global
| :global/pattern/excmd | 對滿足 pattern 的行文本進行 excmd 操作 | 
| :g/^foo/d 等價于 :g/^foo/norm dd | 刪除 foo 開頭的行(g = global,d = delete) | 
| :g!/^foo/d | 刪除不是 foo 開頭的行 | 
| :g/foo/norm @q | update1 | 對含 foo 的行執行 @q 宏,并更新文本2 | 
| :g/^/exe "norm \<s-j>" | 每兩行進行合并3 | 
內置的 vimgrep
| :vimgrep /pattern/[gjf] path_to_file | g 一行內匹配所有(不使用表示一行只匹配第一次出現的);j 不顯示第一個匹配的內容;f 啟用模糊查詢 | 
| :vimgrep /pattern/[gjf] % | 在當前文件中搜索:% 表示當前文件路徑 | 
| :vimgrep /pattern/[gjf] **/* | 在當前項目中搜索 | 
| :vimgrep /ssh/j `find . -type f -name 'tmux*' | 使用 `` 來獲取外部程序的結果:調用 find 程序來搜索文件名 | 
| :vimgrep /<C-r>// * | <C-r>/ 快捷鍵表示 / 寄存器(查詢模式的寄存器);在當前目錄的第一層文件下搜索 / 寄存器的內容 | 
- vimgrep 是全局(唯一)的,每次調用的結果替換上一次的結果;使用 copen 打開 quickfix 窗口,使用 cnext 跳轉下一個,cNext 跳轉上一個
- lvimgrep 是局部的(l 表示 location),每個 buffer 都可以有一個;lopen、lnext、lNext 在不同的 buffer 中獨立地工作;幾乎所有的 vimgrep
 都有一個相應的 lvimgrep 版本
- vimgrepadd、lvimgrepadd 使用方式與 vimgrep 也相同,表示不覆蓋上一次結果,而是添加到上一次結果
- cex[pr] expr 和 lex expr 用于計算 expr 的值,并把結果放到 quickfix: - :cexpr system('grep -n xyz *') 調用 grep 程序的結果
- :cexpr getline(1, '$') 當前 buffer 全文
- :cex [] 清空 quickfix
 
外部的 grep
| :set grepprg | 查詢當前 grep 程序;默認為 grep -n $* /dev/null | 
| :set grepprg=rg\ --vimgrep | :set grepformat=%f:%l:%c:%m,%f:%l:%m | 設置為 rg | 
| :silent! noautocmd grep pattern % | copen | 不彈出搜索結果,并打開 quickfix,打開文件時不運行 autocmd | 
| :set grepprg=ag\ --nogroup\ --nocolor\ --skip-vcs-ignores | 設置為 ag | 
| :<range>!grep foo | 只留下含 foo 的行(刪除不含 foo 的行) | 
| :<range>!grep -v foo | 只留下不含 foo 的行(刪除含 foo 的行) | 
和 vimgrep 系列類似,grep 為全局 quickfix,局部的為 lgrep,全局增加的為 grepadd,局部增加的為 lgrepadd。
telescope
許多插件提供將插件的搜索結果發送至 quickfix。這里介紹最常見的 telescope 的搜索功能。
首先啟用歷史記錄快捷鍵:
-- 更多可設置快捷鍵的功能見 `:help telescope.actions` local action = require 'telescope.actions' local mappings = {i = { -- insert mode-- 上翻/下翻歷史搜索記錄:所有功能共享歷史搜索記錄["<C-Down>"] = action.cycle_history_next,["<C-Up>"] = action.cycle_history_prev,},n = { -- normal mode["t"] = action.toggle_all, -- 反選所有["T"] = action.drop_all, -- 取消所有["d"] = action.delete_buffer,-- `<C-q>` clear + send all to quickfix-- `<A-q>` clear + send the selected to quickfix-- `a` add the selected to quickfix-- `A` add all to quickfix["a"] = action.add_selected_to_qflist,["A"] = action.add_to_qflist,} } require'telescope'.setup {defaults = {mappings = mappings,}, }然后設置常見的一些快捷鍵來快速打開功能對話框:
nnoremap ,f <cmd>Telescope find_files<cr> nnoremap ,l <cmd>Telescope live_grep<cr> nnoremap ,g <cmd>Telescope grep_string<cr> nnoremap ,b <cmd>Telescope buffers<cr> nnoremap ,d <cmd>Telescope diagnostics<cr> nnoremap ,q <cmd>Telescope quickfix<cr> nnoremap ,Q <cmd>Telescope quickfixhistory<cr>Normal 模式中按 ,g 會在當前項目中搜索當前光標下的內容,彈出 telescope 對話框之后4:
- 按 <Ctrl-q> 將所有內容發送至 quickfix
- 或者按 <Tab>/<Shift-Tab> 多選,按 <Alt-q> 把選擇的內容發送至 quickfix
對 quickfix 處理內容常常使用 cdo(見下文)。此外 telescope 可直接操作已有的 quickfix:
- 按 ,q 在 telescope 中打開 quickfix
- 基于已有的 quickfix 創建新的 quickfix,見上述發送至 quickfix 的兩種步驟(這相當于從 quickfix 列表中減少條目)
- 映射 action.add_selected_to_qflist 和 action.add_to_qflist 等函數提供了添加新項至 quickfix 列表的功能
- 按 ,Q 可查看和轉到歷史 quickfix 列表
live_grep 和 grep_string 使用的是 telescope.defaults.vimgrep_arguments,默認為 rg 且進行了一些配置,所以:
- 直接支持正則而無需額外轉義,注意這使用了 Rust regex 庫的正則語法
- 開啟了 smart-case,輸入小寫字母時會查詢該字母的大小寫,而輸入大寫則只查詢大寫
- 如果你想使用其他搜索程序,可以自行配置 defaults = { vimgrep_arguments = { ... }, mappings = mappings, }
更多功能見 telescope 的幫助文檔。
處理搜索結果
通常 vimgrep 和 grep 用于搜索內容,并通過 copen/lopen 把搜索結果(文件路徑、位置信息、內容)放置于 quickfix。
然后使用 <c|l>[f]do 進行文本處理,區別在于:
- cdo vs ldo:全局操作 vs 局部(當前 buffer)操作
- cdo vs cfdo:全部操作 vs 對每個文件只操作一次
例子:
- :cdo s/pat/replacement/ 把 pat 換成 replacement
- :cdo undo 撤銷修改
- :silent! noautocmd cdo ... | update 執行操作文件時不運行 autocmd,并寫入操作,整個過程不顯示消息 - silent! 忽略中途打印的消息(mes 也看不到),! 表示連錯誤消息也忽略
- noautocmd 可以加快操作,因為無需運行自動命令
- update 相當于 :write,但僅發生在文件修改之后寫入(:w 無論有沒有修改文件都會寫入)
- 建議至少使用 :cdo ... | update 形式,因為基于未保存的緩沖區修改可能引發數據競爭,如果需要撤銷,使用 :cdo undo | update 即可
 
- :cfdo %s/foo/bar/g 對每個文件進行一次批量替換 - 相比于 cdo,這減少了替換后寫入的次數(每個文件最多只需寫入一次),因此有時可以避免多次修改造成的意外/動態修改
- 可以想象成,對 quickfix 列出的文件進行全局替換(嗯,類似于下面使用的 :argdo)
- :cfdo %s/foo/bar/g 對每個文件進行一次批量替換 - 每個文件內的所有 foo 被替換成 bar
- % 寄存器訪問當前文件名,對于每次更新了的緩沖區,% 都會以新的文件名更新寄存器
- 注意 :cfdo s/foo/bar/ 表示對每個文件第一次出現的那個 foo 替換成 bar
- 注意 :cfdo s/foo/bar/g 表示對每個文件第一個出現在 quickfix 的那行的所有 foo 替換成 bar
 
 
do 系列還有 tabdo / windo / bufdo / argdo,使用方式類似,只是應用的范圍不同。
args
:args 主要作為 buffers 的子集。在眾多打開的文件 (buffers) 中,選擇其中一部分文件執行操作:
- :args *.md 打開所有 md 文件(注意,這搜索當前目錄下的一級路徑,如果需要遞歸,使用 :args **/*.md) - :args <Tab> 可以選擇 buffer
- :args `fd ...` 根據 fd 搜索結果打開文件
- :args ... 每次執行這個操作意味著創建新的列表(所以不需要刪除列表)
 
- :args 查看待操作的文件列表(注意不帶任何參數)
- :argadd、:argdelete、:argdedupe 對文件進行增加、刪除、去重
- :argdo 對這些文件進行操作(具體例子與 :cdo 差不多)
可以看到,args 并不需要 quickfix,而是基于文件操作,所以自然可以實現文件替換 :argdo %s/foo/bar/g。
Cfilter
對于 quickfix,篩選是常見操作。vim 內置一個插件來處理,完整的基本過程是:
:vimgrep /vim/ **/* :packadd cfilter :Cfilter /\.md$/這里打開一個 quickfix,加載 cfilter 插件,然后使用
- :Cfilter /pat/ 篩選滿足搜索模式的條目
- :Cfilter! /pat/ 篩選不滿足搜索模式的條目
- colder 前一個 quickfix,cnewer 后一個 quickfix
- :Lfilter 應用于 location list (quickfix 的 buffer 局部版本) - lolder / lnewer 前/后一個 location list
 
對于簡單的篩選,這已經足夠。唯一不足的是,似乎沒有內置的條目刪除命令。
nvim-bqf
除了上述的 telescope 之外,你還可以使用 nvim-bqf 插件來增強 quickfix 的預覽、刪除、篩選操作5,通常的步驟:
然后使用 <c|l>[f]do 對新的 quickfix 進行批量處理。
在預覽方面,可搭配 nvim-treesitter 提供高亮。
在篩選方便,可搭配 fzf 提供模糊查詢6:
- zf 調出 fzf
- <Tab> 進行選擇/反選 - 在 visual mode 下可使用 <Tab> 多條選擇/反選
- 使用 '<Tab> 對光標所在的文件的所有條目進行選擇/反選
- 使用 z<Tab> 清除所有選擇
 
- zn 把選中的條目創建新的 quickfix - zN 把未選中的條目創建新的 quickfix
 
在支持 lsp 的許多地方也會使用到 quickfix:
- vim.diagnostic.setqflist、vim.diagnostic.setloclist
- vim.lsp.buf.references()
- vim.lsp.buf.document_symbol()
- vim.lsp.buf.incoming_calls()
- vim.lsp.buf.outgoing_calls()
所以 nvim-bqf 還算一個相對通用的插件。使用 telescope 還是 nvim-bqf 來管理 quickfix 完全是個人偏好。
自動化文本處理
nvim 完全可以當做命令行工具使用,所以對它進行自動化測試不麻煩。
以下操作對目錄下的 md 文件進行文本替換并直接寫入源文件,查看 diff,然后撤銷寫入。
nvim -u NONE --headless\+":args *.md:args:silent! argdo %s/a/.../ge | update"\+":!git diff:silent! argdo undo | update"\+":qa" [a.md] aa.md b.md :!git diff diff --git a/a.md b/a.md index 705a2d7..a2db5c9 100644 --- a/a.md +++ b/a.md @@ -1,2 +1,2 @@ -abc -aed +...bc +...ed diff --git a/b.md b/b.md index 4075523..86a1d9c 100644 --- a/b.md +++ b/b.md @@ -1,2 +1,2 @@ -aqwa -ppa +...qw... +pp...注意:表格中的 \| 實際只需要輸入 |。 ??
norm 表示模擬 norm 模式下操作 ??
execute 用于計算 Ex 命令的值,比如涉及控制鍵 ??
使用 <Ctrl-/> 和 ? 顯示對話框在 Insert/Normal 模式下的快捷鍵映射。 ??
增加到 quickfix 可使用 vim 內置的 vimgrepadd / lvimgrepadd / grepadd / lgrepadd 命令。 ??
telescope 的搜索框直接支持模糊查詢和預覽,所以可以完全無需 nvim-bqf + fzf。 ??
總結
以上是生活随笔為你收集整理的【neovim 技巧系列】quickfix 与文本处理的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: python实现HMAC算法与应用
- 下一篇: 计算机不能切换显卡,NVIDIA控制面板
