From 2769b198812abf82cc3b074852714959213f266a Mon Sep 17 00:00:00 2001 From: Sven Wischnowsky Date: Mon, 19 Jun 2000 09:32:30 +0000 Subject: improve filename completion; use accept-exact for in-path completion; new fake style (11971) --- ChangeLog | 4 + Completion/Core/_path_files | 58 ++++++------- Doc/Zsh/compsys.yo | 18 ++++ Src/Zle/compcore.c | 4 +- Src/Zle/computil.c | 203 +++++++++++++++++++++++++++++++++++++++----- 5 files changed, 233 insertions(+), 54 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7d5eb22b2..18c8fe9c2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,10 @@ 2000-06-19 Sven Wischnowsky + * 11971: Completion/Core/_path_files, Doc/Zsh/compsys.yo, + Src/Zle/compcore.c, Src/Zle/computil.c: improve filename + completion; use accept-exact for in-path completion; new fake style + * users/3188: Completion/Core/_description, Completion/Core/_setup, Doc/Zsh/compsys.yo: restore ZLS_COLORS when possible; better group-name handling in ZLS_COLORS diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files index 7f6f83184..250f6c750 100644 --- a/Completion/Core/_path_files +++ b/Completion/Core/_path_files @@ -6,7 +6,7 @@ local linepath realpath donepath prepath testpath exppath skips skipped local tmp1 tmp2 tmp3 tmp4 i orig eorig pre suf tpre tsuf opre osuf cpre local pats haspats ignore pfxsfx sopt gopt opt sdirs ignpar cfopt -local nm=$compstate[nmatches] menu matcher mopts sort match mid +local nm=$compstate[nmatches] menu matcher mopts sort match mid accex fake typeset -U prepaths exppaths @@ -139,12 +139,14 @@ else skips='((.|..)/)##' fi -zstyle -s ":completion:${curcontext}:paths" special-dirs sdirs && - [[ "$sdirs" = (yes|true|on|1) ]] && sdirs=yes +zstyle -s ":completion:${curcontext}:paths" special-dirs sdirs [[ "$pats" = ((|*[[:blank:]])\*(|[[:blank:]]*)|*\([^[:blank:]]#/[^[:blank:]]#\)*) ]] && sopt=$sopt/ +zstyle -a ":completion:${curcontext}:files" accept-exact accex +zstyle -a ":completion:${curcontext}:files" fake fake + zstyle -s ":completion:${curcontext}:files" ignore-parents ignpar if [[ -n "$compstate[pattern_match]" && @@ -314,33 +316,33 @@ for prepath in "$prepaths[@]"; do # Get the matching files by globbing. if [[ "$tpre$tsuf" = */* ]]; then - compfiles -P$cfopt tmp1 "$skipped" "$_matcher" "$sdirs" + compfiles -P$cfopt tmp1 accex "$skipped" "$_matcher" "$sdirs" fake elif [[ "$sopt" = *[/f]* ]]; then - compfiles -p$cfopt tmp1 "$skipped" "$_matcher" "$sdirs" "$pats[@]" + compfiles -p$cfopt tmp1 accex "$skipped" "$_matcher" "$sdirs" fake "$pats[@]" else - compfiles -p$cfopt tmp1 "$skipped" "$_matcher" '' "$pats[@]" + compfiles -p$cfopt tmp1 accex "$skipped" "$_matcher" '' fake "$pats[@]" fi tmp1=( $~tmp1 ) if [[ -n "$PREFIX$SUFFIX" ]]; then # See which of them match what's on the line. - if [[ -n "$_comp_correct" ]]; then - tmp2=( "$tmp1[@]" ) - builtin compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp1:t}" - - if [[ $#tmp1 -eq 0 ]]; then - tmp1=( "$tmp2[@]" ) - compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp2:t}" - fi - else - if [[ "$tmp1[1]" = */* ]]; then + if [[ "$tmp1[1]" = */* ]]; then + if [[ -n "$_comp_correct" ]]; then tmp2=( "$tmp1[@]" ) + builtin compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp1:t}" + + if [[ $#tmp1 -eq 0 ]]; then + tmp1=( "$tmp2[@]" ) + compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp2:t}" + fi else - tmp2=( '' ) + tmp2=( "$tmp1[@]" ) + compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp1:t}" fi - - compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp1:t}" + else + tmp2=( '' ) + compadd -D tmp1 -F _comp_ignore "$matcher[@]" -a tmp1 fi # If no file matches, save the expanded path and continue with @@ -431,26 +433,18 @@ for prepath in "$prepaths[@]"; do tmp3="$pre$suf" tpre="$pre" tsuf="$suf" - tmp1=( "${(@)tmp1#${prepath}${realpath}${testpath}}" ) + [[ -n "${prepath}${realpath}${testpath}" ]] && + tmp1=( "${(@)tmp1#${prepath}${realpath}${testpath}}" ) while true; do # First we check if some of the files match the original string # for this component. If there are some we remove all other # names. This avoids having `foo' complete to `foo' and `foobar'. + # The return value is non-zero if the component is ambiguous. - if [[ "$tmp3" = */* ]]; then - tmp4=( "${(@M)tmp1:#${tmp3%%/*}/*}" ) - (( $#tmp4 )) && tmp1=( "$tmp4[@]" ) - fi - - # Next we see if this component is ambiguous. - - if [[ "$tmp3" = */* ]]; then - tmp4=$tmp1[(I)^${${tmp1[1]%%/*}//(#b)([][\\<>(|)^#~*?])/\\$match[1]}/*] - else - tmp4=$tmp1[(I)^${tmp1[1]//(#b)([][\\<>(|)^#~*?])/\\$match[1]}] - fi + compfiles -r tmp1 "$tmp3" + tmp4=$? if [[ "$tpre" = */* ]]; then tmp2="${cpre}${tpre%%/*}" diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo index 82a9fd5fe..2afd505fc 100644 --- a/Doc/Zsh/compsys.yo +++ b/Doc/Zsh/compsys.yo @@ -780,6 +780,13 @@ matches. If it is set to `true' for at least one match which is the same as the string on the line, this match will immediately be accepted. +When completing filenames (where it is looked up for the tt(files) +tag), this style also accepts any number of patterns as the value. If +this is used, pathnames matching one of these patterns will be +accepted immediately even if the command line contains some more +partially typed pathname components and these match no file under the +directory accepted. + Note that this is also used by the tt(_expand) completer to decide if words beginning with a tilde or parameter expansion should be expanded. This means that if, for example, there are parameters @@ -968,6 +975,17 @@ generated this way (e.g. due to the option tt(AUTO_MENU) being set), this will also cycle through the names of the files in pathname components after the first ambiguous one. ) +kindex(fake, completion style) +item(tt(fake))( +Currently, this style is only used when completing files and lookup up +with the tag tt(files). Its values are of the form +`var(dir)tt(:)var(names...)'. This will add the var(names) as +possible matches when completing in the directory var(dir), even if no +such files really exist. + +This can be useful on systems that support special filesystems whose +top-level pathnames can not be listed or generated with glob patterns. +) kindex(file-patterns, completion style) item(tt(file-patterns))( In most places where filenames are completed, the function tt(_files) diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index 7497d03b4..62d63000a 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -1914,8 +1914,8 @@ addmatches(Cadata dat, char **argv) if (aign || pign) { int il = ppl + sl + psl, addit = 1; - if (il > ilen) - ibuf = (char *) zhalloc((ilen = il) + 1); + if (il + 1> ilen) + ibuf = (char *) zhalloc((ilen = il) + 2); if (ppl) memcpy(ibuf, dat->ppre, ppl); diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index 51a1f61aa..3955ed69d 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -3058,17 +3058,37 @@ bin_compfmt(char *nam, char **args, char *ops, int func) #define PATH_MAX2 (PATH_MAX * 2) static LinkList -cfp_test_exact(LinkList names, char *skipped) +cfp_test_exact(LinkList names, char **accept, char *skipped) { char buf[PATH_MAX2 + 1], *suf, *p; int l, sl, found = 0; struct stat st; LinkNode node; - LinkList ret = newlinklist(); + LinkList ret = newlinklist(), alist = NULL; - if (!(compprefix && *compprefix) && !(compsuffix && *compsuffix)) + if ((!(compprefix && *compprefix) && !(compsuffix && *compsuffix)) || + (!accept || !*accept || + ((!strcmp(*accept, "false") || !strcmp(*accept, "no") || + !strcmp(*accept, "off") || !strcmp(*accept, "0")) && !accept[1]))) return NULL; + if (accept[1] || + (strcmp(*accept, "true") && strcmp(*accept, "yes") && + strcmp(*accept, "on") && strcmp(*accept, "1"))) { + Patprog prog; + + alist = newlinklist(); + + for (; (p = *accept); accept++) { + if (*p == '*' && !p[1]) { + alist = NULL; + break; + } + tokenize(p = dupstring(p)); + if ((prog = patcompile(p, 0, NULL))) + addlinknode(alist, prog); + } + } sl = strlen(skipped) + (compprefix ? strlen(compprefix) : 0) + (compsuffix ? strlen(compsuffix) : 0); @@ -3078,11 +3098,22 @@ cfp_test_exact(LinkList names, char *skipped) suf = dyncat(skipped, rembslash(dyncat(compprefix, compsuffix))); for (node = firstnode(names); node; incnode(node)) { - if ((l = strlen(p = (char *) getdata(node))) && l + sl < PATH_MAX2) { + l = strlen(p = (char *) getdata(node)); + if (l + sl < PATH_MAX2) { strcpy(buf, p); strcpy(buf + l, suf); - if (!ztat(buf, &st, 0) && S_ISDIR(st.st_mode)) { + if (!ztat(buf, &st, 0)) { + if (alist) { + LinkNode anode; + + for (anode = firstnode(alist); anode; incnode(anode)) + if (pattry((Patprog) getdata(anode), buf)) + break; + + if (!anode) + continue; + } found = 1; addlinknode(ret, dupstring(buf)); } @@ -3334,12 +3365,14 @@ cfp_bld_pats(int dirs, LinkList names, char *skipped, char **pats) } static LinkList -cfp_add_sdirs(LinkList final, LinkList orig, char *skipped, char *sdirs) +cfp_add_sdirs(LinkList final, LinkList orig, char *skipped, + char *sdirs, char **fake) { int add = 0; - if (*sdirs) { - if (!strcmp(sdirs, "yes")) + if (*sdirs && (isset(GLOBDOTS) || (compprefix && *compprefix == '.'))) { + if (!strcmp(sdirs, "yes") || !strcmp(sdirs, "true") || + !strcmp(sdirs, "on") || !strcmp(sdirs, "1")) add = 2; else if (!strcmp(sdirs, "..")) add = 1; @@ -3350,10 +3383,62 @@ cfp_add_sdirs(LinkList final, LinkList orig, char *skipped, char *sdirs) char *s2 = (add == 2 ? dyncat(skipped, ".") : NULL), *m; for (node = firstnode(orig); node; incnode(node)) { - if (*(m = (char *) getdata(node))) { - addlinknode(final, dyncat((char *) getdata(node), s1)); + if ((m = (char *) getdata(node))) { + addlinknode(final, dyncat(m, s1)); if (s2) - addlinknode(final, dyncat((char *) getdata(node), s2)); + addlinknode(final, dyncat(m, s2)); + } + } + } + if (fake && *fake) { + LinkNode node; + char *m, *f, *p, *t, *a, c; + int sl = strlen(skipped) + 1; + struct stat st1, st2; + + for (; (f = *fake); fake++) { + f = dupstring(f); + for (p = t = f; *p; p++) { + if (*p == ':') + break; + else if (*p == '\\' && p[1]) + p++; + *t++ = *p; + } + if (*p) { + *t = *p++ = '\0'; + if (!*p) + continue; + + for (node = firstnode(orig); node; incnode(node)) { + if ((m = (char *) getdata(node)) && + (!strcmp(f, m) || + (!stat(f, &st1) && !stat((*m ? m : "."), &st2) && + st1.st_dev == st2.st_dev && + st1.st_ino == st2.st_ino))) { + while (*p) { + while (*p && inblank(*p)) + p++; + if (!*p) + break; + for (f = t = p; *p; p++) { + if (inblank(*p)) + break; + else if (*p == '\\' && p[1]) + p++; + *t++ = *p; + } + c = *t; + *t = '\0'; + a = (char *) zhalloc(strlen(m) + sl + strlen(f)); + strcpy(a, m); + strcat(a, skipped); + strcat(a, f); + addlinknode(final, a); + *t = c; + } + } + } } } } @@ -3361,14 +3446,14 @@ cfp_add_sdirs(LinkList final, LinkList orig, char *skipped, char *sdirs) } static LinkList -cf_pats(int dirs, int noopt, LinkList names, char *skipped, char *matcher, - char *sdirs, char **pats) +cf_pats(int dirs, int noopt, LinkList names, char **accept, char *skipped, + char *matcher, char *sdirs, char **fake, char **pats) { LinkList ret; char *dpats[2]; - if (dirs && (ret = cfp_test_exact(names, skipped))) - return cfp_add_sdirs(ret, names, skipped, sdirs); + if ((ret = cfp_test_exact(names, accept, skipped))) + return cfp_add_sdirs(ret, names, skipped, sdirs, fake); if (dirs) { dpats[0] = "*(-/)"; @@ -3379,7 +3464,7 @@ cf_pats(int dirs, int noopt, LinkList names, char *skipped, char *matcher, cfp_opt_pats(pats, matcher); return cfp_add_sdirs(cfp_bld_pats(dirs, names, skipped, pats), - names, skipped, sdirs); + names, skipped, sdirs, fake); } static void @@ -3421,6 +3506,61 @@ cf_ignore(char **names, LinkList ign, char *style, char *path) } } +static LinkList +cf_remove_other(char **names, char *pre, int *amb) +{ + char *p; + + if ((p = strchr(pre, '/'))) { + char **n; + + *p = '\0'; + pre = dyncat(pre, "/"); + *p = '/'; + + for (n = names; *n; n++) + if (strpfx(pre, *n)) + break; + + if (*n) { + LinkList ret = newlinklist(); + + for (; *names; names++) + if (strpfx(pre, *names)) + addlinknode(ret, dupstring(*names)); + + *amb = 0; + + return ret; + } else { + if (!(p = *names++)) + *amb = 0; + else { + char *q; + + if ((q = strchr((p = dupstring(p)), '/'))) + *q = '\0'; + + for (; *names; names++) + if (!strpfx(p, *names)) { + *amb = 1; + return NULL; + } + } + } + } else { + if (!(p = *names++)) + *amb = 0; + else + for (; *names; names++) + if (strcmp(p, *names)) { + *amb = 1; + return NULL; + } + } + return NULL; +} + static int bin_compfiles(char *nam, char **args, char *ops, int func) { @@ -3438,8 +3578,8 @@ bin_compfiles(char *nam, char **args, char *ops, int func) char **tmp; LinkList l; - if (!args[1] || !args[2] || !args[3] || !args[4] || - (args[0][1] == 'p' && !args[5])) { + if (!args[1] || !args[2] || !args[3] || !args[4] || !args[5] || + !args[6] || (args[0][1] == 'p' && !args[7])) { zwarnnam(nam, "too few arguments", NULL, 0); return 1; } @@ -3450,8 +3590,9 @@ bin_compfiles(char *nam, char **args, char *ops, int func) for (l = newlinklist(); *tmp; tmp++) addlinknode(l, *tmp); set_list_array(args[1], cf_pats((args[0][1] == 'P'), !!args[0][2], - l, args[2], args[3], args[4], - args + 5)); + l, getaparam(args[2]), args[3], + args[4], args[5], + getaparam(args[6]), args + 7)); return 0; } case 'i': @@ -3483,6 +3624,28 @@ bin_compfiles(char *nam, char **args, char *ops, int func) set_list_array(args[2], l); return 0; } + case 'r': + { + char **tmp; + LinkList l; + int ret = 0; + + if (!args[1] || !args[2]) { + zwarnnam(nam, "too few arguments", NULL, 0); + return 1; + } + if (args[3]) { + zwarnnam(nam, "too many arguments", NULL, 0); + return 1; + } + if (!(tmp = getaparam(args[1]))) { + zwarnnam(nam, "unknown parameter: %s", args[1], 0); + return 0; + } + if ((l = cf_remove_other(tmp, args[2], &ret))) + set_list_array(args[1], l); + return ret; + } } zwarnnam(nam, "invalid option: %s", *args, 0); return 1; -- cgit 1.4.1