Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

multi process bash within fzf --preview feature

Tags:

bash

cut

xargs

fzf

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.

like image 680
Reginald Marr Avatar asked Mar 15 '19 16:03

Reginald Marr


1 Answers

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

like image 125
Reginald Marr Avatar answered Oct 19 '22 22:10

Reginald Marr