zsh-workers
 help / color / mirror / code / Atom feed
From: Marlon Richert <marlon.richert@gmail.com>
To: Bart Schaefer <schaefer@brasslantern.com>
Cc: Zsh hackers list <zsh-workers@zsh.org>, Oliver Kiddle <opk@zsh.org>
Subject: [PATCH] Make _expand handle aliases (was Re: [PATCH] Make _expand_alias more usable as a completer)
Date: Sun, 1 Aug 2021 21:50:22 +0300	[thread overview]
Message-ID: <CAHLkEDs+4vv0xqq2u5g6msgpstrErFjqEFshHocfbqqYyh-SVQ@mail.gmail.com> (raw)
In-Reply-To: <CAH+w=7bhr0Gs1p1io6a9kG2bZS5Eqq9P+OD6y6NDRuNT36ZzUg@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 1696 bytes --]

On Wed, Jul 28, 2021 at 6:23 AM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> I think it's actually a bit weird to allow _expand_alias to work as a
> completer at all, because the completion system generally functions
> very badly when the matches do not consist of single words, and
> aliases don't usually have one-word replacements.  As soon as you
> introduce something that contains spaces, completion gets very
> confused (unless you force it directly into menu completion or
> selection); if (otherwise) you attempt to press TAB twice to enter
> menu completion, it'll instead start a new completion based on the
> word after the rightmost space that is still to the left of the
> cursor.  This is especially true when using compadd -U.
>
> I suspect this may be where Oliver's tests and Marlon's attempt to
> reproduce, went astray from one another.  In any case, I'm fairly sure
> this is the reason that _expand_alias (before the patch) does not
> allow other completers to run, and similarly it's why the user is
> admonished to place _expand before _complete in the zstyle.
>
> Looking independently at the two questions, my feeling is I'd avoid
> the patch as-is, but it wouldn't be unreasonable for _expand to handle
> aliases (under control of a zstyle).

Alright, here's a new patch, which moves the alias expansion code from
_expand_alias to _expand (and refactors it) and deprecates
_expand_alias's use as a completer. I also updated the documentation
and added tests.

Note that I reused the zstyle names from _expand_alias in _alias. I
think their names are less than optimal in _expand, but renaming them
would break people's existing zstyle config for _expand_alias.

[-- Attachment #2: 0001-Make-_expand-handle-aliases.txt --]
[-- Type: text/plain, Size: 12629 bytes --]

From 7467abb482824650913636340844919be4bcf212 Mon Sep 17 00:00:00 2001
From: Marlon Richert <marlonrichert@users.noreply.github.com>
Date: Sun, 1 Aug 2021 21:40:01 +0300
Subject: [PATCH] Make _expand handle aliases

This deprecates _expand_alias's use as a completer.
---
 Completion/Base/Completer/_expand       | 74 ++++++++++++++++++++++++-
 Completion/Base/Completer/_expand_alias | 70 +++--------------------
 Doc/Zsh/compsys.yo                      | 62 +++++++++------------
 Test/Y01completion.ztst                 | 16 +++++-
 4 files changed, 120 insertions(+), 102 deletions(-)

diff --git a/Completion/Base/Completer/_expand b/Completion/Base/Completer/_expand
index 86b4ac6e4..7a24f8aa1 100644
--- a/Completion/Base/Completer/_expand
+++ b/Completion/Base/Completer/_expand
@@ -11,11 +11,12 @@ setopt localoptions nonomatch
 
 [[ _matcher_num -gt 1 ]] && return 1
 
-local exp word sort expr expl subd pref suf=" " force opt asp tmp opre pre epre
-local continue=0
+local -a exp subd tmpa match mbegin mend
+local -i continue=0
+local word sort expl pref suf=' ' force opt asp tmp opre pre epre
 
 (( $# )) &&
-    while getopts gsco opt; do
+    while getopts agsco opt; do
       force="$force$opt"
     done
 
@@ -25,6 +26,73 @@ else
   word="$IPREFIX$PREFIX$SUFFIX$ISUFFIX"
 fi
 
+# Alias expansion:
+() {
+
+  if { (( CURRENT == 1 )) && zstyle -T ":completion:${curcontext}:" regular } ||
+      zstyle -t ":completion:${curcontext}:" regular always; then 
+    exp=( $aliases[$word] )
+    if [[ -n "$exp" ]]; then
+      # If the first shell word in the expansion is the same as the alias, then 
+      # escape it, so it doesn't get re-expanded on execution.
+      [[ "${${(z)exp}[1]}" = "$word" ]] &&
+          exp=( "${(S)exp/#(#b)(*)$word/$match[1]\\$word}" )
+      return 0
+    fi
+  fi
+
+  if zstyle -T ":completion:${curcontext}:" global; then
+    exp=( $galiases[$word] )
+    if [[ -n "$exp" ]]; then
+      # Escape all occurences of the global alias in its expansion, so these 
+      # don't get re-expanded on execution.
+      tmp="$exp"
+      tmpa=( ${(z)tmp} )
+      exp=()
+      while (( ${#tmpa[@]} )); do
+        exp[1]+="${(M)tmp#*$tmpa[1]}"
+        tmp="${tmp#*$tmpa[1]}"
+        [[ "$tmpa[1]" == "$word" ]] &&
+            exp[1]="${exp/%$word/\\$word}"
+        shift tmpa
+      done
+      exp[1]+="$tmp"
+      tmp=
+      return 0
+    fi
+  fi
+
+  if zstyle -t ":completion:${curcontext}:" disabled; then
+    exp=( $dis_aliases[$word] )
+    [[ -n "$exp" ]] && 
+        return
+  fi
+
+  if zstyle -T ":completion:${curcontext}:" global && 
+      zstyle -t ":completion:${curcontext}:" disabled; then
+    exp=( $dis_galiases[$word] )
+    [[ -n "$exp" ]] &&
+        return
+  fi
+
+  return 1
+
+} && {
+  zstyle -T ":completion:${curcontext}:" add-space true yes on 1 &&
+      tmpa=( -S ' ' )
+  _description expansions expl expansions "o:$word"
+
+  if compadd "$expl[@]" -UQ $tmpa[@] -- $exp; then
+    ! zstyle -t ":completion:${curcontext}:" accept-exact continue
+    return
+  fi
+
+}
+
+# If we've been called with `-a`, then don't expand anything else than aliases.
+[[ "$force" = *a* ]] &&
+    return 1
+
 [[ "$word" = *\$(|\{[^\}]#) ||
    ( "$word" = *\$[a-zA-Z0-9_]## && $+parameters[${word##*\$}] -eq 0 ) ]] &&
     return 1
diff --git a/Completion/Base/Completer/_expand_alias b/Completion/Base/Completer/_expand_alias
index 8240e4162..c588dfadb 100644
--- a/Completion/Base/Completer/_expand_alias
+++ b/Completion/Base/Completer/_expand_alias
@@ -1,67 +1,15 @@
-#compdef -K _expand_alias complete-word \C-xa
+#compdef -K _expand_alias complete-word \C-Xa
 
-local word expl tmp pre sel what
-local -a tmpa suf
-
-eval "$_comp_setup"
-
-if [[ -n $funcstack[2] ]]; then
-  if [[ "$funcstack[2]" = _prefix ]]; then
-    word="$IPREFIX$PREFIX$SUFFIX"
-  else
-    word="$IPREFIX$PREFIX$SUFFIX$ISUFFIX"
-  fi
-  pre=()
+if (( ${funcstack[(I)_main_complete]} )); then
+  compstate[to_end]=match
+  _main_complete _expand_alias
 else
-  local curcontext="$curcontext"
 
-  if [[ -z "$curcontext" ]]; then
-    curcontext="expand-alias-word:::"
-  else
-    curcontext="expand-alias-word:${curcontext#*:}"
+  if _expand -a; then
+    compstate[insert]=1
+    return
   fi
 
-  word="$IPREFIX$PREFIX$SUFFIX$ISUFFIX"
-  pre=(_main_complete - aliases)
-fi
-
-zstyle -s ":completion:${curcontext}:" regular tmp || tmp=yes
-case $tmp in
-always) sel=r;;
-yes|1|true|on) [[ CURRENT -eq 1 ]] && sel=r;;
-esac
-zstyle -T ":completion:${curcontext}:" global && sel="g$sel"
-zstyle -t ":completion:${curcontext}:" disabled && sel="${sel}${(U)sel}"
-
-tmp=
-[[ $sel = *r* ]] && tmp=$aliases[$word]
-[[ -z $tmp && $sel = *g* ]] && tmp=$galiases[$word]
-[[ -z $tmp && $sel = *R* ]] && tmp=$dis_aliases[$word]
-[[ -z $tmp && $sel = *G* ]] && tmp=$dis_galiases[$word]
-
-if [[ -n $tmp ]]; then
-  # We used to remove the quoting from the value in the parameter.
-  # That was probably just an oversight: an alias is always replaced
-  # literally.
-  tmp=${tmp%%[[:blank:]]##}
-  if [[ $tmp[1] = [[:alnum:]_] ]]; then
-    tmpa=(${(z)tmp})
-    if [[ $tmpa[1] = $word && $tmp = $aliases[$word] ]]; then
-      # This is an active regular alias and the first word in the result
-      # is the same as what was on the line already.  Quote it so
-      # that it doesn't get reexanded on execution.
-      #
-      # Strictly we also need to check if the original word matches
-      # a later word in the expansion and the previous words are
-      # all aliases where the expansion ends in " ", but I'm
-      # too lazy.
-      tmp="\\$tmp"
-    fi
-  fi
-  zstyle -T ":completion:${curcontext}:" add-space || suf=( -S '' )
-  $pre _wanted aliases expl alias compadd -UQ "$suf[@]" -- ${tmp%%[[:blank:]]##}
-elif (( $#pre )) && zstyle -t ":completion:${curcontext}:" complete; then
-  $pre _aliases -s "$sel" -S ''
-else
-  return 1
+  zstyle -t ":completion:${curcontext}:" complete &&
+      _main_complete - expand-alias _aliases
 fi
diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo
index 89b918d60..88a368608 100644
--- a/Doc/Zsh/compsys.yo
+++ b/Doc/Zsh/compsys.yo
@@ -1179,10 +1179,10 @@ style (see below) to `false'.
 )
 kindex(add-space, completion style)
 item(tt(add-space))(
-This style is used by the tt(_expand) completer.  If it is `true' (the
-default), a space will be inserted after all words resulting from the 
-expansion, or a slash in the case of directory names.  If the value
-is `tt(file)', the completer will only add a space
+This style is used by the tt(_expand) completer and the tt(_expand_alias) 
+bindable command.  If it is `true' (the default), a space will be inserted 
+after all words resulting from the expansion, or a slash in the case of 
+directory names.  If the value is `tt(file)', the completer will only add a space
 to names of existing files.  Either a boolean `true' or the value
 `tt(file)' may be combined with `tt(subst)', in which case the completer
 will not add a space to words generated from the expansion of a
@@ -1295,10 +1295,9 @@ the two strings `tt(start)' and `tt(stop)'.
 )
 kindex(complete, completion style)
 item(tt(complete))(
-This is used by the tt(_expand_alias) function when invoked as a
-bindable command.  If set to `true' and the word on the command
-line is not the name of an alias, matching alias names will be
-completed.
+When the tt(_expand_alias) bindable command is invoked, if tt(complete) is set 
+to `true' and the word on the command line is not the name of an alias, 
+matching alias names will be completed.
 )
 kindex(complete-options, completion style)
 item(tt(complete-options))(
@@ -1362,8 +1361,8 @@ may be empty to force a delimiter to be typed.
 )
 kindex(disabled, completion style)
 item(tt(disabled))(
-If this is set to `true', the tt(_expand_alias) completer and bindable 
-command will try to expand disabled aliases, too.  The default is
+If this is set to `true', the tt(_expand) completer and the tt(_expand_alias) 
+bindable command will try to expand disabled aliases, too.  The default is
 `false'.
 )
 kindex(domains, completion style)
@@ -1656,8 +1655,8 @@ style) or else the original string from the line.
 )
 kindex(global, completion style)
 item(tt(global))(
-If this is set to `true' (the default), the tt(_expand_alias)
-completer and bindable command will try to expand global aliases.
+If this is set to `true' (the default), the tt(_expand) completer and the 
+tt(_expand_alias) bindable command will try to expand global aliases.
 )
 kindex(group-name, completion style)
 item(tt(group-name))(
@@ -2480,11 +2479,11 @@ tt(zle_tr<TAB>) can be completed to tt(Zle/zle_tricky.c).
 )
 kindex(regular, completion style)
 item(tt(regular))(
-This style is used by the tt(_expand_alias) completer and bindable 
-command.  If set to `true' (the default), regular aliases will be
-expanded but only in command position.  If it is set to `false',
-regular aliases will never be expanded.   If it is set to `tt(always)',
-regular aliases will be expanded even if not in command position.
+This style is used by the tt(_expand) completer and the tt(_expand_alias) 
+bindable command.  If set to `true' (the default), regular aliases will be
+expanded but only in command position.  If it is set to `false', regular 
+aliases will never be expanded.   If it is set to `tt(always)', regular aliases 
+will be expanded even if not in command position.
 )
 kindex(rehash, completion style)
 item(tt(rehash))(
@@ -3173,17 +3172,8 @@ tt(glob) and tt(subst-globs-only) styles.
 
 It is also possible to call tt(_expand) as a function, in which case the
 different modes may be selected with options: tt(-s) for
-tt(substitute), tt(-g) for tt(glob) and tt(-o) for tt(subst-globs-only).
-)
-findex(_expand_alias)
-item(tt(_expand_alias))(
-If the word the cursor is on is an alias, it is expanded and no other
-completers are called.  The types of aliases which are to be expanded can
-be controlled with the styles tt(regular), tt(global) and tt(disabled).
-
-This function is also a bindable command, see
-ifzman(the section `Bindable Commands' below)\
-ifnzman(noderef(Bindable Commands)).
+tt(substitute), tt(-g) for tt(glob) and tt(-o) for tt(subst-globs-only). 
+Alternatively, if called with tt(-a), tt(_expand) will do alias expansion only.
 )
 findex(_extensions)
 item(tt(_extensions))(
@@ -3406,17 +3396,15 @@ then calls the tt(_correct) completer.
 )
 findex(_expand_alias (^Xa))
 item(tt(_expand_alias) (tt(^Xa)))(
-This function can be used as a completer and as a bindable command.
-It expands the word the cursor is on if it is an alias.  The types of
-alias expanded can be controlled with the styles tt(regular), tt(global)
+This function expands the word the cursor is on if it is an alias.  The types 
+of alias expanded can be controlled with the styles tt(regular), tt(global)
 and tt(disabled).
 
-When used as a bindable command there is one additional feature that
-can be selected by setting the tt(complete) style to `true'.  In this
-case, if the word is not the name of an alias, tt(_expand_alias) tries
-to complete the word to a full alias name without expanding it.  It
-leaves the cursor directly after the completed word so that invoking
-tt(_expand_alias) once more will expand the now-complete alias name.
+Additionally, if the tt(complete) style is set to `true' and the word is not 
+the name of an alias, then tt(_expand_alias) tries to complete the word to a 
+full alias name without expanding it.  It leaves the cursor directly after the 
+completed word so that invoking tt(_expand_alias) once more will expand the 
+now-complete alias name.
 )
 findex(_expand_word (^Xe))
 item(tt(_expand_word) (tt(^Xe)))(
diff --git a/Test/Y01completion.ztst b/Test/Y01completion.ztst
index 882a0adc4..ed7f6738d 100644
--- a/Test/Y01completion.ztst
+++ b/Test/Y01completion.ztst
@@ -44,8 +44,22 @@
 >line: {: dir1/}{}
 >line: {: dir2/}{}
 
+  comptesteval "alias \*=' * * \$\$ tst'"
+  comptesteval "alias -g '\$\$'=' * \$\$ \$\$ tst'"
+  comptest $' *\C-D'
+0:_expand substitutes regular aliases and escapes them, if necessary
+>DESCRIPTION:{expansions}
+>NO:{ \* * $$ tst}
+
+  comptest $': $$\C-D'
+0:_expand can substitutes global aliases and escapes them, if necessary
+>DESCRIPTION:{expansions}
+>NO:{ * \$$ \$$ tst}
+
+  comptesteval "zstyle '*' regular no"
+  comptesteval "zstyle '*' global no"
   comptest $': *\t\t\t\t\t\t'
-0:_expand shows file types
+0:_expand shows file types (and does not substitute aliases, when this is disabled)
 >line: {: dir1/}{}
 >DESCRIPTION:{expansions}
 >DI:{dir1}
-- 
2.30.1 (Apple Git-130)


  reply	other threads:[~2021-08-01 18:51 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-05-28 21:59 [PATCH] Make _expand_alias more usable as a completer Marlon Richert
2021-06-02 23:23 ` Oliver Kiddle
2021-06-03 21:42   ` Marlon Richert
2021-06-20 19:49     ` Lawrence Velázquez
2021-06-20 21:08       ` Mikael Magnusson
2021-06-20 22:24         ` Marlon Richert
2021-07-18 23:34           ` Lawrence Velázquez
2021-07-28  3:23             ` Bart Schaefer
2021-08-01 18:50               ` Marlon Richert [this message]
2022-03-31 22:35                 ` [PATCH] Make _expand handle aliases (was Re: [PATCH] Make _expand_alias more usable as a completer) Lawrence Velázquez
2022-04-01  0:37                   ` Bart Schaefer
2022-05-06  5:57                     ` Marlon Richert
2022-05-07 20:39                       ` Bart Schaefer
2022-05-11  7:32                         ` Marlon Richert

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAHLkEDs+4vv0xqq2u5g6msgpstrErFjqEFshHocfbqqYyh-SVQ@mail.gmail.com \
    --to=marlon.richert@gmail.com \
    --cc=opk@zsh.org \
    --cc=schaefer@brasslantern.com \
    --cc=zsh-workers@zsh.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).