#!/usr/bin/env bash usage() { LESS=-FEXR less <<'HELP' fv [OPTIONS] [SEARCH] fuzzy file filtering and command executing a) allfiles=1 ;; c) cmd="$OPTARG" ;; d) dtach=1 ;; o) loop=1 ;; s) small=1 ;; -a search all dirs and hidden files (possibly quirky) -c command to execute [defaults to vim] -d run in background (for ie non-terminal programs) -h show this help -l additional arguments to pass to filtering program -o run continuously -s run in a smaller window HELP } declare cmd='' declare cmdopts=() declare search_str='' declare search_cmd='' declare search_opts=() declare allfiles='' declare loop declare small declare -A colors colors[red]=$(tput setaf 1) colors[green]=$(tput setaf 2) colors[blue]=$(tput setaf 4) colors[reset]=$(tput sgr0) color() { local c c="$1" shift printf '%s' "${colors[$c]}" printf '%s\n' "$@" printf '%s' "${colors[reset]}" } err() { color red "$@" >&2 } die() { [[ -n "$1" ]] && err "$1" exit 1 } has() { local o c verbose verbose=0 while getopts 'v' o; do case "$o" in v) v=1 ;; esac done shift "$((OPTIND-1))" for c; do c="${c%% *}" if ! command -v "$c" &> /dev/null; then (( verbose > 0 )) && err "$c not found" return 1 fi done } select_from() { local o c cmd OPTARG OPTIND cmd='command -v' while getopts 'c:' o; do case "$o" in c) cmd="$OPTARG" ;; esac done shift "$((OPTIND-1))" for c; do if $cmd "${c%% *}" &> /dev/null; then echo "$c" return 0 fi done return 1 } while getopts 'ac:dhlos' opt; do case "$opt" in a) allfiles=1 ;; c) cmd="$OPTARG" ;; d) dtach=1 ;; h) usage; exit 0 ;; l) search_opts+=( '-l' ) ;; o) loop=1 ;; s) small=1 ;; esac done shift "$((OPTIND-1))" has -v 'fzf' || die if [[ -v FV_CMD ]]; then cmd="$FV_CMD" elif [[ -z "$cmd" ]]; then cmd=$(select_from 'v' 'vim') fi if [[ -v FV_SEARCH ]]; then search_cmd="$FV_SEARCH" else search_cmd=$(select_from 'ag' 'ack' 'grep') fi if [[ "$search_cmd" == 'grep' ]]; then err 'grep is slow, you should strongly consider installing ag or ack' sleep .75 fi if [[ -n "$1" ]]; then if [[ -e "$1" ]]; then search_opts+=( "$1" ) else search_str="$1" fi shift fi case "$search_cmd" in 'ag') search_opts+=( '--color' ) if [[ -n "$allfiles" ]]; then search_opts+=( '-u' '--hidden' ) fi if [[ "$search_str" == '' ]]; then search_opts+=( '-l' ) fi ;; 'ack') if [[ "$search_str" == '' ]]; then if [[ -z "$allfiles" ]]; then search_opts+=( '-f' ) else search_opts+=( '-g' '^[^\.]' ) fi else search_opts+=( '-l' ) # search_opts+=( '--match' ) fi ;; 'grep') search_opts+=( '-r' '-I' ) if [[ -z "$allfiles" ]]; then if [[ -r ~/.ignore ]]; then while read -r line; do search_opts+=( "--exclude-dir=$line" ) done < ~/.ignore else search_opts+=( '--exclude-dir=bower_components' ) search_opts+=( '--exclude-dir=node_modules' ) search_opts+=( '--exclude-dir=jspm_packages' ) search_opts+=( '--exclude-dir=.cvs' ) search_opts+=( '--exclude-dir=.git' ) search_opts+=( '--exclude-dir=.hg' ) search_opts+=( '--exclude-dir=.svn' ) fi fi if [[ -z "$search_str" ]]; then search_opts+=( -F '' ) else search_opts+=( -P ) fi ;; esac if [[ "$search_str" != '' ]]; then search_opts+=( "$search_str" ) fi main() { local highlight highlight=$(select_from 'bat --color=always --style=header' 'highlight -q --force -O ansi') choices=$($search_cmd "${search_opts[@]}" 2> /dev/null | fzf --ansi --multi --preview="[[ \$(file -ib {}) = *text* ]] && ${highlight} {}") || exit 1 if [[ "$search_str" != '' ]]; then if [[ $search_cmd == 'ag' ]]; then choices=$(cut -d: -f1 <<< "$choices") fi fi mapfile -t choices <<< "$choices" if [[ $dtach ]]; then ($cmd "${cmdopts[@]}" "${choices[@]}" &> /dev/null &) else $cmd "${cmdopts[@]}" "${choices[@]}" fi } if [[ -n "$loop" ]]; then while main; do true done else main fi