|
|
|
@ -1,10 +1,11 @@ |
|
|
|
#!/bin/zsh -f |
|
|
|
#!/bin/zsh -f |
|
|
|
|
|
|
|
|
|
|
|
emulate -L zsh |
|
|
|
emulate -L zsh |
|
|
|
|
|
|
|
local EXIT=return |
|
|
|
if [[ $(whence -w $0) == *:' 'command ]]; then |
|
|
|
if [[ $(whence -w $0) == *:' 'command ]]; then |
|
|
|
emulate -R zsh |
|
|
|
emulate -R zsh |
|
|
|
alias return=exit |
|
|
|
|
|
|
|
local RUNNING_AS_COMMAND=1 |
|
|
|
local RUNNING_AS_COMMAND=1 |
|
|
|
|
|
|
|
EXIT=exit |
|
|
|
fi |
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
local DOC='scd -- smart change to a recently used directory |
|
|
|
local DOC='scd -- smart change to a recently used directory |
|
|
|
@ -37,8 +38,9 @@ local SCD_ALIAS=~/.scdalias.zsh |
|
|
|
local ICASE a d m p i tdir maxrank threshold |
|
|
|
local ICASE a d m p i tdir maxrank threshold |
|
|
|
local opt_help opt_add opt_unindex opt_recursive opt_verbose |
|
|
|
local opt_help opt_add opt_unindex opt_recursive opt_verbose |
|
|
|
local opt_alias opt_unalias opt_list |
|
|
|
local opt_alias opt_unalias opt_list |
|
|
|
local -A drank dalias dkey |
|
|
|
local -A drank dalias |
|
|
|
local dmatching |
|
|
|
local dmatching |
|
|
|
|
|
|
|
local last_directory |
|
|
|
|
|
|
|
|
|
|
|
setopt extendedhistory extendedglob noautonamedirs brace_ccl |
|
|
|
setopt extendedhistory extendedglob noautonamedirs brace_ccl |
|
|
|
|
|
|
|
|
|
|
|
@ -56,11 +58,11 @@ zparseopts -D -- a=opt_add -add=opt_add -unindex=opt_unindex \ |
|
|
|
r=opt_recursive -recursive=opt_recursive \ |
|
|
|
r=opt_recursive -recursive=opt_recursive \ |
|
|
|
-alias:=opt_alias -unalias=opt_unalias -list=opt_list \ |
|
|
|
-alias:=opt_alias -unalias=opt_unalias -list=opt_list \ |
|
|
|
v=opt_verbose -verbose=opt_verbose h=opt_help -help=opt_help \ |
|
|
|
v=opt_verbose -verbose=opt_verbose h=opt_help -help=opt_help \ |
|
|
|
|| return $? |
|
|
|
|| $EXIT $? |
|
|
|
|
|
|
|
|
|
|
|
if [[ -n $opt_help ]]; then |
|
|
|
if [[ -n $opt_help ]]; then |
|
|
|
print $DOC |
|
|
|
print $DOC |
|
|
|
return |
|
|
|
$EXIT |
|
|
|
fi |
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
# load directory aliases if they exist |
|
|
|
# load directory aliases if they exist |
|
|
|
@ -79,8 +81,8 @@ _scd_Y19oug_abspath() { |
|
|
|
# define directory alias |
|
|
|
# define directory alias |
|
|
|
if [[ -n $opt_alias ]]; then |
|
|
|
if [[ -n $opt_alias ]]; then |
|
|
|
if [[ -n $1 && ! -d $1 ]]; then |
|
|
|
if [[ -n $1 && ! -d $1 ]]; then |
|
|
|
print -u2 "'$1' is not a directory" |
|
|
|
print -u2 "'$1' is not a directory." |
|
|
|
return 1 |
|
|
|
$EXIT 1 |
|
|
|
fi |
|
|
|
fi |
|
|
|
a=${opt_alias[-1]#=} |
|
|
|
a=${opt_alias[-1]#=} |
|
|
|
_scd_Y19oug_abspath d ${1:-$PWD} |
|
|
|
_scd_Y19oug_abspath d ${1:-$PWD} |
|
|
|
@ -93,19 +95,19 @@ if [[ -n $opt_alias ]]; then |
|
|
|
hash -d -- $a=$d |
|
|
|
hash -d -- $a=$d |
|
|
|
hash -dL >| $SCD_ALIAS |
|
|
|
hash -dL >| $SCD_ALIAS |
|
|
|
) |
|
|
|
) |
|
|
|
return $? |
|
|
|
$EXIT $? |
|
|
|
fi |
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
# undefine directory alias |
|
|
|
# undefine directory alias |
|
|
|
if [[ -n $opt_unalias ]]; then |
|
|
|
if [[ -n $opt_unalias ]]; then |
|
|
|
if [[ -n $1 && ! -d $1 ]]; then |
|
|
|
if [[ -n $1 && ! -d $1 ]]; then |
|
|
|
print -u2 "'$1' is not a directory" |
|
|
|
print -u2 "'$1' is not a directory." |
|
|
|
return 1 |
|
|
|
$EXIT 1 |
|
|
|
fi |
|
|
|
fi |
|
|
|
_scd_Y19oug_abspath a ${1:-$PWD} |
|
|
|
_scd_Y19oug_abspath a ${1:-$PWD} |
|
|
|
a=$(print -rD ${a}) |
|
|
|
a=$(print -rD ${a}) |
|
|
|
if [[ $a != [~][^/]## ]]; then |
|
|
|
if [[ $a != [~][^/]## ]]; then |
|
|
|
return |
|
|
|
$EXIT |
|
|
|
fi |
|
|
|
fi |
|
|
|
a=${a#[~]} |
|
|
|
a=${a#[~]} |
|
|
|
# unalias in the current shell, update alias file if successful |
|
|
|
# unalias in the current shell, update alias file if successful |
|
|
|
@ -118,35 +120,39 @@ if [[ -n $opt_unalias ]]; then |
|
|
|
hash -dL >| $SCD_ALIAS |
|
|
|
hash -dL >| $SCD_ALIAS |
|
|
|
) |
|
|
|
) |
|
|
|
fi |
|
|
|
fi |
|
|
|
return $? |
|
|
|
$EXIT $? |
|
|
|
fi |
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
# Rewrite the history file if it is at least 20% oversized |
|
|
|
# Rewrite directory index if it is at least 20% oversized |
|
|
|
if [[ -s $SCD_HISTFILE ]] && \ |
|
|
|
if [[ -s $SCD_HISTFILE ]] && \ |
|
|
|
(( $(wc -l <$SCD_HISTFILE) > 1.2 * $SCD_HISTSIZE )); then |
|
|
|
(( $(wc -l <$SCD_HISTFILE) > 1.2 * $SCD_HISTSIZE )); then |
|
|
|
m=( ${(f)"$(<$SCD_HISTFILE)"} ) |
|
|
|
m=( ${(f)"$(<$SCD_HISTFILE)"} ) |
|
|
|
print -lr -- ${m[-$SCD_HISTSIZE,-1]} >| ${SCD_HISTFILE} |
|
|
|
print -lr -- ${m[-$SCD_HISTSIZE,-1]} >| ${SCD_HISTFILE} |
|
|
|
fi |
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Determine the last recorded directory |
|
|
|
|
|
|
|
if [[ -s ${SCD_HISTFILE} ]]; then |
|
|
|
|
|
|
|
last_directory=${"$(tail -1 ${SCD_HISTFILE})"#*;} |
|
|
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
# Internal functions are prefixed with "_scd_Y19oug_". |
|
|
|
# Internal functions are prefixed with "_scd_Y19oug_". |
|
|
|
# The "record" function adds a non-repeating directory to the history |
|
|
|
# The "record" function adds its arguments to the directory index. |
|
|
|
# and turns on history writing. |
|
|
|
|
|
|
|
_scd_Y19oug_record() { |
|
|
|
_scd_Y19oug_record() { |
|
|
|
while [[ -n $1 && $1 == ${history[$HISTCMD]} ]]; do |
|
|
|
while [[ -n $last_directory && $1 == $last_directory ]]; do |
|
|
|
shift |
|
|
|
shift |
|
|
|
done |
|
|
|
done |
|
|
|
if [[ $# != 0 ]]; then |
|
|
|
if [[ $# -gt 0 ]]; then |
|
|
|
( umask 077; : >>| $SCD_HISTFILE ) |
|
|
|
( umask 077 |
|
|
|
p=": ${EPOCHSECONDS}:0;" |
|
|
|
p=": ${EPOCHSECONDS}:0;" |
|
|
|
print -lr -- ${p}${^*} >> $SCD_HISTFILE |
|
|
|
print -lr -- ${p}${^*} >>| $SCD_HISTFILE ) |
|
|
|
fi |
|
|
|
fi |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if [[ -n $opt_add ]]; then |
|
|
|
if [[ -n $opt_add ]]; then |
|
|
|
for a; do |
|
|
|
for d; do |
|
|
|
if [[ ! -d $a ]]; then |
|
|
|
if [[ ! -d $d ]]; then |
|
|
|
print -u 2 "Directory $a does not exist" |
|
|
|
print -u2 "Directory '$d' does not exist." |
|
|
|
return 2 |
|
|
|
$EXIT 2 |
|
|
|
fi |
|
|
|
fi |
|
|
|
done |
|
|
|
done |
|
|
|
_scd_Y19oug_abspath m ${*:-$PWD} |
|
|
|
_scd_Y19oug_abspath m ${*:-$PWD} |
|
|
|
@ -158,13 +164,13 @@ if [[ -n $opt_add ]]; then |
|
|
|
print "[done]" |
|
|
|
print "[done]" |
|
|
|
done |
|
|
|
done |
|
|
|
fi |
|
|
|
fi |
|
|
|
return |
|
|
|
$EXIT |
|
|
|
fi |
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
# take care of removing entries from the directory index |
|
|
|
# take care of removing entries from the directory index |
|
|
|
if [[ -n $opt_unindex ]]; then |
|
|
|
if [[ -n $opt_unindex ]]; then |
|
|
|
if [[ ! -s $SCD_HISTFILE ]]; then |
|
|
|
if [[ ! -s $SCD_HISTFILE ]]; then |
|
|
|
return |
|
|
|
$EXIT |
|
|
|
fi |
|
|
|
fi |
|
|
|
# expand existing directories in the argument list |
|
|
|
# expand existing directories in the argument list |
|
|
|
for i in {1..$#}; do |
|
|
|
for i in {1..$#}; do |
|
|
|
@ -190,43 +196,33 @@ if [[ -n $opt_unindex ]]; then |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
{ print $0 } |
|
|
|
{ print $0 } |
|
|
|
' $SCD_HISTFILE ${*:-$PWD} )" || return $? |
|
|
|
' $SCD_HISTFILE ${*:-$PWD} )" || $EXIT $? |
|
|
|
: >| ${SCD_HISTFILE} |
|
|
|
: >| ${SCD_HISTFILE} |
|
|
|
[[ ${#m} == 0 ]] || print -r -- $m >> ${SCD_HISTFILE} |
|
|
|
[[ ${#m} == 0 ]] || print -r -- $m >> ${SCD_HISTFILE} |
|
|
|
return |
|
|
|
$EXIT |
|
|
|
fi |
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
# The "action" function is called when there is just one target directory. |
|
|
|
# The "action" function is called when there is just one target directory. |
|
|
|
_scd_Y19oug_action() { |
|
|
|
_scd_Y19oug_action() { |
|
|
|
if [[ -n $opt_list ]]; then |
|
|
|
cd $1 || return $? |
|
|
|
for d; do |
|
|
|
|
|
|
|
a=${(k)dalias[(r)${d}]} |
|
|
|
|
|
|
|
print -r -- "# $a" |
|
|
|
|
|
|
|
print -r -- $d |
|
|
|
|
|
|
|
done |
|
|
|
|
|
|
|
elif [[ $# == 1 ]]; then |
|
|
|
|
|
|
|
if [[ -z $SCD_SCRIPT && -n $RUNNING_AS_COMMAND ]]; then |
|
|
|
if [[ -z $SCD_SCRIPT && -n $RUNNING_AS_COMMAND ]]; then |
|
|
|
print -u2 "Warning: running as command with SCD_SCRIPT undefined." |
|
|
|
print -u2 "Warning: running as command with SCD_SCRIPT undefined." |
|
|
|
fi |
|
|
|
fi |
|
|
|
[[ -n $SCD_SCRIPT ]] && (umask 077; |
|
|
|
if [[ -n $SCD_SCRIPT ]]; then |
|
|
|
print -r "cd ${(q)1}" >| $SCD_SCRIPT) |
|
|
|
print -r "cd ${(q)1}" >| $SCD_SCRIPT |
|
|
|
[[ -N $SCD_HISTFILE ]] && touch -a $SCD_HISTFILE |
|
|
|
|
|
|
|
cd $1 |
|
|
|
|
|
|
|
# record the new directory unless already done in some chpwd hook |
|
|
|
|
|
|
|
[[ -N $SCD_HISTFILE ]] || _scd_Y19oug_record $PWD |
|
|
|
|
|
|
|
fi |
|
|
|
fi |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
# handle different argument scenarios ---------------------------------------- |
|
|
|
# Match and rank patterns to the index file |
|
|
|
|
|
|
|
# set global arrays dmatching and drank |
|
|
|
## single argument that is an existing directory |
|
|
|
_scd_Y19oug_match() { |
|
|
|
if [[ $# == 1 && -d $1 && -x $1 ]]; then |
|
|
|
## single argument that is an existing directory or directory alias |
|
|
|
_scd_Y19oug_action $1 |
|
|
|
if [[ $# == 1 ]] && \ |
|
|
|
return $? |
|
|
|
[[ -d ${d::=$1} || -d ${d::=${nameddirs[$1]}} ]] && [[ -x $d ]]; |
|
|
|
## single argument that is an alias |
|
|
|
then |
|
|
|
elif [[ $# == 1 && -d ${d::=${nameddirs[$1]}} ]]; then |
|
|
|
_scd_Y19oug_abspath dmatching $d |
|
|
|
_scd_Y19oug_action $d |
|
|
|
drank[${dmatching[1]}]=1 |
|
|
|
return $? |
|
|
|
return |
|
|
|
fi |
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
# ignore case unless there is an argument with an uppercase letter |
|
|
|
# ignore case unless there is an argument with an uppercase letter |
|
|
|
@ -307,44 +303,51 @@ done |
|
|
|
# discard all directories below the rank threshold |
|
|
|
# discard all directories below the rank threshold |
|
|
|
threshold=$(( maxrank * SCD_THRESHOLD )) |
|
|
|
threshold=$(( maxrank * SCD_THRESHOLD )) |
|
|
|
dmatching=( ${^dmatching}(Ne:'(( ${drank[$REPLY]} >= threshold ))':) ) |
|
|
|
dmatching=( ${^dmatching}(Ne:'(( ${drank[$REPLY]} >= threshold ))':) ) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_scd_Y19oug_match $* |
|
|
|
|
|
|
|
|
|
|
|
## process whatever directories that remained |
|
|
|
## process whatever directories that remained |
|
|
|
case ${#dmatching} in |
|
|
|
if [[ ${#dmatching} == 0 ]]; then |
|
|
|
(0) |
|
|
|
print -u2 "No matching directory." |
|
|
|
print -u2 "no matching directory" |
|
|
|
$EXIT 1 |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
;; |
|
|
|
|
|
|
|
(1) |
|
|
|
## build formatted directory aliases for selection menu or list display |
|
|
|
_scd_Y19oug_action $dmatching |
|
|
|
for d in $dmatching; do |
|
|
|
return $? |
|
|
|
if [[ -n ${opt_verbose} ]]; then |
|
|
|
;; |
|
|
|
dalias[$d]=$(printf "%.3g %s" ${drank[$d]} $d) |
|
|
|
(*) |
|
|
|
else |
|
|
|
# build a list of strings to be displayed in the selection menu |
|
|
|
dalias[$d]=$(print -Dr -- $d) |
|
|
|
m=( ${(f)"$(print -lD ${dmatching})"} ) |
|
|
|
|
|
|
|
if [[ -n $opt_verbose ]]; then |
|
|
|
|
|
|
|
for i in {1..${#dmatching}}; do |
|
|
|
|
|
|
|
d=${dmatching[i]} |
|
|
|
|
|
|
|
m[i]=$(printf "%.3g %s" ${drank[$d]} $d) |
|
|
|
|
|
|
|
done |
|
|
|
|
|
|
|
fi |
|
|
|
fi |
|
|
|
# build a map of string names to actual directory paths |
|
|
|
done |
|
|
|
for i in {1..${#m}}; dalias[${m[i]}]=${dmatching[i]} |
|
|
|
|
|
|
|
# opt_list - output matching directories and exit |
|
|
|
## process the --list option |
|
|
|
if [[ -n $opt_list ]]; then |
|
|
|
if [[ -n $opt_list ]]; then |
|
|
|
_scd_Y19oug_action ${dmatching} |
|
|
|
for d in $dmatching; do |
|
|
|
return |
|
|
|
print -r -- "# ${dalias[$d]}" |
|
|
|
|
|
|
|
print -r -- $d |
|
|
|
|
|
|
|
done |
|
|
|
|
|
|
|
$EXIT |
|
|
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## process single directory match |
|
|
|
|
|
|
|
if [[ ${#dmatching} == 1 ]]; then |
|
|
|
|
|
|
|
_scd_Y19oug_action $dmatching |
|
|
|
|
|
|
|
$EXIT $? |
|
|
|
fi |
|
|
|
fi |
|
|
|
# finally use the selection menu to get the answer |
|
|
|
|
|
|
|
|
|
|
|
## here we have multiple matches - display selection menu |
|
|
|
a=( {a-z} {A-Z} ) |
|
|
|
a=( {a-z} {A-Z} ) |
|
|
|
p=( ) |
|
|
|
p=( ) |
|
|
|
for i in {1..${#m}}; do |
|
|
|
for i in {1..${#dmatching}}; do |
|
|
|
[[ -n ${a[i]} ]] || break |
|
|
|
[[ -n ${a[i]} ]] || break |
|
|
|
dkey[${a[i]}]=${dalias[$m[i]]} |
|
|
|
p+="${a[i]}) ${dalias[${dmatching[i]}]}" |
|
|
|
p+="${a[i]}) ${m[i]}" |
|
|
|
|
|
|
|
done |
|
|
|
done |
|
|
|
|
|
|
|
|
|
|
|
print -c -r -- $p |
|
|
|
print -c -r -- $p |
|
|
|
if read -s -k 1 d && [[ -n ${dkey[$d]} ]]; then |
|
|
|
|
|
|
|
_scd_Y19oug_action ${dkey[$d]} |
|
|
|
if read -s -k 1 d && [[ ${i::=${a[(I)$d]}} -gt 0 ]]; then |
|
|
|
|
|
|
|
_scd_Y19oug_action ${dmatching[i]} |
|
|
|
|
|
|
|
$EXIT $? |
|
|
|
fi |
|
|
|
fi |
|
|
|
return $? |
|
|
|
|
|
|
|
esac |
|
|
|
|
|
|
|
|