Post

Neovim Multiline Search and Replace with grug-far.nvim | ast-grep and waaaaaay more

Have you ever needed to replace really complex strings in Neovim? Probably sometimes you need to replace entire paragraphs that include multiple lines

Neovim Multiline Search and Replace with grug-far.nvim | ast-grep and waaaaaay more

Contents

Table of contents

YouTube video

Pre-requisites


  • If you don’t want to watch the video above, and you already have your own neovim config, and want to quickly get started and follow along in this tutorial
  • Run the git clone commands below to clone my dotfiles in your .config directory and we will run my config below
1
2
mkdir -p ~/.config
git clone git@github.com:linkarzu/dotfiles-latest ~/.config/linkarzu/dotfiles-latest
  • Open the newly downloaded neobean config with:
1
NVIM_APPNAME=linkarzu/dotfiles-latest/neovim/neobean nvim
  • You can create an alias in your .bashrc or .zshrc file to run my config
1
alias neobean='NVIM_APPNAME=linkarzu/dotfiles-latest/neovim/neobean nvim'
  • Then to run this config, just run neobean

  • If you don’t even have neovim yet, of course you will need to install it first, so if you’re just getting started, I have a video for you:

Multiline search and replace

  • I used to do this by hand, fuck that man
  • I needed to add {:target="\_blank"} at the end of each of my links
  • Demo in video
1
2
\[(.*?)\]\((http[s]?:\/\/.*?)\)(?:[^{]|$)
[$1]($2){:target="_blank"}

Add a line at the end of every file

RIPGREP
(\n|.)*
return match .. "\n<!-- very end of the file -->"
--multiline
\x (lua interpreter)

Replace only certain items

  • Sometimes your search includes multiple results, but you need to leave some of them out
  • that’s when you use \s ( Sync all )
    • Sync All does not work with multiline replacement
    • ast-grep only supports \r ( Replace )

Replace only certain files in a sub path

  • Use this in the Files Filter: filter
  • The Paths: filter only accepts fixed paths
  • The Files Filter uses gitignore syntax
1
2
3
_posts/**/*
or
_posts/*.md

Search only current file

  • Look at the cookbook
  • You can (and should) create a keymap for the ones you need
1
:lua require('grug-far').open({ prefills = { paths = vim.fn.expand("%") } })

ast-grep

  • ast-grep understand your codes, that’s all I fucking now
  • Their page
  • “It is like syntax-aware grep/sed! You can write code patterns to locate and modify code, based on AST, in thousands of files, interactively.”

  • Old way of me achieving this
1
2
colors\["(linkarzu_color\d+)"\]
"$1"
  • But with Astgrep
1
2
3
colors[$A]
$A
\e (switch engine to astgrep)
1
brew install ast-grep

  • Another example provided by maintainer
  • By mistake added / and now I need to replace it with . inside a require
1
2
3
4
5
require($A)
return 'require(' .. vars.A:gsub('/', '.') .. ')'
*sample.lua
\x (lua interpreter)
\e (switch engine to astgrep)

Exclude files

  • I wanted to exclude 2 files from the replace, so added each file in a separate line under Files Filter:
    • !wezterm.lua
    • !eldritch.lua
  • But remember that it may be easier to treat the search results like a buffer, delete what you don’t want, and then replace

I don’t remember a flag or command

  • Just type –help in the Flags field, you’ll find all the docs and search
  • Also for help type g?
  • \p

Other videos mentioned

If you like my content, and want to support me

  • If you want to share a tip, you can donate here
  • I recently was laid off, so if you know about any SRE related roles, please let me know

Discord server

Image

Follow me on social media

  • The following links will be in the YouTube video description:
    • Each one of the videos shown
    • A link to this blogpost

How do you manage your passwords?

  • I’ve tried many different password managers in the past, I’ve switched from LastPass to Dashlane and finally ended up in 1password
  • You want to find out why? More info in my article:
  • If you want to support me in keeping this blogpost ad free, start your 1password 14 day free trial by clicking the image below

Image

This post is licensed under CC BY 4.0 by the author.