<commit_before># This tool gives you quick access to your frequent/recent files
#
# INSTALL:
#   Source this file somewhere in your shell rc (.bashrc or .zshrc).
#
# SYNOPSIS:
#   f [options] [query ..]
#     options:
#       -s, --show       show list of files with their ranks
#       -l, --list       list paths only
#       -e, --exec CMD   set command to execute on the result file
#       -a, --any        match files and directories
#       -d, --directory  match directories only
#       -f, --file       match files only
#       -r, --rank       match by rank only
#       -h, --help       show a brief help message
#
# EXAMPLES:
#   f foo # list recent files mathcing foo
#   f foo bar # list recent files mathcing foo and bar
#   f -e vim foo # run vim on the most frecent file matching foo
#
# TIPS:
#   alias z="f -d -e cd"
#   alias v="f -e vim"
#   alias m="f -e mplayer"
#   alias o="f -e xdg-open"

_f() {

  if [ "$1" = "--add" ]; then # add entries
    shift

    # bail out if we don't own ~/.f (we're another user but our ENV is still set)
    [ -f "$_F_DATA" -a ! -O "$_F_DATA" ] && return

    # blacklists
    local each
    for each in "${_F_BLACKLIST[@]}"; do
      [[ "$*" =~ "$each" ]] && return
    done

    # shifts
    for each in "${_F_SHIFT[@]}"; do
      while [ "$1" = "$each" ]; do shift; done
    done

    # ignores
    [[ "${_F_IGNORE[@]}" =~ "$1" ]] && return
    shift

    local FILES
    while [ "$1" ]; do
      # add the adsolute path of the file to FILES
      FILES+="$($_F_READLINK -e "$1" 2>/dev/null)|"
      shift
    done

    # add current pwd if the option set
    [ "$_F_TRACK_PWD" -a "$(pwd -P)" != "$HOME" ] && FILES+="$(pwd -P)"

    [ -z "${FILES//|/}" ] && return # stop if we have nothing to add

    # maintain the file
    local tempfile
    tempfile="$(mktemp $_F_DATA.XXXXXX)" || return
    $_F_AWK -v list="$FILES" -v now="$(date +%s)" -F"|" '
      BEGIN {
        split(list, files, "|")
        for(i in files) {
          path = files[i]
          if ( path == "" ) continue
          paths[path] = path # array for checking
          rank[path] = 1
          time[path] = now
        }
      }
      $2 >= 1 {
        if( $1 in paths ) {
          rank[$1] = $2 + 1
          time[$1] = now
        } else {
          rank[$1] = $2
          time[$1] = $3
        }
        count += $2
      }
      END {
        if( count > 1000 ) {
          for( i in rank ) print i "|" 0.9*rank[i] "|" time[i] # aging
        } else for( i in rank ) print i "|" rank[i] "|" time[i]
      }' "$_F_DATA" 2>/dev/null >| "$tempfile"
    if [ $? -ne 0 -a -f "$_F_DATA" ]; then
      env rm -f "$tempfile"
    else
      env mv -f "$tempfile" "$_F_DATA"
    fi

  elif [ "$1" = "--query" ]; then
    # query the database, this need some local variables to be set
    while read line; do
      [ -${typ} "${line%%|*}" ] && echo "$line"
    done < "$_F_DATA" | \
    $_F_AWK -v t="$(date +%s)" -v mode="$mode" -v q="$fnd" -F"|" '
      function frecent(rank, time) {
        dx = t-time
        if( dx < 3600 ) return rank*4
        if( dx < 86400 ) return rank*2
        if( dx < 604800 ) return rank/2
        return rank/4
      }
      function likelihood(pattern, path) {
        m = gsub( "/+", "/", path )
        r = 1
        for( i in pattern ) {
          tmp = path
          gsub( ".*" pattern[i], "", tmp)
          n = gsub( "/+", "/", tmp )
          if( n == m )
            return 0
          else if( n == 0 )
            r *= 20 # F
          else
            r *= 1 - ( n / m )
        }
        return r
      }
      function getRank() {
        if( mode == "rank" ) {
          f = $2
        } else f = frecent($2, $3)
        wcase[$1] = f * likelihood( pattern, $1 )
        nocase[$1] = f * likelihood( pattern2, tolower($1) )
      }
      BEGIN {
        split(q, pattern, " ")
        for( i in pattern ) pattern2[i] = tolower(pattern[i]) # nocase
      }
      {
        getRank()
        cx = cx || wcase[$1]
        ncx = ncx || nocase[$1]
      }
      END {
        if( cx ) {
          for( i in wcase )
            if( wcase[i] ) printf "%-10s %s\n", wcase[i], i
        } else if( ncx ) {
          for( i in nocase )
            if( nocase[i] ) printf "%-10s %s\n", nocase[i], i
        }
      }' - 2>/dev/null

  else
    # parsing logic and processing
    [ -f "$_F_DATA" ] || return # no db yet
    local fnd; fnd=()
    while [ "$1" ]; do case "$1" in
      --complete) set -- $(echo $2); shift; local list=1;;
      -h|--help) echo "f [options] [query ..]
      options:
        -s, --show       show list of files with their ranks
        -l, --list       list paths only
        -e, --exec CMD   set command to execute on the result file
        -a, --any        match files and directories
        -d, --directory  match directories only
        -f, --file       match files only
        -r, --rank       match by rank only
        -h, --help       show a brief help message" >&2; return;;
      -s|--show) local show=1; shift;;
      -l|--list) local list=1; shift;;
      -r|--rank) local mode="rank"; shift;;
      -t|--recent) local mode="recent"; shift;;
      -e|--exec) local exec=${2:?"Argument needed after -e"}; shift 2;;
      -a|--any) local typ="e"; shift;;
      -d|--directory) local typ="d"; shift;;
      -f|--file) local typ="f"; shift;;
      *) fnd+=("$1"); shift;;
    esac; done

    [ "$fnd" -a "$exec" ] || { [ -z "$list" ] && local show=1 ; }
    [ "$typ" ] || local typ="e" # default to match file and directory

    # if we hit enter on a completion just execute
    [ "$fnd" ] && case "${fnd[${#fnd[@]}-1]}" in
     # completions will always start with /
     # verbose array index for bash < 4.2 support
     /*) [ -z "$show$list" -a -${typ} "${fnd[${#fnd[@]}-1]}" ] && \
       $exec "${fnd[${#fnd[@]}-1]}" && return;;
    esac

    local result
    result="$(_f --query 2>/dev/null)" # query the database
    [ $? -gt 0 ] && return
    if [ -z "$show$list" ]; then # exec
      $exec "$(echo "$result" | sort -n | sed 's/^[0-9.]*[ ]*//' | tail -n1)"
    elif [ -z "$list" ]; then # show
      echo "$result" | sort -n
    else # list
      echo "$result" | sort -n | sed 's/^[0-9.]*[ ]*//'
    fi

  fi<commit_msg>Fixes bash support<commit_after># This tool gives you quick access to your frequent/recent files
#
# INSTALL:
#   Source this file somewhere in your shell rc (.bashrc or .zshrc).
#
# SYNOPSIS:
#   f [options] [query ..]
#     options:
#       -s, --show       show list of files with their ranks
#       -l, --list       list paths only
#       -e, --exec CMD   set command to execute on the result file
#       -a, --any        match files and directories
#       -d, --directory  match directories only
#       -f, --file       match files only
#       -r, --rank       match by rank only
#       -h, --help       show a brief help message
#
# EXAMPLES:
#   f foo # list recent files mathcing foo
#   f foo bar # list recent files mathcing foo and bar
#   f -e vim foo # run vim on the most frecent file matching foo
#
# TIPS:
#   alias z="f -d -e cd"
#   alias v="f -e vim"
#   alias m="f -e mplayer"
#   alias o="f -e xdg-open"

_f() {

  if [ "$1" = "--add" ]; then # add entries
    shift

    # bail out if we don't own ~/.f (we're another user but our ENV is still set)
    [ -f "$_F_DATA" -a ! -O "$_F_DATA" ] && return

    # blacklists
    local each
    for each in "${_F_BLACKLIST[@]}"; do
      [[ "$*" =~ "$each" ]] && return
    done

    # shifts
    for each in "${_F_SHIFT[@]}"; do
      while [ "$1" = "$each" ]; do shift; done
    done

    # ignores
    [[ "${_F_IGNORE[@]}" =~ "$1" ]] && return
    shift

    local FILES
    while [ "$1" ]; do
      # add the adsolute path of the file to FILES
      FILES+="$($_F_READLINK -e "$1" 2>/dev/null)|"
      shift
    done

    # add current pwd if the option set
    [ "$_F_TRACK_PWD" -a "$(pwd -P)" != "$HOME" ] && FILES+="$(pwd -P)"

    [ -z "${FILES//|/}" ] && return # stop if we have nothing to add

    # maintain the file
    local tempfile
    tempfile="$(mktemp $_F_DATA.XXXXXX)" || return
    $_F_AWK -v list="$FILES" -v now="$(date +%s)" -F"|" '
      BEGIN {
        split(list, files, "|")
        for(i in files) {
          path = files[i]
          if ( path == "" ) continue
          paths[path] = path # array for checking
          rank[path] = 1
          time[path] = now
        }
      }
      $2 >= 1 {
        if( $1 in paths ) {
          rank[$1] = $2 + 1
          time[$1] = now
        } else {
          rank[$1] = $2
          time[$1] = $3
        }
        count += $2
      }
      END {
        if( count > 1000 ) {
          for( i in rank ) print i "|" 0.9*rank[i] "|" time[i] # aging
        } else for( i in rank ) print i "|" rank[i] "|" time[i]
      }' "$_F_DATA" 2>/dev/null >| "$tempfile"
    if [ $? -ne 0 -a -f "$_F_DATA" ]; then
      env rm -f "$tempfile"
    else
      env mv -f "$tempfile" "$_F_DATA"
    fi

  elif [ "$1" = "--query" ]; then
    # query the database, this need some local variables to be set
    while read line; do
      [ -${typ} "${line%%|*}" ] && echo "$line"
    done < "$_F_DATA" | \
    $_F_AWK -v t="$(date +%s)" -v mode="$mode" -v q="$fnd" -F"|" '
      function frecent(rank, time) {
        dx = t-time
        if( dx < 3600 ) return rank*4
        if( dx < 86400 ) return rank*2
        if( dx < 604800 ) return rank/2
        return rank/4
      }
      function likelihood(pattern, path) {
        m = gsub( "/+", "/", path )
        r = 1
        for( i in pattern ) {
          tmp = path
          gsub( ".*" pattern[i], "", tmp)
          n = gsub( "/+", "/", tmp )
          if( n == m )
            return 0
          else if( n == 0 )
            r *= 20 # F
          else
            r *= 1 - ( n / m )
        }
        return r
      }
      function getRank() {
        if( mode == "rank" ) {
          f = $2
        } else f = frecent($2, $3)
        wcase[$1] = f * likelihood( pattern, $1 )
        nocase[$1] = f * likelihood( pattern2, tolower($1) )
      }
      BEGIN {
        split(q, pattern, " ")
        for( i in pattern ) pattern2[i] = tolower(pattern[i]) # nocase
      }
      {
        getRank()
        cx = cx || wcase[$1]
        ncx = ncx || nocase[$1]
      }
      END {
        if( cx ) {
          for( i in wcase )
            if( wcase[i] ) printf "%-10s %s\n", wcase[i], i
        } else if( ncx ) {
          for( i in nocase )
            if( nocase[i] ) printf "%-10s %s\n", nocase[i], i
        }
      }' - 2>/dev/null

  else
    # parsing logic and processing
    [ -f "$_F_DATA" ] || return # no db yet
    local fnd; fnd=()
    while [ "$1" ]; do case "$1" in
      --complete) set -- $(echo $2); local list=1;;
      -h|--help) echo "f [options] [query ..]
      options:
        -s, --show       show list of files with their ranks
        -l, --list       list paths only
        -e, --exec CMD   set command to execute on the result file
        -a, --any        match files and directories
        -d, --directory  match directories only
        -f, --file       match files only
        -r, --rank       match by rank only
        -h, --help       show a brief help message" >&2; return;;
      -s|--show) local show=1;;
      -l|--list) local list=1;;
      -r|--rank) local mode="rank";;
      -t|--recent) local mode="recent";;
      -e|--exec) local exec=${2:?"Argument needed after -e"}; shift;;
      -a|--any) local typ="e";;
      -d|--directory) local typ="d";;
      -f|--file) local typ="f";;
      *) fnd+="$1 ";;
    esac; local last="$1"; shift; done

    [ "$fnd" -a "$exec" ] || { [ -z "$list" ] && local show=1 ; }
    [ "$typ" ] || local typ="e" # default to match file and directory

    # if we hit enter on a completion just execute
    case "$last" in
     # completions will always start with /
     /*) [ -z "$show$list" -a -${typ} "$last" ] && $exec "$last" && return;;
    esac

    local result
    result="$(_f --query 2>/dev/null)" # query the database
    [ $? -gt 0 ] && return
    if [ -z "$show$list" ]; then # exec
      $exec "$(echo "$result" | sort -n | sed 's/^[0-9.]*[ ]*//' | tail -n1)"
    elif [ -z "$list" ]; then # show
      echo "$result" | sort -n
    else # list
      echo "$result" | sort -n | sed 's/^[0-9.]*[ ]*//'
    fi

  fi
{'fraction_non_alphanumeric': 0.17443462754316286, 'fraction_numerical': 0.014509199967577206, 'mean_word_length': 1.645368782161235, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '383448', 'n_tokens_mistral': 4444, 'n_tokens_neox': 4262, 'n_words': 1416}