1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-10-29 05:21:00 +01:00

users/15864: turn zsh_directory_name into a hook

This commit is contained in:
Peter Stephenson 2011-03-11 16:32:07 +00:00
parent cc69ecfb9e
commit f2dca9e155
9 changed files with 148 additions and 62 deletions

View file

@ -1,5 +1,11 @@
2011-03-11 Peter Stephenson <pws@csr.com> 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 * 28886: Src/Zle/zle_utils.c: Fix 28772 for the case where
regions have the "P" flag to include $PREDISPLAY in the regions have the "P" flag to include $PREDISPLAY in the
offsets. offsets.
@ -14316,5 +14322,5 @@
***************************************************** *****************************************************
* This is used by the shell to define $ZSH_PATCHLEVEL * This is used by the shell to define $ZSH_PATCHLEVEL
* $Revision: 1.5218 $ * $Revision: 1.5219 $
***************************************************** *****************************************************

View file

@ -1,7 +1,15 @@
#autoload #autoload
if [[ -n $functions[zsh_directory_name] ]]; then local func
zsh_directory_name c 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 else
_message 'dynamic directory name: implemented as zsh_directory_name c' _message 'dynamic directory name: implemented as zsh_directory_name c'
fi fi

View file

@ -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 The shell function tt(add-zsh-hook) provides a simple way of adding or
removing functions from the array. removing functions from the array.
var(hook) is one of tt(chpwd), tt(periodic), tt(precmd) or tt(preexec), var(hook) is one of tt(chpwd), tt(periodic), tt(precmd), tt(preexec),
the special functions in question. 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 are given this will be added to the array of functions to be executed
in the given context. 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 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 and any matching names of functions are removed from the array of
functions to be executed. 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() enditem()
@ -544,36 +551,15 @@ enditem()
subsect(Use with dynamic directory naming) subsect(Use with dynamic directory naming)
It is possible to refer to recent directories using the dynamic directory 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 name syntax by using the supplied function tt(zsh_directory_name_cdr)
autoload a function tt(zsh_directory_name) containing the following code, a hook:
tt(~[1]) will refer to the most recent directory other than $PWD, and so on.
This also includes completion.
example(if [[ $1 = n ]]; then example(autoload -Uz add-zsh-hook
if [[ $2 = <-> ]]; then add-zsh-hook -Uz zsh_directory_name zsh_directory_name_cdr)
# Recent directory
typeset -ga reply When this is done, tt(~[1]) will refer to the most recent
autoload -Uz cdr directory other than $PWD, and so on. Completion after tt(~[)var(...)
cdr -r also works.
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)
subsect(Details of directory handling) subsect(Details of directory handling)

View file

@ -1517,8 +1517,12 @@ cindex(directories, named, dynamic)
cindex(named directories, dynamic) cindex(named directories, dynamic)
cindex(dynamic named directories) cindex(dynamic named directories)
The feature described here is only available if the shell function If the function tt(zsh_directory_name) exists, or the shell variable
tt(zsh_directory_name) exists. 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 A `tt(~)' followed by a string var(namstr) in unquoted square brackets is
treated specially as a dynamic directory name. Note that the first 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 (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 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 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 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 arguments: the string tt(d) (for directory) and the candidate for dynamic
naming. The function should either return non-zero status, if the 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 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 length matched (16 in the example) is longer than that matched by any
static name. 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 complete dynamic names for directories. The code for this should be
as for any other completion function as described in as for any other completion function as described in
ifnzman(noderef(Completion System))\ ifnzman(noderef(Completion System))\

View file

@ -5,4 +5,5 @@ _cdr
chpwd_recent_add chpwd_recent_add
chpwd_recent_dirs chpwd_recent_dirs
chpwd_recent_filehandler chpwd_recent_filehandler
zsh_directory_name_cdr
' '

View file

@ -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

View file

@ -1,6 +1,6 @@
# Add to HOOK the given FUNCTION. # Add to HOOK the given FUNCTION.
# HOOK is one of chpwd, precmd, preexec, periodic, zshaddhistory, # 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 # With -d, remove the function from the hook instead; delete the hook
# variable if it is empty. # variable if it is empty.
@ -15,7 +15,10 @@
emulate -L zsh emulate -L zsh
local -a hooktypes local -a hooktypes
hooktypes=(chpwd precmd preexec periodic zshaddhistory zshexit) hooktypes=(
chpwd precmd preexec periodic zshaddhistory zshexit
zsh_directory_name
)
local opt local opt
local -a autoopts local -a autoopts

View file

@ -579,7 +579,6 @@ filesubstr(char **namptr, int assign)
char *str = *namptr; char *str = *namptr;
if (*str == Tilde && str[1] != '=' && str[1] != Equals) { if (*str == Tilde && str[1] != '=' && str[1] != Equals) {
Shfunc dirfunc;
char *ptr, *tmp, *res, *ptr2; char *ptr, *tmp, *res, *ptr2;
int val; int val;
@ -594,12 +593,11 @@ filesubstr(char **namptr, int assign)
*namptr = dyncat((tmp = oldpwd) ? tmp : pwd, str + 2); *namptr = dyncat((tmp = oldpwd) ? tmp : pwd, str + 2);
return 1; return 1;
} else if (str[1] == Inbrack && } else if (str[1] == Inbrack &&
(dirfunc = getshfunc("zsh_directory_name")) &&
(ptr2 = strchr(str+2, Outbrack))) { (ptr2 = strchr(str+2, Outbrack))) {
char **arr; char **arr;
untokenize(tmp = dupstrpfx(str+2, ptr2 - (str+2))); untokenize(tmp = dupstrpfx(str+2, ptr2 - (str+2)));
remnulargs(tmp); remnulargs(tmp);
arr = subst_string_by_func(dirfunc, "n", tmp); arr = subst_string_by_hook("zsh_directory_name", "n", tmp);
res = arr ? *arr : NULL; res = arr ? *arr : NULL;
if (res) { if (res) {
*namptr = dyncat(res, ptr2+1); *namptr = dyncat(res, ptr2+1);

View file

@ -890,7 +890,8 @@ finddir(char *s)
{ {
static struct nameddir homenode = { {NULL, "", 0}, NULL, 0 }; static struct nameddir homenode = { {NULL, "", 0}, NULL, 0 };
static int ffsz; static int ffsz;
Shfunc func = getshfunc("zsh_directory_name"); char **ares;
int len;
/* Invalidate directory cache if argument is NULL. This is called * /* Invalidate directory cache if argument is NULL. This is called *
* whenever a node is added to or removed from the hash table, and * * whenever a node is added to or removed from the hash table, and *
@ -906,9 +907,16 @@ finddir(char *s)
return finddir_last = NULL; return finddir_last = NULL;
} }
/* It's not safe to use the cache while we have function transformations.*/ #if 0
if(!func && !strcmp(s, finddir_full) && *finddir_full) /*
* 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; return finddir_last;
#endif
if ((int)strlen(s) >= ffsz) { if ((int)strlen(s) >= ffsz) {
free(finddir_full); free(finddir_full);
@ -920,18 +928,15 @@ finddir(char *s)
finddir_scan(&homenode.node, 0); finddir_scan(&homenode.node, 0);
scanhashtable(nameddirtab, 0, 0, 0, finddir_scan, 0); scanhashtable(nameddirtab, 0, 0, 0, finddir_scan, 0);
if (func) { ares = subst_string_by_hook("zsh_directory_name", "d", finddir_full);
char **ares = subst_string_by_func(func, "d", finddir_full); if (ares && arrlen(ares) >= 2 &&
int len; (len = (int)zstrtol(ares[1], NULL, 10)) > finddir_best) {
if (ares && arrlen(ares) >= 2 && /* better duplicate this string since it's come from REPLY */
(len = (int)zstrtol(ares[1], NULL, 10)) > finddir_best) { finddir_last = (Nameddir)hcalloc(sizeof(struct nameddir));
/* better duplicate this string since it's come from REPLY */ finddir_last->node.nam = zhtricat("[", dupstring(ares[0]), "]");
finddir_last = (Nameddir)hcalloc(sizeof(struct nameddir)); finddir_last->dir = dupstrpfx(finddir_full, len);
finddir_last->node.nam = zhtricat("[", dupstring(ares[0]), "]"); finddir_last->diff = len - strlen(finddir_last->node.nam);
finddir_last->dir = dupstrpfx(finddir_full, len); finddir_best = len;
finddir_last->diff = len - strlen(finddir_last->node.nam);
finddir_best = len;
}
} }
return finddir_last; return finddir_last;
@ -3212,7 +3217,7 @@ getshfunc(char *nam)
char ** char **
subst_string_by_func(Shfunc func, char *arg1, char *orig) subst_string_by_func(Shfunc func, char *arg1, char *orig)
{ {
int osc = sfcontext; int osc = sfcontext, osm = stopmsg;
LinkList l = newlinklist(); LinkList l = newlinklist();
char **ret; char **ret;
@ -3228,6 +3233,47 @@ subst_string_by_func(Shfunc func, char *arg1, char *orig)
ret = getaparam("reply"); ret = getaparam("reply");
sfcontext = osc; 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; return ret;
} }