replaceneovim

:args and ripgrep in neovim


Question

I am trying to find a string across multiple files using ripgrep where the string has lots of characters that would need to be manually escaped if I were to subsequently replace the string using something like sed, so I would prefer to make the replacement using the %s builtin substitution command in nvim in combination with the no magic characters symbol \V. I would like to feed the output from ripgrep to the :args command in nvim and then use :argdo %s,\V<c-r>0,newstring,g. How can I do this with ripgrep and :args?

Context and Attempt

In nvim, I know I can use :args **/*.py to get all occurrences of python files loaded into the nvim argument list, similarly I can use something like :args +!ls src/*.py (just to illustrate using an external command). But I would like to use a more intelligent file search utility like ripgrep since I could paste in my string of interest (i'd still have to escape the double quotes, unfortunately) and then call :argdo %s,"{environ['HOME']}/.cartopy_backgrounds/BlueMarble_3600x1800.png",newstring,g.

My desired workflow is something like:

# launch nvim
nvim
# while in nvim, assuming i've yanked the string of interest,
# list the files with the string then replace that string occurrence in those 
# files 
:args +!rg -l -F "\"{environ['HOME']}/.cartopy_backgrounds/BlueMarble_3600x1800.png\""  . --glob "*.py" 
:argdo %s,\V<c-r>0,foo,g | update

but this throws

ripgrep error message

Here are some example files

# src/file1.py 
def main():
  first_str_occurence = "{environ['HOME']}/.cartopy_backgrounds/BlueMarble_3600x1800.png"

# src/file2.py
def foo():
  another_str_occurence = "{environ['HOME']}/.cartopy_backgrounds/BlueMarble_3600x1800.png"

Solution

  • After playing around a bit, I found a placeholder solution in which I simply write the outputs of ripgrep to a file and then populate the nvim argument list with the contents of that file. This looks simply like this:

    # launch nvim
    nvim
    # search the desired string in files and write the outputs to a tmp file
    :!rg -l -F "\"{environ['HOME']}/.cartopy_backgrounds/BlueMarble_3600x1800.png\""  . --glob "*.py" > /tmp/out.txt
    :args `cat /tmp/out.txt`
    # replace string occurrences in all the files in argumentlist 
    # where the string is in the unnamed register (i..e, whatever
    # you just yanked) with "new_text"....
    :argdo %s,\V<c-r>0,"new_text",g | update 
    

    I am open to more solutions!

    Edit:

    I have decided to accept my own solution here because it requires no changes to vim configuration and does not require the user to leave vim. The user could easily substitute their own grep statement here in combination with any other tools (e.g., using find with grep).