浏览代码

Update to the upstream smart-change-directory.

Synced with the master branch
pavoljuhas/smart-change-directory@20b5a80cf05f72c07e88a9b166593f52517029e4.
Pavol Juhas 10 年之前
父节点
当前提交
b6012bc2c3
共有 2 个文件被更改,包括 86 次插入48 次删除
  1. 8 7
      plugins/scd/README.md
  2. 78 41
      plugins/scd/scd

+ 8 - 7
plugins/scd/README.md

@@ -11,12 +11,9 @@ the index.  A selection menu is displayed in case of several matches, with a
 preference given to recently visited paths.  `scd` can create permanent
 directory aliases, which appear as named directories in zsh session.
 
-## INSTALLATION
+## INSTALLATION NOTES
 
-For oh-my-zsh, add `scd` to the `plugins` array in the ~/.zshrc file as in the
-[template file](../../templates/zshrc.zsh-template#L45).
-
-Besides 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](http://www.vim.org/) plugin and
 [IPython](http://ipython.org/) extension.  For installation details, see
 https://github.com/pavoljuhas/smart-change-directory.
@@ -34,7 +31,7 @@ scd [options] [pattern1 pattern2 ...]
   add specified directories to the directory index.</dd><dt>
 
 --unindex</dt><dd>
-  remove specified directories from the index.</dd><dt>
+  remove current or specified directories from the index.</dd><dt>
 
 -r, --recursive</dt><dd>
   apply options <em>--add</em> or <em>--unindex</em> recursively.</dd><dt>
@@ -47,6 +44,10 @@ scd [options] [pattern1 pattern2 ...]
   remove ALIAS definition for the current or specified directory from
   <em>~/.scdalias.zsh</em>.</dd><dt>
 
+-A, --all</dt><dd>
+  include all matching directories.  Disregard matching by directory
+  alias and filtering of less likely paths.</dd><dt>
+
 --list</dt><dd>
   show matching directories and exit.</dd><dt>
 
@@ -70,7 +71,7 @@ scd doc
 scd a b c
 
 # Change to a directory path that ends with "ts"
-scd "ts(#e)"
+scd "ts$"
 
 # Show selection menu and ranking of 20 most likely directories
 scd -v

+ 78 - 41
plugins/scd/scd

@@ -11,20 +11,22 @@ 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
-recently visited directories and directories with patterns in their tail
-component.  Display a selection menu in case of multiple matches.
+recent or frequently visited directories as found in the directory index.
+Display a selection menu in case of multiple matches.
 
 Options:
-  -a, --add         add specified directories to the directory index
-  --unindex         remove specified directories from the index
-  -r, --recursive   apply options --add or --unindex recursively
+  -a, --add         add specified directories to the directory 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
+                    store it in ~/.scdalias.zsh.
   --unalias         remove ALIAS definition for the current or specified
-                    directory from ~/.scdalias.zsh
-  --list            show matching directories and exit
-  -v, --verbose     display directory rank in the selection menu
-  -h, --help        display this message and exit
+                    directory from ~/.scdalias.zsh.
+  -A, --all         include all matching directories.  Disregard matching by
+                    directory alias and filtering of less likely paths.
+  --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}
@@ -35,9 +37,9 @@ local SCD_THRESHOLD=${SCD_THRESHOLD:-0.005}
 local SCD_SCRIPT=${RUNNING_AS_COMMAND:+$SCD_SCRIPT}
 local SCD_ALIAS=~/.scdalias.zsh
 
-local ICASE a d m p i tdir maxrank threshold
+local ICASE a d m p i maxrank threshold
 local opt_help opt_add opt_unindex opt_recursive opt_verbose
-local opt_alias opt_unalias opt_list
+local opt_alias opt_unalias opt_all opt_list
 local -A drank dalias
 local dmatching
 local last_directory
@@ -56,7 +58,8 @@ zmodload -i zsh/zutil
 zmodload -i zsh/datetime
 zparseopts -D -- a=opt_add -add=opt_add -unindex=opt_unindex \
     r=opt_recursive -recursive=opt_recursive \
-    -alias:=opt_alias -unalias=opt_unalias -list=opt_list \
+    -alias:=opt_alias -unalias=opt_unalias \
+    A=opt_all -all=opt_all -list=opt_list \
     v=opt_verbose -verbose=opt_verbose h=opt_help -help=opt_help \
     || $EXIT $?
 
@@ -68,6 +71,11 @@ fi
 # load directory aliases if they exist
 [[ -r $SCD_ALIAS ]] && source $SCD_ALIAS
 
+# Private internal functions are prefixed with _scd_Y19oug_.
+# Clean them up when the scd function returns.
+setopt localtraps
+trap 'unfunction -m "_scd_Y19oug_*"' EXIT
+
 # works faster than the (:a) modifier and is compatible with zsh 4.2.6
 _scd_Y19oug_abspath() {
     set -A $1 ${(ps:\0:)"$(
@@ -123,11 +131,52 @@ if [[ -n $opt_unalias ]]; then
     $EXIT $?
 fi
 
+# The "compress" function collapses repeated directories to
+# one entry with a time stamp that gives equivalent-probability.
+_scd_Y19oug_compress() {
+    awk -v epochseconds=$EPOCHSECONDS -v meanlife=$SCD_MEANLIFE '
+        BEGIN { FS = "[:;]"; }
+        length($0) < 4096 && $2 > 0 {
+            tau = 1.0 * ($2 - epochseconds) / meanlife;
+            if (tau < -6.9078)  tau = -6.9078;
+            prob = exp(tau);
+            sub(/^[^;]*;/, "");
+            if (NF)  {
+                dlist[last[$0]] = "";
+                dlist[NR] = $0;
+                last[$0] = NR;
+                ptot[$0] += prob;
+            }
+        }
+        END {
+            for (i = 1; i <= NR; ++i) {
+                d = dlist[i];
+                if (d) {
+                    ts = log(ptot[d]) * meanlife + epochseconds;
+                    printf(": %.0f:0;%s\n", ts, d);
+                }
+            }
+        }
+    ' $*
+}
+
 # Rewrite directory index if it is at least 20% oversized
 if [[ -s $SCD_HISTFILE ]] && \
 (( $(wc -l <$SCD_HISTFILE) > 1.2 * $SCD_HISTSIZE )); then
-    m=( ${(f)"$(<$SCD_HISTFILE)"} )
-    print -lr -- ${m[-$SCD_HISTSIZE,-1]} >| ${SCD_HISTFILE}
+    # 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}
 fi
 
 # Determine the last recorded directory
@@ -135,7 +184,6 @@ if [[ -s ${SCD_HISTFILE} ]]; then
     last_directory=${"$(tail -1 ${SCD_HISTFILE})"#*;}
 fi
 
-# Internal functions are prefixed with "_scd_Y19oug_".
 # The "record" function adds its arguments to the directory index.
 _scd_Y19oug_record() {
     while [[ -n $last_directory && $1 == $last_directory ]]; do
@@ -217,7 +265,7 @@ _scd_Y19oug_action() {
 # set global arrays dmatching and drank
 _scd_Y19oug_match() {
     ## single argument that is an existing directory or directory alias
-    if [[ $# == 1 ]] && \
+    if [[ -z $opt_all && $# == 1 ]] && \
         [[ -d ${d::=$1} || -d ${d::=${nameddirs[$1]}} ]] && [[ -x $d ]];
     then
         _scd_Y19oug_abspath dmatching $d
@@ -227,6 +275,8 @@ _scd_Y19oug_match() {
 
     # ignore case unless there is an argument with an uppercase letter
     [[ "$*" == *[[:upper:]]* ]] || ICASE='(#i)'
+    # support "$" as an anchor for the directory name ending
+    argv=( ${argv/(#m)?[$](#e)/${MATCH[1]}(#e)} )
 
     # calculate rank of all directories in the SCD_HISTFILE and keep it as drank
     # include a dummy entry for splitting of an empty string is buggy
@@ -237,10 +287,10 @@ _scd_Y19oug_match() {
             BEGIN { FS = "[:;]"; }
             length($0) < 4096 && $2 > 0 {
                 tau = 1.0 * ($2 - epochseconds) / meanlife;
-                if (tau < -4.61)  tau = -4.61;
-                prec = exp(tau);
+                if (tau < -6.9078)  tau = -6.9078;
+                prob = exp(tau);
                 sub(/^[^;]*;/, "");
-                if (NF)  ptot[$0] += prec;
+                if (NF)  ptot[$0] += prob;
             }
             END { for (di in ptot)  { print di; print ptot[di]; } }'
         )"}
@@ -249,9 +299,12 @@ _scd_Y19oug_match() {
 
     # filter drank to the entries that match all arguments
     for a; do
-        p=${ICASE}"*${a}*"
+        p=${ICASE}"*(${a})*"
         drank=( ${(kv)drank[(I)${~p}]} )
     done
+    # require at least one argument matches the directory name
+    p=${ICASE}"*(${(j:|:)argv})[^/]#"
+    drank=( ${(kv)drank[(I)${~p}]} )
 
     # build a list of matching directories reverse-sorted by their probabilities
     dmatching=( ${(f)"$(
@@ -261,26 +314,6 @@ _scd_Y19oug_match() {
         )"}
     )
 
-    # if some directory paths match all patterns in order, discard all others
-    p=${ICASE}"*${(j:*:)argv}*"
-    m=( ${(M)dmatching:#${~p}} )
-    [[ -d ${m[1]} ]] && dmatching=( $m )
-    # if some directory names match last pattern, discard all others
-    p=${ICASE}"*${(j:*:)argv}[^/]#"
-    m=( ${(M)dmatching:#${~p}} )
-    [[ -d ${m[1]} ]] && dmatching=( $m )
-    # if some directory names match all patterns, discard all others
-    m=( $dmatching )
-    for a; do
-        p=${ICASE}"*/[^/]#${a}[^/]#"
-        m=( ${(M)m:#${~p}} )
-    done
-    [[ -d ${m[1]} ]] && dmatching=( $m )
-    # if some directory names match all patterns in order, discard all others
-    p=${ICASE}"/*${(j:[^/]#:)argv}[^/]#"
-    m=( ${(M)dmatching:#${~p}} )
-    [[ -d ${m[1]} ]] && dmatching=( $m )
-
     # do not match $HOME or $PWD when run without arguments
     if [[ $# == 0 ]]; then
         dmatching=( ${dmatching:#(${HOME}|${PWD})} )
@@ -302,6 +335,9 @@ _scd_Y19oug_match() {
 
     # 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 ))':) )
 }
 
@@ -339,6 +375,7 @@ fi
 
 ## here we have multiple matches - display selection menu
 a=( {a-z} {A-Z} )
+a=( ${a[1,${#dmatching}]} )
 p=( )
 for i in {1..${#dmatching}}; do
     [[ -n ${a[i]} ]] || break