summaryrefslogtreecommitdiffstats
path: root/bash/j.sh
blob: ed8e0c17e9618a0a04dfcf44f801a34747f21d6e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/bin/bash

# pure shell version of 'new j'
# has frecency, rank, and recent
# has common and case preference
# lists to stderr
# protect multiple PROMPT_COMMAND appends

j() {
 local datafile=$HOME/.j
 if [ "$1" = "--add" ]; then
  # add
  shift
  # $HOME isn't worth matching
  [ "$*" = "$HOME" ] && return
  awk -v q="$*" -v t="$(date +%s)" -F"|" '
   BEGIN { l[q] = 1; d[q] = t }
   $2 >= 1 {
    if( $1 == q ) {
     l[$1] = $2 + 1
     d[$1] = t
    } else {
     l[$1] = $2
     d[$1] = $3
    }
    count += $2
   }
   END {
    if( count > 1000 ) {
     for( i in l ) print i "|" 0.9*l[i] "|" d[i] # aging
    } else for( i in l ) print i "|" l[i] "|" d[i]
   }
  ' $datafile 2>/dev/null > $datafile.tmp
  mv -f $datafile.tmp $datafile
 elif [ "$1" = "--complete" ]; then
  # tab completion
  awk -v q="$2" -F"|" '
   BEGIN { split(substr(q,3),a," ") }
   {
    if( system("test -d \"" $1 "\"") ) next
    for( i in a ) $1 !~ a[i] && $1 = ""; if( $1 ) print $1
   }
  ' $datafile 2>/dev/null
 else
  # list/go
  while [ "$1" ]; do case $1 in
   -h) echo "j [-h][-l][-r][-t] args" >/dev/stderr; return;;
   -l) local list=1;;
   -r) local typ="rank";;
   -t) local typ="recent";;
   --) while [ "$1" ]; do shift; local q="$q $1";done;;
    *) local q="$q $1";;
  esac; local n=$1; shift; done
  [ "$q" ] || local list=1
  # if we hit enter on a completion just go there
  [ -d "$n" ] && {
   cd "$n"
   return
  }
  cd="$(awk -v t="$(date +%s)" -v list="$list" -v typ="$typ" -v q="$q" -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 output(r, s, c) {
    if( list ) {
     if( typ == "recent" ) {
      cmd = "sort -nr >/dev/stderr"
     } else cmd = "sort -n >/dev/stderr"
     for( i in r ) if( r[i] ) printf "%-15s %s\n", r[i], i | cmd
     if( c ) printf "%-15s %s\n", "common:", c > "/dev/stderr"
    } else {
     if( c ) s = c
     print s
    }
   }
   function common(r, a, nc) {
    for( i in r ) {
     if( ! r[i] ) continue
     if( !shortest || length(i) < length(shortest) ) shortest = i
    }
    if( shortest == "/" ) return
    for( i in r ) {
     if( ! r[i] ) continue
     if( i !~ shortest ) x = 1
    }
    if( x ) return
    if( nc ) {
     for( i in a ) if( tolower(shortest) !~ tolower(a[i]) ) x = 1
    } else for( i in a ) if( shortest !~ a[i] ) x = 1
    if( !x ) return shortest
   }
   BEGIN { split(q, a, " ") }
   {
    if( system("test -d \"" $1 "\"") ) next
    if( typ == "rank" ) {
     f = $2
    } else if( typ == "recent" ) {
     f = t-$3
    } else f = frecent($2, $3)
    case[$1] = nocase[$1] = f
    for( i in a ) if( $1 !~ a[i] ) delete case[$1]
    for( i in a ) if( tolower($1) !~ tolower(a[i]) ) delete nocase[$1]
    if( case[$1] > oldf ) {
     cx = $1
     oldf = case[$1]
    } else if( nocase[$1] > noldf ) {
     ncx = $1
     noldf = nocase[$1]
    }
    print $0 >> FILENAME ".tmp"
   }
   END {
    if( cx ) {
     output(case, cx, common(case, a, 0))
    } else if( ncx ) {
     output(nocase, ncx, common(nocase, a, 1))
    }
   }
  ' $datafile)"
  mv -f $datafile.tmp $datafile
  [ "$cd" ] && cd "$cd"
 fi
}
# tab completion
complete -C 'j --complete "$COMP_LINE"' j
# populate directory list. avoid clobbering other PROMPT_COMMANDs.
echo $PROMPT_COMMAND | grep -q "j --add" || {
 PROMPT_COMMAND='j --add "$(pwd -P)";'"$PROMPT_COMMAND"
}