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

Re: How to fix run-help-* functions?

On Mon, Mar 20, 2023 at 7:37 AM Michele Venturi <dardo82@xxxxxxxxx> wrote:
> I hope that's enough context

Typically we'd like at least OS, zsh version, whether you built it
yourself or installed packaged binaries, and a copy-paste of text from
your terminal showing the command you ran and the resulting output
(edited to relevant portions if very long).

Also "functions -T run-help" (in this case) would produce xtrace
output of the function, which might help you investigate before asking
others to do so for you.

However, given we're already this far down a thread:

> I've an alias named "pkgu" set to "sudo pacman -S -y -u" and "run-help pkgu" gives
> the same error as before, can you check it on your end?

Besides not anticipating use outside widget context, some recent
changes to run-help also did not consider that aliases result in a
recursive call, or (at least) presumed that aliases would always
mention their own name somewhere in the expansion (e.g., alias
foo="sudo foo" or the like).  Same symptom, somewhat different cause.
Both stem from an attempt to optimize out a while-loop by using
${ary[(i)...]} which in turn didn't consider what happens when the (i)
search fails.

However, we're approaching a point of diminishing returns here.
Further attempts to make sense of the foregoing risk e.g. regressing
workers/31634.  It may be better to discard the error output from the
failed shift and get on with things.  After some memory-refreshment
with the mailing list archives of past changes, the problem comes down
to something like this:

Suppose we have an alias like g='git' and on the command line is "g
diff".  We'd like the run-help widget to first unpack "g" and then
analyze the full command line as "git diff" which ultimately invokes
"run-help-git diff" to finally produce the help for the git "diff"

So in widget context, run-help starts by looking up "g" as an alias
and makes a recursive call on "run-help git" -- which isn't good
enough, it's lost the argument "diff".  This is where getln/print -z
come in, the recursive call pulls up the original command line
(getln), discards the "g", and appends the remainder to get "git diff"
and off we go.  However, there are all sorts of complications -- what
if the alias is g='LANG=C sudo git' or some such?  Skipping arbitrary
prefix stuff to reach the "remainder" is what that "shift" is
attempting to do.

When run-help is invoked outside widget context, there's no source
line to search for the original command name.  My first pass at this
fell back on searching the arguments passed to run-help, but that
fails on the recursive call into the expansion of the alias because
the "original command" doesn't actually appear in the expansion.  It's
possible this would also fail in widget context if one alias itself
expanded to a second alias, etc.  And that doesn't even begin to get
into possible side-effects of "alias -g".

There are several other ways this can fail -- so the most reasonable
thing might be to give up if "skip the prefix" fails and treat the
command as too complex to generate help.

The attached (follows on to previous patch) might do a little better
than simply "give up", but it also might introduce some unwanted
diff --git a/Functions/Misc/run-help b/Functions/Misc/run-help
index 14d09bd65..462044b72 100644
--- a/Functions/Misc/run-help
+++ b/Functions/Misc/run-help
@@ -58,11 +58,11 @@ do
     case $what in
     (*( is an alias for (noglob|nocorrect))*)
 	[[ ${what[(w)7]:t} != ${what[(w)1]} ]] &&
-	  run_help_orig_cmd=${what[(w)1]} run-help ${what[(w)7]:t}
+	  run_help_orig_cmd=${what[(w)1]} run-help ${what[(w)7]:t} ${(z)${what[(w)8,-1]}}
     (*( is an alias)*)
 	[[ ${what[(w)6]:t} != ${what[(w)1]} ]] &&
-	  run_help_orig_cmd=${what[(w)1]} run-help ${what[(w)6]:t}
+	  run_help_orig_cmd=${what[(w)1]} run-help ${what[(w)6]:t} ${(z)${what[(w)7,-1]}}
     (*( is a * function))
 	case ${what[(w)1]} in
@@ -103,7 +103,7 @@ do
 		cmd_args=( ${(z)${cmd_args:-"$*"}} )
                 # Discard the command itself & everything before it.
-                shift $cmd_args[(i)${run_help_orig_cmd:-$1}] cmd_args ||
+                shift $cmd_args[(i)(${run_help_orig_cmd}|$1)] cmd_args 2>/dev/null ||
                 # Discard options, parameter assignments & paths.

Messages sorted by: Reverse Date, Date, Thread, Author