14 November, 2019
Vim: Substitute across all files
Let's say you want to do a refactoring in your Scala project and replace all occurrences of new LocalDate(2020, 10, 1) with DateTime(2020, 10, 1).
- Search for what you want to match in your local file to try to find the right query
/\vnew LocalDate\((.{-})\)\vactivates regex search as you would expect it, we capture the exact date in a capture group and.{-}makes the.match non-greedy compared to.*which would not stop at the first)
- Find all files containing
new LocalDate(...):vimgrep // **/*.scalafind all occurrences in files which match the//last executed search and writes these into the quickfix list.**/*.scalamatches all.scalafiles, regardless where in the project.- Alternatively with fzf vim:
:Rg patternand thenalt - aorTabandShift - Tabto select wanted matches,Enterto send to quickfix list.
- Run the replacing over all files
:cfdo %s//DateTime(\1)/gcapplies the substitute command to each item in the quickfix list.%sstarts the substitute command,//again is the last used search andDateTime(\1)is the replacement, where\1is the first capture group, so e.g.2020, 10, 1./gcsays we want to apply it to each occurrences in a line andclets you view and accept the replacement, it can be left out. It can be useful to chain aupdatewith:cfdo %s//DateTime(\1)/gc | update, which ensures all changed files are saved.
Bonus: run a normal mode command across multiple files
Sometimes you want to do some refactoring to many files, so you record a macro qq in the current file, and want to apply that to other files as well:
- Record the macro once in the current file and afterwards undo all the changes
- Add all files you want to apply the macro to the args list
argadd ./src/stuff/*.md- Or with fzf vim:
:GFilesand thenTabandShift + Tabselect wanted files and hitCtrl - l(custom function see **) to add to args list.
- Check that the args list is correct
args
- Apply the macro to all files
argdo normal @q | update
**
" Populates the args list with file search results from fzf
func s:fnameescape(key, val)
return fnameescape(a:val)
endfunc
function! s:populate_arg_list(lines)
execute 'args ' . join(map(a:lines, function('s:fnameescape')), ' ')
endfunction
let g:fzf_action = { 'ctrl-l': function('s:populate_arg_list') }