Nvim + FZF from Terminal
Tags: neovim, productivity, showdev
Published: 2022-07-02 17:06:43


![A gif displaying opening files in nvim via fuzzy file finding](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/se72bife63zuk5xsb9rs.gif)

For my first post, I want to share a bit of my workflow. I'm a vim user, and I had this problem at work: there's a huge codebase to navigate, and a ton of file or folder locations to remember. Not only that, I want to navigate to a specific line where an error was caught or some debug logging took place. There's many tools an individual can use and I'm gonna talk about fzf, rg, and bat. 

If you're asking what FZF is, practically it's a fuzzy file finder that let's you do live greps from your terminal and provides a live preview of the file, as shown in the gif above. And what is rg? rg is a recursive grep that searches for a specified regex pattern and, out of the box, respects .gitignore rules. So you may be asking how rg fits into the big picture? The answer is we'll pipe the output from rg to fzf so we can perform a fuzzy search on the output generated from rg. Lastly, bat will help provide us a cool color theme for our preview window as show in the gif. 

Back to the problem, I wanted to quickly open up neovim/vim with the file I sought for or at the line I was looking for. I have some snippets for that.

First, let's provide the snippet for finding files.

```
function displayFZFFiles {
    echo $(fzf --preview 'bat --theme=gruvbox-dark --color=always --style=header,grid --line-range :400 {}')
}

function nvimGoToFiles {
    nvimExists=$(which nvim)
    if [ -z "$nvimExists" ]; then
      return;
    fi;

    selection=$(displayFZFFiles);
    if [ -z "$selection" ]; then
        return;
    else
        nvim $selection;
    fi;
}

```

I'll provide a high-level view of what's going on in the snippet above. The meat and potatoes is in the function `displayFZFFiles`. It does as it says, displays all of the files in our current working directory via fzf. When we type into the search bar hit enter to select our desired file, fzf will generate the file's relative path concatenated with it's name, ex: `./myfolder/myfile.txt`. We then execute `nvim $selection` to open up the file in nvim.

Yes, the magic is real, and it's gonna get better. Next is navigating to a specific line.

Here's the snippet for navigating to the line via `rg`:

```
function displayRgPipedFzf {
    echo $(rg . -n --glob "!.git/" --glob "!vendor/" --glob "!node_modules/" | fzf);
}

function nvimGoToLine {
    nvimExists=$(which nvim)
    if [ -z "$nvimExists" ]; then
      return;
    fi
    selection=$(displayRgPipedFzf)
    if [ -z "$selection" ]; then
      return;
    else 
        filename=$(echo $selection | awk -F ':' '{print $1}')
        line=$(echo $selection | awk -F ':' '{print $2}')
        nvim $(printf "+%s %s" $line $filename) +"normal zz";
    fi
}

```

Whoa, that's a lot to unpack right now. Again, I'll provide a high level view. In `displayRgPipedFzf`, we want to get the output of `rg` and pipe it `fzf` so we can have it displayed and perform a live grep on the output. You'll notice the `--glob` flags and the purpose they serve is to filter out some output. We don't want to perform live greps on `node_modules` or any packages/libraries we have installed for our project (`vendor` is specific to golang and though we have go modules, some teams at your company may still use them). Similar for selecting our files, we'll record our selection when we hit enter on our keyboard. The thing is, the recorded selection is not in a format that is nvim-friendly, ex: 

```
main.js:const {Writable} = require('stream')
ch2/os-basics.js:1:const os = require('os')
``` 

As we can see, we need to process this output and parse for some data. `awk` to the rescue. `awk` will help us split our output by a delimeter. That delimeter will be the `:` character because when we split by `:` we can get the relative path and file name, along with the line number of our file we selected. Once we get `filename` and `line`, we can then use nvim to open the file and jump to the specified line. The usual way to do that with nvim/vim is: 

```
nvim $lineNumber $filename
``` 

Hence, why we have:
```
nvim $(printf "+%s %s" $line $filename)
```

You may be asking what's the `+"normal zz"` all about. When we have neovim open and we're in normal mode and hit `zz`, it will redraw our cursor and cursorline in the middle of the buffer. This is more of a personal preference because I wanted to keep the cursorline at eye-level when I perform this script. To each their own, you can include or exclude this part of the script based upon your personal preference. 

We've made it this far, and I didn't forget about `bat`. Again, it will provide us a color scheme of our live grep preview of our file. Recalling `displayFZFFiles`:

```
function displayFZFFiles {
    echo $(fzf --preview 'bat --theme=gruvbox-dark --color=always --style=header,grid --line-range :400 {}')
}
```

Here we're telling fzf to use a theme set by bat, and bat has further control on what that preview window will look like. The theme bat chooses is `gruvbox-dark`, and it also adjusts the style and sets a max limit of lines display at a height of 400. A neat thing is that the preview is scrollable, so we can hover over it with our mouse and scroll up or down. 

One last thing is `FZF_DEFAULT_OPTS` and `FZF_DEFAULT_COMMAND`. We need this declared in our bashrc/zshrc because it's a global option that adds some additional configuration or behavior for fzf. 


```
export FZF_DEFAULT_OPTS='--prompt="🔭 " --height 80% --layout=reverse --border'

export FZF_DEFAULT_COMMAND='rg --files --no-ignore --hidden --follow --glob "!.git/" --glob "!node_modules/" --glob "!vendor/" --glob "!undo/" --glob "!plugged/"'
```

To further explain, the default opts will allow us to have that neat prompt shown in the gif. And, touching upon the default command, by default, fzf uses `find` to perform a search of files. I chose to instead use `rg` as our file finder. This command gets overridden when we piped `rg` to fzf for `displayRgFzf`. 

So, this pretty much concludes what I wanted to share. To sum it up, nvim+fzf+rg+bat is an option to speed up your workflow, quickly navigate across a code base by fuzzy finding files or performing live greps and jump to a desired line in a file. I hope this helps. My whole script is found here, which also includes some aliases for my functions. Feel free to rename the aliases to something that works for you. I hope this speeds up your productivity, as it did for me :) 

My script on github:

https://github.com/manofthelionarmy/vimFzf-Rg

Please checkout fzf, rg, and bat at their repos for installation instructions. 

FZF:
https://github.com/junegunn/fzf 

Rg:
https://github.com/BurntSushi/ripgrep

Bat:

https://github.com/sharkdp/bat

Part of my script, I also use fd to do live greps so I can navigate files. Again, here's the repo and follow installation instructions :) 

Fd:
https://github.com/sharkdp/fd