Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Re: PATCH: 3.1.5-pws-10: _tar
- X-seq: zsh-workers 5640
- From: Sven Wischnowsky <wischnow@xxxxxxxxxxxxxxxxxxxxxxx>
- To: zsh-workers@xxxxxxxxxxxxxx
- Subject: Re: PATCH: 3.1.5-pws-10: _tar
- Date: Thu, 4 Mar 1999 16:52:14 +0100 (MET)
- In-reply-to: Peter Stephenson's message of Tue, 02 Mar 1999 11:35:01 +0100
- Mailing-list: contact zsh-workers-help@xxxxxxxxxxxxxx; run by ezmlm
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<TAB>' 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 `[^<sep>]', where `<sep>' 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 `*<sep>'.
+
+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
`<user>@<host>'
+ _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@xxxxxxxxxxxxxxxxxxxxxxx
Messages sorted by:
Reverse Date,
Date,
Thread,
Author