diff options
Diffstat (limited to 'Functions/Chpwd')
-rw-r--r-- | Functions/Chpwd/cdr | 37 | ||||
-rw-r--r-- | Functions/Chpwd/zsh_directory_name_generic | 151 |
2 files changed, 182 insertions, 6 deletions
diff --git a/Functions/Chpwd/cdr b/Functions/Chpwd/cdr index 4f399106b..4bed88b13 100644 --- a/Functions/Chpwd/cdr +++ b/Functions/Chpwd/cdr @@ -51,6 +51,13 @@ # (and only /). Usually the first entry should be left as the current # directory. # +# "cdr -p 'pattern'" prunes anything matching the given extended glob +# pattern from the directory list. The match is against the fully +# expanded directory path and the full string must match (use wildcards +# at the ends if needed). If output is going to a terminal, the +# function will print the new list for the user to confrim; this can be +# skipped by giving -P instead of -p. +# # Details of directory handling # ============================= # @@ -217,11 +224,11 @@ setopt extendedglob autoload -Uz chpwd_recent_filehandler chpwd_recent_add -integer list set_reply i bad edit -local opt dir +integer list set_reply i bad edit force_prune +local opt dir prune local -aU dirs -while getopts "elr" opt; do +while getopts "elp:P:r" opt; do case $opt in (e) edit=1 @@ -231,6 +238,12 @@ while getopts "elr" opt; do list=1 ;; + ([pP]) + prune=$OPTARG + edit=1 + [[ $opt = P ]] && force_prune=1 + ;; + (r) set_reply=1 ;; @@ -278,10 +291,22 @@ if [[ $PWD != $reply[1] ]]; then fi if (( edit )); then - local compcontext='directories:directory:_path_files -/' -IFS=' + if [[ -n $prune ]]; then + reply=(${reply:#$~prune}) + if [[ force_prune -eq 0 && -t 1 ]]; then + print -nrl "New list:" $reply 'Accept? ' + if ! read -q; then + print + return 1 + fi + print + fi + else + local compcontext='directories:directory:_path_files -/' + IFS=' ' vared reply || return 1 -chpwd_recent_filehandler $reply + fi + chpwd_recent_filehandler $reply fi # Skip current directory if present (may have been pruned). diff --git a/Functions/Chpwd/zsh_directory_name_generic b/Functions/Chpwd/zsh_directory_name_generic new file mode 100644 index 000000000..9430c95e4 --- /dev/null +++ b/Functions/Chpwd/zsh_directory_name_generic @@ -0,0 +1,151 @@ +## zsh_directory_name_generic +# +# This function is useful as a hook function for the zsh_directory_name +# facility. +# +# See the zsh-contrib manual page for more. + +emulate -L zsh +setopt extendedglob +local -a match mbegin mend + +# The variable containing the top level mapping. +local _zdn_topvar + +zmodload -i zsh/parameter +zstyle -s ":zdn:${funcstack[2]}:" mapping _zdn_topvar || _zdn_topvar=zdn_top + +if (( ! ${(P)#_zdn_topvar} )); then + print -r -- "$0: $_zdn_topver is not set" >&2 + return 1 +fi + +local _zdn_var=$_zdn_topvar +local -A _zdn_assoc + +if [[ $1 = n ]]; then + # Turning a name into a directory. + local _zdn_name=$2 + local -a _zdn_words + local _zdn_dir _zdn_cpt + + _zdn_words=(${(s.:.)_zdn_name}) + while (( ${#_zdn_words} )); do + if [[ -z ${_zdn_var} ]]; then + print -r -- "$0: too many components in directory name \`$_zdn_name'" >&2 + return 1 + fi + + # Subscripting (P)_zdn_var directly seems not to work. + _zdn_assoc=(${(Pkv)_zdn_var}) + _zdn_cpt=${_zdn_assoc[${_zdn_words[1]}]} + shift _zdn_words + + if [[ -z $_zdn_cpt ]]; then + # If top level component, just try another expansion + if [[ $_zdn_var != $_zdn_top ]]; then + # Committed to this expansion, so report failure. + print -r -- "$0: no expansion for directory name \`$_zdn_name'" >&2 + fi + return 1 + fi + if [[ $_zdn_cpt = (#b)(*)/:([[:IDENT:]]##) ]]; then + _zdn_cpt=$match[1] + _zdn_var=$match[2] + else + # may be empty + _zdn_var=${${_zdn_assoc[:default:]}##*/:} + fi + _zdn_dir=${_zdn_dir:+$_zdn_dir/}$_zdn_cpt + done + if (( ${#_zdn_dir} )); then + typeset -ag reply + reply=($_zdn_dir) + return 0 + fi +elif [[ $1 = d ]]; then + # Turning a directory into a name. + local _zdn_dir=$2 + local _zdn_rest=$_zdn_dir + local -a _zdn_cpts + local _zdn_pref _zdn_pref_raw _zdn_matched _zdn_cpt _zdn_name + + while [[ -n $_zdn_var && -n $_zdn_rest ]]; do + _zdn_assoc=(${(Pkv)_zdn_var}) + # Sorting in descending order will ensure prefixes + # come after longer strings with that perfix, so + # we match more specific directory names preferentially. + _zdn_cpts=(${(Ov)_zdn_assoc}) + _zdn_cpt='' + for _zdn_pref_raw in $_zdn_cpts; do + _zdn_pref=${_zdn_pref_raw%/:*} + [[ -z $_zdn_pref ]] && continue + if [[ $_zdn_rest = $_zdn_pref(#b)(/|)(*) ]]; then + _zdn_cpt=${(k)_zdn_assoc[(r)$_zdn_pref_raw]} + # if we matched a /, too, add it... + _zdn_matched+=$_zdn_pref$match[1] + _zdn_rest=$match[2] + break + fi + done + if [[ -n $_zdn_cpt ]]; then + _zdn_name+=${_zdn_name:+${_zdh_name}:}$_zdn_cpt + if [[ ${_zdn_assoc[$_zdn_cpt]} = (#b)*/:([[:IDENT:]]##) ]]; then + _zdn_var=$match[1] + else + _zdn_var=${${_zdn_assoc[:default:]}##*/:} + fi + else + break + fi + done + if [[ -n $_zdn_name ]]; then + # matched something, so report that. + integer _zdn_len=${#_zdn_matched} + [[ $_zdn_matched[-1] = / ]] && (( _zdn_len-- )) + typeset -ag reply + reply=($_zdn_name $_zdn_len) + return 0 + fi + # else let someone else have a go. +elif [[ $1 = c ]]; then + # Completion + + if [[ -n $SUFFIX ]]; then + _message "Can't complete in the middle of a dynamic directory name" + else + local -a _zdn_cpts + local _zdn_word _zdn_cpt _zdn_desc _zdn_sofar expl + + while [[ -n ${_zdn_var} && ${PREFIX} = (#b)([^:]##):* ]]; do + _zdn_word=$match[1] + compset -P '[^:]##:' + _zdn_assoc=(${(Pkv)_zdn_var}) + _zdn_cpt=${_zdn_assoc[$_zdn_word]} + # We only complete at the end so must match here + [[ -z $_zdn_cpt ]] && return 1 + if [[ $_zdn_cpt = (#b)(*)/:([[:IDENT:]]##) ]]; then + _zdn_cpt=$match[1] + _zdn_var=$match[2] + else + _zdn_var=${${_zdn_assoc[:default:]}##*/:} + fi + _zdn_sofar+=${_zdn_sofar:+${_zdn_sofar}/}$_zdn_cpt + done + if [[ -n $_zdn_var ]]; then + _zdn_assoc=(${(Pkv)_zdn_var}) + local -a _zdn_cpts + for _zdn_cpt _zdn_desc in ${(kv)_zdn_assoc}; do + [[ $_zdn_cpt = :* ]] && continue + _zdn_cpts+=(${_zdn_cpt}:${_zdn_desc%/:[[:IDENT:]]##}) + done + _describe -t dirnames "directory name under ${_zdn_sofar%%/}" \ + _zdn_cpts -S: -r ':]' + return + fi + fi +fi + +# Failed +return 1 +## end |