Update to the upstream smart-change-directory.
Synced with the master branch pavoljuhas/smart-change-directory@20b5a80cf0.
This commit is contained in:
parent
7f07facf41
commit
b6012bc2c3
|
@ -11,12 +11,9 @@ the index. A selection menu is displayed in case of several matches, with a
|
||||||
preference given to recently visited paths. `scd` can create permanent
|
preference given to recently visited paths. `scd` can create permanent
|
||||||
directory aliases, which appear as named directories in zsh session.
|
directory aliases, which appear as named directories in zsh session.
|
||||||
|
|
||||||
## INSTALLATION
|
## INSTALLATION NOTES
|
||||||
|
|
||||||
For oh-my-zsh, add `scd` to the `plugins` array in the ~/.zshrc file as in the
|
Besides oh-my-zsh, `scd` can be used with *bash*, *dash* or *tcsh*
|
||||||
[template file](../../templates/zshrc.zsh-template#L45).
|
|
||||||
|
|
||||||
Besides zsh, `scd` can be used with *bash*, *dash* or *tcsh*
|
|
||||||
shells and is also available as [Vim](http://www.vim.org/) plugin and
|
shells and is also available as [Vim](http://www.vim.org/) plugin and
|
||||||
[IPython](http://ipython.org/) extension. For installation details, see
|
[IPython](http://ipython.org/) extension. For installation details, see
|
||||||
https://github.com/pavoljuhas/smart-change-directory.
|
https://github.com/pavoljuhas/smart-change-directory.
|
||||||
|
@ -34,7 +31,7 @@ scd [options] [pattern1 pattern2 ...]
|
||||||
add specified directories to the directory index.</dd><dt>
|
add specified directories to the directory index.</dd><dt>
|
||||||
|
|
||||||
--unindex</dt><dd>
|
--unindex</dt><dd>
|
||||||
remove specified directories from the index.</dd><dt>
|
remove current or specified directories from the index.</dd><dt>
|
||||||
|
|
||||||
-r, --recursive</dt><dd>
|
-r, --recursive</dt><dd>
|
||||||
apply options <em>--add</em> or <em>--unindex</em> recursively.</dd><dt>
|
apply options <em>--add</em> or <em>--unindex</em> recursively.</dd><dt>
|
||||||
|
@ -47,6 +44,10 @@ scd [options] [pattern1 pattern2 ...]
|
||||||
remove ALIAS definition for the current or specified directory from
|
remove ALIAS definition for the current or specified directory from
|
||||||
<em>~/.scdalias.zsh</em>.</dd><dt>
|
<em>~/.scdalias.zsh</em>.</dd><dt>
|
||||||
|
|
||||||
|
-A, --all</dt><dd>
|
||||||
|
include all matching directories. Disregard matching by directory
|
||||||
|
alias and filtering of less likely paths.</dd><dt>
|
||||||
|
|
||||||
--list</dt><dd>
|
--list</dt><dd>
|
||||||
show matching directories and exit.</dd><dt>
|
show matching directories and exit.</dd><dt>
|
||||||
|
|
||||||
|
@ -70,7 +71,7 @@ scd doc
|
||||||
scd a b c
|
scd a b c
|
||||||
|
|
||||||
# Change to a directory path that ends with "ts"
|
# Change to a directory path that ends with "ts"
|
||||||
scd "ts(#e)"
|
scd "ts$"
|
||||||
|
|
||||||
# Show selection menu and ranking of 20 most likely directories
|
# Show selection menu and ranking of 20 most likely directories
|
||||||
scd -v
|
scd -v
|
||||||
|
|
119
plugins/scd/scd
119
plugins/scd/scd
|
@ -11,20 +11,22 @@ fi
|
||||||
local DOC='scd -- smart change to a recently used directory
|
local DOC='scd -- smart change to a recently used directory
|
||||||
usage: scd [options] [pattern1 pattern2 ...]
|
usage: scd [options] [pattern1 pattern2 ...]
|
||||||
Go to a directory path that contains all fixed string patterns. Prefer
|
Go to a directory path that contains all fixed string patterns. Prefer
|
||||||
recently visited directories and directories with patterns in their tail
|
recent or frequently visited directories as found in the directory index.
|
||||||
component. Display a selection menu in case of multiple matches.
|
Display a selection menu in case of multiple matches.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-a, --add add specified directories to the directory index
|
-a, --add add specified directories to the directory index.
|
||||||
--unindex remove specified directories from the index
|
--unindex remove current or specified directories from the index.
|
||||||
-r, --recursive apply options --add or --unindex recursively
|
-r, --recursive apply options --add or --unindex recursively.
|
||||||
--alias=ALIAS create alias for the current or specified directory and
|
--alias=ALIAS create alias for the current or specified directory and
|
||||||
store it in ~/.scdalias.zsh
|
store it in ~/.scdalias.zsh.
|
||||||
--unalias remove ALIAS definition for the current or specified
|
--unalias remove ALIAS definition for the current or specified
|
||||||
directory from ~/.scdalias.zsh
|
directory from ~/.scdalias.zsh.
|
||||||
--list show matching directories and exit
|
-A, --all include all matching directories. Disregard matching by
|
||||||
-v, --verbose display directory rank in the selection menu
|
directory alias and filtering of less likely paths.
|
||||||
-h, --help display this message and exit
|
--list show matching directories and exit.
|
||||||
|
-v, --verbose display directory rank in the selection menu.
|
||||||
|
-h, --help display this message and exit.
|
||||||
'
|
'
|
||||||
|
|
||||||
local SCD_HISTFILE=${SCD_HISTFILE:-${HOME}/.scdhistory}
|
local SCD_HISTFILE=${SCD_HISTFILE:-${HOME}/.scdhistory}
|
||||||
|
@ -35,9 +37,9 @@ local SCD_THRESHOLD=${SCD_THRESHOLD:-0.005}
|
||||||
local SCD_SCRIPT=${RUNNING_AS_COMMAND:+$SCD_SCRIPT}
|
local SCD_SCRIPT=${RUNNING_AS_COMMAND:+$SCD_SCRIPT}
|
||||||
local SCD_ALIAS=~/.scdalias.zsh
|
local SCD_ALIAS=~/.scdalias.zsh
|
||||||
|
|
||||||
local ICASE a d m p i tdir maxrank threshold
|
local ICASE a d m p i 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_all opt_list
|
||||||
local -A drank dalias
|
local -A drank dalias
|
||||||
local dmatching
|
local dmatching
|
||||||
local last_directory
|
local last_directory
|
||||||
|
@ -56,7 +58,8 @@ zmodload -i zsh/zutil
|
||||||
zmodload -i zsh/datetime
|
zmodload -i zsh/datetime
|
||||||
zparseopts -D -- a=opt_add -add=opt_add -unindex=opt_unindex \
|
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 \
|
||||||
|
A=opt_all -all=opt_all -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 \
|
||||||
|| $EXIT $?
|
|| $EXIT $?
|
||||||
|
|
||||||
|
@ -68,6 +71,11 @@ fi
|
||||||
# load directory aliases if they exist
|
# load directory aliases if they exist
|
||||||
[[ -r $SCD_ALIAS ]] && source $SCD_ALIAS
|
[[ -r $SCD_ALIAS ]] && source $SCD_ALIAS
|
||||||
|
|
||||||
|
# Private internal functions are prefixed with _scd_Y19oug_.
|
||||||
|
# Clean them up when the scd function returns.
|
||||||
|
setopt localtraps
|
||||||
|
trap 'unfunction -m "_scd_Y19oug_*"' EXIT
|
||||||
|
|
||||||
# works faster than the (:a) modifier and is compatible with zsh 4.2.6
|
# works faster than the (:a) modifier and is compatible with zsh 4.2.6
|
||||||
_scd_Y19oug_abspath() {
|
_scd_Y19oug_abspath() {
|
||||||
set -A $1 ${(ps:\0:)"$(
|
set -A $1 ${(ps:\0:)"$(
|
||||||
|
@ -123,11 +131,52 @@ if [[ -n $opt_unalias ]]; then
|
||||||
$EXIT $?
|
$EXIT $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# The "compress" function collapses repeated directories to
|
||||||
|
# one entry with a time stamp that gives equivalent-probability.
|
||||||
|
_scd_Y19oug_compress() {
|
||||||
|
awk -v epochseconds=$EPOCHSECONDS -v meanlife=$SCD_MEANLIFE '
|
||||||
|
BEGIN { FS = "[:;]"; }
|
||||||
|
length($0) < 4096 && $2 > 0 {
|
||||||
|
tau = 1.0 * ($2 - epochseconds) / meanlife;
|
||||||
|
if (tau < -6.9078) tau = -6.9078;
|
||||||
|
prob = exp(tau);
|
||||||
|
sub(/^[^;]*;/, "");
|
||||||
|
if (NF) {
|
||||||
|
dlist[last[$0]] = "";
|
||||||
|
dlist[NR] = $0;
|
||||||
|
last[$0] = NR;
|
||||||
|
ptot[$0] += prob;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
for (i = 1; i <= NR; ++i) {
|
||||||
|
d = dlist[i];
|
||||||
|
if (d) {
|
||||||
|
ts = log(ptot[d]) * meanlife + epochseconds;
|
||||||
|
printf(": %.0f:0;%s\n", ts, d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' $*
|
||||||
|
}
|
||||||
|
|
||||||
# Rewrite directory index 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)"} )
|
# compress repeated entries
|
||||||
print -lr -- ${m[-$SCD_HISTSIZE,-1]} >| ${SCD_HISTFILE}
|
m=( ${(f)"$(_scd_Y19oug_compress $SCD_HISTFILE)"} )
|
||||||
|
# purge non-existent directories
|
||||||
|
m=( ${(f)"$(
|
||||||
|
for a in $m; do
|
||||||
|
if [[ -d ${a#*;} ]]; then print -r -- $a; fi
|
||||||
|
done
|
||||||
|
)"}
|
||||||
|
)
|
||||||
|
# cut old entries if still oversized
|
||||||
|
if [[ $#m -gt $SCD_HISTSIZE ]]; then
|
||||||
|
m=( ${m[-$SCD_HISTSIZE,-1]} )
|
||||||
|
fi
|
||||||
|
print -lr -- $m >| ${SCD_HISTFILE}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Determine the last recorded directory
|
# Determine the last recorded directory
|
||||||
|
@ -135,7 +184,6 @@ if [[ -s ${SCD_HISTFILE} ]]; then
|
||||||
last_directory=${"$(tail -1 ${SCD_HISTFILE})"#*;}
|
last_directory=${"$(tail -1 ${SCD_HISTFILE})"#*;}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Internal functions are prefixed with "_scd_Y19oug_".
|
|
||||||
# The "record" function adds its arguments to the directory index.
|
# The "record" function adds its arguments to the directory index.
|
||||||
_scd_Y19oug_record() {
|
_scd_Y19oug_record() {
|
||||||
while [[ -n $last_directory && $1 == $last_directory ]]; do
|
while [[ -n $last_directory && $1 == $last_directory ]]; do
|
||||||
|
@ -217,7 +265,7 @@ _scd_Y19oug_action() {
|
||||||
# set global arrays dmatching and drank
|
# set global arrays dmatching and drank
|
||||||
_scd_Y19oug_match() {
|
_scd_Y19oug_match() {
|
||||||
## single argument that is an existing directory or directory alias
|
## single argument that is an existing directory or directory alias
|
||||||
if [[ $# == 1 ]] && \
|
if [[ -z $opt_all && $# == 1 ]] && \
|
||||||
[[ -d ${d::=$1} || -d ${d::=${nameddirs[$1]}} ]] && [[ -x $d ]];
|
[[ -d ${d::=$1} || -d ${d::=${nameddirs[$1]}} ]] && [[ -x $d ]];
|
||||||
then
|
then
|
||||||
_scd_Y19oug_abspath dmatching $d
|
_scd_Y19oug_abspath dmatching $d
|
||||||
|
@ -227,6 +275,8 @@ _scd_Y19oug_match() {
|
||||||
|
|
||||||
# ignore case unless there is an argument with an uppercase letter
|
# ignore case unless there is an argument with an uppercase letter
|
||||||
[[ "$*" == *[[:upper:]]* ]] || ICASE='(#i)'
|
[[ "$*" == *[[:upper:]]* ]] || ICASE='(#i)'
|
||||||
|
# support "$" as an anchor for the directory name ending
|
||||||
|
argv=( ${argv/(#m)?[$](#e)/${MATCH[1]}(#e)} )
|
||||||
|
|
||||||
# calculate rank of all directories in the SCD_HISTFILE and keep it as drank
|
# calculate rank of all directories in the SCD_HISTFILE and keep it as drank
|
||||||
# include a dummy entry for splitting of an empty string is buggy
|
# include a dummy entry for splitting of an empty string is buggy
|
||||||
|
@ -237,10 +287,10 @@ _scd_Y19oug_match() {
|
||||||
BEGIN { FS = "[:;]"; }
|
BEGIN { FS = "[:;]"; }
|
||||||
length($0) < 4096 && $2 > 0 {
|
length($0) < 4096 && $2 > 0 {
|
||||||
tau = 1.0 * ($2 - epochseconds) / meanlife;
|
tau = 1.0 * ($2 - epochseconds) / meanlife;
|
||||||
if (tau < -4.61) tau = -4.61;
|
if (tau < -6.9078) tau = -6.9078;
|
||||||
prec = exp(tau);
|
prob = exp(tau);
|
||||||
sub(/^[^;]*;/, "");
|
sub(/^[^;]*;/, "");
|
||||||
if (NF) ptot[$0] += prec;
|
if (NF) ptot[$0] += prob;
|
||||||
}
|
}
|
||||||
END { for (di in ptot) { print di; print ptot[di]; } }'
|
END { for (di in ptot) { print di; print ptot[di]; } }'
|
||||||
)"}
|
)"}
|
||||||
|
@ -249,9 +299,12 @@ _scd_Y19oug_match() {
|
||||||
|
|
||||||
# filter drank to the entries that match all arguments
|
# filter drank to the entries that match all arguments
|
||||||
for a; do
|
for a; do
|
||||||
p=${ICASE}"*${a}*"
|
p=${ICASE}"*(${a})*"
|
||||||
drank=( ${(kv)drank[(I)${~p}]} )
|
drank=( ${(kv)drank[(I)${~p}]} )
|
||||||
done
|
done
|
||||||
|
# require at least one argument matches the directory name
|
||||||
|
p=${ICASE}"*(${(j:|:)argv})[^/]#"
|
||||||
|
drank=( ${(kv)drank[(I)${~p}]} )
|
||||||
|
|
||||||
# build a list of matching directories reverse-sorted by their probabilities
|
# build a list of matching directories reverse-sorted by their probabilities
|
||||||
dmatching=( ${(f)"$(
|
dmatching=( ${(f)"$(
|
||||||
|
@ -261,26 +314,6 @@ _scd_Y19oug_match() {
|
||||||
)"}
|
)"}
|
||||||
)
|
)
|
||||||
|
|
||||||
# if some directory paths match all patterns in order, discard all others
|
|
||||||
p=${ICASE}"*${(j:*:)argv}*"
|
|
||||||
m=( ${(M)dmatching:#${~p}} )
|
|
||||||
[[ -d ${m[1]} ]] && dmatching=( $m )
|
|
||||||
# if some directory names match last pattern, discard all others
|
|
||||||
p=${ICASE}"*${(j:*:)argv}[^/]#"
|
|
||||||
m=( ${(M)dmatching:#${~p}} )
|
|
||||||
[[ -d ${m[1]} ]] && dmatching=( $m )
|
|
||||||
# if some directory names match all patterns, discard all others
|
|
||||||
m=( $dmatching )
|
|
||||||
for a; do
|
|
||||||
p=${ICASE}"*/[^/]#${a}[^/]#"
|
|
||||||
m=( ${(M)m:#${~p}} )
|
|
||||||
done
|
|
||||||
[[ -d ${m[1]} ]] && dmatching=( $m )
|
|
||||||
# if some directory names match all patterns in order, discard all others
|
|
||||||
p=${ICASE}"/*${(j:[^/]#:)argv}[^/]#"
|
|
||||||
m=( ${(M)dmatching:#${~p}} )
|
|
||||||
[[ -d ${m[1]} ]] && dmatching=( $m )
|
|
||||||
|
|
||||||
# do not match $HOME or $PWD when run without arguments
|
# do not match $HOME or $PWD when run without arguments
|
||||||
if [[ $# == 0 ]]; then
|
if [[ $# == 0 ]]; then
|
||||||
dmatching=( ${dmatching:#(${HOME}|${PWD})} )
|
dmatching=( ${dmatching:#(${HOME}|${PWD})} )
|
||||||
|
@ -302,6 +335,9 @@ _scd_Y19oug_match() {
|
||||||
|
|
||||||
# discard all directories below the rank threshold
|
# discard all directories below the rank threshold
|
||||||
threshold=$(( maxrank * SCD_THRESHOLD ))
|
threshold=$(( maxrank * SCD_THRESHOLD ))
|
||||||
|
if [[ -n ${opt_all} ]]; then
|
||||||
|
threshold=0
|
||||||
|
fi
|
||||||
dmatching=( ${^dmatching}(Ne:'(( ${drank[$REPLY]} >= threshold ))':) )
|
dmatching=( ${^dmatching}(Ne:'(( ${drank[$REPLY]} >= threshold ))':) )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,6 +375,7 @@ fi
|
||||||
|
|
||||||
## here we have multiple matches - display selection menu
|
## here we have multiple matches - display selection menu
|
||||||
a=( {a-z} {A-Z} )
|
a=( {a-z} {A-Z} )
|
||||||
|
a=( ${a[1,${#dmatching}]} )
|
||||||
p=( )
|
p=( )
|
||||||
for i in {1..${#dmatching}}; do
|
for i in {1..${#dmatching}}; do
|
||||||
[[ -n ${a[i]} ]] || break
|
[[ -n ${a[i]} ]] || break
|
||||||
|
|
Loading…
Reference in a new issue