summaryrefslogtreecommitdiff
path: root/oh-my-zsh/plugins/scd
diff options
context:
space:
mode:
authorAditya <bluenerd@protonmail.com>2023-02-27 20:04:56 +0530
committerAditya <bluenerd@protonmail.com>2023-02-27 20:04:56 +0530
commitedc449275b6c04445f58b108ca0937a87c1e8430 (patch)
tree9fd484d58145b616f29a78857cc0b1c8b1c18f05 /oh-my-zsh/plugins/scd
parent6f5424ca96c4221ef433f545642669e9c104d0ed (diff)
add zsh
Diffstat (limited to 'oh-my-zsh/plugins/scd')
-rw-r--r--oh-my-zsh/plugins/scd/README.md159
-rw-r--r--oh-my-zsh/plugins/scd/_scd60
-rwxr-xr-xoh-my-zsh/plugins/scd/scd533
-rw-r--r--oh-my-zsh/plugins/scd/scd.plugin.zsh17
4 files changed, 769 insertions, 0 deletions
diff --git a/oh-my-zsh/plugins/scd/README.md b/oh-my-zsh/plugins/scd/README.md
new file mode 100644
index 0000000..d8535f9
--- /dev/null
+++ b/oh-my-zsh/plugins/scd/README.md
@@ -0,0 +1,159 @@
+# scd - smart change of directory
+
+Define `scd` shell function for changing to any directory with
+a few keystrokes.
+
+`scd` keeps history of the visited directories, which serves as an index of
+the known paths. The directory index is updated after every `cd` command in
+the shell and can be also filled manually by running `scd -a`. To switch to
+some directory, `scd` needs few fragments of the desired path to match with
+the index. A selection menu is displayed in case of several matches, with a
+preference given to recently visited paths. `scd` can create permanent
+directory aliases, which appear as named directories in zsh session.
+
+## INSTALLATION NOTES
+
+Besides oh-my-zsh, `scd` can be used with *bash*, *dash* or *tcsh*
+shells and is also available as Vim plugin
+[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.
+
+## SYNOPSIS
+
+```sh
+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
+
+<dl><dt>
+-a, --add</dt><dd>
+ add current or specified directories to the directory index.</dd><dt>
+
+--unindex</dt><dd>
+ remove current or specified directories from the index.</dd><dt>
+
+-r, --recursive</dt><dd>
+ apply options <em>--add</em> or <em>--unindex</em> recursively.</dd><dt>
+
+--alias=ALIAS</dt><dd>
+ create alias for the current or specified directory and save it to
+ <em>~/.scdalias.zsh</em>.</dd><dt>
+
+--unalias</dt><dd>
+ remove ALIAS definition for the current or specified directory from
+ <em>~/.scdalias.zsh</em>. Use "OLD" to purge aliases to non-existent
+ directories.</dd><dt>
+
+-A, --all</dt><dd>
+ display all directories even those excluded by patterns in
+ <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>
+ show matching directories and exit.</dd><dt>
+
+-v, --verbose</dt><dd>
+ display directory rank in the selection menu.</dd><dt>
+
+-h, --help</dt><dd>
+ display this options summary and exit.</dd>
+</dl>
+
+
+## Examples
+
+```sh
+# Index recursively some paths for the very first run
+scd -ar ~/Documents/
+
+# Change to a directory path matching "doc"
+scd doc
+
+# Change to a path matching all of "a", "b" and "c"
+scd a b c
+
+# Change to a directory path that ends with "ts"
+scd "ts$"
+
+# Show selection menu and ranking of 20 most likely directories
+scd -v
+
+# Alias current directory as "xray"
+scd --alias=xray
+
+# Jump to a previously defined aliased directory
+scd xray
+```
+
+## FILES
+
+<dl><dt>
+~/.scdhistory</dt><dd>
+ time-stamped index of visited directories.</dd><dt>
+
+~/.scdalias.zsh</dt><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>
+
+
+## ENVIRONMENT
+
+<dl><dt>
+SCD_HISTFILE</dt><dd>
+ path to the scd index file (by default ~/.scdhistory).</dd><dt>
+
+SCD_HISTSIZE</dt><dd>
+ maximum number of entries in the index (5000). Index is trimmed when it
+ exceeds <em>SCD_HISTSIZE</em> by more than 20%.</dd><dt>
+
+SCD_MENUSIZE</dt><dd>
+ maximum number of items for directory selection menu (20).</dd><dt>
+
+SCD_MEANLIFE</dt><dd>
+ mean lifetime in seconds for exponential decay of directory
+ likelihood (86400).</dd><dt>
+
+SCD_THRESHOLD</dt><dd>
+ threshold for cumulative directory likelihood. Directories with
+ a lower likelihood compared to the best match are excluded (0.005).
+ </dd><dt>
+
+SCD_SCRIPT</dt><dd>
+ command script file where scd writes the final <code>cd</code>
+ command. This variable must be defined when scd runs in its own
+ process rather than as a shell function. It is up to the
+ scd caller to use the output in <em>SCD_SCRIPT</em>.</dd>
+</dl>
diff --git a/oh-my-zsh/plugins/scd/_scd b/oh-my-zsh/plugins/scd/_scd
new file mode 100644
index 0000000..39c7fa4
--- /dev/null
+++ b/oh-my-zsh/plugins/scd/_scd
@@ -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
diff --git a/oh-my-zsh/plugins/scd/scd b/oh-my-zsh/plugins/scd/scd
new file mode 100755
index 0000000..7e9654b
--- /dev/null
+++ b/oh-my-zsh/plugins/scd/scd
@@ -0,0 +1,533 @@
+#!/bin/zsh -f
+
+emulate -L zsh
+
+local RUNNING_AS_COMMAND=
+local EXIT=return
+if [[ $(whence -w $0) == *:' 'command ]]; then
+ RUNNING_AS_COMMAND=1
+ EXIT=exit
+fi
+
+local DOC='scd -- smart change to a recently used directory
+usage: scd [options] [pattern1 pattern2 ...]
+Go to a directory path that matches all patterns. Prefer recent or
+frequently visited directories as found in the directory index.
+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:
+ -a, --add add current or specified directories to the index.
+ --unindex remove current or specified directories from the index.
+ -r, --recursive apply options --add or --unindex recursively.
+ --alias=ALIAS create alias for the current or specified directory and
+ store it in ~/.scdalias.zsh.
+ --unalias remove ALIAS definition for the current or specified
+ directory from ~/.scdalias.zsh.
+ Use "OLD" to purge aliases to non-existent directories.
+ -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.
+ -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_HISTSIZE=${SCD_HISTSIZE:-5000}
+local SCD_MENUSIZE=${SCD_MENUSIZE:-20}
+local SCD_MEANLIFE=${SCD_MEANLIFE:-86400}
+local SCD_THRESHOLD=${SCD_THRESHOLD:-0.005}
+local SCD_SCRIPT=${RUNNING_AS_COMMAND:+$SCD_SCRIPT}
+local SCD_ALIAS=~/.scdalias.zsh
+local SCD_IGNORE=~/.scdignore
+
+# 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_alias opt_unalias opt_all opt_push opt_list
+local -A drank dalias scdignore
+local dmatching
+local last_directory
+
+setopt extendedglob noautonamedirs brace_ccl
+
+# If SCD_SCRIPT is defined make sure that that file exists and is empty.
+# This removes any old previous commands from the SCD_SCRIPT file.
+[[ -n "$SCD_SCRIPT" ]] && [[ -s $SCD_SCRIPT || ! -f $SCD_SCRIPT ]] && (
+ umask 077
+ : >| $SCD_SCRIPT
+)
+
+# process command line options
+zmodload -i zsh/zutil
+zmodload -i zsh/datetime
+zmodload -i zsh/parameter
+zparseopts -D -E -- a=opt_add -add=opt_add -unindex=opt_unindex \
+ r=opt_recursive -recursive=opt_recursive \
+ -alias:=opt_alias -unalias=opt_unalias \
+ 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 \
+ || $EXIT $?
+
+# remove the first instance of "--" from positional arguments
+argv[(i)--]=( )
+
+if [[ -n $opt_help ]]; then
+ print $DOC
+ $EXIT
+fi
+
+# load directory aliases if they exist
+[[ -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_.
+# 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
+_scd_Y19oug_abspath() {
+ set -A $1 ${(ps:\0:)"$(
+ setopt pushdsilent
+ unfunction -m "*"
+ unalias -m "*"
+ unset CDPATH
+ shift
+ for d; do
+ pushd $d || continue
+ $PWDCASECORRECT &&
+ print -Nr -- $PWD ||
+ print -Nr -- (#i)$PWD
+ popd 2>/dev/null
+ done
+ )"}
+}
+
+# define directory alias
+if [[ -n $opt_alias ]]; then
+ if [[ -n $1 && ! -d $1 ]]; then
+ print -u2 "'$1' is not a directory."
+ $EXIT 1
+ fi
+ a=${opt_alias[-1]#=}
+ _scd_Y19oug_abspath d ${1:-$PWD}
+ # alias in the current shell, update alias file if successful
+ hash -d -- $a=$d &&
+ (
+ umask 077
+ hash -dr
+ [[ -r $SCD_ALIAS ]] && source $SCD_ALIAS
+ hash -d -- $a=$d
+ hash -dL >| $SCD_ALIAS
+ )
+ $EXIT $?
+fi
+
+# undefine one or more directory aliases
+if [[ -n $opt_unalias ]]; then
+ local -U uu
+ local ec=0
+ 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
+ m=( )
+ for p in $uu; do
+ d=$p
+ if [[ ${+nameddirs[$d]} == 0 && -d $d ]]; then
+ _scd_Y19oug_abspath d $d
+ fi
+ a=${(k)nameddirs[$d]:-${(k)nameddirs[(r)$d]}}
+ 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
+ hash -dr
+ source $SCD_ALIAS
+ for a in $m; do
+ unhash -d -- $a 2>/dev/null
+ done
+ hash -dL >| $SCD_ALIAS
+ ) || ec=$?
+ fi
+ $EXIT $ec
+fi
+
+# The "compress" function collapses repeated directories into
+# a single entry with a time-stamp yielding an equivalent probability.
+_scd_Y19oug_compress() {
+ awk -v epochseconds=$EPOCHSECONDS \
+ -v meanlife=$SCD_MEANLIFE \
+ -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;
+ prob = (tau < minlogprob) ? pmin : exp(tau);
+ dlist[last[df]] = "";
+ dlist[NR] = df;
+ last[df] = NR;
+ ptot[df] += 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.
+local curhistsize
+if [[ -z $opt_unindex && -s $SCD_HISTFILE ]] && \
+curhistsize=$(wc -l <$SCD_HISTFILE) && \
+(( $curhistsize > 1.2 * $SCD_HISTSIZE )); then
+ # Compress repeated entries in a background process.
+ (
+ m=( ${(f)"$(_scd_Y19oug_compress $SCD_HISTFILE)"} )
+ # purge non-existent and ignored directories
+ m=( ${(f)"$(
+ for a in $m; do
+ d=${a#*;}
+ [[ -z ${scdignore[(k)$d]} ]] || continue
+ [[ -d $d ]] || continue
+ $PWDCASECORRECT || d=( (#i)${d} )
+ t=${a%%;*}
+ 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
+
+# Determine the last recorded directory
+if [[ -s ${SCD_HISTFILE} ]]; then
+ last_directory=${"$(tail -n 1 ${SCD_HISTFILE})"#*;}
+fi
+
+# The "record" function adds its arguments to the directory index.
+_scd_Y19oug_record() {
+ while [[ -n $last_directory && $1 == $last_directory ]]; do
+ shift
+ done
+ if [[ $# -gt 0 ]]; then
+ ( umask 077
+ p=": ${EPOCHSECONDS}:0;"
+ print -lr -- ${p}${^*} >>| $SCD_HISTFILE )
+ fi
+}
+
+if [[ -n $opt_add ]]; then
+ m=( ${^${argv:-$PWD}}(N-/) )
+ _scd_Y19oug_abspath m ${m}
+ _scd_Y19oug_record $m
+ if [[ -n $opt_recursive ]]; then
+ for d in $m; do
+ print -n "scanning ${d} ... "
+ _scd_Y19oug_record ${d}/**/*(-/N)
+ print "[done]"
+ done
+ fi
+ $EXIT
+fi
+
+# take care of removing entries from the directory index
+if [[ -n $opt_unindex ]]; then
+ if [[ ! -s $SCD_HISTFILE ]]; then
+ $EXIT
+ fi
+ argv=( ${argv:-$PWD} )
+ # expand existing directories in the argument list
+ for i in {1..$#}; do
+ if [[ -d ${argv[i]} ]]; then
+ _scd_Y19oug_abspath d ${argv[i]}
+ argv[i]=${d}
+ fi
+ done
+ # strip trailing slashes, but preserve the root path
+ argv=( ${argv/(#m)?\/##(#e)/${MATCH[1]}} )
+ m="$(awk -v recursive=${opt_recursive} '
+ BEGIN {
+ for (i = 2; i < ARGC; ++i) {
+ argset[ARGV[i]] = 1;
+ delete ARGV[i];
+ }
+ unindex_root = ("/" in argset);
+ }
+ 1 {
+ d = $0; sub(/^[^;]*;/, "", d);
+ if (d in argset) next;
+ }
+ recursive {
+ if (unindex_root) exit;
+ for (a in argset) {
+ if (substr(d, 1, length(a) + 1) == a"/") next;
+ }
+ }
+ { print $0 }
+ ' $SCD_HISTFILE $* )" || $EXIT $?
+ : >| ${SCD_HISTFILE}
+ [[ ${#m} == 0 ]] || print -r -- $m >> ${SCD_HISTFILE}
+ $EXIT
+fi
+
+# The "action" function is called when there is just one target directory.
+_scd_Y19oug_action() {
+ local cdcmd=cd
+ [[ -z ${opt_push} ]] || cdcmd=pushd
+ builtin $cdcmd $1 || return $?
+ if [[ -z $SCD_SCRIPT && -n $RUNNING_AS_COMMAND ]]; then
+ print -u2 "Warning: running as command with SCD_SCRIPT undefined."
+ fi
+ if [[ -n $SCD_SCRIPT ]]; then
+ local d=$1
+ if [[ $OSTYPE == cygwin && ${(L)SCD_SCRIPT} == *.bat ]]; then
+ d=$(cygpath -aw .)
+ fi
+ print -r "${cdcmd} ${(qqq)d}" >| $SCD_SCRIPT
+ fi
+}
+
+# Select and order indexed directories by matching command-line patterns.
+# Set global arrays dmatching and drank.
+_scd_Y19oug_match() {
+ ## single argument that is an existing directory or directory alias
+ if [[ -z $opt_all && $# == 1 ]] && \
+ [[ -d ${d::=${nameddirs[$1]}} || -d ${d::=$1} ]] && [[ -x $d ]];
+ then
+ _scd_Y19oug_abspath dmatching $d
+ drank[${dmatching[1]}]=1
+ return
+ fi
+
+ # quote brackets when PWD is /Volumes/[C]/
+ local qpwd=${PWD//(#m)[][]/\\${MATCH}}
+
+ # 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)} )
+
+ # support prefix ":" to match over the tail component.
+ 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)"$(
+ print -l /dev/null -10
+ <$SCD_HISTFILE \
+ awk -v epochseconds=$EPOCHSECONDS \
+ -v meanlife=$SCD_MEANLIFE \
+ -v minlogprob=$MINLOGPROB \
+ '
+ BEGIN {
+ FS = "[:;]";
+ pmin = exp(minlogprob);
+ }
+ /^: 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]"
+
+ # filter drank to the entries that match all arguments
+ for a; do
+ p="(#l)*(${a})*"
+ drank=( ${(kv)drank[(I)${~p}]} )
+ done
+ # require that at least one argument matches in directory tail name.
+ p="(#l)*(${(j:|:)argv})[^/]#"
+ 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
+ dmatching=( ${(f)"$(
+ builtin printf "%s %s\n" ${(Oakv)drank} |
+ /usr/bin/sort -grk1 )"}
+ )
+ dmatching=( ${dmatching#*[[:blank:]]} )
+
+ # do not match $HOME or $PWD when run without arguments
+ if [[ $# == 0 ]]; then
+ dmatching=( ${dmatching:#(${HOME}|${PWD})} )
+ fi
+
+ # keep at most SCD_MENUSIZE of matching and valid directories
+ # mark up any deleted entries in the index
+ local -A isdeleted
+ m=( )
+ isdeleted=( )
+ for d in $dmatching; do
+ [[ ${#m} == $SCD_MENUSIZE ]] && break
+ (( ${+isdeleted[$d]} == 0 )) || continue
+ [[ -d $d ]] || { isdeleted[$d]=1; continue }
+ [[ -x $d ]] && m+=$d
+ done
+ dmatching=( $m )
+ if [[ -n ${isdeleted} ]]; then
+ print -lr -- ": deleted:0;"${^${(k)isdeleted}} >> $SCD_HISTFILE
+ fi
+
+ # find the maximum rank
+ maxrank=0.0
+ for d in $dmatching; do
+ [[ ${drank[$d]} -lt maxrank ]] || maxrank=${drank[$d]}
+ done
+
+ # discard all directories below the rank threshold
+ threshold=$(( maxrank * SCD_THRESHOLD ))
+ if [[ -n ${opt_all} ]]; then
+ threshold=0
+ fi
+ dmatching=( ${^dmatching}(Ne:'(( ${drank[$REPLY]} >= threshold ))':) )
+}
+
+_scd_Y19oug_match $*
+
+## process matching directories.
+if [[ ${#dmatching} == 0 ]]; then
+ print -u2 "No matching directory."
+ $EXIT 1
+fi
+
+## build formatted directory aliases for selection menu or list display
+for d in $dmatching; do
+ if [[ -n ${opt_verbose} ]]; then
+ dalias[$d]=$(printf "%.3g %s" ${drank[$d]} $d)
+ else
+ dalias[$d]=$(print -Dr -- $d)
+ fi
+done
+
+## process the --list option
+if [[ -n $opt_list ]]; then
+ for d in $dmatching; do
+ print -r -- "# ${dalias[$d]}"
+ print -r -- $d
+ done
+ $EXIT
+fi
+
+## handle a single matching directory here.
+if [[ ${#dmatching} == 1 ]]; then
+ _scd_Y19oug_action $dmatching
+ $EXIT $?
+fi
+
+## Here we have multiple matches. Let's use the selection menu.
+a=( {a-z} {A-Z} )
+a=( ${a[1,${#dmatching}]} )
+p=( )
+for i in {1..${#dmatching}}; do
+ [[ -n ${a[i]} ]] || break
+ p+="${a[i]}) ${dalias[${dmatching[i]}]}"
+done
+
+print -c -r -- $p
+
+if read -s -k 1 d && [[ ${i::=${a[(I)$d]}} -gt 0 ]]; then
+ _scd_Y19oug_action ${dmatching[i]}
+ $EXIT $?
+fi
diff --git a/oh-my-zsh/plugins/scd/scd.plugin.zsh b/oh-my-zsh/plugins/scd/scd.plugin.zsh
new file mode 100644
index 0000000..1a6c186
--- /dev/null
+++ b/oh-my-zsh/plugins/scd/scd.plugin.zsh
@@ -0,0 +1,17 @@
+## The scd script should autoload as a shell function.
+autoload -Uz scd
+
+
+## If the scd function exists, define a change-directory-hook function
+## to record visited directories in the scd index.
+if [[ ${+functions[scd]} == 1 ]]; then
+ chpwd_scd() { scd --add $PWD }
+ autoload -Uz add-zsh-hook
+ add-zsh-hook chpwd chpwd_scd
+fi
+
+
+## Load the directory aliases created by scd if any.
+if [[ -s ~/.scdalias.zsh ]]; then
+ source ~/.scdalias.zsh
+fi