Finding notes and blog posts with Fuzzyfinder and Ripgrep

This blog post describes a nice script to fuzzily search for note files in a directory using fzf and The Silver Searcher (ag). Prompted by this I've done something similar for searching within my Markdown notes, and (separately) within my blog posts, and using Ripgrep instead of the latter.

Because I wanted to use this for searching in multiple places, I wrote a more general-purpose function for searching within a given directory:

ff() {
    cd "${1-"."}"
    rg "$2" . -lS \
        | fzf -m --preview "rg \"$2\" ./{} -S --color=always "
}

The first argument to the function specifies the directory in which to search, or if no arguments are provided, searching is done in the current directory. Giving zero or one arguments just opens up a directory listing for the current or the specified directory, respectively.

The second argument is the search term. The rg "$2" . -l command lists the names of files that match the search term, which is quoted to allow whitespace, so we can do e.g.:

ff ~/notes "Obsidian Sync"

as well as:

ff ~/blog spotify

The -S option to rg switches on smart case mode, so lower-case search patterns will be matched case-insensitively, and patterns containing upper case will be matched case-sensitively.

The lists of matching files is piped into fzf. The fzf command is used to create an interactive, multi-selection interface where you can search and select from the initial set of files that matched the search pattern. The -m option enables multi-selection mode, allowing the user to select multiple items from the list. The --preview "rg \"$2\" ./{} -S --color=always " option specifies the preview command that's to be executed when an item is highlighted in the list, for which we use Ripgrep again. $2 is our search pattern again; ./{} represents the currently-highlighted item in the fzf list.

This general-purpose function is useful on its own so I added it to my .functions dotfile, which is sourced in .bash_profile. But I also created a couple of bespoke wrappers for the two directories I imagine using it the most, where I keep my notes, and blog posts, respectively:

note() {
    ff ~/notes "$1"
}

blog() {
    ff ~/blog "$1"
}

That's it for now - all these do is return the name of the selected file, so need a but more work to return the path relative to the user's current working directory (or absolute path), open the selected file in an editor, short-cut the fzf part if only a single file matches the initial search, and open an editor with a new file if there are no matches, as per the original post.