diff --git a/plugins/z/z.1 b/plugins/z/z.1 index bbc1bf5df..d4cac1ac2 100644 --- a/plugins/z/z.1 +++ b/plugins/z/z.1 @@ -22,6 +22,9 @@ OPTIONS \fB\-c\fR restrict matches to subdirectories of the current directory .TP +\fB\-e\fR +echo the best match, don't cd +.TP \fB\-h\fR show a brief help message .TP @@ -90,7 +93,8 @@ Set \fB$_Z_OWNER\fR to allow usage when in 'sudo -s' mode. (These settings should go in .bashrc/.zshrc before the line added above.) .RE .RS -Install the provided man page \fBz.1\fR somewhere like \fB/usr/local/man/man1\fR. +Install the provided man page \fBz.1\fR somewhere in your \f$MANPATH, like +\fB/usr/local/man/man1\fR. .RE .SS Aging: diff --git a/plugins/z/z.sh b/plugins/z/z.sh index d0eeb97ef..4fc75dc6a 100644 --- a/plugins/z/z.sh +++ b/plugins/z/z.sh @@ -1,4 +1,4 @@ -# Copyright (c) 2009 rupa deadwyler under the WTFPL license +# Copyright (c) 2009 rupa deadwyler. Licensed under the WTFPL license, Version 2 # maintains a jump-list of the directories you actually use # @@ -21,6 +21,7 @@ # * z -r foo # cd to highest ranked dir matching foo # * z -t foo # cd to most recently accessed dir matching foo # * z -l foo # list matches instead of cd +# * z -e foo # echo the best match, don't cd # * z -c foo # restrict matches to subdirs of $PWD [ -d "${_Z_DATA:-$HOME/.z}" ] && { @@ -31,9 +32,21 @@ _z() { local datafile="${_Z_DATA:-$HOME/.z}" + # if symlink, dereference + [ -h "$datafile" ] && datafile=$(readlink "$datafile") + # bail if we don't own ~/.z and $_Z_OWNER not set [ -z "$_Z_OWNER" -a -f "$datafile" -a ! -O "$datafile" ] && return + _z_dirs () { + local line + while read line; do + # only count directories + [ -d "${line%%\|*}" ] && echo "$line" + done < "$datafile" + return 0 + } + # add entries if [ "$1" = "--add" ]; then shift @@ -49,10 +62,7 @@ _z() { # maintain the data file local tempfile="$datafile.$RANDOM" - while read line; do - # only count directories - [ -d "${line%%\|*}" ] && echo $line - done < "$datafile" | awk -v path="$*" -v now="$(date +%s)" -F"|" ' + _z_dirs | awk -v path="$*" -v now="$(date +%s)" -F"|" ' BEGIN { rank[path] = 1 time[path] = now @@ -75,7 +85,7 @@ _z() { } else for( x in rank ) print x "|" rank[x] "|" time[x] } ' 2>/dev/null >| "$tempfile" - # do our best to avoid clobbering the datafile in a race condition + # do our best to avoid clobbering the datafile in a race condition. if [ $? -ne 0 -a -f "$datafile" ]; then env rm -f "$tempfile" else @@ -85,17 +95,15 @@ _z() { # tab completion elif [ "$1" = "--complete" -a -s "$datafile" ]; then - while read line; do - [ -d "${line%%\|*}" ] && echo $line - done < "$datafile" | awk -v q="$2" -F"|" ' + _z_dirs | awk -v q="$2" -F"|" ' BEGIN { - if( q == tolower(q) ) imatch = 1 q = substr(q, 3) - gsub(" ", ".*", q) + if( q == tolower(q) ) imatch = 1 + gsub(/ /, ".*", q) } { if( imatch ) { - if( tolower($1) ~ tolower(q) ) print $1 + if( tolower($1) ~ q ) print $1 } else if( $1 ~ q ) print $1 } ' 2>/dev/null @@ -106,11 +114,12 @@ _z() { --) while [ "$1" ]; do shift; local fnd="$fnd${fnd:+ }$1";done;; -*) local opt=${1:1}; while [ "$opt" ]; do case ${opt:0:1} in c) local fnd="^$PWD $fnd";; - h) echo "${_Z_CMD:-z} [-chlrtx] args" >&2; return;; - x) sed -i -e "\:^${PWD}|.*:d" "$datafile";; + e) local echo=1;; + h) echo "${_Z_CMD:-z} [-cehlrtx] args" >&2; return;; l) local list=1;; r) local typ="rank";; t) local typ="recent";; + x) sed -i -e "\:^${PWD}|.*:d" "$datafile";; esac; opt=${opt:1}; done;; *) local fnd="$fnd${fnd:+ }$1";; esac; local last=$1; [ "$#" -gt 0 ] && shift; done @@ -119,16 +128,14 @@ _z() { # if we hit enter on a completion just go there case "$last" in # completions will always start with / - /*) [ -z "$list" -a -d "$last" ] && cd "$last" && return;; + /*) [ -z "$list" -a -d "$last" ] && builtin cd "$last" && return;; esac # no file yet [ -f "$datafile" ] || return local cd - cd="$(while read line; do - [ -d "${line%%\|*}" ] && echo $line - done < "$datafile" | awk -v t="$(date +%s)" -v list="$list" -v typ="$typ" -v q="$fnd" -F"|" ' + cd="$( < <( _z_dirs ) awk -v t="$(date +%s)" -v list="$list" -v typ="$typ" -v q="$fnd" -F"|" ' function frecent(rank, time) { # relate frequency and time dx = t - time @@ -137,19 +144,21 @@ _z() { if( dx < 604800 ) return rank / 2 return rank / 4 } - function output(files, out, common) { + function output(matches, best_match, common) { # list or return the desired directory if( list ) { cmd = "sort -n >&2" - for( x in files ) { - if( files[x] ) printf "%-10s %s\n", files[x], x | cmd + for( x in matches ) { + if( matches[x] ) { + printf "%-10s %s\n", matches[x], x | cmd + } } if( common ) { printf "%-10s %s\n", "common:", common > "/dev/stderr" } } else { - if( common ) out = common - print out + if( common ) best_match = common + print best_match } } function common(matches) { @@ -160,11 +169,9 @@ _z() { } } if( short == "/" ) return - # use a copy to escape special characters, as we want to return - # the original. yeah, this escaping is awful. - clean_short = short - gsub(/\[\(\)\[\]\|\]/, "\\\\&", clean_short) - for( x in matches ) if( matches[x] && x !~ clean_short ) return + for( x in matches ) if( matches[x] && index(x, short) != 1 ) { + return + } return short } BEGIN { @@ -197,8 +204,10 @@ _z() { } } ')" - [ $? -gt 0 ] && return - [ "$cd" ] && cd "$cd" + + [ $? -eq 0 ] && [ "$cd" ] && { + if [ "$echo" ]; then echo "$cd"; else builtin cd "$cd"; fi + } fi } @@ -212,11 +221,11 @@ if type compctl >/dev/null 2>&1; then # populate directory list, avoid clobbering any other precmds. if [ "$_Z_NO_RESOLVE_SYMLINKS" ]; then _z_precmd() { - _z --add "${PWD:a}" + (_z --add "${PWD:a}" &) } else _z_precmd() { - _z --add "${PWD:A}" + (_z --add "${PWD:A}" &) } fi [[ -n "${precmd_functions[(r)_z_precmd]}" ]] || { @@ -237,7 +246,7 @@ elif type complete >/dev/null 2>&1; then [ "$_Z_NO_PROMPT_COMMAND" ] || { # populate directory list. avoid clobbering other PROMPT_COMMANDs. grep "_z --add" <<< "$PROMPT_COMMAND" >/dev/null || { - PROMPT_COMMAND="$PROMPT_COMMAND"$'\n''_z --add "$(command pwd '$_Z_RESOLVE_SYMLINKS' 2>/dev/null)" 2>/dev/null;' + PROMPT_COMMAND="$PROMPT_COMMAND"$'\n''(_z --add "$(command pwd '$_Z_RESOLVE_SYMLINKS' 2>/dev/null)" 2>/dev/null &);' } } fi