diff options
author | Peter Stephenson <pws@users.sourceforge.net> | 2010-07-09 14:47:48 +0000 |
---|---|---|
committer | Peter Stephenson <pws@users.sourceforge.net> | 2010-07-09 14:47:48 +0000 |
commit | 5da6530d831ea8a00943b39359d535ea59996894 (patch) | |
tree | 2a4fcd827214fa4fd2a8d172341a07cb8b76cda0 /Functions/Chpwd/cdr | |
parent | 924f40b772d7c222ffe8db502fb88cc897fd9902 (diff) | |
download | zsh-5da6530d831ea8a00943b39359d535ea59996894.tar.gz zsh-5da6530d831ea8a00943b39359d535ea59996894.tar.xz zsh-5da6530d831ea8a00943b39359d535ea59996894.zip |
28065 plus unposted zsh.mdd:
add cdr and related functions and docs
Diffstat (limited to 'Functions/Chpwd/cdr')
-rw-r--r-- | Functions/Chpwd/cdr | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/Functions/Chpwd/cdr b/Functions/Chpwd/cdr new file mode 100644 index 000000000..551710499 --- /dev/null +++ b/Functions/Chpwd/cdr @@ -0,0 +1,311 @@ +# Description +# =========== +# +# Change to a recently used directory recorded in a file so that the +# recent file list persists across sessions. +# +# To use this system, +# +# autoload -Uz chpwd_recent_dirs cdr add-zsh-hook +# add-zsh-hook chpwd chpwd_recent_dirs +# +# (add-zsh-hook appeared in zsh version 4.3.4.) This ensures that all +# directories you change to interactively are registered. The +# chpwd_recent_dirs function does some guesswork to see if you are "really" +# changing directory permanently, see below. +# +# The argument to cdr is a number corresponding to the Nth most recently +# changed-to directory starting at 1 for the immediately preceeding +# directory (the current directory is remembered but is not offered as a +# destination). You can use directory arguments if you set the +# recent-dirs-default style, see below; however, it should be noted +# that if you do you gain nothing over using cd directly (the recent +# directory list is updated in either case). +# +# If the argument is omitted, 1 is assumed. +# +# Completion is available if compinit has been run; menu selection is +# recommended, using +# +# zstyle ':completion:*:*:cdr:*:*' menu selection +# +# and also the verbose style to ensure the directory is shown (this +# is on by default). +# +# Options +# ======= +# +# "cdr -l" lists the numbers and the corresponding directories in +# abbreviated form (i.e. with "~" substitution reapplied), one per line. +# The directories here are not quoted (this would only be an issue if a +# directory name contained a newline). This is used by the completion +# system. +# +# "cdr -r" sets the parameter "reply" to the current set of directories. +# +# "cdr -e" allows you to edit the list of directories, one per line. The +# list can be edited to any extent you like; no sanity checking is +# performed. Completion is available. No quoting is necessary (except for +# newlines, where I have in any case no sympathy); directories are in +# unabbreviated from and contain an absolute path, i.e. they start with / +# (and only /). Usually the first entry should be left as the current +# directory. +# +# Details of directory handling +# ============================= +# +# Recent directories are saved to a file immediately and hence are +# preserved across sessions. Note currently no file locking is applied: +# the list is updated immediately on interactive commands and nowhere else +# (unlike history), and it is assumed you are only going to change +# directory in one window at once. This is not safe on shared accounts, +# but in any case the system has limited utility when someone else is +# changing to a different set of directories behind your back. +# +# To make this a little safer, only directory changes instituted from the +# command line, either directly or indirectly through shell function calls +# (but not through subshells, evals, traps, completion functions and the +# like) are saved. This works best in versions of the shell from 4.3.11 +# which has facilities to check the evaluation context. Shell functions +# should use cd -q or pushd -q to avoid side effects if the change to the +# directory is to be invisible at the command line. See the function +# chpwd_recent_dirs for more details. +# +# Styles +# ====== +# +# Various styles are available. The context for setting styles should be +# ':chpwd:*' in case the meaning of the context is extended in future, for +# example: +# +# zstyle ':chpwd:*' recent-dirs-max 0 +# +# although the style name is specific enough that a context of '*' should +# be fine in practice. The only exception is recent-dirs-insert, which is +# used exclusively by the completion system and so has the usual completion +# system context (':completion:*' if nothing more specific is needed, +# though again '*' should be fine in practice). +# +# recent-dirs-default +# If true, and the command is expecting a recent directory index, and +# either there is more than one argument or the argument is not an +# integer, then fall through to "cd". This allows the lazy to use only +# one command for directory changing. Completion recognises this, too; +# see recent-dirs-insert for how to control completion when this option +# is in use. +# +# recent-dirs-file +# The file where the list of directories is saved. The default +# is ${ZDOTDIR:-$HOME}/.chpwd-recent-dirs, i.e. this is in your +# home directory unless you have set ZDOTDIR to point somewhere else. +# Directory names are saved in $'...' quoted form, so each line +# in the file can be supplied directly to the shell as an argument. +# +# The value of this style may be an array. In this case, the first +# file in the list will always be used for saving directories while any +# other files are left untouched. When reading the recent directory +# list, if there are fewer than the maximum number of entries in the +# first file, the contents of later files in the array will be appended +# with duplicates removed from the list shown. The contents of the two +# files are not sorted together, i.e. all the entries in the first file +# are shown first. The special value "+" can appear in the list to +# indicate the default file should be read at that point. This allows +# effects like the following: +# +# zstyle recent-dirs-file ':chpwd:*' ~/.chpwd-recent-dirs-${TTY##*/} + +# +# Recent directories are read from a file numbered according to +# the terminal. If there are insufficient entries the list +# is supplemented from the default file. +# +# recent-dirs-insert +# Used by completion. If recent-dirs-default is true, then setting +# this to true causes the actual directory, rather than its index, to +# be inserted on the command line; this has the same effect as using +# the corresponding index, but makes the history clearer and the line +# easier to edit. With this setting, if part of an argument was +# already typed, normal directory completion rather than recent +# directory completion is done; this is because recent directory +# completion is expected to be done by cycling through entries menu +# fashion. However, if the value of the style is "always", then only +# recent directories will be completed; in that case, use the cd +# command when you want to complete other directories. If the value is +# "fallback", recent directories will be tried first, then normal +# directory completion is performed if recent directory completion +# failed to find a match. Finally, if the value is "both" then both +# sets of completions are presented; the usual tag mechanism can be +# used to distinguish results, with recent directories tagged as +# "recent-dirs". Note that the recent directories inserted are +# abbreviated with directory names where appropriate. +# +# recent-dirs-max +# The maximum number of directories to save to the file. If +# this is zero or negative there is no maximum. The default is 20. +# Note this includes the current directory, which isn't offered, +# so the highest number of directories you will be offered +# is one less than the maximum. +# +# recent-dirs-prune +# This style is an array determining what directories should (or should +# not) be added to the recent list. Elements of the array can include: +# parent +# Prune parents (more accurately, ancestors) from the recent list. +# If present, changing directly down by any number of directories +# causes the current directory to be overwritten. For example, +# changing from ~pws to ~pws/some/other/dir causes ~pws not to be +# left on the recent directory stack. This only applies to direct +# changes to descendant diretories; earlier directories on the +# list are not pruned. For example, changing from ~pws/yet/another +# to ~pws/some/other/dir does not cause ~pws to be pruned. +# pattern:<pattern> +# Gives a zsh pattern for directories that should not be +# added to the recent list (if not already there). This element +# can be repeated to add different patterns. For example, +# 'pattern:/tmp(|/*)' stops /tmp or its descendants from being +# added. The EXTENDED_GLOB option is always turned on for +# these patterns. +# +# recent-dirs-pushd +# If set to true, cdr will use pushd instead of cd to change the +# directory, so the directory is saved on the directory stack. As the +# directory stack is completely separate from the list of files saved +# by the mechanism used in this file there is no obvious reason to do +# this. +# +# Use with dynamic directory naming +# ================================= +# +# It is possible to refer to recent directories using the dynamic directory +# name syntax that appeared in zsh version 4.3.7. If you create and +# autoload a function zsh_directory_name containing the following code, +# ~[1] will refer to the most recent directory other than $PWD, and so on. +# This also includes completion (version 4.3.11 is required for this to +# work; previous versions needed the file _dynamic_directory_name to +# be overloaded). +# +# if [[ $1 = n ]]; then +# if [[ $2 = <-> ]]; then +# # Recent directory +# typeset -ga reply +# autoload -Uz cdr +# cdr -r +# if [[ -n ${reply[$2]} ]]; then +# reply=(${reply[$2]}) +# return 0 +# else +# reply=() +# return 1 +# fi +# fi +# elif [[ $1 = c ]]; then +# if [[ $PREFIX = <-> || -z $PREFIX ]]; then +# typeset -a keys values +# +# values=(${${(f)"$(cdr -l)"}/ ##/:}) +# keys=(${values%%:*}) +# +# _describe -t dir-index 'recent directory index' \ +# values keys -V unsorted -S']' +# return +# fi +# fi +# return 1 + + +emulate -L zsh +setopt extendedglob + +autoload -Uz chpwd_recent_filehandler chpwd_recent_add + +integer list set_reply i bad edit +local opt dir +local -aU dirs + +while getopts "elr" opt; do + case $opt in + (e) + edit=1 + ;; + + (l) + list=1 + ;; + + (r) + set_reply=1 + ;; + + (*) + return 1 + ;; + esac +done +shift $(( OPTIND - 1 )) + +if (( set_reply )); then + typeset -ga reply +else + local -a reply +fi + +if (( list || set_reply || edit )); then + (( $# )) && bad=1 +else + if [[ $#1 -eq 0 ]]; then + 1=1 + elif [[ $# -ne 1 || $1 != <-> ]]; then + if zstyle -t ':chpwd:' recent-dirs-default; then + cd "$@" + return + else + bad=1 + fi + fi +fi + +if (( bad )); then + print "Usage: $0 [-l | -r | <dir-num> ] +Use $0 -l or completion to see possible directories." + return 1 +fi + +chpwd_recent_filehandler + +if [[ $PWD != $reply[1] ]]; then + # When we first start we don't have the current directory. + # Add it now for consistency. + chpwd_recent_add $PWD && chpwd_recent_filehandler $reply +fi + +if (( edit )); then + local compcontext='directories:directory:_path_files -/' +IFS=' +' vared reply || return 1 +chpwd_recent_filehandler $reply +fi + +# Skip current directory if present (may have been pruned). +[[ $reply[1] = $PWD ]] && reply=($reply[2,-1]) + +if (( list )); then + dirs=($reply) + for (( i = 1; i <= ${#dirs}; i++ )); do + print -n ${(r.5.)i} + print -D ${dirs[i]} + done + return +fi + +(( set_reply || edit )) && return + +if (( $1 > ${#reply} )); then + print "Not enough directories ($(( ${#dirs} - 1)) possibilities)" >&2 + return 1 +fi +dir=${reply[$1]} + +if zstyle -t ':chpwd:' recent-dirs-pushd; then + pushd -- $dir +else + cd -- $dir +fi |