summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--Completion/Zsh/Context/_dynamic_directory_name12
-rw-r--r--Doc/Zsh/contrib.yo52
-rw-r--r--Doc/Zsh/expn.yo25
-rw-r--r--Functions/Chpwd/.distfiles1
-rw-r--r--Functions/Chpwd/zsh_directory_name_cdr25
-rw-r--r--Functions/Misc/add-zsh-hook7
-rw-r--r--Src/subst.c4
-rw-r--r--Src/utils.c78
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;
 }