mirror of
				https://github.com/ohmyzsh/ohmyzsh.git
				synced 2025-11-04 21:31:19 +08:00 
			
		
		
		
	scd: update to 1.4.0 (#9066)
This commit is contained in:
		
							parent
							
								
									cfb86cd08d
								
							
						
					
					
						commit
						8d08f1634a
					
				@ -14,8 +14,9 @@ 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](https://www.vim.org/) plugin and
 | 
			
		||||
[IPython](https://ipython.org/) extension.  For installation details, see
 | 
			
		||||
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
 | 
			
		||||
@ -24,11 +25,31 @@ https://github.com/pavoljuhas/smart-change-directory.
 | 
			
		||||
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 specified directories to the directory index.</dd><dt>
 | 
			
		||||
  add current or specified directories to the directory index.</dd><dt>
 | 
			
		||||
 | 
			
		||||
--unindex</dt><dd>
 | 
			
		||||
  remove current or specified directories from the index.</dd><dt>
 | 
			
		||||
@ -42,11 +63,16 @@ scd [options] [pattern1 pattern2 ...]
 | 
			
		||||
 | 
			
		||||
--unalias</dt><dd>
 | 
			
		||||
  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>
 | 
			
		||||
  include all matching directories.  Disregard matching by directory
 | 
			
		||||
  alias and filtering of less likely paths.</dd><dt>
 | 
			
		||||
  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>
 | 
			
		||||
@ -58,6 +84,7 @@ scd [options] [pattern1 pattern2 ...]
 | 
			
		||||
  display this options summary and exit.</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Examples
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
@ -83,17 +110,26 @@ scd --alias=xray
 | 
			
		||||
scd xray
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# FILES
 | 
			
		||||
## 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>
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
## ENVIRONMENT
 | 
			
		||||
 | 
			
		||||
<dl><dt>
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
emulate -L zsh
 | 
			
		||||
 | 
			
		||||
local RUNNING_AS_COMMAND=
 | 
			
		||||
local EXIT=return
 | 
			
		||||
if [[ $(whence -w $0) == *:' 'command ]]; then
 | 
			
		||||
    emulate -R zsh
 | 
			
		||||
    local RUNNING_AS_COMMAND=1
 | 
			
		||||
    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 contains all fixed string patterns.  Prefer
 | 
			
		||||
recent or frequently visited directories as found in the directory index.
 | 
			
		||||
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 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.
 | 
			
		||||
  -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.
 | 
			
		||||
  -A, --all         include all matching directories.  Disregard matching by
 | 
			
		||||
                    directory alias and filtering of less likely paths.
 | 
			
		||||
                    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.
 | 
			
		||||
@ -36,18 +46,28 @@ 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
 | 
			
		||||
 | 
			
		||||
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_alias opt_unalias opt_all opt_list
 | 
			
		||||
local -A drank dalias
 | 
			
		||||
local opt_alias opt_unalias opt_all opt_push opt_list
 | 
			
		||||
local -A drank dalias scdignore
 | 
			
		||||
local dmatching
 | 
			
		||||
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.
 | 
			
		||||
# This removes any previous old commands.
 | 
			
		||||
# 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
 | 
			
		||||
@ -56,13 +76,17 @@ setopt extendedhistory extendedglob noautonamedirs brace_ccl
 | 
			
		||||
# process command line options
 | 
			
		||||
zmodload -i zsh/zutil
 | 
			
		||||
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 \
 | 
			
		||||
    -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 \
 | 
			
		||||
    || $EXIT $?
 | 
			
		||||
 | 
			
		||||
# remove the first instance of "--" from positional arguments
 | 
			
		||||
argv[(i)--]=( )
 | 
			
		||||
 | 
			
		||||
if [[ -n $opt_help ]]; then
 | 
			
		||||
    print $DOC
 | 
			
		||||
    $EXIT
 | 
			
		||||
@ -71,6 +95,22 @@ 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
 | 
			
		||||
@ -79,9 +119,17 @@ 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:)"$(
 | 
			
		||||
        unfunction -m "*"; shift
 | 
			
		||||
        setopt pushdsilent
 | 
			
		||||
        unfunction -m "*"
 | 
			
		||||
        unalias -m "*"
 | 
			
		||||
        unset CDPATH
 | 
			
		||||
        shift
 | 
			
		||||
        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
 | 
			
		||||
        )"}
 | 
			
		||||
}
 | 
			
		||||
@ -106,47 +154,76 @@ if [[ -n $opt_alias ]]; then
 | 
			
		||||
    $EXIT $?
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# undefine directory alias
 | 
			
		||||
# undefine one or more directory aliases
 | 
			
		||||
if [[ -n $opt_unalias ]]; then
 | 
			
		||||
    if [[ -n $1 && ! -d $1 ]]; then
 | 
			
		||||
        print -u2 "'$1' is not a directory."
 | 
			
		||||
        $EXIT 1
 | 
			
		||||
    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
 | 
			
		||||
    _scd_Y19oug_abspath a ${1:-$PWD}
 | 
			
		||||
    a=$(print -rD ${a})
 | 
			
		||||
    if [[ $a != [~][^/]## ]]; then
 | 
			
		||||
        $EXIT
 | 
			
		||||
    fi
 | 
			
		||||
    a=${a#[~]}
 | 
			
		||||
    # unalias in the current shell, update alias file if successful
 | 
			
		||||
    if unhash -d -- $a 2>/dev/null && [[ -r $SCD_ALIAS ]]; then
 | 
			
		||||
    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
 | 
			
		||||
            unhash -d -- $a 2>/dev/null &&
 | 
			
		||||
            for a in $m; do
 | 
			
		||||
                unhash -d -- $a 2>/dev/null
 | 
			
		||||
            done
 | 
			
		||||
            hash -dL >| $SCD_ALIAS
 | 
			
		||||
        )
 | 
			
		||||
        ) || ec=$?
 | 
			
		||||
    fi
 | 
			
		||||
    $EXIT $?
 | 
			
		||||
    $EXIT $ec
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# The "compress" function collapses repeated directories to
 | 
			
		||||
# one entry with a time stamp that gives equivalent-probability.
 | 
			
		||||
# 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 '
 | 
			
		||||
        BEGIN { FS = "[:;]"; }
 | 
			
		||||
        length($0) < 4096 && $2 > 0 {
 | 
			
		||||
    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;
 | 
			
		||||
            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;
 | 
			
		||||
            }
 | 
			
		||||
            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) {
 | 
			
		||||
@ -157,26 +234,38 @@ _scd_Y19oug_compress() {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    ' $*
 | 
			
		||||
        ' $*
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Rewrite directory index if it is at least 20% oversized
 | 
			
		||||
if [[ -s $SCD_HISTFILE ]] && \
 | 
			
		||||
(( $(wc -l <$SCD_HISTFILE) > 1.2 * $SCD_HISTSIZE )); then
 | 
			
		||||
    # compress repeated entries
 | 
			
		||||
    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}
 | 
			
		||||
# 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
 | 
			
		||||
@ -197,13 +286,8 @@ _scd_Y19oug_record() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if [[ -n $opt_add ]]; then
 | 
			
		||||
    for d; do
 | 
			
		||||
        if [[ ! -d $d ]]; then
 | 
			
		||||
            print -u2 "Directory '$d' does not exist."
 | 
			
		||||
            $EXIT 2
 | 
			
		||||
        fi
 | 
			
		||||
    done
 | 
			
		||||
    _scd_Y19oug_abspath m ${*:-$PWD}
 | 
			
		||||
    m=( ${^${argv:-$PWD}}(N-/) )
 | 
			
		||||
    _scd_Y19oug_abspath m ${m}
 | 
			
		||||
    _scd_Y19oug_record $m
 | 
			
		||||
    if [[ -n $opt_recursive ]]; then
 | 
			
		||||
        for d in $m; do
 | 
			
		||||
@ -220,6 +304,7 @@ 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
 | 
			
		||||
@ -227,24 +312,28 @@ if [[ -n $opt_unindex ]]; then
 | 
			
		||||
            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 ${*:-$PWD} )" || $EXIT $?
 | 
			
		||||
        ' $SCD_HISTFILE $* )" || $EXIT $?
 | 
			
		||||
    : >| ${SCD_HISTFILE}
 | 
			
		||||
    [[ ${#m} == 0 ]] || print -r -- $m >> ${SCD_HISTFILE}
 | 
			
		||||
    $EXIT
 | 
			
		||||
@ -252,67 +341,113 @@ fi
 | 
			
		||||
 | 
			
		||||
# The "action" function is called when there is just one target directory.
 | 
			
		||||
_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
 | 
			
		||||
        print -u2 "Warning: running as command with SCD_SCRIPT undefined."
 | 
			
		||||
    fi
 | 
			
		||||
    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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Match and rank patterns to the index file
 | 
			
		||||
# set global arrays dmatching and drank
 | 
			
		||||
# 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::=$1} || -d ${d::=${nameddirs[$1]}} ]] && [[ -x $d ]];
 | 
			
		||||
        [[ -d ${d::=${nameddirs[$1]}} || -d ${d::=$1} ]] && [[ -x $d ]];
 | 
			
		||||
    then
 | 
			
		||||
        _scd_Y19oug_abspath dmatching $d
 | 
			
		||||
        drank[${dmatching[1]}]=1
 | 
			
		||||
        return
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # ignore case unless there is an argument with an uppercase letter
 | 
			
		||||
    [[ "$*" == *[[:upper:]]* ]] || ICASE='(#i)'
 | 
			
		||||
    # support "$" as an anchor for the directory name ending
 | 
			
		||||
    # 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)} )
 | 
			
		||||
 | 
			
		||||
    # 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
 | 
			
		||||
    # 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 '
 | 
			
		||||
            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)  ptot[$0] += prob;
 | 
			
		||||
        awk -v epochseconds=$EPOCHSECONDS \
 | 
			
		||||
            -v meanlife=$SCD_MEANLIFE \
 | 
			
		||||
            -v minlogprob=$MINLOGPROB \
 | 
			
		||||
            '
 | 
			
		||||
            BEGIN {
 | 
			
		||||
                FS = "[:;]";
 | 
			
		||||
                pmin = exp(minlogprob);
 | 
			
		||||
            }
 | 
			
		||||
            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]"
 | 
			
		||||
 | 
			
		||||
    # filter drank to the entries that match all arguments
 | 
			
		||||
    for a; do
 | 
			
		||||
        p=${ICASE}"*(${a})*"
 | 
			
		||||
        p="(#l)*(${a})*"
 | 
			
		||||
        drank=( ${(kv)drank[(I)${~p}]} )
 | 
			
		||||
    done
 | 
			
		||||
    # require at least one argument matches the directory name
 | 
			
		||||
    p=${ICASE}"*(${(j:|:)argv})[^/]#"
 | 
			
		||||
    # 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)"$(
 | 
			
		||||
        for d p in ${(kv)drank}; do
 | 
			
		||||
            print -r -- "$p $d";
 | 
			
		||||
        done | sort -grk1 | cut -d ' ' -f 2-
 | 
			
		||||
        )"}
 | 
			
		||||
        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
 | 
			
		||||
@ -320,12 +455,20 @@ _scd_Y19oug_match() {
 | 
			
		||||
    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
 | 
			
		||||
        [[ -d $d && -x $d ]] && m+=$d
 | 
			
		||||
        (( ${+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
 | 
			
		||||
@ -343,7 +486,7 @@ _scd_Y19oug_match() {
 | 
			
		||||
 | 
			
		||||
_scd_Y19oug_match $*
 | 
			
		||||
 | 
			
		||||
## process whatever directories that remained
 | 
			
		||||
## process matching directories.
 | 
			
		||||
if [[ ${#dmatching} == 0 ]]; then
 | 
			
		||||
    print -u2 "No matching directory."
 | 
			
		||||
    $EXIT 1
 | 
			
		||||
@ -367,13 +510,13 @@ if [[ -n $opt_list ]]; then
 | 
			
		||||
    $EXIT
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
## process single directory match
 | 
			
		||||
## handle a single matching directory here.
 | 
			
		||||
if [[ ${#dmatching} == 1 ]]; then
 | 
			
		||||
    _scd_Y19oug_action $dmatching
 | 
			
		||||
    $EXIT $?
 | 
			
		||||
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[1,${#dmatching}]} )
 | 
			
		||||
p=( )
 | 
			
		||||
 | 
			
		||||
@ -1,19 +1,17 @@
 | 
			
		||||
## 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
 | 
			
		||||
## to record visited directories in the scd index.
 | 
			
		||||
if [[ ${+functions[scd]} == 1 ]]; then
 | 
			
		||||
    scd_chpwd_hook() { scd --add $PWD }
 | 
			
		||||
    autoload add-zsh-hook
 | 
			
		||||
    add-zsh-hook chpwd scd_chpwd_hook
 | 
			
		||||
    chpwd_scd() { scd --add $PWD }
 | 
			
		||||
    autoload -Uz add-zsh-hook
 | 
			
		||||
    add-zsh-hook chpwd chpwd_scd
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Allow scd usage with unquoted wildcard characters such as "*" or "?".
 | 
			
		||||
alias scd='noglob scd'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user