7

I am trying to use fzf in the following manner, I would like to be able to search for a term within my codebase and then with the preview window be able to see the file which contains the string I am searching for at the line where the string is found.

So far I have managed to fuzzy search through the codebase for various terms by piping a ripgrep search of all files in the directory and below. And I have used cut to parse out the file name for cat or tail to read and print to the preview window. This is the command used for that.

rg . -n | fzf --preview-"cut -d":" -f1 <<< {} | xargs cat"

Note the string represented by {} is in the following format:

myfile.c:72:The string I am fuzzy searching

My issue is that I cannot parse out both the filename and the line number.

I have tried passing a bashscript within the preview command as well as using $() in the following example. (Note that here I use tail with the --lines+N argument to print the file after line N)

rg . -n | fzf --preview-"tail $(cut -d":" -f1 <<< {}) --lines=+$(cut -d":" -f2 <<< {})"

This does not work nor does a variety of variants on this attempt. Any help or feedback is appreciated.

Edit(1) :

I've tried to split it into an array like so

rg . -n | fzf --preview="IFS=":" read -r -a arr <<< {}| xargs tail ${arr[0]} --lines=+${arr[1]}"

This works in that the preview does show the file at the line where the string is found however it does not update as I cycle through other fuzzy found suggestions.

Reginald Marr
  • 387
  • 2
  • 16

2 Answers2

9

So I eventually figured out a solution that works.

It involves calling a separate script from my subprocess running inside --preview. I used the following script to take the string which fzf passes to --preview (in the format of filename:linenumber:found_string) and then used bat to render a preview window with syntax highlighting.

This method is pretty good but is somewhat resource intensive. I'm hoping to lessen the load by adding to the ignore glob and using ripgrep rather then find as it seems that is more efficient.

The bashscript I call string2arg.sh

#!/bin/bash

string2arg() {
    export arg_filename=$(cut -d":" -f1 <<< $1);
    export arg_linenum=$(cut -d":" -f2 <<< $1);
    min_offset=25
    let max_offset="min_offset*3"
    min=0
    if (($min_offset < $arg_linenum)); then
        let min="arg_linenum-$min_offset"
    fi
    let max="arg_linenum+$max_offset"
    bat --color=always --highlight-line $arg_linenum --style=header,grid,numbers --line-range $min:$max $arg_filename;
}

This is then called from my fzf alias for searching as such:

alias fsearch='rg . -n -g "!*.html" | fzf --preview="source $SC/string2arg.sh; string2arg {}"'

where $SC is the path to my bashscript string2arg.sh.

If I'm searching for a term with the intent to open the file its found in at the line its found in I use the following bash alias.

alias vfsearch='export vfile=$(fsearch);vim +$(cut -d":" -f2 <<< $vfile) $(cut -d":" -f1 <<< $vfile)'

Also I happen to use the following defaults for fzf and find them to work for me although since I've moved to tmux I find it sometimes better to show the preview window above rather then to the side.

export FZF_DEFAULT_COMMAND="fd --type file --color=always"
export FZF_DEFAULT_OPTS="--reverse --inline-info --ansi"
export FZF_COMPLETION_TRIGGER=']]'

I find this extremely useful and am planning on moving it inside of my vim sessions. Hope it helps others !

Screenshot to better illustrate the use case.

enter image description here

rofrol
  • 14,438
  • 7
  • 79
  • 77
Reginald Marr
  • 387
  • 2
  • 16
1

The fzf command has the option to split selected item for you:

-d, --delimiter=STR    Field delimiter regex (default: AWK-style)

also, you can set offset on pager, which in your case it is bat,

--preview-window=OPT   Preview window layout (default: right:50%)

for your use case, with extra options the command will be:

$ rg . -n | fzf --ansi --border --reverse --delimiter=: \
                --preview 'bat --color always {1} -n  -H {2}' \
                --preview-window 'top,80%,border-bottom,+{2}+3/3'

the output will be something like this:

README.md:119:

to merge them all I did this for myself as below:

# nff nvim find files
# usage:
# nff file_ext content
#
# example:
# to find `install` in md files:
# nff md install
# and then hit enter on the fzf list will open nvim on the line number in selected list
function nff() {
    file_line=`rg "$2" -i --no-ignore --no-heading --column --color ansi -g \*.$1 | 
        fzf --ansi --border --reverse --delimiter=: \
        --preview 'bat --color always {1} -n  -H {2}' \
        --preview-window 'top,80%,border-bottom,+{2}+3/3'`

    file=`echo "$file_line" | gawk -F: '{print $1}'`
    line=`echo "$file_line" | gawk -F: '{print $2}'`
    if [[ -n $file ]]; then
        nvim +"$line" "$file"
    fi
}

you can put the above code in your .bashrc, .zshrc, or what ever file you use to initiate your shell.

using nff to find install on md file

Mamrezo
  • 1,309
  • 16
  • 20