sfm

Simple File Manager
git clone git://git.emmett1.my/sfm.git
Log | Files | Refs | README | LICENSE

commit 2a1f8fc0a57d31a5fe38015c88e64ffb690529cf
parent 346ef7b7cbe95e043c9ee79f2fd408adc7b3d082
Author: emmett1 <me@emmett1.my>
Date:   Sun,  5 Apr 2026 20:00:02 +0800

add find function

Diffstat:
Msfm | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 120 insertions(+), 6 deletions(-)

diff --git a/sfm b/sfm @@ -894,7 +894,7 @@ do_help() { bw=$((COLS - 4)) [ "$bw" -gt 52 ] && bw=52 [ "$bw" -lt 36 ] && bw=36 - bh=45 + bh=46 _help_bx=$(( (COLS - bw) / 2 )); [ "$_help_bx" -lt 1 ] && _help_bx=1 _help_by=$(( (ROWS - bh) / 2 )); [ "$_help_by" -lt 1 ] && _help_by=1 _help_iw=$((bw - 2)) @@ -941,11 +941,12 @@ do_help() { help_row 36 " ~ go to home directory" help_row 37 " \` jump to previous directory" help_row 38 " : jump to path" - help_sep 39 - help_row 40 " q quit" - help_row 41 " ? this help" - help_row 42 "" - help_row 43 " press any key to close..." + help_row 39 " f find files recursively" + help_sep 40 + help_row 41 " q quit" + help_row 42 " ? this help" + help_row 43 "" + help_row 44 " press any key to close..." goto "$(( _help_by + bh ))" "$_help_bx" printf '%s+%s+%s' "${BOLD}${CYAN}" "$_help_hl" "${RESET}" @@ -1026,6 +1027,118 @@ do_toggle_preview() { NEED_FULL_REDRAW=1 } +do_find() { + goto "$(term_rows)" 1 + printf '%s%s find (recursive): %s' "${ERASE_LINE}" "${CYAN}${BOLD}" "${RESET}" + if ! read_line || [ -z "$READ_LINE" ]; then + NEED_FULL_REDRAW=1; draw; return + fi + _query="$READ_LINE" + + # run find and collect results into a tmp file + _tmp=/tmp/_sfm_find + find "$CWD" -name "*${_query}*" 2>/dev/null | sort > "$_tmp" + _total=$(wc -l < "$_tmp" | tr -d '[:space:]') + + if [ "$_total" -eq 0 ]; then + INFO_MSG="no results for: ${_query}" + NEED_FULL_REDRAW=1; return + fi + + # show results in an interactive picker + ROWS=$(term_rows); COLS=$(term_cols) + _pw=$(( COLS - 4 )); [ "$_pw" -gt 80 ] && _pw=80; [ "$_pw" -lt 30 ] && _pw=30 + _ph=$(( ROWS - 4 )); [ "$_ph" -lt 5 ] && _ph=5 + _px=$(( (COLS - _pw) / 2 )); [ "$_px" -lt 1 ] && _px=1 + _py=$(( (ROWS - _ph) / 2 )); [ "$_py" -lt 1 ] && _py=1 + _iw=$((_pw - 2)) + _hl=$(printf '%*s' "$_iw" '' | tr ' ' '-') + _vis=$((_ph - 3)) # visible result rows + + _fsel=1 + _foff=1 # scroll offset (1-based) + + while true; do + # draw box + goto "$_py" "$_px" + printf '%s+%s+%s' "${BOLD}${CYAN}" "$_hl" "${RESET}" + + # title + goto "$((_py+1))" "$_px" + _title=" find: ${_query} (${_total} results)" + [ "${#_title}" -gt "$_iw" ] && _title=$(printf '%s' "$_title" | cut -c1-"$_iw") + _tpl=$((_iw - ${#_title})); _tp=$(printf '%*s' "$_tpl" '') + printf '%s|%s%s%s%s|%s' "${BOLD}${CYAN}" "${RESET}" "$_title" "$_tp" "${BOLD}${CYAN}" "${RESET}" + + goto "$((_py+2))" "$_px" + printf '%s|%s|%s' "${BOLD}${CYAN}" "$_hl" "${RESET}" + + # results + _ri=0 + while [ "$_ri" -lt "$_vis" ]; do + _ridx=$((_foff + _ri)) + goto "$((_py+3+_ri))" "$_px" + if [ "$_ridx" -le "$_total" ]; then + _rline=$(awk -v n="$_ridx" 'NR==n{print;exit}' "$_tmp") + # strip CWD prefix for display + _rdisplay="${_rline#$CWD/}" + [ "${#_rdisplay}" -gt "$_iw" ] && \ + _rdisplay="...$(printf '%s' "$_rdisplay" | rev | cut -c1-$((_iw-4)) | rev)" + _rpl=$((_iw - ${#_rdisplay})); _rp=$(printf '%*s' "$_rpl" '') + if [ "$_ridx" -eq "$_fsel" ]; then + printf '%s|%s%s%s%s|%s' "${BOLD}${CYAN}" "${REV}${YELLOW}" "$_rdisplay" "$_rp" "${RESET}${BOLD}${CYAN}" "${RESET}" + else + printf '%s|%s%s%s%s|%s' "${BOLD}${CYAN}" "${RESET}" "$_rdisplay" "$_rp" "${BOLD}${CYAN}" "${RESET}" + fi + else + _ep=$(printf '%*s' "$_iw" '') + printf '%s|%s|%s' "${BOLD}${CYAN}" "$_ep" "${RESET}" + fi + _ri=$((_ri+1)) + done + + goto "$((_py+_ph))" "$_px" + printf '%s+%s+%s' "${BOLD}${CYAN}" "$_hl" "${RESET}" + + # read key + IFS= read -r -n1 _fk 2>/dev/null || IFS= read -r _fk + case "$_fk" in + j) _fsel=$((_fsel+1)); [ "$_fsel" -gt "$_total" ] && _fsel=$_total + [ "$_fsel" -ge $((_foff+_vis)) ] && _foff=$((_foff+1)) ;; + k) _fsel=$((_fsel-1)); [ "$_fsel" -lt 1 ] && _fsel=1 + [ "$_fsel" -lt "$_foff" ] && _foff=$((_foff-1)); [ "$_foff" -lt 1 ] && _foff=1 ;; + "$(printf '\033')") + IFS= read -r -n2 -t 0.1 _fseq 2>/dev/null || _fseq="" + case "$_fseq" in + '[A') _fsel=$((_fsel-1)); [ "$_fsel" -lt 1 ] && _fsel=1 + [ "$_fsel" -lt "$_foff" ] && _foff=$((_foff-1)); [ "$_foff" -lt 1 ] && _foff=1 ;; + '[B') _fsel=$((_fsel+1)); [ "$_fsel" -gt "$_total" ] && _fsel=$_total + [ "$_fsel" -ge $((_foff+_vis)) ] && _foff=$((_foff+1)) ;; + *) NEED_FULL_REDRAW=1; return ;; # esc — close + esac ;; + "$(printf '\n')"|\ + "$(printf '\r')"|\ + l) + _chosen=$(awk -v n="$_fsel" 'NR==n{print;exit}' "$_tmp") + if [ -d "$_chosen" ]; then + PREV_CWD="$CWD"; CWD="$_chosen"; norm_cwd + elif [ -e "$_chosen" ]; then + PREV_CWD="$CWD"; CWD=$(dirname "$_chosen"); norm_cwd + fi + FILTER=""; SELECTED="" + SEL=0; OFFSET=0; PREV_SEL=0; PREV_OFFSET=0 + load_entries + # try to highlight the found file + _fname="${_chosen##*/}" + [ -d "$_chosen" ] && _fname="${_fname}/" + _fidx=$(find_entry "$_fname") + [ "$_fidx" -ge 0 ] 2>/dev/null && SEL=$_fidx + return ;; + q|Q|h) NEED_FULL_REDRAW=1; return ;; + esac + done +} + do_jump_path() { goto "$(term_rows)" 1 printf '%s%s jump to: %s' "${ERASE_LINE}" "${CYAN}${BOLD}" "${RESET}" @@ -1582,6 +1695,7 @@ while true; do s) do_sort ;; u) do_trash ;; U) do_open_trash ;; + f) do_find ;; ':') do_jump_path ;; '~') PREV_CWD="$CWD"; CWD=$(cd "$HOME" && pwd); FILTER=""; SELECTED="" SEL=0; OFFSET=0; PREV_SEL=0; PREV_OFFSET=0; load_entries ;;