Skip to content

Instantly share code, notes, and snippets.

@nkh
Last active June 13, 2022 18:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nkh/6144e0dfcaf5848c43fa962ad47be865 to your computer and use it in GitHub Desktop.
Save nkh/6144e0dfcaf5848c43fa962ad47be865 to your computer and use it in GitHub Desktop.
#!/bin/env bash
bff() # Inspired by fff. Run: bbf 3> some_file, to get exit-time selection
{
shopt -s checkwinsize ; trap 'rdir' SIGWINCH ;
declare -A dir_file filters marks tags ; show_dirs=1 ;kolumn=1
local BFF=(/ /home/nadim /home/nadim/nadim /home/nadim/nadim/downloads)
pushd "$1" &>/dev/null ; tabs+=("$PWD") ; tab=0 ; rdir
while true; do [[ -n $key ]] && { REPLY=$key ; key= ; } || read -rsn 1 ; case "${REPLY: -1}" in
q|Q) ((${#tabs[@]} - 1 == 0)) \
&& { true >&3 && [[ $REPLY == 'q' ]] && selection '\n' >&3 || :>&3 ; quit ; } \
|| { unset -v 'tabs[tab]' ; tabs=("${tabs[@]}") ; ((tab -= 1)) ; key=$'\t'; } ;;
g|G) dir_file["$PWD"]=0 ; [[ "$REPLY" == 'G' ]] && ((dir_file["$PWD"] = nfiles - 1)) ; list ;;
B|j) ((file < nfiles - 1)) && { ((dir_file["$PWD"] = file + 1)) ; list; } ;;
A|k) ((file > 0)) && { ((dir_file["$PWD"] = file - 1)) ; list; } ;;
5) ((file -= $LINES, file = file < 0 ? 0 : file)) ; list $file ;;
6) ((file += $LINES, file = file > nfiles ? nfiles : file)) ; list $file ;;
D|h) [[ "$PWD" != / ]] && { nd="${PWD%/*}" ; opwd=$PWD ; cdir "${nd:-/}" "$opwd"; } ;;
C|l|"") [[ -f "${files[file]}" ]] && { "${EDITOR}" "${files[file]}"; echo -e '\e[?25l' ; } \
|| { (($nfiles)) ; cdir "${files[file]}" ; } ;;
.) [[ $show_hidden ]] && show_hidden= || show_hidden=1 ; rdir ;;
_) ((show_dirs ^= 1)) ; rdir ;;
S) ((show_stat ^= 1)) ; list ;;
f) prompt "~ " -ei "${filters[$tab]}" ; filters[$tab]="$REPLY" ; filter_mark="~" ; rdir ;;
F) filters[$tab]= ; filter_mark= ; rdir ;;
K) prompt 'sort -k ' -sn 1 && [[ -n $REPLY ]] && kolumn=$REPLY ; rdir ;;
#) sorting by ...
[0-9]) cdir "${BFF[REPLY]}" ;;
b|n|N) how="$REPLY" ; [[ $how == 'b' ]] && prompt "find: " ; ffind "$REPLY" $how ;;
/) g="$(cd "$PWD" ; fd . --color=always | fzf --ansi --info=inline --layout=reverse)" ; \
[[ "$g" ]] && nd="$PWD/"$(dirname "$g") && cdir "$nd" "$PWD/$g" ;;
-) cdir "${PPWD}" ;;
c) prompt 'cd: ' -e && [[ -n $REPLY ]] && cdir "$REPLY" ;;
m) prompt 'M' -n 1 && [[ -n $REPLY ]] && marks[$REPLY]=$(dirname "${files[file]}") ; refresh ; list ;;
\') prompt "'" -n 1 ; refresh ; list ; [[ -n ${marks[$REPLY]} ]] && cdir ${marks[$REPLY]} ;;
y) n=${files[file]} ; [[ ${tags[$n]} == '' ]] && unset -v 'tags[$n]' || tags[$n]='' ; list ; key=j ;;
Y) n=${files[file]} ; [[ ${tags[$n]} == '' ]] && unset -v 'tags[$n]' || tags[$n]='' ; list ; key=k ;;
u) for p in "${files[@]}" ; do tags["$p"]='' ; done ; list ;;
U) tags=() ; refrsh ; list ;;
r) prompt "rename ${files[file]##*/} to: " && mv "${files[file]}" "$PWD/$REPLY" ; rdir ;;
M) prompt 'mkdir: ' && [[ "$REPLY" ]] && mkdir -p "$PWD/$REPLY" ; rdir ;;
p) ((${#tags[@]})) && { cp "${!tags[@]}" "$PWD" ; rdir ; } ;;
P) ((${#tags[@]})) && { mv "${!tags[@]}" "$PWD" ; rdir ; } ;;
x) chmod a+x $(selection ' ') ; rdir ;;
X) chmod a-x $(selection ' ') ; rdir ;;
d) ((${#tags[@]})) && sel=", tags: ${#tags[@]}" || sel= ; \
prompt "delete$sel ? [y/N]: " -n1 && [[ $REPLY == y ]] && { rm -rf $(selection ' ') ; tags=() ; } ; \
rdir ;;
t) tabs+=("$PWD") ; ((tab = ${#tabs[@]} - 1)) ; list ;;
$'\t') ((tab += 1, tab >= ${#tabs[@]})) && tab=0 ; cdir ${tabs[tab]} ;;
\!) tmux split-pane -v -c "$PWD" -p20 ;;
#:) shell command with %p %f %e %tags ... set
esac ; done
}
cdir()
{
[[ -d "$1" ]] || return
[[ "$PPWD" != "$PWD" ]] && PPWD="$PWD" ; PWD="${1}" ; tabs[tab]="$PWD" ; cd $PWD
LINES=$(tput lines) ; COLS=$(tput cols) ; files=() ; files_color=() ; search=$2 ; select= ; nfiles=0 ; get_dir
while true
do
read -s -u 4 -t 0.01 p ; [ $? -gt 128 ] && break ; read -s -u 5 pc ;
files[$nfiles]="$PWD/$p" ; files_color[$nfiles]="$pc"
[[ "$PWD/$p" == "$search" ]] && select=$nfiles ; ((nfiles++))
done
((lines = nfiles > LINES - 1 ? LINES - 1 : nfiles, center = lines / 2))
refresh ; list $select
}
header()
{
a=$* ; l=$(( ${#PWD} - ($COLS - (${#a} + 11) ) ))
echo -e "\e[H\e[K\e[94m${PWD:l<0?0:l}\e[94m\e[95m$filter_mark $@ $((tab+1))/${#tabs[@]} ${#tags[@]}\e[m"
}
list()
{
(($nfiles == 0)) && header "${#tags[@]}\e[33m<Empty>" && return
[[ $1 ]] && dir_file[$PWD]=$1 ; file=${dir_file[$PWD]:-0}
((file >= nfiles)) && ((file=nfiles-1))
((top = nfiles < lines || file <= center ? 0 : file >= nfiles - center ? nfiles - lines : file - center))
((show_stat)) && stat="$(stat -c ' %A %U' ${files[file]}) $(stat -c %s ${files[file]} | numfmt --to=iec)" || stat=
header "$((file+1))/${nfiles}$stat"
for((i=$top ; i <= ((bottom = top + lines - 1, bottom < 0 ? 0 : bottom)) ; i++))
do
tag=${tags[${files[$i]}]:- }
[[ $i != $file ]] && cursor=$tag || cursor="\e[7;34m$tag\e[m"
[[ $i == $bottom ]] && nl= || nl='\n'
echo -ne "\e[K$cursor${files_color[i]}$nl"
done
}
get_dir() { mkapipe 4 ; mkapipe 5 ; ((show_dirs)) && _get_dir d "%f" ; _get_dir f "%f" ; }
_get_dir()
{
find "$PWD/" -mindepth 1 -maxdepth 1 ${show_hidden:+\( ! -iname ".*" \)} -type $1 -printf "$2\t%i\t%f\t%A@\n" | \
rg "${filters[$tab]}" | \
sort -k $kolumn | \
tee >( cut -f 3 >&4) | \
cut -f 1 | lscolors | awk '{print substr($0, 0, '$COLS'-1)}' >&5
}
ffind()
{
[[ $2 == 'b' ]] && to_search="$1"
[[ $2 == 'N' ]] && { ((from = $file > 0 ? $file - 1 : 0)) ; to=0 ; inc='--' ; } \
|| { ((from = $file >= $nfiles ? $file : (($file + 1)))) ; to=$nfiles ; inc='++' ; }
for((i=$from ; i != $to ; i$inc)) ; do [[ "${files[i]##*/}" =~ "$to_search" ]] & rrefesh && list $i && break ; done
}
refresh() { echo -ne "\e[?25l\e[2J\e[H\e[m${1}" ; }
prompt() { echo -ne '\e[999B\e[0;H\e[K\e[33m\e[?25h' ; read -rp "$@" ; echo $REPLY ; }
rdir() { cdir "$PWD" ; }
quit() { refresh "\e[?25h" ; exit 0 ; }
mkapipe() { PIPE=$(mktemp -u) && mkfifo $PIPE && pc="exec $1<>$PIPE" && eval "$pc" && rm $PIPE ; }
selection() { ((${#tags[@]})) && printf "%s$1" "${!tags[@]}" || printf "%s$1" "${files[file]}" ; }
bff $@
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment