mirror of
https://github.com/ohmyzsh/ohmyzsh.git
synced 2024-11-18 11:50:07 +00:00
scd: update to 1.4.0 (#9066)
This commit is contained in:
parent
cfb86cd08d
commit
8d08f1634a
4 changed files with 357 additions and 120 deletions
|
@ -14,8 +14,9 @@ directory aliases, which appear as named directories in zsh session.
|
||||||
## INSTALLATION NOTES
|
## INSTALLATION NOTES
|
||||||
|
|
||||||
Besides oh-my-zsh, `scd` can be used with *bash*, *dash* or *tcsh*
|
Besides oh-my-zsh, `scd` can be used with *bash*, *dash* or *tcsh*
|
||||||
shells and is also available as [Vim](https://www.vim.org/) plugin and
|
shells and is also available as Vim plugin
|
||||||
[IPython](https://ipython.org/) extension. For installation details, see
|
[scd.vim](https://github.com/pavoljuhas/scd.vim) and
|
||||||
|
[IPython](https://ipython.org) extension. For installation details, see
|
||||||
https://github.com/pavoljuhas/smart-change-directory.
|
https://github.com/pavoljuhas/smart-change-directory.
|
||||||
|
|
||||||
## SYNOPSIS
|
## SYNOPSIS
|
||||||
|
@ -24,11 +25,31 @@ https://github.com/pavoljuhas/smart-change-directory.
|
||||||
scd [options] [pattern1 pattern2 ...]
|
scd [options] [pattern1 pattern2 ...]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## PATTERNS
|
||||||
|
|
||||||
|
Patterns may use all zsh [glob operators](
|
||||||
|
http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Operators)
|
||||||
|
available with *extendedglob* option. Specified patterns must match
|
||||||
|
the absolute path and at least one of them must match in the tail.
|
||||||
|
Several special patterns are also recognized as follows:
|
||||||
|
|
||||||
|
<dl><dt>
|
||||||
|
^PAT</dt><dd>
|
||||||
|
PAT must match at the beginning of the path, for example, "^/home"</dd><dt>
|
||||||
|
PAT$</dt><dd>
|
||||||
|
require PAT to match the end of the path, "man$"</dd><dt>
|
||||||
|
./</dt><dd>
|
||||||
|
match only subdirectories of the current directory</dd><dt>
|
||||||
|
:PAT</dt><dd>
|
||||||
|
require PAT to match over the tail component, ":doc", ":re/doc"</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
|
||||||
## OPTIONS
|
## OPTIONS
|
||||||
|
|
||||||
<dl><dt>
|
<dl><dt>
|
||||||
-a, --add</dt><dd>
|
-a, --add</dt><dd>
|
||||||
add specified directories to the directory index.</dd><dt>
|
add current or specified directories to the directory index.</dd><dt>
|
||||||
|
|
||||||
--unindex</dt><dd>
|
--unindex</dt><dd>
|
||||||
remove current or specified directories from the index.</dd><dt>
|
remove current or specified directories from the index.</dd><dt>
|
||||||
|
@ -42,11 +63,16 @@ scd [options] [pattern1 pattern2 ...]
|
||||||
|
|
||||||
--unalias</dt><dd>
|
--unalias</dt><dd>
|
||||||
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>. Use "OLD" to purge aliases to non-existent
|
||||||
|
directories.</dd><dt>
|
||||||
|
|
||||||
-A, --all</dt><dd>
|
-A, --all</dt><dd>
|
||||||
include all matching directories. Disregard matching by directory
|
display all directories even those excluded by patterns in
|
||||||
alias and filtering of less likely paths.</dd><dt>
|
<em>~/.scdignore</em>. Disregard the unique matching for a
|
||||||
|
directory alias and filtering of less likely paths.</dd><dt>
|
||||||
|
|
||||||
|
-p, --push</dt><dd>
|
||||||
|
use "pushd" to change to the target directory.</dd><dt>
|
||||||
|
|
||||||
--list</dt><dd>
|
--list</dt><dd>
|
||||||
show matching directories and exit.</dd><dt>
|
show matching directories and exit.</dd><dt>
|
||||||
|
@ -58,6 +84,7 @@ scd [options] [pattern1 pattern2 ...]
|
||||||
display this options summary and exit.</dd>
|
display this options summary and exit.</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
@ -83,17 +110,26 @@ scd --alias=xray
|
||||||
scd xray
|
scd xray
|
||||||
```
|
```
|
||||||
|
|
||||||
# FILES
|
## FILES
|
||||||
|
|
||||||
<dl><dt>
|
<dl><dt>
|
||||||
~/.scdhistory</dt><dd>
|
~/.scdhistory</dt><dd>
|
||||||
time-stamped index of visited directories.</dd><dt>
|
time-stamped index of visited directories.</dd><dt>
|
||||||
|
|
||||||
~/.scdalias.zsh</dt><dd>
|
~/.scdalias.zsh</dt><dd>
|
||||||
scd-generated definitions of directory aliases.</dd>
|
scd-generated definitions of directory aliases.</dd><dt>
|
||||||
|
|
||||||
|
~/.scdignore</dt><dd>
|
||||||
|
<a href="http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Operators">
|
||||||
|
glob patterns</a> for paths to be ignored in the scd search, for example,
|
||||||
|
<code>/mnt/backup/*</code>. The patterns are specified one per line
|
||||||
|
and are matched assuming the <em>extendedglob</em> zsh option. Lines
|
||||||
|
starting with "#" are skipped as comments. The .scdignore patterns
|
||||||
|
are not applied in the <em>--all</em> mode.</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
# ENVIRONMENT
|
|
||||||
|
## ENVIRONMENT
|
||||||
|
|
||||||
<dl><dt>
|
<dl><dt>
|
||||||
SCD_HISTFILE</dt><dd>
|
SCD_HISTFILE</dt><dd>
|
||||||
|
|
60
plugins/scd/_scd
Normal file
60
plugins/scd/_scd
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#compdef scd
|
||||||
|
#description smart change directory
|
||||||
|
|
||||||
|
local curcontext="$curcontext" state line expl ret=1
|
||||||
|
typeset -A opt_args
|
||||||
|
|
||||||
|
local -a indexopts myargs
|
||||||
|
indexopts=( --add -a --unindex )
|
||||||
|
|
||||||
|
myargs=(
|
||||||
|
# common options
|
||||||
|
"(--help -h)"{--help,-h}"[print help and exit]"
|
||||||
|
|
||||||
|
# options for manipulating directory index
|
||||||
|
- index
|
||||||
|
"(--recursive -r)"{--recursive,-r}"[use recursive --add or --unindex]"
|
||||||
|
"($indexopts)"{--add,-a}"[add specified directories to the index]"
|
||||||
|
"($indexopts)--unindex[remove specified directories from the index]"
|
||||||
|
"*:directory:{ (( ${words[(I)-a|--add|--unindex]} )) && _path_files -/ }"
|
||||||
|
|
||||||
|
# define new directory alias
|
||||||
|
- alias
|
||||||
|
"--alias=[create alias for this or given directory]:directory-alias:()"
|
||||||
|
'1:directory:{ (( words[(I)--alias*] )) && _path_files -/ }'
|
||||||
|
|
||||||
|
# remove definition of directory alias
|
||||||
|
- unalias
|
||||||
|
"--unalias[remove definition of directory alias]"
|
||||||
|
"*::directory alias:->scd-alias-target"
|
||||||
|
|
||||||
|
# act on the directory change
|
||||||
|
- scd
|
||||||
|
"(--all -A)"{--all,-A}"[include less likely and ignored paths]"
|
||||||
|
"--list[print matching directories and exit]"
|
||||||
|
"(--verbose -v)"{--verbose,-v}"[show directory ranking and full paths]"
|
||||||
|
"(--push -p)"{--push,-p}"[change directory with 'pushd']"
|
||||||
|
"1::directory alias:->scd-alias-target"
|
||||||
|
"*:patterns:()"
|
||||||
|
)
|
||||||
|
|
||||||
|
_arguments -S -C $myargs && ret=0
|
||||||
|
|
||||||
|
|
||||||
|
if [[ "$state" == scd-alias-target && -s ~/.scdalias.zsh ]]; then
|
||||||
|
local -a scdaliases
|
||||||
|
scdaliases=( )
|
||||||
|
eval "$(setopt extendedglob
|
||||||
|
phome="(#b)(#s)${HOME}(/*)#(#e)"
|
||||||
|
builtin hash -dr
|
||||||
|
source ~/.scdalias.zsh &&
|
||||||
|
for k v in ${(kv)nameddirs}; do
|
||||||
|
scdaliases+=( $k:${v/${~phome}/"~"${match[1]}} )
|
||||||
|
done
|
||||||
|
complete_unalias=${+opt_args[unalias---unalias]}
|
||||||
|
if (( complete_unalias && ! ${+nameddirs[OLD]} )); then
|
||||||
|
scdaliases+=( 'OLD:all aliases to non-existent paths' )
|
||||||
|
fi
|
||||||
|
typeset -p scdaliases )"
|
||||||
|
_describe -t scdaliases scdalias scdaliases
|
||||||
|
fi
|
347
plugins/scd/scd
Normal file → Executable file
347
plugins/scd/scd
Normal file → Executable file
|
@ -1,29 +1,39 @@
|
||||||
#!/bin/zsh -f
|
#!/bin/zsh -f
|
||||||
|
|
||||||
emulate -L zsh
|
emulate -L zsh
|
||||||
|
|
||||||
|
local RUNNING_AS_COMMAND=
|
||||||
local EXIT=return
|
local EXIT=return
|
||||||
if [[ $(whence -w $0) == *:' 'command ]]; then
|
if [[ $(whence -w $0) == *:' 'command ]]; then
|
||||||
emulate -R zsh
|
RUNNING_AS_COMMAND=1
|
||||||
local RUNNING_AS_COMMAND=1
|
|
||||||
EXIT=exit
|
EXIT=exit
|
||||||
fi
|
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 matches all patterns. Prefer recent or
|
||||||
recent or frequently visited directories as found in the directory index.
|
frequently visited directories as found in the directory index.
|
||||||
Display a selection menu in case of multiple matches.
|
Display a selection menu in case of multiple matches.
|
||||||
|
|
||||||
|
Special patterns:
|
||||||
|
^PAT match at the path root, "^/home"
|
||||||
|
PAT$ match paths ending with PAT, "man$"
|
||||||
|
./ match paths under the current directory
|
||||||
|
:PAT require PAT to span the tail, ":doc", ":re/doc"
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-a, --add add specified directories to the directory index.
|
-a, --add add current or specified directories to the index.
|
||||||
--unindex remove current or 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.
|
||||||
-A, --all include all matching directories. Disregard matching by
|
Use "OLD" to purge aliases to non-existent directories.
|
||||||
directory alias and filtering of less likely paths.
|
-A, --all display all directories even those excluded by patterns
|
||||||
|
in ~/.scdignore. Disregard unique match for a directory
|
||||||
|
alias and filtering of less likely paths.
|
||||||
|
-p, --push use "pushd" to change to the target directory.
|
||||||
--list show matching directories and exit.
|
--list show matching directories and exit.
|
||||||
-v, --verbose display directory rank in the selection menu.
|
-v, --verbose display directory rank in the selection menu.
|
||||||
-h, --help display this message and exit.
|
-h, --help display this message and exit.
|
||||||
|
@ -36,18 +46,28 @@ local SCD_MEANLIFE=${SCD_MEANLIFE:-86400}
|
||||||
local SCD_THRESHOLD=${SCD_THRESHOLD:-0.005}
|
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 SCD_IGNORE=~/.scdignore
|
||||||
|
|
||||||
local ICASE a d m p i maxrank threshold
|
# Minimum logarithm of probability. Avoids out of range warning in exp().
|
||||||
|
local -r MINLOGPROB=-15
|
||||||
|
|
||||||
|
# When false, use case-insensitive globbing to fix PWD capitalization.
|
||||||
|
local PWDCASECORRECT=true
|
||||||
|
if [[ ${OSTYPE} == darwin* ]]; then
|
||||||
|
PWDCASECORRECT=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
local 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_all opt_list
|
local opt_alias opt_unalias opt_all opt_push opt_list
|
||||||
local -A drank dalias
|
local -A drank dalias scdignore
|
||||||
local dmatching
|
local dmatching
|
||||||
local last_directory
|
local last_directory
|
||||||
|
|
||||||
setopt extendedhistory extendedglob noautonamedirs brace_ccl
|
setopt extendedglob noautonamedirs brace_ccl
|
||||||
|
|
||||||
# If SCD_SCRIPT is defined make sure the file exists and is empty.
|
# If SCD_SCRIPT is defined make sure that that file exists and is empty.
|
||||||
# This removes any previous old commands.
|
# This removes any old previous commands from the SCD_SCRIPT file.
|
||||||
[[ -n "$SCD_SCRIPT" ]] && [[ -s $SCD_SCRIPT || ! -f $SCD_SCRIPT ]] && (
|
[[ -n "$SCD_SCRIPT" ]] && [[ -s $SCD_SCRIPT || ! -f $SCD_SCRIPT ]] && (
|
||||||
umask 077
|
umask 077
|
||||||
: >| $SCD_SCRIPT
|
: >| $SCD_SCRIPT
|
||||||
|
@ -56,13 +76,17 @@ setopt extendedhistory extendedglob noautonamedirs brace_ccl
|
||||||
# process command line options
|
# process command line options
|
||||||
zmodload -i zsh/zutil
|
zmodload -i zsh/zutil
|
||||||
zmodload -i zsh/datetime
|
zmodload -i zsh/datetime
|
||||||
zparseopts -D -- a=opt_add -add=opt_add -unindex=opt_unindex \
|
zmodload -i zsh/parameter
|
||||||
|
zparseopts -D -E -- 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 \
|
-alias:=opt_alias -unalias=opt_unalias \
|
||||||
A=opt_all -all=opt_all -list=opt_list \
|
A=opt_all -all=opt_all p=opt_push -push=opt_push -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 $?
|
||||||
|
|
||||||
|
# remove the first instance of "--" from positional arguments
|
||||||
|
argv[(i)--]=( )
|
||||||
|
|
||||||
if [[ -n $opt_help ]]; then
|
if [[ -n $opt_help ]]; then
|
||||||
print $DOC
|
print $DOC
|
||||||
$EXIT
|
$EXIT
|
||||||
|
@ -71,6 +95,22 @@ 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
|
||||||
|
|
||||||
|
# load scd-ignore patterns if available
|
||||||
|
if [[ -s $SCD_IGNORE ]]; then
|
||||||
|
setopt noglob
|
||||||
|
<$SCD_IGNORE \
|
||||||
|
while read p; do
|
||||||
|
[[ $p != [\#]* ]] || continue
|
||||||
|
[[ -n $p ]] || continue
|
||||||
|
# expand leading tilde if it has valid expansion
|
||||||
|
if [[ $p == [~]* ]] && ( : ${~p} ) 2>/dev/null; then
|
||||||
|
p=${~p}
|
||||||
|
fi
|
||||||
|
scdignore[$p]=1
|
||||||
|
done
|
||||||
|
setopt glob
|
||||||
|
fi
|
||||||
|
|
||||||
# Private internal functions are prefixed with _scd_Y19oug_.
|
# Private internal functions are prefixed with _scd_Y19oug_.
|
||||||
# Clean them up when the scd function returns.
|
# Clean them up when the scd function returns.
|
||||||
setopt localtraps
|
setopt localtraps
|
||||||
|
@ -79,9 +119,17 @@ 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:)"$(
|
||||||
unfunction -m "*"; shift
|
setopt pushdsilent
|
||||||
|
unfunction -m "*"
|
||||||
|
unalias -m "*"
|
||||||
|
unset CDPATH
|
||||||
|
shift
|
||||||
for d; do
|
for d; do
|
||||||
cd $d && print -Nr -- $PWD && cd $OLDPWD
|
pushd $d || continue
|
||||||
|
$PWDCASECORRECT &&
|
||||||
|
print -Nr -- $PWD ||
|
||||||
|
print -Nr -- (#i)$PWD
|
||||||
|
popd 2>/dev/null
|
||||||
done
|
done
|
||||||
)"}
|
)"}
|
||||||
}
|
}
|
||||||
|
@ -106,47 +154,76 @@ if [[ -n $opt_alias ]]; then
|
||||||
$EXIT $?
|
$EXIT $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# undefine directory alias
|
# undefine one or more directory aliases
|
||||||
if [[ -n $opt_unalias ]]; then
|
if [[ -n $opt_unalias ]]; then
|
||||||
if [[ -n $1 && ! -d $1 ]]; then
|
local -U uu
|
||||||
print -u2 "'$1' is not a directory."
|
local ec=0
|
||||||
$EXIT 1
|
uu=( ${*:-${PWD}} )
|
||||||
|
if (( ${uu[(I)OLD]} && ${+nameddirs[OLD]} == 0 )); then
|
||||||
|
uu=( ${uu:#OLD} ${(ps:\0:)"$(
|
||||||
|
hash -dr
|
||||||
|
if [[ -r $SCD_ALIAS ]]; then
|
||||||
|
source $SCD_ALIAS
|
||||||
|
fi
|
||||||
|
for a d in ${(kv)nameddirs}; do
|
||||||
|
[[ -d $d ]] || print -Nr -- $a
|
||||||
|
done
|
||||||
|
)"}
|
||||||
|
)
|
||||||
fi
|
fi
|
||||||
_scd_Y19oug_abspath a ${1:-$PWD}
|
m=( )
|
||||||
a=$(print -rD ${a})
|
for p in $uu; do
|
||||||
if [[ $a != [~][^/]## ]]; then
|
d=$p
|
||||||
$EXIT
|
if [[ ${+nameddirs[$d]} == 0 && -d $d ]]; then
|
||||||
fi
|
_scd_Y19oug_abspath d $d
|
||||||
a=${a#[~]}
|
fi
|
||||||
# unalias in the current shell, update alias file if successful
|
a=${(k)nameddirs[$d]:-${(k)nameddirs[(r)$d]}}
|
||||||
if unhash -d -- $a 2>/dev/null && [[ -r $SCD_ALIAS ]]; then
|
if [[ -z $a ]]; then
|
||||||
|
ec=1
|
||||||
|
print -u2 "'$p' is neither a directory alias nor an aliased path."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
# unalias in the current shell and remember to update the alias file
|
||||||
|
if unhash -d -- $a 2>/dev/null; then
|
||||||
|
m+=( $a )
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ $#m != 0 && -r $SCD_ALIAS ]]; then
|
||||||
(
|
(
|
||||||
umask 077
|
umask 077
|
||||||
hash -dr
|
hash -dr
|
||||||
source $SCD_ALIAS
|
source $SCD_ALIAS
|
||||||
unhash -d -- $a 2>/dev/null &&
|
for a in $m; do
|
||||||
|
unhash -d -- $a 2>/dev/null
|
||||||
|
done
|
||||||
hash -dL >| $SCD_ALIAS
|
hash -dL >| $SCD_ALIAS
|
||||||
)
|
) || ec=$?
|
||||||
fi
|
fi
|
||||||
$EXIT $?
|
$EXIT $ec
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# The "compress" function collapses repeated directories to
|
# The "compress" function collapses repeated directories into
|
||||||
# one entry with a time stamp that gives equivalent-probability.
|
# a single entry with a time-stamp yielding an equivalent probability.
|
||||||
_scd_Y19oug_compress() {
|
_scd_Y19oug_compress() {
|
||||||
awk -v epochseconds=$EPOCHSECONDS -v meanlife=$SCD_MEANLIFE '
|
awk -v epochseconds=$EPOCHSECONDS \
|
||||||
BEGIN { FS = "[:;]"; }
|
-v meanlife=$SCD_MEANLIFE \
|
||||||
length($0) < 4096 && $2 > 0 {
|
-v minlogprob=$MINLOGPROB \
|
||||||
|
'
|
||||||
|
BEGIN {
|
||||||
|
FS = "[:;]";
|
||||||
|
pmin = exp(minlogprob);
|
||||||
|
}
|
||||||
|
/^: deleted:0;/ { next; }
|
||||||
|
length($0) < 4096 && $2 > 1000 {
|
||||||
|
df = $0;
|
||||||
|
sub("^[^;]*;", "", df);
|
||||||
|
if (!df) next;
|
||||||
tau = 1.0 * ($2 - epochseconds) / meanlife;
|
tau = 1.0 * ($2 - epochseconds) / meanlife;
|
||||||
if (tau < -6.9078) tau = -6.9078;
|
prob = (tau < minlogprob) ? pmin : exp(tau);
|
||||||
prob = exp(tau);
|
dlist[last[df]] = "";
|
||||||
sub(/^[^;]*;/, "");
|
dlist[NR] = df;
|
||||||
if (NF) {
|
last[df] = NR;
|
||||||
dlist[last[$0]] = "";
|
ptot[df] += prob;
|
||||||
dlist[NR] = $0;
|
|
||||||
last[$0] = NR;
|
|
||||||
ptot[$0] += prob;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
END {
|
END {
|
||||||
for (i = 1; i <= NR; ++i) {
|
for (i = 1; i <= NR; ++i) {
|
||||||
|
@ -157,26 +234,38 @@ _scd_Y19oug_compress() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
' $*
|
' $*
|
||||||
}
|
}
|
||||||
|
|
||||||
# Rewrite directory index if it is at least 20% oversized
|
# Rewrite directory index if it is at least 20% oversized.
|
||||||
if [[ -s $SCD_HISTFILE ]] && \
|
local curhistsize
|
||||||
(( $(wc -l <$SCD_HISTFILE) > 1.2 * $SCD_HISTSIZE )); then
|
if [[ -z $opt_unindex && -s $SCD_HISTFILE ]] && \
|
||||||
# compress repeated entries
|
curhistsize=$(wc -l <$SCD_HISTFILE) && \
|
||||||
m=( ${(f)"$(_scd_Y19oug_compress $SCD_HISTFILE)"} )
|
(( $curhistsize > 1.2 * $SCD_HISTSIZE )); then
|
||||||
# purge non-existent directories
|
# Compress repeated entries in a background process.
|
||||||
m=( ${(f)"$(
|
(
|
||||||
for a in $m; do
|
m=( ${(f)"$(_scd_Y19oug_compress $SCD_HISTFILE)"} )
|
||||||
if [[ -d ${a#*;} ]]; then print -r -- $a; fi
|
# purge non-existent and ignored directories
|
||||||
done
|
m=( ${(f)"$(
|
||||||
)"}
|
for a in $m; do
|
||||||
)
|
d=${a#*;}
|
||||||
# cut old entries if still oversized
|
[[ -z ${scdignore[(k)$d]} ]] || continue
|
||||||
if [[ $#m -gt $SCD_HISTSIZE ]]; then
|
[[ -d $d ]] || continue
|
||||||
m=( ${m[-$SCD_HISTSIZE,-1]} )
|
$PWDCASECORRECT || d=( (#i)${d} )
|
||||||
fi
|
t=${a%%;*}
|
||||||
print -lr -- $m >| ${SCD_HISTFILE}
|
print -r -- "${t};${d}"
|
||||||
|
done
|
||||||
|
)"}
|
||||||
|
)
|
||||||
|
# cut old entries if still oversized
|
||||||
|
if [[ $#m -gt $SCD_HISTSIZE ]]; then
|
||||||
|
m=( ${m[-$SCD_HISTSIZE,-1]} )
|
||||||
|
fi
|
||||||
|
# Checking existence of many directories could have taken a while.
|
||||||
|
# Append any index entries added in meantime.
|
||||||
|
m+=( ${(f)"$(sed "1,${curhistsize}d" $SCD_HISTFILE)"} )
|
||||||
|
print -lr -- $m >| ${SCD_HISTFILE}
|
||||||
|
) &|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Determine the last recorded directory
|
# Determine the last recorded directory
|
||||||
|
@ -197,13 +286,8 @@ _scd_Y19oug_record() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if [[ -n $opt_add ]]; then
|
if [[ -n $opt_add ]]; then
|
||||||
for d; do
|
m=( ${^${argv:-$PWD}}(N-/) )
|
||||||
if [[ ! -d $d ]]; then
|
_scd_Y19oug_abspath m ${m}
|
||||||
print -u2 "Directory '$d' does not exist."
|
|
||||||
$EXIT 2
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
_scd_Y19oug_abspath m ${*:-$PWD}
|
|
||||||
_scd_Y19oug_record $m
|
_scd_Y19oug_record $m
|
||||||
if [[ -n $opt_recursive ]]; then
|
if [[ -n $opt_recursive ]]; then
|
||||||
for d in $m; do
|
for d in $m; do
|
||||||
|
@ -220,6 +304,7 @@ if [[ -n $opt_unindex ]]; then
|
||||||
if [[ ! -s $SCD_HISTFILE ]]; then
|
if [[ ! -s $SCD_HISTFILE ]]; then
|
||||||
$EXIT
|
$EXIT
|
||||||
fi
|
fi
|
||||||
|
argv=( ${argv:-$PWD} )
|
||||||
# expand existing directories in the argument list
|
# expand existing directories in the argument list
|
||||||
for i in {1..$#}; do
|
for i in {1..$#}; do
|
||||||
if [[ -d ${argv[i]} ]]; then
|
if [[ -d ${argv[i]} ]]; then
|
||||||
|
@ -227,24 +312,28 @@ if [[ -n $opt_unindex ]]; then
|
||||||
argv[i]=${d}
|
argv[i]=${d}
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
# strip trailing slashes, but preserve the root path
|
||||||
|
argv=( ${argv/(#m)?\/##(#e)/${MATCH[1]}} )
|
||||||
m="$(awk -v recursive=${opt_recursive} '
|
m="$(awk -v recursive=${opt_recursive} '
|
||||||
BEGIN {
|
BEGIN {
|
||||||
for (i = 2; i < ARGC; ++i) {
|
for (i = 2; i < ARGC; ++i) {
|
||||||
argset[ARGV[i]] = 1;
|
argset[ARGV[i]] = 1;
|
||||||
delete ARGV[i];
|
delete ARGV[i];
|
||||||
}
|
}
|
||||||
|
unindex_root = ("/" in argset);
|
||||||
}
|
}
|
||||||
1 {
|
1 {
|
||||||
d = $0; sub(/^[^;]*;/, "", d);
|
d = $0; sub(/^[^;]*;/, "", d);
|
||||||
if (d in argset) next;
|
if (d in argset) next;
|
||||||
}
|
}
|
||||||
recursive {
|
recursive {
|
||||||
|
if (unindex_root) exit;
|
||||||
for (a in argset) {
|
for (a in argset) {
|
||||||
if (substr(d, 1, length(a) + 1) == a"/") next;
|
if (substr(d, 1, length(a) + 1) == a"/") next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{ print $0 }
|
{ print $0 }
|
||||||
' $SCD_HISTFILE ${*:-$PWD} )" || $EXIT $?
|
' $SCD_HISTFILE $* )" || $EXIT $?
|
||||||
: >| ${SCD_HISTFILE}
|
: >| ${SCD_HISTFILE}
|
||||||
[[ ${#m} == 0 ]] || print -r -- $m >> ${SCD_HISTFILE}
|
[[ ${#m} == 0 ]] || print -r -- $m >> ${SCD_HISTFILE}
|
||||||
$EXIT
|
$EXIT
|
||||||
|
@ -252,67 +341,113 @@ 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() {
|
||||||
cd $1 || return $?
|
local cdcmd=cd
|
||||||
|
[[ -z ${opt_push} ]] || cdcmd=pushd
|
||||||
|
builtin $cdcmd $1 || return $?
|
||||||
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
|
||||||
if [[ -n $SCD_SCRIPT ]]; then
|
if [[ -n $SCD_SCRIPT ]]; then
|
||||||
print -r "cd ${(q)1}" >| $SCD_SCRIPT
|
local d=$1
|
||||||
|
if [[ $OSTYPE == cygwin && ${(L)SCD_SCRIPT} == *.bat ]]; then
|
||||||
|
d=$(cygpath -aw .)
|
||||||
|
fi
|
||||||
|
print -r "${cdcmd} ${(qqq)d}" >| $SCD_SCRIPT
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Match and rank patterns to the index file
|
# Select and order indexed directories by matching command-line patterns.
|
||||||
# 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 [[ -z $opt_all && $# == 1 ]] && \
|
if [[ -z $opt_all && $# == 1 ]] && \
|
||||||
[[ -d ${d::=$1} || -d ${d::=${nameddirs[$1]}} ]] && [[ -x $d ]];
|
[[ -d ${d::=${nameddirs[$1]}} || -d ${d::=$1} ]] && [[ -x $d ]];
|
||||||
then
|
then
|
||||||
_scd_Y19oug_abspath dmatching $d
|
_scd_Y19oug_abspath dmatching $d
|
||||||
drank[${dmatching[1]}]=1
|
drank[${dmatching[1]}]=1
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ignore case unless there is an argument with an uppercase letter
|
# quote brackets when PWD is /Volumes/[C]/
|
||||||
[[ "$*" == *[[:upper:]]* ]] || ICASE='(#i)'
|
local qpwd=${PWD//(#m)[][]/\\${MATCH}}
|
||||||
# support "$" as an anchor for the directory name ending
|
|
||||||
|
# support "./" as an alias for $PWD to match only subdirectories.
|
||||||
|
argv=( ${argv/(#s).\/(#e)/(#s)${qpwd}(|/*)(#e)} )
|
||||||
|
|
||||||
|
# support "./pat" as an alias for $PWD/pat.
|
||||||
|
argv=( ${argv/(#m)(#s).\/?*/(#s)${qpwd}${MATCH#.}} )
|
||||||
|
|
||||||
|
# support "^" as an anchor for the root directory, e.g., "^$HOME".
|
||||||
|
argv=( ${argv/(#m)(#s)\^?*/(#s)${${~MATCH[2,-1]}}} )
|
||||||
|
|
||||||
|
# support "$" as an anchor at the end of directory name.
|
||||||
argv=( ${argv/(#m)?[$](#e)/${MATCH[1]}(#e)} )
|
argv=( ${argv/(#m)?[$](#e)/${MATCH[1]}(#e)} )
|
||||||
|
|
||||||
# calculate rank of all directories in the SCD_HISTFILE and keep it as drank
|
# support prefix ":" to match over the tail component.
|
||||||
# include a dummy entry for splitting of an empty string is buggy
|
argv=( ${argv/(#m)(#s):?*/${MATCH[2,-1]}[^/]#(#e)} )
|
||||||
|
|
||||||
|
# calculate rank of all directories in SCD_HISTFILE and store it in drank.
|
||||||
|
# include a dummy entry to avoid issues with splitting an empty string.
|
||||||
[[ -s $SCD_HISTFILE ]] && drank=( ${(f)"$(
|
[[ -s $SCD_HISTFILE ]] && drank=( ${(f)"$(
|
||||||
print -l /dev/null -10
|
print -l /dev/null -10
|
||||||
<$SCD_HISTFILE \
|
<$SCD_HISTFILE \
|
||||||
awk -v epochseconds=$EPOCHSECONDS -v meanlife=$SCD_MEANLIFE '
|
awk -v epochseconds=$EPOCHSECONDS \
|
||||||
BEGIN { FS = "[:;]"; }
|
-v meanlife=$SCD_MEANLIFE \
|
||||||
length($0) < 4096 && $2 > 0 {
|
-v minlogprob=$MINLOGPROB \
|
||||||
tau = 1.0 * ($2 - epochseconds) / meanlife;
|
'
|
||||||
if (tau < -6.9078) tau = -6.9078;
|
BEGIN {
|
||||||
prob = exp(tau);
|
FS = "[:;]";
|
||||||
sub(/^[^;]*;/, "");
|
pmin = exp(minlogprob);
|
||||||
if (NF) ptot[$0] += prob;
|
|
||||||
}
|
}
|
||||||
END { for (di in ptot) { print di; print ptot[di]; } }'
|
/^: deleted:0;/ {
|
||||||
|
df = $0;
|
||||||
|
sub("^[^;]*;", "", df);
|
||||||
|
delete ptot[df];
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
length($0) < 4096 && $2 > 0 {
|
||||||
|
df = $0;
|
||||||
|
sub("^[^;]*;", "", df);
|
||||||
|
if (!df) next;
|
||||||
|
dp = df;
|
||||||
|
while (!(dp in ptot)) {
|
||||||
|
ptot[dp] = pmin;
|
||||||
|
sub("//*[^/]*$", "", dp);
|
||||||
|
if (!dp) break;
|
||||||
|
}
|
||||||
|
if ($2 <= 1000) next;
|
||||||
|
tau = 1.0 * ($2 - epochseconds) / meanlife;
|
||||||
|
prob = (tau < minlogprob) ? pmin : exp(tau);
|
||||||
|
ptot[df] += prob;
|
||||||
|
}
|
||||||
|
END { for (di in ptot) { print di; print ptot[di]; } }
|
||||||
|
'
|
||||||
)"}
|
)"}
|
||||||
)
|
)
|
||||||
unset "drank[/dev/null]"
|
unset "drank[/dev/null]"
|
||||||
|
|
||||||
# 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="(#l)*(${a})*"
|
||||||
drank=( ${(kv)drank[(I)${~p}]} )
|
drank=( ${(kv)drank[(I)${~p}]} )
|
||||||
done
|
done
|
||||||
# require at least one argument matches the directory name
|
# require that at least one argument matches in directory tail name.
|
||||||
p=${ICASE}"*(${(j:|:)argv})[^/]#"
|
p="(#l)*(${(j:|:)argv})[^/]#"
|
||||||
drank=( ${(kv)drank[(I)${~p}]} )
|
drank=( ${(kv)drank[(I)${~p}]} )
|
||||||
|
|
||||||
|
# discard ignored directories
|
||||||
|
if [[ -z ${opt_all} ]]; then
|
||||||
|
for d in ${(k)drank}; do
|
||||||
|
[[ -z ${scdignore[(k)$d]} ]] || unset "drank[$d]"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
# 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)"$(
|
||||||
for d p in ${(kv)drank}; do
|
builtin printf "%s %s\n" ${(Oakv)drank} |
|
||||||
print -r -- "$p $d";
|
/usr/bin/sort -grk1 )"}
|
||||||
done | sort -grk1 | cut -d ' ' -f 2-
|
|
||||||
)"}
|
|
||||||
)
|
)
|
||||||
|
dmatching=( ${dmatching#*[[:blank:]]} )
|
||||||
|
|
||||||
# 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
|
||||||
|
@ -320,12 +455,20 @@ _scd_Y19oug_match() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# keep at most SCD_MENUSIZE of matching and valid directories
|
# keep at most SCD_MENUSIZE of matching and valid directories
|
||||||
|
# mark up any deleted entries in the index
|
||||||
|
local -A isdeleted
|
||||||
m=( )
|
m=( )
|
||||||
|
isdeleted=( )
|
||||||
for d in $dmatching; do
|
for d in $dmatching; do
|
||||||
[[ ${#m} == $SCD_MENUSIZE ]] && break
|
[[ ${#m} == $SCD_MENUSIZE ]] && break
|
||||||
[[ -d $d && -x $d ]] && m+=$d
|
(( ${+isdeleted[$d]} == 0 )) || continue
|
||||||
|
[[ -d $d ]] || { isdeleted[$d]=1; continue }
|
||||||
|
[[ -x $d ]] && m+=$d
|
||||||
done
|
done
|
||||||
dmatching=( $m )
|
dmatching=( $m )
|
||||||
|
if [[ -n ${isdeleted} ]]; then
|
||||||
|
print -lr -- ": deleted:0;"${^${(k)isdeleted}} >> $SCD_HISTFILE
|
||||||
|
fi
|
||||||
|
|
||||||
# find the maximum rank
|
# find the maximum rank
|
||||||
maxrank=0.0
|
maxrank=0.0
|
||||||
|
@ -343,7 +486,7 @@ _scd_Y19oug_match() {
|
||||||
|
|
||||||
_scd_Y19oug_match $*
|
_scd_Y19oug_match $*
|
||||||
|
|
||||||
## process whatever directories that remained
|
## process matching directories.
|
||||||
if [[ ${#dmatching} == 0 ]]; then
|
if [[ ${#dmatching} == 0 ]]; then
|
||||||
print -u2 "No matching directory."
|
print -u2 "No matching directory."
|
||||||
$EXIT 1
|
$EXIT 1
|
||||||
|
@ -367,13 +510,13 @@ if [[ -n $opt_list ]]; then
|
||||||
$EXIT
|
$EXIT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
## process single directory match
|
## handle a single matching directory here.
|
||||||
if [[ ${#dmatching} == 1 ]]; then
|
if [[ ${#dmatching} == 1 ]]; then
|
||||||
_scd_Y19oug_action $dmatching
|
_scd_Y19oug_action $dmatching
|
||||||
$EXIT $?
|
$EXIT $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
## here we have multiple matches - display selection menu
|
## Here we have multiple matches. Let's use the selection menu.
|
||||||
a=( {a-z} {A-Z} )
|
a=( {a-z} {A-Z} )
|
||||||
a=( ${a[1,${#dmatching}]} )
|
a=( ${a[1,${#dmatching}]} )
|
||||||
p=( )
|
p=( )
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
## The scd script should autoload as a shell function.
|
## The scd script should autoload as a shell function.
|
||||||
autoload scd
|
autoload -Uz scd
|
||||||
|
|
||||||
|
|
||||||
## If the scd function exists, define a change-directory-hook function
|
## If the scd function exists, define a change-directory-hook function
|
||||||
## to record visited directories in the scd index.
|
## to record visited directories in the scd index.
|
||||||
if [[ ${+functions[scd]} == 1 ]]; then
|
if [[ ${+functions[scd]} == 1 ]]; then
|
||||||
scd_chpwd_hook() { scd --add $PWD }
|
chpwd_scd() { scd --add $PWD }
|
||||||
autoload add-zsh-hook
|
autoload -Uz add-zsh-hook
|
||||||
add-zsh-hook chpwd scd_chpwd_hook
|
add-zsh-hook chpwd chpwd_scd
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
## Allow scd usage with unquoted wildcard characters such as "*" or "?".
|
|
||||||
alias scd='noglob scd'
|
|
||||||
|
|
||||||
|
|
||||||
## Load the directory aliases created by scd if any.
|
## Load the directory aliases created by scd if any.
|
||||||
if [[ -s ~/.scdalias.zsh ]]; then source ~/.scdalias.zsh; fi
|
if [[ -s ~/.scdalias.zsh ]]; then
|
||||||
|
source ~/.scdalias.zsh
|
||||||
|
fi
|
||||||
|
|
Loading…
Reference in a new issue