diff --git a/fff b/fff index 16766e3..ad6fb73 100755 --- a/fff +++ b/fff @@ -90,6 +90,8 @@ setup_options() { # Find supported 'file' arguments. file -I &>/dev/null || : "${file_flags:=biL}" + + helping=0 } get_term_size() { @@ -157,10 +159,16 @@ get_mime_type() { status_line() { # Status_line to print when files are marked for operation. local mark_ui="[${#marked_files[@]}] selected (${file_program[*]}) [p] ->" + mark_ui="${marked_files[*]:+"$mark_ui"}" - # Escape the directory string. - # Remove all non-printable characters. - PWD_escaped=${PWD//[^[:print:]]/^[} + if ((helping)); then + mark_ui= + PWD_escaped=help + else + # Escape the directory string. + # Remove all non-printable characters. + PWD_escaped=${PWD//[^[:print:]]/^[} + fi # '\e7': Save cursor position. # This is more widely supported than '\e[s'. @@ -180,7 +188,7 @@ status_line() { "${FFF_COL2:-1}" \ "$COLUMNS" "" \ "($((scroll+1))/$((list_total+1)))" \ - "${marked_files[*]:+${mark_ui}}" \ + "$mark_ui" \ "${1:-${PWD_escaped:-/}}" \ "$LINES" } @@ -230,8 +238,20 @@ print_line() { local format local suffix + # Help line + if ((helping)); then + file_name=${list[$1]} + if [[ "$file_name" ]]; then + # Highlight the key(s), escaping any specials in overrides to a human-readable form + format+=\\e[${di:-1;3${FFF_COL1:-2}}m + local action="${file_name%: *}" + format+="$(cat -A <<<"$action" | head -c -2)\\e[${fi:-37}m: " + file_name="${file_name##*: }" + fi + format+=\\e[${fi:-37}m + # If the dir item doesn't exist, end here. - if [[ -z ${list[$1]} ]]; then + elif [[ -z ${list[$1]} ]]; then return # Directory. @@ -294,7 +314,7 @@ print_line() { format+="\\e[1;3${FFF_COL4:-6};7m" # If the list item is marked for operation. - [[ ${marked_files[$1]} == "${list[$1]:-null}" ]] && { + !((helping)) && [[ ${marked_files[$1]} == "${list[$1]:-null}" ]] && { format+=\\e[3${FFF_COL3:-1}m${mark_pre} suffix+=${mark_post} } @@ -450,15 +470,95 @@ redraw() { # Redraw the current window. # If 'full' is passed, re-fetch the directory list. [[ $1 == full ]] && { + ((helping)) && { + helping=0 + find_previous=1 + } read_dir scroll=0 } + # If 'help' is passed, list help text. + [[ $1 == help ]] && { + helping=1 + list_help + scroll=0 + } + clear_screen draw_dir status_line } +list_help() { + # Set window name. + printf '\e]2;fff: help\e'\\ + + list=( + "${FFF_KEY_SCROLL_DOWN1:-j}: scroll down" + "${FFF_KEY_SCROLL_UP1:-k}: scroll up" + "${FFF_KEY_PARENT1:-h}: go to parent dir" + "${FFF_KEY_CHILD1:-l}: go to child dir" + '' + 'enter: go to child dir' + 'backspace: go to parent dir' + '' + "${FFF_KEY_PREVIOUS:--}: go to previous dir" + '' + "${FFF_KEY_TO_TOP:-g}: go to top" + "${FFF_KEY_TO_BOTTOM:-G}: go to bottom" + '' + "${FFF_KEY_GO_DIR:-:}: go to a directory by typing" + '' + "${FFF_KEY_HIDDEN:-.}: toggle hidden files" + "${FFF_KEY_SEARCH:-/}: search" + "${FFF_KEY_GO_TRASH:-t}: go to trash" + "${FFF_KEY_GO_HOME:-~}: go to home" + "${FFF_KEY_REFRESH:-e}: refresh current dir" + "${FFF_KEY_SHELL:-!}: open shell in current dir" + '' + "${FFF_KEY_ATTRIBUTES:-x}: view file/dir attributes" + "${FFF_KEY_IMAGE:-i}: display image with w3m-img" + '' + 'down: scroll down' + 'up: scroll up' + 'left: go to parent dir' + 'right: go to child dir' + '' + "${FFF_KEY_MKFILE:-f}: new file" + "${FFF_KEY_MKDIR:-n}: new dir" + "${FFF_KEY_RENAME:-r}: rename" + "${FFF_KEY_EXECUTABLE:-X}: toggle executable" + '' + "${FFF_KEY_YANK:-y}: mark copy" + "${FFF_KEY_MOVE:-m}: mark move" + "${FFF_KEY_TRASH:-d}: mark trash (~/.local/share/fff/trash/)" + "${FFF_KEY_LINK:-s}: mark symbolic link" + "${FFF_KEY_BULK_RENAME:-b}: mark bulk rename" + '' + "${FFF_KEY_YANK_ALL:-Y}: mark all for copy" + "${FFF_KEY_MOVE_ALL:-M}: mark all for move" + "${FFF_KEY_TRASH_ALL:-D}: mark all for trash (~/.local/share/fff/trash/)" + "${FFF_KEY_LINK_ALL:-S}: mark all for symbolic link" + "${FFF_KEY_BULK_RENAME_ALL:-B}: mark all for bulk rename" + '' + "${FFF_KEY_PASTE:-p}: paste/move/delete/bulk_rename" + "${FFF_KEY_CLEAR:-c}: clear file selections" + '' + '[1-9]: favourites/bookmarks (see customization in man page)' + '' + "q: exit with 'cd' (if enabled) or exit this help" + "Ctrl+C: exit without 'cd'" + '' + "${FFF_KEY_HELP:-?}: show this help" + ) + + ((list_total=${#list[@]}-1)) + + # Save the original list in a second list as a backup. + cur_list=("${list[@]}") +} + mark() { # Mark file for operation. # If an item is marked in a second directory, @@ -648,6 +748,8 @@ cmd_line() { # Tab. $'\t') + ((helping)) && return + comp_glob="$cmd_reply*" # Pass the argument dirs to limit completion to directories. @@ -676,7 +778,7 @@ cmd_line() { "") # If there's only one search result and its a directory, # enter it on one enter keypress. - [[ $2 == search && -d ${list[0]} ]] && ((list_total == 0)) && { + !((helping)) && [[ $2 == search && -d ${list[0]} ]] && ((list_total == 0)) && { # '\e[?25l': Hide the cursor. printf '\e[?25l' @@ -717,7 +819,15 @@ cmd_line() { printf '\e[?25l' # Use a greedy glob to search. - list=("$PWD"/*"$cmd_reply"*) + if ((helping)); then + local item + list=() + for item in "${cur_list[@]}"; do + [[ "$item" == *"$cmd_reply"* ]] && list+=("$item") + done + else + list=("$PWD"/*"$cmd_reply"*) + fi ((list_total=${#list[@]}-1)) # Draw the search results on screen. @@ -752,6 +862,10 @@ key() { } case ${special_key:-$1} in + "${FFF_KEY_HELP:=?}") + redraw help + ;; + # Open list item. # 'C' is what bash sees when the right arrow is pressed # ('\e[C' or '\eOC'). @@ -760,6 +874,8 @@ key() { ${FFF_KEY_CHILD2:=$'\e[C'}|\ ${FFF_KEY_CHILD3:=""}|\ ${FFF_KEY_CHILD4:=$'\eOC'}) + ((helping)) && return + open "${list[scroll]}" ;; @@ -773,6 +889,8 @@ key() { ${FFF_KEY_PARENT3:=$'\177'}|\ ${FFF_KEY_PARENT4:=$'\b'}|\ ${FFF_KEY_PARENT5:=$'\eOD'}) + ((helping)) && return + # If a search was done, clear the results and open the current dir. if ((search == 1 && search_end_early != 1)); then open "$PWD" @@ -790,7 +908,7 @@ key() { ${FFF_KEY_SCROLL_DOWN1:=j}|\ ${FFF_KEY_SCROLL_DOWN2:=$'\e[B'}|\ ${FFF_KEY_SCROLL_DOWN3:=$'\eOB'}) - ((scroll < list_total)) && { + while ((scroll < list_total)); do ((scroll++)) ((y < max_items)) && ((y++)) @@ -798,7 +916,9 @@ key() { printf '\n' print_line "$scroll" status_line - } + + [[ "${list[scroll]}" ]] && break + done ;; # Scroll up. @@ -809,7 +929,7 @@ key() { ${FFF_KEY_SCROLL_UP3:=$'\eOA'}) # '\e[1L': Insert a line above the cursor. # '\e[A': Move cursor up a line. - ((scroll > 0)) && { + while ((scroll > 0)); do ((scroll--)) print_line "$((scroll+1))" @@ -823,7 +943,9 @@ key() { print_line "$scroll" status_line - } + + [[ "${list[scroll]}" ]] && break + done ;; # Go to top. @@ -844,6 +966,8 @@ key() { # Show hidden files. ${FFF_KEY_HIDDEN:=.}) + ((helping)) && return + # 'a=a>0?0:++a': Toggle between both values of 'shopt_flags'. # This also works for '3' or more values with # some modification. @@ -869,6 +993,8 @@ key() { # Spawn a shell. ${FFF_KEY_SHELL:=!}) + ((helping)) && return + reset_terminal # Make fff aware of how many times it is nested. @@ -886,6 +1012,8 @@ key() { ${FFF_KEY_TRASH:=d}|\ ${FFF_KEY_LINK:=s}|\ ${FFF_KEY_BULK_RENAME:=b}) + ((helping)) && return + mark "$scroll" "$1" ;; @@ -895,11 +1023,15 @@ key() { ${FFF_KEY_TRASH_ALL:=D}|\ ${FFF_KEY_LINK_ALL:=S}|\ ${FFF_KEY_BULK_RENAME_ALL:=B}) + ((helping)) && return + mark all "$1" ;; # Do the file operation. ${FFF_KEY_PASTE:=p}) + ((helping)) && return + [[ ${marked_files[*]} ]] && { [[ ! -w $PWD ]] && { cmd_line "warn: no write access to dir." @@ -923,6 +1055,8 @@ key() { # Clear all marked files. ${FFF_KEY_CLEAR:=c}) + ((helping)) && return + [[ ${marked_files[*]} ]] && { marked_files=() redraw @@ -931,6 +1065,8 @@ key() { # Rename list item. ${FFF_KEY_RENAME:=r}) + ((helping)) && return + [[ ! -e ${list[scroll]} ]] && return @@ -951,6 +1087,8 @@ key() { # Create a directory. ${FFF_KEY_MKDIR:=n}) + ((helping)) && return + cmd_line "mkdir: " "dirs" [[ $cmd_reply ]] && @@ -968,6 +1106,8 @@ key() { # Create a file. ${FFF_KEY_MKFILE:=f}) + ((helping)) && return + cmd_line "mkfile: " [[ $cmd_reply ]] && @@ -985,6 +1125,8 @@ key() { # Show file attributes. ${FFF_KEY_ATTRIBUTES:=x}) + ((helping)) && return + [[ -e "${list[scroll]}" ]] && { clear_screen status_line "${list[scroll]}" @@ -996,6 +1138,8 @@ key() { # Toggle executable flag. ${FFF_KEY_EXECUTABLE:=X}) + ((helping)) && return + [[ -f ${list[scroll]} && -w ${list[scroll]} ]] && { if [[ -x ${list[scroll]} ]]; then chmod -x "${list[scroll]}" @@ -1009,11 +1153,15 @@ key() { # Show image in terminal. ${FFF_KEY_IMAGE:=i}) + ((helping)) && return + draw_img ;; # Go to dir. ${FFF_KEY_GO_DIR:=:}) + ((helping)) && return + cmd_line "go to dir: " "dirs" # Let 'cd' know about the current directory. @@ -1026,27 +1174,41 @@ key() { # Go to '$HOME'. ${FFF_KEY_GO_HOME:='~'}) + ((helping)) && return + open ~ ;; # Go to trash. ${FFF_KEY_GO_TRASH:=t}) + ((helping)) && return + get_os open "$FFF_TRASH" ;; # Go to previous dir. ${FFF_KEY_PREVIOUS:=-}) + ((helping)) && return + open "$OLDPWD" ;; # Refresh current dir. ${FFF_KEY_REFRESH:=e}) + ((helping)) && { + list=("${cur_list[@]}") + redraw + return + } + open "$PWD" ;; # Directory favourites. [1-9]) + ((helping)) && return + favourite="FFF_FAV${1}" favourite="${!favourite}" @@ -1058,6 +1220,11 @@ key() { # Don't allow user to redefine 'q' so a bad keybinding doesn't # remove the option to quit. q) + ((helping)) && { + redraw full + return + } + : "${FFF_CD_FILE:=${XDG_CACHE_HOME:=${HOME}/.cache}/fff/.fff_d}" [[ -w $FFF_CD_FILE ]] && diff --git a/fff.1 b/fff.1 index 7880c30..c2a5e5f 100644 --- a/fff.1 +++ b/fff.1 @@ -19,12 +19,12 @@ l: go to child dir enter: go to child dir backspace: go to parent dir -\-: Go to previous dir\. +\-: go to previous dir g: go to top G: go to bottom -:: go to a directory by typing. +:: go to a directory by typing \.: toggle hidden files /: search @@ -63,8 +63,10 @@ c: clear file selections [1-9]: favourites/bookmarks (see customization) -q: exit with 'cd' (if enabled). -Ctrl+C: exit without 'cd'. +q: exit with 'cd' (if enabled) or exit help +Ctrl+C: exit without 'cd' + +?: show help . .fi .