4

I find and replace a pattern in all the files in my project (which is a big one). After the search is done, quickfix window is opened with the matches. The matches are in different files in different directories/sub-directories.

When I want to replace all the matches in the quickfix list, I run the following command:

:cfdo %s/old/new/g

Sometimes I want to replace only the matches in files that are in a specific directory (e.g., DPUST) and don't touch the matches in the files in the other directories.

I tried running the following command while the cursor is in quickfix window:

:g/DPUST/cfdo %s/old/new/gc

But I found that this tries to replace the matches in every quickfix entry.

What am I missing to get this to work?

3
  • 1
    I guess you populate your quickfix list with a grep command or something similar. Wouldn't it be easier to restrict this grep command to the directory you want to modify instead of trying to bend :cfdo?
    – statox
    Commented Jul 26, 2018 at 7:55
  • I use ripgrep for searching. I want to search all the project files. Only based on the search result, I can decide in which directory should I replace the matches. Commented Jul 26, 2018 at 7:57
  • When you read :h :cfdo you see that cfdo actually uses :cfirst, {cmd}, :cnfile, {cmd}, etc so calling cfdo in a global command is useless, and I don't think you have a way to filter the entries of the quickfix list. I think your best bet is to use a second call to ripgrep to restrict to the directory you want to treat. Let's see if someone come up with a better solution :)
    – statox
    Commented Jul 26, 2018 at 8:02

2 Answers 2

8

Update: New official vim plugin cfilter

Since 21.8.2018 (patch: 8.1.0311) the plugin cfilter is distributed with vim in $VIMRUNTIME. It is documented under :h cfilter-plugin.

Load plugin cfilter when needed or load it always in your vimrc

:packadd cfilter

Using abbreviations in case of a vanilla vim this can be as short as :pa c<tab><cr>.

Filter quickfix list with

:Cfilter DPUST

Again, using abbreviations in case of vanilla vim the command :Cfilter can be shortened to :Cf.

Run :cfdo

:cfdo %s/old/new/g

Original Answer

As far as I see it, :cfdo works on the quickfix list. This will not be changed by prefixing the command with :g/DPUST/. You could have come up with :v/DPUST/delete and then :cfdo %s/old/new/gc. However, the quickfix list cannot be changed easily. There are quite a few plugins which provide support for this

Following plugins allow to remove lines from the quickfix list with vim commands e.g. :v/DPUST/d

If do not want a full plugin, add following lines to your vimrc to get the command :Qfilter

" From https://github.com/dhruvasagar/dotfiles/blob/master/vim/plugin/filter_list.vim
function! s:FilterList(list, bang, pattern)
  let list = deepcopy(a:list)
  let [cmp, and_or] = a:bang ? ['!~#', '&&'] : ['=~#', '||']
  return filter(a:list, "bufname(v:val.bufnr) " . cmp . " a:pattern " . and_or . " v:val.text " . cmp . " a:pattern")
endfunction
function! s:FilterQuickfixList(bang, pattern)
  call setqflist(s:FilterList(getqflist(), a:bang, a:pattern))
endfunction
command! -bang -nargs=1 -complete=file Qfilter call s:FilterQuickfixList(<bang>0, <q-args>)

This has already been asked:

Note grep stands for global/regular expression/print. Questions using the term grep or global are referring possibly to identical tasks.

1
  • @update of the question: I do not see your point here. It is actually helpful. As it has been pointed out :cfdo operates on the quickfix list which means there is not much you can do: except not using :cfdo, creating the quickfix list correctly in the first place, or adjusting it to your need. BTW vim-qf allows you to restore the list after you have changed it. This means you do not need to re-run your search if you decide to run :cfdo on a different folder.
    – Hotschke
    Commented Jul 26, 2018 at 8:44
2

If you need the quickfix list to decide what directory you want to use/exclude why do you not just execute another search just before the :cfdo? Like this:

  1. first you search :RIPGREP search-term
  2. look at the result and decide what to include
  3. rerun the last command with some explicit directory :some-dir
  4. then run your replace command: :cfdo %s/foo/bar/gc

If your custom :RIPGREP command supports command chaining you can type 3 and 4 on one line and if you use this really often you can try to turn it into a custom command (retrieve the last command from history with histget()).

Not the answer you're looking for? Browse other questions tagged or ask your own question.