Browse Source

Merge pull request #1688 from mbauhardt/z

ohmyzsh plugin of the z project: https://github.com/rupa/z
Robby Russell 11 years ago
parent
commit
90c28b786a
5 changed files with 528 additions and 0 deletions
  1. 4 0
      plugins/z/Makefile
  2. 135 0
      plugins/z/README
  3. 155 0
      plugins/z/z.1
  4. 6 0
      plugins/z/z.plugin.zsh
  5. 228 0
      plugins/z/z.sh

+ 4 - 0
plugins/z/Makefile

@@ -0,0 +1,4 @@
+readme:
+	@groff -man -Tascii z.1 | col -bx
+
+.PHONY: readme

+ 135 - 0
plugins/z/README

@@ -0,0 +1,135 @@
+Z(1)                             User Commands                            Z(1)
+
+
+
+NAME
+       z - jump around
+
+SYNOPSIS
+       z [-chlrt] [regex1 regex2 ... regexn]
+
+AVAILABILITY
+       bash, zsh
+
+DESCRIPTION
+       Tracks your most used directories, based on 'frecency'.
+
+       After  a  short  learning  phase, z will take you to the most 'frecent'
+       directory that matches ALL of the regexes given on the command line.
+
+OPTIONS
+       -c     restrict matches to subdirectories of the current directory.
+
+       -h     show a brief help message
+
+       -l     list only
+
+       -r     match by rank only
+
+       -t     match by recent access only
+
+EXAMPLES
+       z foo         cd to most frecent dir matching foo
+
+       z foo bar     cd to most frecent dir matching foo and bar
+
+       z -r foo      cd to highest ranked dir matching foo
+
+       z -t foo      cd to most recently accessed dir matching foo
+
+       z -l foo      list all dirs matching foo (by frecency)
+
+NOTES
+   Installation:
+       Put something like this in your $HOME/.bashrc or $HOME/.zshrc:
+
+              . /path/to/z.sh
+
+       cd around for a while to build up the db.
+
+       PROFIT!!
+
+       Optionally:
+              Set $_Z_CMD to change the command name (default z).
+              Set $_Z_DATA to change the datafile (default $HOME/.z).
+              Set $_Z_NO_RESOLVE_SYMLINKS to prevent symlink resolution.
+              Set $_Z_NO_PROMPT_COMMAND to handle PROMPT_COMMAND/precmd  your-
+              self.
+              Set $_Z_EXCLUDE_DIRS to an array of directories to exclude.
+              (These  settings  should  go  in .bashrc/.zshrc before the lines
+              added above.)
+              Install   the   provided   man   page   z.1    somewhere    like
+              /usr/local/man/man1.
+
+   Aging:
+       The rank of directories maintained by z undergoes aging based on a sim-
+       ple formula. The rank of each entry is incremented  every  time  it  is
+       accessed.  When  the  sum  of ranks is greater than 6000, all ranks are
+       multiplied by 0.99. Entries with a rank lower than 1 are forgotten.
+
+   Frecency:
+       Frecency is a portmantaeu of 'recent' and 'frequency'. It is a weighted
+       rank  that  depends on how often and how recently something occured. As
+       far as I know, Mozilla came up with the term.
+
+       To z, a directory that has low ranking but has been  accessed  recently
+       will  quickly  have  higher rank than a directory accessed frequently a
+       long time ago.
+
+       Frecency is determined at runtime.
+
+   Common:
+       When multiple directories match all queries, and they all have a common
+       prefix, z will cd to the shortest matching directory, without regard to
+       priority.  This has been in effect, if  undocumented,  for  quite  some
+       time, but should probably be configurable or reconsidered.
+
+   Tab Completion:
+       z  supports tab completion. After any number of arguments, press TAB to
+       complete on directories that match each argument. Due to limitations of
+       the  completion  implementations,  only  the last argument will be com-
+       pleted in the shell.
+
+       Internally, z decides you've requested a completion if the  last  argu-
+       ment  passed  is  an  absolute  path to an existing directory. This may
+       cause unexpected behavior if the last argument to z begins with /.
+
+ENVIRONMENT
+       A function _z() is defined.
+
+       The contents of the variable $_Z_CMD is aliased to _z 2>&1. If not set,
+       $_Z_CMD defaults to z.
+
+       The  environment  variable $_Z_DATA can be used to control the datafile
+       location. If it is not defined, the location defaults to $HOME/.z.
+
+       The environment variable $_Z_NO_RESOLVE_SYMLINKS can be set to  prevent
+       resolving  of  symlinks.  If  it  is  not  set,  symbolic links will be
+       resolved when added to the datafile.
+
+       In bash, z prepends a command to the PROMPT_COMMAND  environment  vari-
+       able  to  maintain its database. In zsh, z appends a function _z_precmd
+       to the precmd_functions array.
+
+       The environment variable $_Z_NO_PROMPT_COMMAND can be set if  you  want
+       to handle PROMPT_COMMAND or precmd yourself.
+
+       The  environment  variable  $_Z_EXCLUDE_DIRS  can be set to an array of
+       directories to exclude from tracking. $HOME is always excluded.  Direc-
+       tories must be full paths without trailing slashes.
+
+FILES
+       Data  is  stored  in  $HOME/.z.  This  can be overridden by setting the
+       $_Z_DATA environment variable. When initialized, z will raise an  error
+       if this path is a directory, and not function correctly.
+
+       A man page (z.1) is provided.
+
+SEE ALSO
+       regex(7), pushd, popd, autojump, cdargs
+
+       Please file bugs at https://github.com/rupa/z/
+
+
+
+z                                January 2013                             Z(1)

+ 155 - 0
plugins/z/z.1

@@ -0,0 +1,155 @@
+.TH "Z" "1" "January 2013" "z" "User Commands"
+.SH
+NAME
+z \- jump around
+.SH
+SYNOPSIS
+z [\-chlrt] [regex1 regex2 ... regexn]
+.SH
+AVAILABILITY
+bash, zsh
+.SH
+DESCRIPTION
+Tracks your most used directories, based on 'frecency'.
+.P
+After a short learning phase, \fBz\fR will take you to the most 'frecent'
+directory that matches ALL of the regexes given on the command line.
+.SH
+OPTIONS
+.TP
+\fB\-c\fR
+restrict matches to subdirectories of the current directory.
+.TP
+\fB\-h\fR
+show a brief help message
+.TP
+\fB\-l\fR
+list only
+.TP
+\fB\-r\fR
+match by rank only
+.TP
+\fB\-t\fR
+match by recent access only
+.SH EXAMPLES
+.TP 14
+\fBz foo\fR
+cd to most frecent dir matching foo
+.TP 14
+\fBz foo bar\fR
+cd to most frecent dir matching foo and bar
+.TP 14
+\fBz -r foo\fR
+cd to highest ranked dir matching foo
+.TP 14
+\fBz -t foo\fR
+cd to most recently accessed dir matching foo
+.TP 14
+\fBz -l foo\fR
+list all dirs matching foo (by frecency)
+.SH
+NOTES
+.SS
+Installation:
+.P
+Put something like this in your \fB$HOME/.bashrc\fR or \fB$HOME/.zshrc\fR:
+.RS
+.P
+\fB. /path/to/z.sh\fR
+.RE
+.P
+\fBcd\fR around for a while to build up the db.
+.P
+PROFIT!!
+.P
+Optionally:
+.RS
+Set \fB$_Z_CMD\fR to change the command name (default \fBz\fR).
+.RE
+.RS
+Set \fB$_Z_DATA\fR to change the datafile (default \fB$HOME/.z\fR).
+.RE
+.RS
+Set \fB$_Z_NO_RESOLVE_SYMLINKS\fR to prevent symlink resolution.
+.RE
+.RS
+Set \fB$_Z_NO_PROMPT_COMMAND\fR to handle \fBPROMPT_COMMAND/precmd\fR yourself.
+.RE
+.RS
+Set \fB$_Z_EXCLUDE_DIRS\fR to an array of directories to exclude.
+.RE
+.RS
+(These settings should go in .bashrc/.zshrc before the lines added above.)
+.RE
+.RS
+Install the provided man page \fBz.1\fR somewhere like \fB/usr/local/man/man1\fR.
+.RE
+.SS
+Aging:
+The rank of directories maintained by \fBz\fR undergoes aging based on a simple
+formula. The rank of each entry is incremented every time it is accessed. When
+the sum of ranks is greater than 6000, all ranks are multiplied by 0.99. Entries
+with a rank lower than 1 are forgotten.
+.SS
+Frecency:
+Frecency is a portmantaeu of 'recent' and 'frequency'. It is a weighted rank
+that depends on how often and how recently something occured. As far as I
+know, Mozilla came up with the term.
+.P
+To \fBz\fR, a directory that has low ranking but has been accessed recently
+will quickly have higher rank than a directory accessed frequently a long time
+ago.
+.P
+Frecency is determined at runtime.
+.SS
+Common:
+When multiple directories match all queries, and they all have a common prefix,
+\fBz\fR will cd to the shortest matching directory, without regard to priority.
+This has been in effect, if undocumented, for quite some time, but should
+probably be configurable or reconsidered.
+.SS
+Tab Completion:
+\fBz\fR supports tab completion. After any number of arguments, press TAB to
+complete on directories that match each argument. Due to limitations of the
+completion implementations, only the last argument will be completed in the
+shell.
+.P
+Internally, \fBz\fR decides you've requested a completion if the last argument
+passed is an absolute path to an existing directory. This may cause unexpected
+behavior if the last argument to \fBz\fR begins with \fB/\fR.
+.SH
+ENVIRONMENT
+A function \fB_z()\fR is defined.
+.P
+The contents of the variable \fB$_Z_CMD\fR is aliased to \fB_z 2>&1\fR. If not
+set, \fB$_Z_CMD\fR defaults to \fBz\fR.
+.P
+The environment variable \fB$_Z_DATA\fR can be used to control the datafile
+location. If it is not defined, the location defaults to \fB$HOME/.z\fR.
+.P
+The environment variable \fB$_Z_NO_RESOLVE_SYMLINKS\fR can be set to prevent
+resolving of symlinks. If it is not set, symbolic links will be resolved when
+added to the datafile.
+.P
+In bash, \fBz\fR prepends a command to the \fBPROMPT_COMMAND\fR environment
+variable to maintain its database. In zsh, \fBz\fR appends a function
+\fB_z_precmd\fR to the \fBprecmd_functions\fR array.
+.P
+The environment variable \fB$_Z_NO_PROMPT_COMMAND\fR can be set if you want to
+handle \fBPROMPT_COMMAND\fR or \fBprecmd\fR yourself.
+.P
+The environment variable \fB$_Z_EXCLUDE_DIRS\fR can be set to an array of
+directories to exclude from tracking. \fB$HOME\fR is always excluded.
+Directories must be full paths without trailing slashes.
+.SH
+FILES
+Data is stored in \fB$HOME/.z\fR. This can be overridden by setting the
+\fB$_Z_DATA\fR environment variable. When initialized, \fBz\fR will raise an
+error if this path is a directory, and not function correctly.
+.P
+A man page (\fBz.1\fR) is provided.
+.SH
+SEE ALSO
+regex(7), pushd, popd, autojump, cdargs
+.P
+Please file bugs at https://github.com/rupa/z/

+ 6 - 0
plugins/z/z.plugin.zsh

@@ -0,0 +1,6 @@
+_load_z() {
+  source $1/z.sh
+}
+
+[[ -f $ZSH_CUSTOM/plugins/z/z.plugin.zsh ]] && _load_z $ZSH_CUSTOM/plugins/z
+[[ -f $ZSH/plugins/z/z.plugin.zsh ]] && _load_z $ZSH/plugins/z

+ 228 - 0
plugins/z/z.sh

@@ -0,0 +1,228 @@
+# Copyright (c) 2009 rupa deadwyler under the WTFPL license
+
+# maintains a jump-list of the directories you actually use
+#
+# INSTALL:
+#   * put something like this in your .bashrc/.zshrc:
+#     . /path/to/z.sh
+#   * cd around for a while to build up the db
+#   * PROFIT!!
+#   * optionally:
+#     set $_Z_CMD in .bashrc/.zshrc to change the command (default z).
+#     set $_Z_DATA in .bashrc/.zshrc to change the datafile (default ~/.z).
+#     set $_Z_NO_RESOLVE_SYMLINKS to prevent symlink resolution.
+#     set $_Z_NO_PROMPT_COMMAND if you're handling PROMPT_COMMAND yourself.
+#     set $_Z_EXCLUDE_DIRS to an array of directories to exclude.
+#
+# USE:
+#   * z foo     # cd to most frecent dir matching foo
+#   * z foo bar # cd to most frecent dir matching foo and bar
+#   * z -r foo  # cd to highest ranked dir matching foo
+#   * z -t foo  # cd to most recently accessed dir matching foo
+#   * z -l foo  # list matches instead of cd
+#   * z -c foo  # restrict matches to subdirs of $PWD
+
+case $- in
+ *i*) ;;
+   *) echo 'ERROR: z.sh is meant to be sourced, not directly executed.'
+esac
+
+[ -d "${_Z_DATA:-$HOME/.z}" ] && {
+    echo "ERROR: z.sh's datafile (${_Z_DATA:-$HOME/.z}) is a directory."
+}
+
+_z() {
+
+ local datafile="${_Z_DATA:-$HOME/.z}"
+
+ # bail out if we don't own ~/.z (we're another user but our ENV is still set)
+ [ -f "$datafile" -a ! -O "$datafile" ] && return
+
+ # add entries
+ if [ "$1" = "--add" ]; then
+  shift
+
+  # $HOME isn't worth matching
+  [ "$*" = "$HOME" ] && return
+
+  # don't track excluded dirs
+  local exclude
+  for exclude in "${_Z_EXCLUDE_DIRS[@]}"; do
+   [ "$*" = "$exclude" ] && return
+  done
+
+  # maintain the file
+  local tempfile
+  tempfile="$(mktemp "$datafile.XXXXXX")" || return
+  while read line; do
+   [ -d "${line%%\|*}" ] && echo $line
+  done < "$datafile" | awk -v path="$*" -v now="$(date +%s)" -F"|" '
+   BEGIN {
+    rank[path] = 1
+    time[path] = now
+   }
+   $2 >= 1 {
+    if( $1 == path ) {
+     rank[$1] = $2 + 1
+     time[$1] = now
+    } else {
+     rank[$1] = $2
+     time[$1] = $3
+    }
+    count += $2
+   }
+   END {
+    if( count > 6000 ) {
+     for( i in rank ) print i "|" 0.99*rank[i] "|" time[i] # aging
+    } else for( i in rank ) print i "|" rank[i] "|" time[i]
+   }
+  ' 2>/dev/null >| "$tempfile"
+  if [ $? -ne 0 -a -f "$datafile" ]; then
+   env rm -f "$tempfile"
+  else
+   env mv -f "$tempfile" "$datafile"
+  fi
+
+ # tab completion
+ elif [ "$1" = "--complete" ]; then
+  while read line; do
+   [ -d "${line%%\|*}" ] && echo $line
+  done < "$datafile" | awk -v q="$2" -F"|" '
+   BEGIN {
+    if( q == tolower(q) ) nocase = 1
+    split(substr(q,3),fnd," ")
+   }
+   {
+    if( nocase ) {
+     for( i in fnd ) tolower($1) !~ tolower(fnd[i]) && $1 = ""
+    } else {
+     for( i in fnd ) $1 !~ fnd[i] && $1 = ""
+    }
+    if( $1 ) print $1
+   }
+  ' 2>/dev/null
+
+ else
+  # list/go
+  while [ "$1" ]; do case "$1" in
+   --) while [ "$1" ]; do shift; local fnd="$fnd $1";done;;
+   -*) local opt=${1:1}; while [ "$opt" ]; do case ${opt:0:1} in
+        c) local fnd="^$PWD $fnd";;
+        h) echo "${_Z_CMD:-z} [-chlrt] args" >&2; return;;
+        l) local list=1;;
+        r) local typ="rank";;
+        t) local typ="recent";;
+       esac; opt=${opt:1}; done;;
+    *) local fnd="$fnd $1";;
+  esac; local last=$1; shift; done
+  [ "$fnd" -a "$fnd" != "^$PWD " ] || local list=1
+
+  # if we hit enter on a completion just go there
+  case "$last" in
+   # completions will always start with /
+   /*) [ -z "$list" -a -d "$last" ] && cd "$last" && return;;
+  esac
+
+  # no file yet
+  [ -f "$datafile" ] || return
+
+  local cd
+  cd="$(while read line; do
+   [ -d "${line%%\|*}" ] && echo $line
+  done < "$datafile" | awk -v t="$(date +%s)" -v list="$list" -v typ="$typ" -v q="$fnd" -F"|" '
+   function frecent(rank, time) {
+    dx = t-time
+    if( dx < 3600 ) return rank*4
+    if( dx < 86400 ) return rank*2
+    if( dx < 604800 ) return rank/2
+    return rank/4
+   }
+   function output(files, toopen, override) {
+    if( list ) {
+     cmd = "sort -n >&2"
+     for( i in files ) if( files[i] ) printf "%-10s %s\n", files[i], i | cmd
+     if( override ) printf "%-10s %s\n", "common:", override > "/dev/stderr"
+    } else {
+     if( override ) toopen = override
+     print toopen
+    }
+   }
+   function common(matches) {
+    # shortest match
+    for( i in matches ) {
+     if( matches[i] && (!short || length(i) < length(short)) ) short = i
+    }
+    if( short == "/" ) return
+    # shortest match must be common to each match. escape special characters in
+    # a copy when testing, so we can return the original.
+    clean_short = short
+    gsub(/[\(\)\[\]\|]/, "\\\\&", clean_short)
+    for( i in matches ) if( matches[i] && i !~ clean_short ) return
+    return short
+   }
+   BEGIN { split(q, a, " "); oldf = noldf = -9999999999 }
+   {
+    if( typ == "rank" ) {
+     f = $2
+    } else if( typ == "recent" ) {
+     f = $3-t
+    } else f = frecent($2, $3)
+    wcase[$1] = nocase[$1] = f
+    for( i in a ) {
+     if( $1 !~ a[i] ) delete wcase[$1]
+     if( tolower($1) !~ tolower(a[i]) ) delete nocase[$1]
+    }
+    if( wcase[$1] && wcase[$1] > oldf ) {
+     cx = $1
+     oldf = wcase[$1]
+    } else if( nocase[$1] && nocase[$1] > noldf ) {
+     ncx = $1
+     noldf = nocase[$1]
+    }
+   }
+   END {
+    if( cx ) {
+     output(wcase, cx, common(wcase))
+    } else if( ncx ) output(nocase, ncx, common(nocase))
+   }
+  ')"
+  [ $? -gt 0 ] && return
+  [ "$cd" ] && cd "$cd"
+ fi
+}
+
+alias ${_Z_CMD:-z}='_z 2>&1'
+
+[ "$_Z_NO_RESOLVE_SYMLINKS" ] || _Z_RESOLVE_SYMLINKS="-P"
+
+if compctl &> /dev/null; then
+ [ "$_Z_NO_PROMPT_COMMAND" ] || {
+  # zsh populate directory list, avoid clobbering any other precmds
+  if [ "$_Z_NO_RESOLVE_SYMLINKS" ]; then
+    _z_precmd() {
+      _z --add "${PWD:a}"
+    }
+  else
+    _z_precmd() {
+      _z --add "${PWD:A}"
+    }
+  fi
+  precmd_functions+=(_z_precmd)
+ }
+ # zsh tab completion
+ _z_zsh_tab_completion() {
+  local compl
+  read -l compl
+  reply=(${(f)"$(_z --complete "$compl")"})
+ }
+ compctl -U -K _z_zsh_tab_completion _z
+elif complete &> /dev/null; then
+ # bash tab completion
+ complete -o filenames -C '_z --complete "$COMP_LINE"' ${_Z_CMD:-z}
+ [ "$_Z_NO_PROMPT_COMMAND" ] || {
+  # bash populate directory list. avoid clobbering other PROMPT_COMMANDs.
+  echo $PROMPT_COMMAND | grep -q "_z --add" || {
+   PROMPT_COMMAND='_z --add "$(pwd '$_Z_RESOLVE_SYMLINKS' 2>/dev/null)" 2>/dev/null;'"$PROMPT_COMMAND"
+  }
+ }
+fi