#! /bin/echo this file must be sources: . function kb_ls() { /usr/bin/ls -1dsqh "$@"; } function kb_highlight() { case $cmdname in #Video mplayer|mwrap|mpv|vlc|gmplayer|smplayer|mencoder|kmplayer|Parole|whaawmp|dragonplayer|ffmpeg) extlist='mkv|m4v|mp.|avi|wmv|rmvb|as?|divx|vob|ogm|rm|flv|part|iso|ogg|wav|flac|m4a' ;; #Audio mpg123|mpg123s|mpg321|mpv|mp3blaster|cmus|cplay|moc|xmms|xmms2|sonata|deadbeef|ogg123|mnama) extlist='mp.|aac|wav|ogg|gsm|dct|flac|au|aiff|vox|wma|aac|ra|m4a' ;; #PDF llpp|xpdf|epdfview|evince|foxit|mupdf|okular|apvlv|zathura) extlist='pdf' ;; #Images feh|geeqie|gqview|eog|gpicview|gthumb|mirage|qiv|ristretto|xnview|xv|viewnior) extlist='jpg|jpeg|png|gif|bmp|icon?|tiff?' ;; #Games sdlmame|openmsx|msxplay|zsnes|desmume|VirtualBoy) extlist='rom|dsk' ;; #Wine wine|winewrap|wineconsole) extlist='exe|com|bat' ;; #Archives atool|x|xi|gunzip|extract|unzip|unrar|zip|rar|7z|mcomix|v) extlist='tgz|zip|rar|bz2|gz|tar|exe|pk3|lha|Z|lzma' ;; #Text vim|nano|acme|beaver|geany|leafpad|medit|mousepad|pyroom|sam|vi|gvim|emacs|tea|scite) extlist='txt|rc|sh|c|bash|py|ini' ;; #Default *) extlist='';; esac } function kb_listcheck() { for tempfile in $1; do if [[ "$cmdname" =~ ^(c|cd|popd|rmdir)$ ]]; then if [[ -d "$tempfile" ]]; then displayres+=( "$tempfile" ) ((colorsw++)) else badlist+=( "$tempfile" ) fi elif [[ "$extlist" && "$tempfile" =~ \.($extlist)$ ]]; then displayres+=( "$tempfile" ) ((colorsw++)) else badlist+=( "$tempfile" ) fi done } function kb_menu() { local end=0 h badlist total colorsw page pages sel=0 count key newk change=1 lim=12 displayres tempfile ls_reform colcount cols prereset local -A lslist tput rmam #don't wrap long lines IFS=$'\n' prompt="$2" while ((end==0)); do # the [a-z] or [abcd] structure messes up globbing prompt=${prompt//]/\\]} prompt=${prompt//\\\\]/\\]} # build a new list if ((change != 0)); then sel=0 resetcounter=0 change=0 if [[ "$1" == command ]]; then displayres=( $(compgen -A command -A alias -A builtin -A function $prompt | sort -u) ) elif [[ "$1" == file ]]; then colorsw=0 badlist=() displayres=() kb_listcheck "$prompt*" fi [[ "$1" == file ]] && colorsw=${#displayres[@]} && displayres+=( "${badlist[@]}" ) total=${#displayres[@]} # on zero results retry without front match if [[ "$1" == file ]] && ((total==0)); then colorsw=0 if [[ "${prompt//[^\/]}" ]]; then kb_listcheck "${prompt%/*}/*${prompt##*/}*" else kb_listcheck "*$prompt*" fi colorsw=${#displayres[@]} displayres+=( "${badlist[@]}" ) total=${#displayres[@]} fi ((total==1 && browse==0)) && prompt=${displayres[0]} && break browse=0 (( total < lim )) && h=$total || h=$lim pages=$(( total /lim)) (( total % lim == 0)) && ((pages--)) fi ((sel < 0)) && sel=0 ((sel >= total)) && sel=$((total -1)) if [[ "$reset" ]]; then for tempfile in "${displayres[@]}"; do if [[ "${tempfile##*/}" == "${reset##*/}" ]]; then sel=$resetcounter reset= break else ((resetcounter++)) fi done fi page=$((sel /lim)) ((page==pages))&&lastpage=$'\e[7m'||lastpage= echo -n $'\e[0m'"${lastpage}p$page/$pages"$'\e[m'": $before$prompt"$'\e[47m \e[0m ' [[ "$key" != "ESC[C" ]] && rightonce=0 [[ $rightonce == 1 ]] && echo -n "$helptext" for ((count = lim *(sel /lim); count < lim +lim *(sel /lim); count++)); do echo -ne '\e[0m' [[ $count == $total && $page == 0 ]] && break || echo # (($sel == $count)) && echo -n "X "$'\e[7m' || echo -n " " (($sel == $count)) && echo -n $'\e[7m' ((count < colorsw)) && echo -ne '\e[1;34m' if [[ "$1" == file ]]; then tempfile=${displayres[$count]//\'} tempfile=${tempfile//[} tempfile=${tempfile//]} if [[ "$tempfile" ]]; then if ! [[ "${lslist["$tempfile"]}" ]]; then ls_reform=$(kb_ls "${displayres[$count]}") [[ -d "${displayres[$count]}" ]] && ls_reform=$'\e[4m'"----" printf -v "lslist[$tempfile]" '%4s|%s' "${ls_reform%% *}" "${displayres[$count]}" fi echo -n "${lslist["$tempfile"]}" fi else echo -n "${displayres[$count]}" fi (($sel == $count)) && for ((colcount=0;colcount/dev/null; pwd -L | sed 's_^.*/__') reset=$reset if [[ ${prompt: -3} == ../ ]]; then prompt+=../ elif [[ "${prompt//[^\/]}" == / ]]; then prompt= elif [[ "${prompt//[^\/]}" ]]; then ! [[ ${prompt: -1} == / ]] && prompt=${prompt%/*}/ prompt=${prompt%/*/*}/ else prompt=../ fi;; 'ESC[C') if [[ "$rightonce" == 0 ]]; then #RIGHT if [[ -d "${displayres[$sel]}" ]]; then helptext=$'\e[31m'"$(/usr/bin/ls "${displayres[$sel]}" | wc -l) files: $(/usr/bin/ls "${displayres[$sel]}" | tr '\n' ' ')" else helptext=$'\e[31m'$(/usr/bin/ls -hlF --quoting-style=shell-always "${displayres[$sel]}") fi rightonce=1 else browse=1 rightonce=0 change=1; prompt=${displayres[$sel]} [[ -d "$prompt" ]] && prompt+=/ || end=1 fi;; 'ESC[4~'|'ESC[F'|'ESC[8~'|'ESCOF') ((sel=$total));; #END 'ESC[1~'|'ESC[H'|'ESC[7~'|'ESCOH') ((sel=0));; #HOME 'ESC[12~'|'ESCOQ') oldprompt="$prompt" #F2 oldbefore=$before prereset=${displayres[$sel]} before="Enter new command: " for (( count=0; count < $h; count++)); do echo -ne "\e[2K\e[A\r" done echo -ne '\e[2K\r' kb_menu command "" doubleclear=1 change=1 cmdname=$prompt kb_highlight before=$cmdname\ ${oldbefore#* } prompt=$oldprompt reset=$prereset ;; 'ESC[13~'|'ESCOR') prompt=$(echo "$prompt" | sed 's/[^/]*$//') #F3 [[ "$prompt" ]] || prompt=. sel=0 displayres=( $(/usr/bin/ls -d1qt $prompt/*) ) ;; 'ENTER') [[ "${displayres[$sel]}" ]] && prompt="${displayres[$sel]}"; end=1;; #ENTER 'ESC[2~') appendprompt=\ $prompt; prompt="${displayres[$sel]}"; end=1;; #INSERT 'ESC') nospace=1; end=1;; #ESC $'\x7f'|$'\x08') change=1; [[ $prompt ]] && prompt=${prompt::-1};; #BACKSPACE 'ESC'*) :;; #UNUSED KEY *) change=1; prompt+=$key;; esac if [[ $doubleclear ]]; then doubleclear= else for (( count=0; count < $h; count++)); do echo -ne "\e[2K\e[A\r" done echo -ne '\e[2K\r' fi done } function kb_replace_var() { local var fill OIFS=$IFS IFS=$'\n' last="${last/\~/$HOME}" [[ "${last//[^$]}" ]] && \ for var in $(echo "$last" | /usr/bin/grep -o '\$[a-zA-Z_][a-zA-Z0-9_]*' | tr -d '$'); do fill=${!var} [[ $fill ]] && last="${last/\$$var/$fill}" done IFS=$OIFS } function kb_write_prompt() { local out escquote lang IFS=$' \t\n' [[ -d "$1" ]] && dir=/ || dir=' ' #single quote # (( nospace == 1 )) && { dir= && nospace=; } || nospace=\' # [[ $1 ]] || { nospace=; dir=; } # escquote=${1//\'/\'\\\'\'} #escape (( nospace == 1 )) && dir=; nospace= escquote=$(echo "$1" | sed 's/[] !@#^&()`"<>\\{};=|'\'' []/\\&/g') out="$before$nospace$escquote$nospace$dir$appendprompt" lang=$LC_ALL LC_ALL=C ((READLINE_POINT+=${#out} - ${#READFIRST})) LC_ALL=$LC_ALL READLINE_LINE=$out$READLAST } function kb_main() { local cmdname extlist appendprompt recursive quotes last before psheight nospace=0 dotglob nocaseglob nullglob globstar READFIRST READLAST lastquote lastpage resetcounter rightonce lastquote lastpage resetcounter rightonce prompt out local oldbefore browse beforequote cols doubleclear oldprompt out reset invisible nospace=0 invisible=$(tput civis) #turn cursor invisible READFIRST="${READLINE_LINE::$READLINE_POINT}" READLAST="${READLINE_LINE:$READLINE_POINT}" psheight=$( echo -ne "$PS1" | wc -l) # ((psheight--)) cols=$(( $(tput cols) / 10)) IFS=$' \t\n' # is the line has no words, add a tab. if [[ "$READFIRST" =~ ^[[:blank:]]*$ ]]; then READLINE_LINE="$READFIRST"$'\t'"$READLAST" ((READLINE_POINT++)) return fi # count non-escaped quotes quotes=${READFIRST//\\\'} quotes=${quotes//[^\']} quotes=${#quotes} # the last word is empty, start completing from scratch if [[ "${READFIRST: -1}" == ' ' && "${READFIRST: -2:1}" != '\' ]] && (( quotes % 2 == 0)); then before=$READFIRST last= # complete a command elif [[ "$READFIRST" =~ ^\ *[^=\ ]+$ ]]; then kb_menu command "$READFIRST" prompt+=" " ((READLINE_POINT+=${#prompt} - ${#READFIRST})) READLINE_LINE=$prompt$READLAST IFS=$' \t\n' echo -ne "\e[${psheight}A\r\e[2K" return #variable=filename on first word. elif ! [[ "$READFIRST" =~ \ ]]; then last=$READFIRST before= # complete part of a word else last="${READFIRST##* }" recursive="${READFIRST% *}" fi nocaseglob=$(shopt nocaseglob | /usr/bin/grep -F on) [[ $nocaseglob ]] || shopt -s nocaseglob nullglob=$(shopt nullglob | /usr/bin/grep -F on) [[ $nullglob ]] || shopt -s nullglob dotglob=$(shopt dotglob | /usr/bin/grep -F on) [[ $dotglob ]] || shopt -s dotglob globstar=$(shopt globstar | /usr/bin/grep -F off) [[ $globstar ]] || shopt -u globstar cmdname=$(echo $READFIRST) cmdname=${cmdname%% *} kb_highlight "$cmdname" # add words that end in \ to the $last word while [[ -z "$before" ]] ; do if [[ "${recursive: -1}" == '\' ]]; then last="${recursive##* } $last" recursive=${recursive% *} else before=$recursive\ fi done # for structures like cat 'word1 word2 if (( quotes % 2 == 1 )) && ! [[ "$last" =~ \' ]]; then # $last will be part of a quote instead. # assume last quote is not escape. before="${READFIRST%\'*}" last="${READFIRST##*\'}" fi # for structures like cat 'word1 word2'/ lastquote="${READFIRST##* }" beforequote="${READFIRST% *}" if [[ "${lastquote//[^\']}" == \' && ${lastquote::1} != \' && "${beforequote}" =~ \ \' ]]; then last=${beforequote##* \'}\ $lastquote before=${beforequote% \'*}\ fi # for variable=filename if [[ "$last" =~ ^[a-zA-Z_][a-zA-Z0-9_]+= ]]; then before=$before${last%%=*}= last=${last#*=} fi # for sudo setsid ; etc if [[ "$before" =~ (setsid|sudo|which|whatis|whereis|\||\(|&|\{|;)\ *$ ]]; then kb_menu command "$last" ((nospace==0)) && prompt+=" " lang=$LC_ALL LC_ALL=C ((READLINE_POINT+=${#prompt} - ${#last})) LC_ALL=$lang READLINE_LINE=$before$prompt$READLAST IFS=$' \t\n' echo -ne "\e[${psheight}A\r\e[2K" return fi kb_replace_var last=${last//\'} kb_menu 'file' "$last" kb_write_prompt "$prompt" [[ $nocaseglob ]] || shopt -u nocaseglob [[ $nullglob ]] || shopt -u nullglob [[ $dotglob ]] || shopt -u dotglob [[ $globstar ]] || shopt -s globstar echo -ne "\r\e[2K\e[${psheight}A\r\e[2K" tput smam tput cnorm } bind -x '"\t":kb_main'