Creating (rfv) the rg, fzf, nvim and bat collaboration

Cover Image for Creating (rfv) the rg, fzf, nvim and bat collaboration
Justin Bender
Justin Bender
Contents

Creating (rfv) the rg, fzf, nvim and bat collaboration

This is a post that I don't really want to take the credit for. Someone named Junegunn Choi write a blog post called Ripgrep integration, a walkthrough. Which I recommend reading before reading this.

We are just talking about the script that I build off of the article and what I might use in some of my daily coding life. It's a neat tool and I'm a fan so far.

This is a really neat tool to add to the toolbox of terminal use. From a Security Operations point of view. Tools like this are not something you should probably use of secure work computers, but on a less secure level. Like when you have to download editors with setup third party packages.

Right away I want to talk about how we will start this script file. We will run a few checks to see if the installed command exist and tell the interpreter where to find bash. Really just adding some error handling capabilities. Then we will create the function and allow it to be called with all the arguments passed in. So our basic script, before the function build out will look like this:

#!/usr/bin/env bash

# Function to check if a command exists
command_exists() {
  command -v "$1" >/dev/null 2>&1
}

# Check for required commands
for cmd in rg fzf nvim bat; do
  if ! command_exists "$cmd"; then
    echo "Error: $cmd is not installed." >&2
    exit 1
  fi
done

rfv() (
  # ... function code goes here
)

# Call the rfv function with passed arguments
rfv "$@"

Seems simple enough. We won't focus too much on bash syntax and all of the functionality in there. I'm hoping you have a preliminary knowledge on the topic. If you have little knowledge. Maybe don't copy scripts on then internet from others. Or ask AI LLMs to help you understand.

Before I start going over some of the next part. I'm going to recommend this post again Ripgrep integration, a walkthrough from the original author of this function of the script. We will slightly look at what's going on and break it down at a very minimum way.

Let's take a look at the function we will be using:

rfv() (
  RELOAD='reload:rg --column --color=always --smart-case {q} || :'
  OPENER='if [[ $FZF_SELECT_COUNT -eq 0 ]]; then
            nvim {1} +{2}     # No selection. Open the current line in Vim.
          else
            nvim +cw -q {+f}  # Build quickfix list for the selected items.
          fi'
  fzf < /dev/null \
      --disabled --ansi --multi \
      --bind "start:$RELOAD" --bind "change:$RELOAD" \
      --bind "enter:become:$OPENER" \
      --bind "ctrl-o:execute:$OPENER" \
      --bind 'alt-a:select-all,alt-d:deselect-all,ctrl-/:toggle-preview' \
      --delimiter : \
      --preview 'bat --style=full --color=always --highlight-line {2} {1}' \
      --preview-window '~4,+{2}+4/3,<80(up)' \
      --query "$*"
)

When we look at this function it's hard to really know what's going on. It's going to be using Ripgrep, fuzzyfinder and bat. To make a more powerful tool.

Pretty neat that these 3 terminal tools can work together. Bat with the display, fzf with the interactive menu and rg to search the files for text. Can't fzf search text, possibly. That's not the program that we want to use. With Ripgrep, it's a bit more performant for large directories and files.

We will turn of the filtering for fzf and replace it with rg. We will then add some modifier keys to open and use vim. We will even add in the preview using bat with all the styling included to have a more pleasant terminal experience.

Let's make some commands in strings that we can reuse later called RELOAD and OPENER. The reload, will be to use rg to run a search. The opener will be to use nvim to open and edit the files. The opener will have a few ways to open. If there are more than one path selected. It will run a command. That will open the list of the items. These strings are here:

RELOAD='reload:rg --column --color=always --smart-case {q} || :'
OPENER='if [[ $FZF_SELECT_COUNT -eq 0 ]]; then
          nvim {1} +{2}     # No selection. Open the current line in Vim.
        else
          nvim +cw -q {+f}  # Build quickfix list for the selected items.
        fi'

Now we can start building out the fzf command to run. We will have a few options to disable the fuzzy finder text search, add on ansi so that coloring works and multi for multi-lines.

We will then bind the start and change with the string commands we created earlier with Reload. Bind some more command like enter to become Opener, or ctrl-o to execute opener. Even alt-a as select all, alt-d as deselect all, ctrl-/ to toggle preview.

After we have binded a few things. We have to add the delimiter, preview command, preview window setup and the basics for the query.

Just a reminder that we will be using bat for this preview command. That is why you'll see that command being used below.

fzf < /dev/null \
    --disabled --ansi --multi \
    --bind "start:$RELOAD" --bind "change:$RELOAD" \
    --bind "enter:become:$OPENER" \
    --bind "ctrl-o:execute:$OPENER" \
    --bind 'alt-a:select-all,alt-d:deselect-all,ctrl-/:toggle-preview' \
    --delimiter : \
    --preview 'bat --style=full --color=always --highlight-line {2} {1}' \
    --preview-window '~4,+{2}+4/3,<80(up)' \
    --query "$*"

Overall the command had a lot going on being pieced together like this and I can recommend the original article enough. I don't want to take credit. I just wanted to show some of the adaptations I used to help make it work for me.

The full script:

#!/usr/bin/env bash

# Function to check if a command exists
command_exists() {
  command -v "$1" >/dev/null 2>&1
}

# Check for required commands
for cmd in rg fzf nvim bat; do
  if ! command_exists "$cmd"; then
    echo "Error: $cmd is not installed." >&2
    exit 1
  fi
done

rfv() (
  RELOAD='reload:rg --column --color=always --smart-case {q} || :'
  OPENER='if [[ $FZF_SELECT_COUNT -eq 0 ]]; then
            nvim {1} +{2}     # No selection. Open the current line in Vim.
          else
            nvim +cw -q {+f}  # Build quickfix list for the selected items.
          fi'
  fzf < /dev/null \
      --disabled --ansi --multi \
      --bind "start:$RELOAD" --bind "change:$RELOAD" \
      --bind "enter:become:$OPENER" \
      --bind "ctrl-o:execute:$OPENER" \
      --bind 'alt-a:select-all,alt-d:deselect-all,ctrl-/:toggle-preview' \
      --delimiter : \
      --preview 'bat --style=full --color=always --highlight-line {2} {1}' \
      --preview-window '~4,+{2}+4/3,<80(up)' \
      --query "$*"
)

# Call the rfv function with passed arguments
rfv "$@"

Thanks for making it to the end of the page. I hope you learned something or just had some enjoyment. Cheers 😃