zsh-users
 help / color / mirror / code / Atom feed
* Global Aliases, but as a function?
@ 2021-12-18 10:38 Zach Riggle
  2021-12-18 22:40 ` Bart Schaefer
  0 siblings, 1 reply; 10+ messages in thread
From: Zach Riggle @ 2021-12-18 10:38 UTC (permalink / raw)
  To: Zsh Users

I have long made use of Global Aliases such as...

    alias -g H='| head'
    alias -g L='| less'
    alias -g LL='2>&1 | less'
    alias -g LLC='--color=always 2>&1 | less -R'
    alias -g LLT='2>&1 | less +F'
    alias -g NE='2>/dev/null'
    alias -g NUL='1>/dev/null 2>&1'

These are all very useful, but the aliases do not have access to the
original command.

Is there any way to define something similar to a "global alias" but
which can modify the original command?

There's a lot of use-cases for this, but the one that I'm wondering
about specifically is to pass anything with "--help" in the command
line into a pager that does a best-effort colorization IFF the output
is not already colored.

I don't really want to declare "--help" as a global alias, since any
useful command would necessitate a pipe -- and therefore the original
binary would not emit colorized output to begin with.

Anyway, this is a toy idea at 4:00AM and I'm curious if there's
anything that can be done.

Zach Riggle


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Global Aliases, but as a function?
  2021-12-18 10:38 Global Aliases, but as a function? Zach Riggle
@ 2021-12-18 22:40 ` Bart Schaefer
  2021-12-20 18:42   ` Daniel Shahaf
  0 siblings, 1 reply; 10+ messages in thread
From: Bart Schaefer @ 2021-12-18 22:40 UTC (permalink / raw)
  To: Zach Riggle; +Cc: Zsh Users

On Sat, Dec 18, 2021 at 2:38 AM Zach Riggle <zachriggle@gmail.com> wrote:
>
> Is there any way to define something similar to a "global alias" but
> which can modify the original command?
>
> There's a lot of use-cases for this, but the one that I'm wondering
> about specifically is to pass anything with "--help" in the command
> line into a pager that does a best-effort colorization IFF the output
> is not already colored.

The only way to approximate this is by hooking into the ZLE editor to
modify $BUFFER before passing it to the lexer/parser.  (Aliases modify
the single substituted word, during lexical analysis.)

There are a bunch of ways to do this, but mainly there are three
choices, any/all of which involve creating a custom widget:

1/  Explicitly invoke your widget with a key binding.  This gives you
the most flexibility because you can edit the result or undo it.  (In
this case, though, you might instead want to consider using the
run-help binding with a suitable function.)
2/  Override the accept-line widget (or the keybindings for it) with
your custom widget (and then invoke the ".accept-line" widget at the
end).  This may be tricky to get right, and to avoid having it
interfere with other plugins etc.  Be prepared to handle $PREBUFFER
and other multi-line-input situations.
3/  Add your widget to the zle-line-finish hooks.  This is more likely
to play well with plugin managers and you don't need .accept-line, but
you still need to handle multi-line input.

All of these (except run-help) end up putting the whole command
including the appended pipeline into the history.  If you want to
avoid that, look at zshaddhistory hooks.

Naive/incomplete example of #3:

page-the-help() {
  if [[ -z $PREBUFFER && $BUFFER = *--help* ]]
  then RBUFFER+=" | less"
  fi
}
zle -N page-the-help
add-zle-hook-widget zle-line-finish page-the-help


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Global Aliases, but as a function?
  2021-12-18 22:40 ` Bart Schaefer
@ 2021-12-20 18:42   ` Daniel Shahaf
  2021-12-20 20:04     ` Bart Schaefer
  2021-12-21  0:56     ` Zach Riggle
  0 siblings, 2 replies; 10+ messages in thread
From: Daniel Shahaf @ 2021-12-20 18:42 UTC (permalink / raw)
  To: Zsh Users; +Cc: Zach Riggle

Bart Schaefer wrote on Sat, 18 Dec 2021 22:40 +00:00:
> On Sat, Dec 18, 2021 at 2:38 AM Zach Riggle <zachriggle@gmail.com> wrote:
>>
>> Is there any way to define something similar to a "global alias" but
>> which can modify the original command?
>>
>> There's a lot of use-cases for this, but the one that I'm wondering
>> about specifically is to pass anything with "--help" in the command
>> line into a pager that does a best-effort colorization IFF the output
>> is not already colored.
>
> The only way to approximate this is by hooking into the ZLE editor to
> modify $BUFFER before passing it to the lexer/parser.  (Aliases modify
> the single substituted word, during lexical analysis.)
>

One could have preexec() install wrapper functions that shadow the
command words of any simple command that has a «--help» argument.

I'm not sure whether or not that's preferable to the zle approaches.

Cheers,

Daniel

> There are a bunch of ways to do this, but mainly there are three
> choices, any/all of which involve creating a custom widget:
>
> 1/  Explicitly invoke your widget with a key binding.  This gives you
> the most flexibility because you can edit the result or undo it.  (In
> this case, though, you might instead want to consider using the
> run-help binding with a suitable function.)
> 2/  Override the accept-line widget (or the keybindings for it) with
> your custom widget (and then invoke the ".accept-line" widget at the
> end).  This may be tricky to get right, and to avoid having it
> interfere with other plugins etc.  Be prepared to handle $PREBUFFER
> and other multi-line-input situations.
> 3/  Add your widget to the zle-line-finish hooks.  This is more likely
> to play well with plugin managers and you don't need .accept-line, but
> you still need to handle multi-line input.
>
> All of these (except run-help) end up putting the whole command
> including the appended pipeline into the history.  If you want to
> avoid that, look at zshaddhistory hooks.
>
> Naive/incomplete example of #3:
>
> page-the-help() {
>   if [[ -z $PREBUFFER && $BUFFER = *--help* ]]
>   then RBUFFER+=" | less"
>   fi
> }
> zle -N page-the-help
> add-zle-hook-widget zle-line-finish page-the-help


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Global Aliases, but as a function?
  2021-12-20 18:42   ` Daniel Shahaf
@ 2021-12-20 20:04     ` Bart Schaefer
  2021-12-20 20:11       ` Roman Perepelitsa
  2021-12-21  0:56     ` Zach Riggle
  1 sibling, 1 reply; 10+ messages in thread
From: Bart Schaefer @ 2021-12-20 20:04 UTC (permalink / raw)
  To: Daniel Shahaf; +Cc: Zsh Users, Zach Riggle

On Mon, Dec 20, 2021 at 10:42 AM Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
>
> One could have preexec() install wrapper functions that shadow the
> command words of any simple command that has a «--help» argument.

Yes, this is another possible approach.  Such a function would then
have to uninstall itself again ... that gets a bit messy if there
might be other wrappers.  The conditions for whether to install the
wrapper would be similar to those implied for the zle-line-finish
hook.

Personally I'm curious what the necessary command-agnostic
help-coloring pager might be.


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Global Aliases, but as a function?
  2021-12-20 20:04     ` Bart Schaefer
@ 2021-12-20 20:11       ` Roman Perepelitsa
  2021-12-20 20:24         ` Bart Schaefer
  0 siblings, 1 reply; 10+ messages in thread
From: Roman Perepelitsa @ 2021-12-20 20:11 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Daniel Shahaf, Zsh Users, Zach Riggle

On Mon, Dec 20, 2021 at 9:05 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> Personally I'm curious what the necessary command-agnostic
> help-coloring pager might be.

lolcat -f | less

/duck


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Global Aliases, but as a function?
  2021-12-20 20:11       ` Roman Perepelitsa
@ 2021-12-20 20:24         ` Bart Schaefer
  2021-12-21  0:57           ` Zach Riggle
  0 siblings, 1 reply; 10+ messages in thread
From: Bart Schaefer @ 2021-12-20 20:24 UTC (permalink / raw)
  To: Roman Perepelitsa; +Cc: Daniel Shahaf, Zsh Users, Zach Riggle

On Mon, Dec 20, 2021 at 12:11 PM Roman Perepelitsa
<roman.perepelitsa@gmail.com> wrote:
>
> lolcat -f | less

Needs --raw-control-chars
;-D


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Global Aliases, but as a function?
  2021-12-20 18:42   ` Daniel Shahaf
  2021-12-20 20:04     ` Bart Schaefer
@ 2021-12-21  0:56     ` Zach Riggle
  2021-12-21  1:55       ` Bart Schaefer
  1 sibling, 1 reply; 10+ messages in thread
From: Zach Riggle @ 2021-12-21  0:56 UTC (permalink / raw)
  To: Daniel Shahaf; +Cc: Zsh Users

Where can I read in the documentation about the preexec wrapper and
specifically how to modify the command being executed?


Zach Riggle

On Mon, Dec 20, 2021 at 12:42 PM Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
>
> Bart Schaefer wrote on Sat, 18 Dec 2021 22:40 +00:00:
> > On Sat, Dec 18, 2021 at 2:38 AM Zach Riggle <zachriggle@gmail.com> wrote:
> >>
> >> Is there any way to define something similar to a "global alias" but
> >> which can modify the original command?
> >>
> >> There's a lot of use-cases for this, but the one that I'm wondering
> >> about specifically is to pass anything with "--help" in the command
> >> line into a pager that does a best-effort colorization IFF the output
> >> is not already colored.
> >
> > The only way to approximate this is by hooking into the ZLE editor to
> > modify $BUFFER before passing it to the lexer/parser.  (Aliases modify
> > the single substituted word, during lexical analysis.)
> >
>
> One could have preexec() install wrapper functions that shadow the
> command words of any simple command that has a «--help» argument.
>
> I'm not sure whether or not that's preferable to the zle approaches.
>
> Cheers,
>
> Daniel
>
> > There are a bunch of ways to do this, but mainly there are three
> > choices, any/all of which involve creating a custom widget:
> >
> > 1/  Explicitly invoke your widget with a key binding.  This gives you
> > the most flexibility because you can edit the result or undo it.  (In
> > this case, though, you might instead want to consider using the
> > run-help binding with a suitable function.)
> > 2/  Override the accept-line widget (or the keybindings for it) with
> > your custom widget (and then invoke the ".accept-line" widget at the
> > end).  This may be tricky to get right, and to avoid having it
> > interfere with other plugins etc.  Be prepared to handle $PREBUFFER
> > and other multi-line-input situations.
> > 3/  Add your widget to the zle-line-finish hooks.  This is more likely
> > to play well with plugin managers and you don't need .accept-line, but
> > you still need to handle multi-line input.
> >
> > All of these (except run-help) end up putting the whole command
> > including the appended pipeline into the history.  If you want to
> > avoid that, look at zshaddhistory hooks.
> >
> > Naive/incomplete example of #3:
> >
> > page-the-help() {
> >   if [[ -z $PREBUFFER && $BUFFER = *--help* ]]
> >   then RBUFFER+=" | less"
> >   fi
> > }
> > zle -N page-the-help
> > add-zle-hook-widget zle-line-finish page-the-help


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Global Aliases, but as a function?
  2021-12-20 20:24         ` Bart Schaefer
@ 2021-12-21  0:57           ` Zach Riggle
  0 siblings, 0 replies; 10+ messages in thread
From: Zach Riggle @ 2021-12-21  0:57 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Roman Perepelitsa, Daniel Shahaf, Zsh Users

$ declare LESS
LESS='--quit-if-one-screen --ignore-case --LONG-PROMPT
--RAW-CONTROL-CHARS --chop-long-lines --no-init --window -4
--jump-target=.5'

Zach Riggle

On Mon, Dec 20, 2021 at 2:24 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> On Mon, Dec 20, 2021 at 12:11 PM Roman Perepelitsa
> <roman.perepelitsa@gmail.com> wrote:
> >
> > lolcat -f | less
>
> Needs --raw-control-chars
> ;-D


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Global Aliases, but as a function?
  2021-12-21  0:56     ` Zach Riggle
@ 2021-12-21  1:55       ` Bart Schaefer
  2021-12-22 20:14         ` Daniel Shahaf
  0 siblings, 1 reply; 10+ messages in thread
From: Bart Schaefer @ 2021-12-21  1:55 UTC (permalink / raw)
  To: Zach Riggle; +Cc: Daniel Shahaf, Zsh Users

On Mon, Dec 20, 2021 at 4:56 PM Zach Riggle <zachriggle@gmail.com> wrote:
>
> Where can I read in the documentation about the preexec wrapper and
> specifically how to modify the command being executed?

Very little of this is just going to fall out of the documentation for you.

The preexec function (and the preexec_functions array) are described
in 9.3.1 Hook Functions, which is in "man zshmisc".  There is also doc
about the add-zsh-hook helper function in 26.2.5 Manipulating Hook
Functions (man zshcontrib).

You'd be looking at the third argument passed to preexec ("... the
full text that is being executed") which you will have to take apart
with something like cmdline=(${(z)3}) to figure out whether it's a
simple command and whether one of the arguments is "--help".

Assuming it is just one command and has that argument, you've got the
command name in $cmdline[1] (in my foregoing example) so you're going
to create function with that name that ends by deleting itself; e.g.
(naively again, ignoring the "is this a simple command" test):

preexec() {
  local cmdline=(${(z)3})
  if [[ -n $cmdline[(R)--help] ]]; then
    function $cmdline[1] {
     $cmdline[1] "$@" | $LESS
     unfunction $cmdline[1]
    }
  fi
}

And you're done.  This works as long as $cmdline[1] isn't already
defined as a different function, in which case you have to figure out
how to save and restore that.


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Global Aliases, but as a function?
  2021-12-21  1:55       ` Bart Schaefer
@ 2021-12-22 20:14         ` Daniel Shahaf
  0 siblings, 0 replies; 10+ messages in thread
From: Daniel Shahaf @ 2021-12-22 20:14 UTC (permalink / raw)
  To: Zsh Users; +Cc: Zach Riggle

Bart Schaefer wrote on Mon, Dec 20, 2021 at 17:55:15 -0800:
> You'd be looking at the third argument passed to preexec ("... the
> full text that is being executed") which you will have to take apart
> with something like cmdline=(${(z)3}) to figure out whether it's a
> simple command and whether one of the arguments is "--help".
> 

If INTERACTIVE_COMMENTS is set then the taking apart should use ${(zZ+c+)}.

> Assuming it is just one command and has that argument, you've got the
> command name in $cmdline[1] (in my foregoing example) so you're going
> to create function with that name that ends by deleting itself; e.g.
> (naively again, ignoring the "is this a simple command" test):
> 
> preexec() {
>   local cmdline=(${(z)3})
>   if [[ -n $cmdline[(R)--help] ]]; then
>     function $cmdline[1] {
>      $cmdline[1] "$@" | $LESS
>      unfunction $cmdline[1]

The parameter «cmdline» will have been undefined by the time the inner
function runs, and the first line of the inner function would infinite
loop.  Therefore:
.
    eval "function ${(qq)cmdline[1]} {
        unfunction -- ${(q)cmdline[1]}
        ${(q)cmdline[1]} "\$@" | …
    }"

>

For the "is this a simple command?" test, my first sketch would be:

- No command separator tokens (';', '|', '||', etc.)

- First word's «type» is «command» [this rules out reserved words,
  assignments, aliases, functions, parameter expansions, history
  expansions…]

>     }
>   fi
> }
> 
> And you're done.  This works as long as $cmdline[1] isn't already
> defined as a different function, in which case you have to figure out
> how to save and restore that.

«functions -c»?

Cheers,

Daniel



^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2021-12-22 20:16 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-18 10:38 Global Aliases, but as a function? Zach Riggle
2021-12-18 22:40 ` Bart Schaefer
2021-12-20 18:42   ` Daniel Shahaf
2021-12-20 20:04     ` Bart Schaefer
2021-12-20 20:11       ` Roman Perepelitsa
2021-12-20 20:24         ` Bart Schaefer
2021-12-21  0:57           ` Zach Riggle
2021-12-21  0:56     ` Zach Riggle
2021-12-21  1:55       ` Bart Schaefer
2021-12-22 20:14         ` Daniel Shahaf

Code repositories for project(s) associated with this 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).