1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-09-02 10:01:11 +02: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>
* 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 $
*****************************************************

View file

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

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
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.
name syntax by using the supplied function tt(zsh_directory_name_cdr)
a hook:
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)
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)

View file

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

View file

@ -5,4 +5,5 @@ _cdr
chpwd_recent_add
chpwd_recent_dirs
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.
# 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

View file

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

View file

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