From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 25421 invoked from network); 4 Mar 1999 15:53:23 -0000 Received: from sunsite.auc.dk (130.225.51.30) by ns1.primenet.com.au with SMTP; 4 Mar 1999 15:53:23 -0000 Received: (qmail 11505 invoked by alias); 4 Mar 1999 15:53:02 -0000 Mailing-List: contact zsh-workers-help@sunsite.auc.dk; run by ezmlm Precedence: bulk X-No-Archive: yes X-Seq: 5640 Received: (qmail 11495 invoked from network); 4 Mar 1999 15:53:01 -0000 Date: Thu, 4 Mar 1999 16:52:14 +0100 (MET) Message-Id: <199903041552.QAA02979@beta.informatik.hu-berlin.de> From: Sven Wischnowsky To: zsh-workers@sunsite.auc.dk In-reply-to: Peter Stephenson's message of Tue, 02 Mar 1999 11:35:01 +0100 Subject: Re: PATCH: 3.1.5-pws-10: _tar Peter Stephenson wrote: > Here's an enhancement to the completion for tar; a lot of it is just > comment, which doesn't clog your shell. Its main feature is that > completing files for extraction is now pretty close to ordinary file > handling. Hrmpf. `tar zxf zsh-3.1.5-pws-10.tar.gz z/s/z/z_tr' didn't work which is unacceptable for me. Also, completion of filenames with metacharacters and white spaces didn't work. The things below adds a helper function `_multi_parts' that gets two arguments: a separator character and an array (name or `(...)'). It will then complete the parts of the words that are separated by the separator character. It also changes the `_tar' function to use `_multi_parts', maybe Peter would like to put the changed one into `_tar2' so that users can decide which one they want. There is also a bit of fixing in `_path_files' (four double quotes). Bye Sven diff -u -r oc/Core/_multi_parts Completion/Core/_multi_parts --- oc/Core/_multi_parts Thu Mar 4 16:42:54 1999 +++ Completion/Core/_multi_parts Thu Mar 4 16:42:38 1999 @@ -0,0 +1,173 @@ +#autoload + +# This gets two arguments, a separator (which should be only one +# character) and an array. As usual, the array may be given by it's +# name or literal as in `(foo bar baz)' (words separated by spaces in +# parentheses). +# The parts of words from the array that are separated by the +# separator character are then completed independently. + +local sep matches patstr orig matchflags pref i tmp1 tmp2 gsep nm + +_match_test _multi_parts || return 1 + +# Save the current number of matches to be able to return if we added +# matches or not. + +nm=$compstate[nmatches] + +# Get the arguments, first the separator, then the array. The array is +# stored in `matches'. Further on this array will always contain those +# words from the original array that still match everything we have +# tried to match while we walk through the string from the line. + +sep="$1" +if [[ "${2[1]}" = '(' ]]; then + matches=( ${2[2,-2]} ) +else + matches=( "${(@P)2}" ) +fi + +# Since we will do some matching and this is not globbing, a `/' will +# not be treated specially in the matching. So we will have to replace +# `*'s we get by expressions of the form `[^]', where `' is +# our separator. We will do this using modifiers of the form +# `:gs/.../.../'. But if the separator is a slash, this will not work, +# so we need to get a character to separate the parts of the `:gs' +# that is different than the separator character we got. This +# character is stored in `gsep'. + +if [[ "$sep" = / ]]; then + gsep=. +else + gsep=/ +fi + +# Now build the pattern from what we have on the line. We also save +# the original string in `orig'. The `eval' is used to replace our +# separator character by `*'. + +patstr="${PREFIX:q}*${SUFFIX:q}*" +orig="${PREFIX:q}${SUFFIX:q}" + +matchflags="" +_match_pattern _path_files patstr matchflags +[[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)" + +eval patstr\="\$patstr:gs-${sep}-\*${sep}-:gs/\*\*/\*/" + +# First we will skip over those parts of the matches for which we have +# exact substrings on the line. In `pref' we will build the +# unambiguous prefix string. + +pref='' +while [[ "$orig" = *${sep}* ]] do + + # First build the pattern to use, then collect all strings from + # `matches' that match the prefix we have and the exact substring in + # the array `tmp1'. + + eval "pat=\"\${\${patstr#*\${sep}}:gs${gsep}*${gsep}[^${sep}]#${gsep}}\"" + tmp1=( "${(@M)matches:#${~matchflags}${orig%%${sep}*}${sep}${~pat}}" ) + + # If there are no words matching the exact substring, stop. + + (( $#tmp1 )) || break + + # Otherwise add the part to the prefix, remove it from the matches + # (which will also remove all words not matching the string at all), + # and set `patstr' and `orig' to the next component. + + pref="$pref${orig%%${sep}*}${sep}" + matches=( "${(@)${(@)matches#${orig%%${sep}*}${sep}}:#}" ) + orig="${orig#*${sep}}" + patstr="${patstr#*${sep}}" +done + +# Now we get all the words that still match in `tmp1'. + +eval "pat=\"\$patstr:gs${gsep}*${gsep}[^${sep}]#${gsep}\"" +tmp1=( "${(@M)matches:#${~matchflags}${~pat}}" ) + +if (( $#tmp1 )); then + + # There are words that are matched, put them int `matches' and then + # move all unambiguous components from the beginning into `pref'. + + matches=( "$tmp1[@]" ) + while [[ "$matches[1]" = *${sep}* ]]; do + + # We just take the first component of the first match and see if + # there are other matches with a different prefix (these are + # collected in `tmp2'). If there are any, we give up. + + tmp1="${matches[1]%%${sep}*}${sep}" + tmp2=( "${(@)matches:#${tmp1}*}" ) + (( $#tmp2 )) && break + + # All matches have the same prefix, but it into `pref' and remove + # it from the matches. + + pref="$pref$tmp1" + matches=( "${(@)${(@)matches#$tmp1}:#}" ) + done + + # Now we can tell the completion code about the things we + # found. Strings that have a separator will be added with a suffix. + + for i in "$matches[@]" ; do + if [[ "$i" = *${sep}* ]]; then + compadd -U -i "$IPREFIX" -p "$pref" -s "${sep}${i#*${sep}}" - "${i%%${sep}*}" + else + compadd -U -i "$IPREFIX" -p "$pref" - "$i" + fi + done + +elif [[ "$patstr" = */* ]]; then + + # We had no words matching the string from the line. But we want to + # be friendly and at least expand the prefix as far as we can. So we + # will loop through the rest of the string from the line and test + # the components one by one. + + while [[ "$patstr" = *${sep}* ]]; do + + # First we get all words matching at least this component in + # `tmp1'. If there are none, we give up. + + tmp1=( "${(@M)matches:#${~matchflags}${~patstr%%${sep}*}${sep}*}" ) + (( $#tmp1 )) || break + + # Then we check if there are words that have a different prefix. + + tmp2=( "${(@)tmp1:#${tmp1[1]%%${sep}*}${sep}*}" ) + if (( $#tmp2 )); then + + # There are words with another prefix, so we have found an + # ambiguous component. So we just give all possible prefixes to + # the completion code together with our prefix and the rest of + # the string from the line as the suffix. + + compadd -U -S '' -i "$IPREFIX" -p "$pref" \ + -s "${sep}${orig#*${sep}}" - "${(@)matches%%${sep}*}" + return 0 + fi + + # All words have the same prefix, so add it to `pref' again and + # try the next component. + + pref="$pref${tmp1[1]%%${sep}*}${sep}" + matches=( "${(@)matches#${tmp1[1]%%${sep}*}${sep}}" ) + orig="${orig#*${sep}}" + patstr="${patstr#*${sep}}" + done + + # Finally, add the unambiguous prefix and the rest of the string + # from the line. + + compadd -U -S '' -i "$IPREFIX" -p "$pref" - "$orig" +fi + +# This sets the return value to indicate that we added matches (or not). + +[[ nm -ne compstate[nmatches] ]] diff -u -r oc/Core/_path_files Completion/Core/_path_files --- oc/Core/_path_files Wed Mar 3 17:21:55 1999 +++ Completion/Core/_path_files Thu Mar 4 14:53:01 1999 @@ -245,7 +245,7 @@ # the suffixes we just built are used to produce possible matches # via globbing. - for i in $tmp1; do + for i in "$tmp1[@]" ; do tmp2=( ${~i}/${~matchflags}${~suffixes} ) [[ $#tmp2 -ne 0 ]] && collect=( $collect $i ) done @@ -329,6 +329,6 @@ else compadd -U "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \ -i "$IPREFIX" -p "$linepath$testpath" -f "$ignore[@]" \ - -W "$prepath$realpath$testpath" - ${(@)tmp2#$tmp1} + -W "$prepath$realpath$testpath" - "${(@)tmp2#$tmp1}" fi done diff -u -r oc/User/_tar Completion/User/_tar --- oc/User/_tar Tue Mar 2 12:37:56 1999 +++ Completion/User/_tar Thu Mar 4 14:55:17 1999 @@ -52,39 +52,14 @@ # on the file, keeping the list of filenames cached, plus the # name of the tarfile so we know if it changes. local largs=-tf + [[ $words[2] = *z* ]] && largs=-tzf [[ $words[2] = *Z* ]] && largs=-tZf if [[ $tf != $tar_cache_name ]]; then - tar_cache_list=($($words[1] $largs $tf)) + tar_cache_list=("${(@f)$($words[1] $largs $tf)}") tar_cache_name=$tf fi - - local pref matched matchdir - # Now generate the matches. First, treat a directory prefix - # separately, just like for real files. - [[ $PREFIX = */* ]] && pref="${PREFIX%/*}/" - if [[ $SUFFIX != */* ]]; then - # From inner to outer: - # Filter out anything which does not match what's on the line, - # remembering no / should come between $PREFIX and $SUFFIX; - # remove $pref from the remainder; - # filter out anything with extra directories beyond what we need. - matched=( - ${${${tar_cache_list##^${~PREFIX}[^/]#${~SUFFIX}*}#$pref}##*/*?} - ) - # We need to separate matches with a slash at the end, where - # something else could be completed. Instead of making the slash - # a suffix, since that wouldn't appear in the listing, we leave - # it but add matches with an empty suffix. - matchdir=(${matched##^*/}) - (( $#matchdir )) && compadd -p "$pref" -S '' $matchdir - matched=(${matched##*/}) - (( $#matched )) && compadd -p "$pref" $matched - else - # Completing in the middle: don't trim trailing suffixes. - matched=(${tar_cache_list##^${~PREFIX}*${~SUFFIX}#$pref}) - (( $#matched )) && compadd -p "$pref" $matched - fi + _multi_parts / tar_cache_list elif [[ "$tcmd" = *c*f* && $CURRENT -ge 4 ]] then _files elif [[ "$tcmd" = *[zZ]*f* && $CURRENT -eq 3 ]] then --- oc/README Mon Mar 1 14:19:38 1999 +++ Completion/README Thu Mar 4 16:49:42 1999 @@ -29,6 +29,9 @@ _comp_parts Utility used for completing words with multiple separate parts, such as `@' + _multi_parts + Utility for completion parts of words given a separator character and + a list of words. _compalso Utility for calling a function to add additional completions to an already existing set. -- Sven Wischnowsky wischnow@informatik.hu-berlin.de