diff options
-rw-r--r-- | ChangeLog | 8 | ||||
-rw-r--r-- | Completion/Zsh/Context/_dynamic_directory_name | 12 | ||||
-rw-r--r-- | Doc/Zsh/contrib.yo | 52 | ||||
-rw-r--r-- | Doc/Zsh/expn.yo | 25 | ||||
-rw-r--r-- | Functions/Chpwd/.distfiles | 1 | ||||
-rw-r--r-- | Functions/Chpwd/zsh_directory_name_cdr | 25 | ||||
-rw-r--r-- | Functions/Misc/add-zsh-hook | 7 | ||||
-rw-r--r-- | Src/subst.c | 4 | ||||
-rw-r--r-- | Src/utils.c | 78 |
9 files changed, 149 insertions, 63 deletions
diff --git a/ChangeLog b/ChangeLog index 1a6a69d09..bdbc02d84 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2011-03-11 Peter Stephenson <pws@csr.com> + * users/15864: Completion/Zsh/Context/_dynamic_directory_name, + Doc/Zsh/contrib.yo, Doc/Zsh/expn.yo, Functions/Chpwd/.distfiles, + Functions/Chpwd/zsh_directory_name_cdr, + Functions/Misc/add-zsh-hook, Src/subst.c, Src/utils.c: + turn zsh_directory_name into a hook. + * 28886: Src/Zle/zle_utils.c: Fix 28772 for the case where regions have the "P" flag to include $PREDISPLAY in the offsets. @@ -14316,5 +14322,5 @@ ***************************************************** * This is used by the shell to define $ZSH_PATCHLEVEL -* $Revision: 1.5218 $ +* $Revision: 1.5219 $ ***************************************************** diff --git a/Completion/Zsh/Context/_dynamic_directory_name b/Completion/Zsh/Context/_dynamic_directory_name index 5f7fe9a90..fbea15a7a 100644 --- a/Completion/Zsh/Context/_dynamic_directory_name +++ b/Completion/Zsh/Context/_dynamic_directory_name @@ -1,7 +1,15 @@ #autoload -if [[ -n $functions[zsh_directory_name] ]]; then - zsh_directory_name c +local func +integer ret=1 + +if [[ -n $functions[zsh_directory_name] || \ + ${+zsh_directory_name_functions} -ne 0 ]] ; then + zsh_directory_name c && ret=0 + for func in $zsh_directory_name_functions; do + $func c && ret=0 + done + return ret else _message 'dynamic directory name: implemented as zsh_directory_name c' fi diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo index c5808fbeb..5b9af12f3 100644 --- a/Doc/Zsh/contrib.yo +++ b/Doc/Zsh/contrib.yo @@ -302,10 +302,13 @@ called at the same point; these are so-called `hook functions'. The shell function tt(add-zsh-hook) provides a simple way of adding or removing functions from the array. -var(hook) is one of tt(chpwd), tt(periodic), tt(precmd) or tt(preexec), -the special functions in question. +var(hook) is one of tt(chpwd), tt(periodic), tt(precmd), tt(preexec), +tt(zshaddhistory), tt(zshexit), or tt(zsh_directory_name), +the special functions in question. Note that tt(zsh_directory_name) +is called in a different way from the other functions, but may +still be manipulated as a hook. -var(functions) is name of an ordinary shell function. If no options +var(function) is name of an ordinary shell function. If no options are given this will be added to the array of functions to be executed in the given context. @@ -315,6 +318,10 @@ the array of functions to be executed. If the option tt(-D) is given, the var(function) is treated as a pattern and any matching names of functions are removed from the array of functions to be executed. + +The options tt(-U), tt(-z) and tt(-k) are passed as arguments to +tt(autoload) for var(function). For functions contributed with zsh, the +options tt(-Uz) are appropriate. ) enditem() @@ -544,36 +551,15 @@ enditem() subsect(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 tt(zsh_directory_name) containing the following code, -tt(~[1]) will refer to the most recent directory other than $PWD, and so on. -This also includes completion. - -example(if [[ $1 = n ]]; then - if [[ $2 = <-> ]]; then - # Recent directory - typeset -ga reply - autoload -Uz cdr - cdr -r - if [[ -n ${reply[$2]} ]]; then - reply=LPAR()${reply[$2]}RPAR() - return 0 - else - reply=LPAR()RPAR() - return 1 - fi - fi -elif [[ $1 = c ]]; then - if [[ $PREFIX = <-> || -z $PREFIX ]]; then - typeset -a keys values - values=LPAR()${${(f)"$+LPAR()cdr -l+RPAR()"}/ ##/:}RPAR() - keys=LPAR()${values%%:*}RPAR() - _describe -t dir-index 'recent directory index' \ - values keys -V unsorted -S']' - return - fi -fi -return 1) +name syntax by using the supplied function tt(zsh_directory_name_cdr) +a hook: + +example(autoload -Uz add-zsh-hook +add-zsh-hook -Uz zsh_directory_name zsh_directory_name_cdr) + +When this is done, tt(~[1]) will refer to the most recent +directory other than $PWD, and so on. Completion after tt(~[)var(...) +also works. subsect(Details of directory handling) diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index 9c4491386..1bace49c3 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -1517,8 +1517,12 @@ cindex(directories, named, dynamic) cindex(named directories, dynamic) cindex(dynamic named directories) -The feature described here is only available if the shell function -tt(zsh_directory_name) exists. +If the function tt(zsh_directory_name) exists, or the shell variable +tt(zsh_directory_name_functions) exists and contains an array of +function names, then the functions are used to implement dynamic +directory naming. The functions are tried in order until one returns +status zero, so it is important that functions test whether they can +handle the case in question and return an appropriate status. A `tt(~)' followed by a string var(namstr) in unquoted square brackets is treated specially as a dynamic directory name. Note that the first @@ -1529,11 +1533,12 @@ which is the directory corresponding to the name and return status zero (executing an assignment as the last statement is usually sufficient), or it should return status non-zero. In the former case the element of reply is used as the directory; in the latter case the substitution is deemed to -have failed and tt(NOMATCH) handling is applied if the option is set. +have failed. If all functions fail and the option tt(NOMATCH) is set, +an error results. -The function tt(zsh_directory_name) is also used to see if a directory can +The functions defined as above are also used to see if a directory can be turned into a name, for example when printing the directory stack or -when expanding tt(%~) in prompts. In this case the function is passed two +when expanding tt(%~) in prompts. In this case each function is passed two arguments: the string tt(d) (for directory) and the candidate for dynamic naming. The function should either return non-zero status, if the directory cannot be named by the function, or it should set the array reply @@ -1551,7 +1556,15 @@ parts of the directory path, as described below; it is used if the prefix length matched (16 in the example) is longer than that matched by any static name. -The completion system calls `tt(zsh_directory_name c)' in order to +It is not a requirement that a function implements both +tt(n) and tt(d) calls; for example, it might be appropriate for certain +dynamic forms of expansion not to be contracted to names. In that case +any call with the first argument tt(d) should cause a non-zero status to +be returned. + +The completion system calls `tt(zsh_directory_name c)' followed by +equivalent calls to elements of the array +tt(zsh_directory_name_functions), if it exists, in order to complete dynamic names for directories. The code for this should be as for any other completion function as described in ifnzman(noderef(Completion System))\ diff --git a/Functions/Chpwd/.distfiles b/Functions/Chpwd/.distfiles index 39ccd830c..89779a686 100644 --- a/Functions/Chpwd/.distfiles +++ b/Functions/Chpwd/.distfiles @@ -5,4 +5,5 @@ _cdr chpwd_recent_add chpwd_recent_dirs chpwd_recent_filehandler +zsh_directory_name_cdr ' diff --git a/Functions/Chpwd/zsh_directory_name_cdr b/Functions/Chpwd/zsh_directory_name_cdr new file mode 100644 index 000000000..09aa35a93 --- /dev/null +++ b/Functions/Chpwd/zsh_directory_name_cdr @@ -0,0 +1,25 @@ +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 diff --git a/Functions/Misc/add-zsh-hook b/Functions/Misc/add-zsh-hook index aedc1e754..c49688643 100644 --- a/Functions/Misc/add-zsh-hook +++ b/Functions/Misc/add-zsh-hook @@ -1,6 +1,6 @@ # Add to HOOK the given FUNCTION. # HOOK is one of chpwd, precmd, preexec, periodic, zshaddhistory, -# zshexit (the _functions subscript is not required). +# zshexit, zsh_directory_name (the _functions subscript is not required). # # With -d, remove the function from the hook instead; delete the hook # variable if it is empty. @@ -15,7 +15,10 @@ emulate -L zsh local -a hooktypes -hooktypes=(chpwd precmd preexec periodic zshaddhistory zshexit) +hooktypes=( + chpwd precmd preexec periodic zshaddhistory zshexit + zsh_directory_name +) local opt local -a autoopts diff --git a/Src/subst.c b/Src/subst.c index 37d63cabe..9b3699a47 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -579,7 +579,6 @@ filesubstr(char **namptr, int assign) char *str = *namptr; if (*str == Tilde && str[1] != '=' && str[1] != Equals) { - Shfunc dirfunc; char *ptr, *tmp, *res, *ptr2; int val; @@ -594,12 +593,11 @@ filesubstr(char **namptr, int assign) *namptr = dyncat((tmp = oldpwd) ? tmp : pwd, str + 2); return 1; } else if (str[1] == Inbrack && - (dirfunc = getshfunc("zsh_directory_name")) && (ptr2 = strchr(str+2, Outbrack))) { char **arr; untokenize(tmp = dupstrpfx(str+2, ptr2 - (str+2))); remnulargs(tmp); - arr = subst_string_by_func(dirfunc, "n", tmp); + arr = subst_string_by_hook("zsh_directory_name", "n", tmp); res = arr ? *arr : NULL; if (res) { *namptr = dyncat(res, ptr2+1); diff --git a/Src/utils.c b/Src/utils.c index 844a43e1b..9857303bb 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -890,7 +890,8 @@ finddir(char *s) { static struct nameddir homenode = { {NULL, "", 0}, NULL, 0 }; static int ffsz; - Shfunc func = getshfunc("zsh_directory_name"); + char **ares; + int len; /* Invalidate directory cache if argument is NULL. This is called * * whenever a node is added to or removed from the hash table, and * @@ -906,9 +907,16 @@ finddir(char *s) return finddir_last = NULL; } - /* It's not safe to use the cache while we have function transformations.*/ - if(!func && !strcmp(s, finddir_full) && *finddir_full) +#if 0 + /* + * It's not safe to use the cache while we have function + * transformations, and it's not clear it's worth the + * complexity of guessing here whether subst_string_by_hook + * is going to turn up the goods. + */ + if (!strcmp(s, finddir_full) && *finddir_full) return finddir_last; +#endif if ((int)strlen(s) >= ffsz) { free(finddir_full); @@ -920,18 +928,15 @@ finddir(char *s) finddir_scan(&homenode.node, 0); scanhashtable(nameddirtab, 0, 0, 0, finddir_scan, 0); - if (func) { - char **ares = subst_string_by_func(func, "d", finddir_full); - int len; - if (ares && arrlen(ares) >= 2 && - (len = (int)zstrtol(ares[1], NULL, 10)) > finddir_best) { - /* better duplicate this string since it's come from REPLY */ - finddir_last = (Nameddir)hcalloc(sizeof(struct nameddir)); - finddir_last->node.nam = zhtricat("[", dupstring(ares[0]), "]"); - finddir_last->dir = dupstrpfx(finddir_full, len); - finddir_last->diff = len - strlen(finddir_last->node.nam); - finddir_best = len; - } + ares = subst_string_by_hook("zsh_directory_name", "d", finddir_full); + if (ares && arrlen(ares) >= 2 && + (len = (int)zstrtol(ares[1], NULL, 10)) > finddir_best) { + /* better duplicate this string since it's come from REPLY */ + finddir_last = (Nameddir)hcalloc(sizeof(struct nameddir)); + finddir_last->node.nam = zhtricat("[", dupstring(ares[0]), "]"); + finddir_last->dir = dupstrpfx(finddir_full, len); + finddir_last->diff = len - strlen(finddir_last->node.nam); + finddir_best = len; } return finddir_last; @@ -3212,7 +3217,7 @@ getshfunc(char *nam) char ** subst_string_by_func(Shfunc func, char *arg1, char *orig) { - int osc = sfcontext; + int osc = sfcontext, osm = stopmsg; LinkList l = newlinklist(); char **ret; @@ -3228,6 +3233,47 @@ subst_string_by_func(Shfunc func, char *arg1, char *orig) ret = getaparam("reply"); sfcontext = osc; + stopmsg = osm; + return ret; +} + +/** + * Front end to subst_string_by_func to use hook-like logic. + * name can refer to a function, and name + "_hook" can refer + * to an array containing a list of functions. The functions + * are tried in order until one returns success. + */ +/**/ +char ** +subst_string_by_hook(char *name, char *arg1, char *orig) +{ + Shfunc func; + char **ret = NULL; + + if ((func = getshfunc(name))) { + ret = subst_string_by_func(func, arg1, orig); + } + + if (!ret) { + char **arrptr; + int namlen = strlen(name); + VARARR(char, arrnam, namlen + HOOK_SUFFIX_LEN); + memcpy(arrnam, name, namlen); + memcpy(arrnam + namlen, HOOK_SUFFIX, HOOK_SUFFIX_LEN); + + if ((arrptr = getaparam(arrnam))) { + /* Guard against internal modification of the array */ + arrptr = arrdup(arrptr); + for (; *arrptr; arrptr++) { + if ((func = getshfunc(*arrptr))) { + ret = subst_string_by_func(func, arg1, orig); + if (ret) + break; + } + } + } + } + return ret; } |