Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

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