zsh-workers
 help / color / mirror / code / Atom feed
* [RFC][PATCH] Add change-directory() widget function
@ 2021-04-20 13:36 Marlon Richert
  2021-04-20 19:43 ` Bart Schaefer
  0 siblings, 1 reply; 30+ messages in thread
From: Marlon Richert @ 2021-04-20 13:36 UTC (permalink / raw)
  To: Zsh hackers list

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

Attached is the widget that was discussed in workers/48408 and ancestors (Re: Rewrite of zsh-newuser-install).

There was some discussion as to whether this should or shouldn’t leave the executed commands visible in the terminal and/or history. I tried a version of it that doesn’t put the commands in the buffer but executes them directly and then does zle .reset-prompt, but this can leave the prompt in an incorrect state if the prompt uses precmd hooks. I also tried “manually” running precmd_functions before zle .reset-prompt, but this causes problems when any precmd hook contains code that doesn’t work when executed inside a widget (for example, echoing an escape code that result in a response from the terminal). Therefore, I instead settled on pushing the current line, setting the buffer, and then accepting the line. This seems to me the least likely to break anything, but I’m open to alternatives.


[-- Attachment #2: 0001-Add-change-directory-widget-function.txt --]
[-- Type: text/plain, Size: 11729 bytes --]

From 7cf2d34d4066a5979d47256865680b36ea08d0c0 Mon Sep 17 00:00:00 2001
From: Marlon Richert <marlon.richert@gmail.com>
Date: Tue, 20 Apr 2021 16:25:07 +0300
Subject: [PATCH] Add change-directory() widget function

---
 Doc/Zsh/contrib.yo             | 73 ++++++++++++++++++++--------------
 Functions/Zle/change-directory | 29 ++++++++++++++
 2 files changed, 73 insertions(+), 29 deletions(-)
 create mode 100644 Functions/Zle/change-directory

diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index 3c4fdded0..519f0db43 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -123,9 +123,9 @@ If the tt(-p) option is given, the var(arg)s are interpreted as one
 or more sets of arguments for tt(zcompile), separated by `tt(-)tt(-)'.
 For example:
 
-example(zrecompile -p \ 
-           -R ~/.zshrc -- \ 
-           -M ~/.zcompdump -- \ 
+example(zrecompile -p \
+           -R ~/.zshrc -- \
+           -M ~/.zcompdump -- \
            ~/zsh/comp.zwc ~/zsh/Completion/*/_*)
 
 This compiles tt(~/.zshrc) into tt(~/.zshrc.zwc) if that doesn't exist or
@@ -529,7 +529,7 @@ are shown first.  The special value tt(+) can appear in the list to
 indicate the default file should be read at that point.  This allows
 effects like the following:
 
-example(zstyle ':chpwd:*' recent-dirs-file \ 
+example(zstyle ':chpwd:*' recent-dirs-file \
 ~/.chpwd-recent-dirs-${TTY##*/} +)
 
 Recent directories are read from a file numbered according to
@@ -890,9 +890,9 @@ subsect(Quickstart)
 To get this feature working quickly (including colors), you can do the
 following (assuming, you loaded tt(vcs_info) properly - see above):
 
-example(zstyle ':vcs_info:*' actionformats \ 
+example(zstyle ':vcs_info:*' actionformats \
     '%F{5}(%f%s%F{5})%F{3}-%F{5}[%F{2}%b%F{3}|%F{1}%a%F{5}]%f '
-zstyle ':vcs_info:*' formats       \ 
+zstyle ':vcs_info:*' formats       \
     '%F{5}(%f%s%F{5})%F{3}-%F{5}[%F{2}%b%F{5}]%f '
 zstyle ':vcs_info:(sv[nk]|bzr):*' branchformat '%b%F{1}:%F{3}%r'
 precmd () { vcs_info }
@@ -1418,7 +1418,7 @@ to know which patches of a series are not yet applied, you need to activate
 the tt(get-unapplied) style in the appropriate context.
 
 tt(vcs_info) allows for very detailed control over how the gathered
-information is presented (see 
+information is presented (see
 ifzman(the bf(Configuration) and bf(Hooks in vcs_info) sections)\
 ifnzman(noderef(vcs_info Configuration) and noderef(vcs_info Hooks))),
 all of which are documented below. Note there are a number of
@@ -1826,7 +1826,7 @@ example(zstyle ':vcs_info:bzr:*' use-simple true)
 If you do use tt(use-simple), please report if it does `the-right-thing[tm]'.
 
 Display the revision number in yellow for tt(bzr) and tt(svn):
-example(zstyle ':vcs_info:(svn|bzr):*' \ 
+example(zstyle ':vcs_info:(svn|bzr):*' \
        branchformat '%b%%F{yellow}:%r')
 
 The doubled percent sign is explained in
@@ -1834,7 +1834,7 @@ ifzman(the bf(Oddities) section)ifnzman(noderef(vcs_info Oddities)).
 
 Alternatively, one can use the raw colour codes directly:
 
-example(zstyle ':vcs_info:(svn|bzr):*' \ 
+example(zstyle ':vcs_info:(svn|bzr):*' \
        branchformat '%b%{'${fg[yellow]}'%}:%r')
 
 Normally when a variable is interpolated into a format string, the variable
@@ -2273,7 +2273,7 @@ Neither of the styles tt(word-chars) nor tt(word-class) is used in this case.
 Here are some examples of use of the tt(word-context) style to extend
 the context.
 
-example(zstyle ':zle:*' word-context \ 
+example(zstyle ':zle:*' word-context \
        "*/*" filename "[[:space:]]" whitespace
 zstyle ':zle:transpose-words:whitespace' word-style shell
 zstyle ':zle:transpose-words:filename' word-style normal
@@ -2383,7 +2383,7 @@ paste itself from proceeding.
 Loading tt(bracketed-paste-magic) defines tt(backward-extend-paste), a
 helper function for use in tt(paste-init).
 
-example(zstyle :bracketed-paste-magic paste-init \ 
+example(zstyle :bracketed-paste-magic paste-init \
        backward-extend-paste)
 
 When a paste would insert into the middle of a word or append text to a
@@ -2403,16 +2403,16 @@ the paste itself from proceeding.
 Loading tt(bracketed-paste-magic) also defines tt(quote-paste), a helper
 function for use in tt(paste-finish).
 
-example(zstyle :bracketed-paste-magic paste-finish \ 
+example(zstyle :bracketed-paste-magic paste-finish \
        quote-paste
-zstyle :bracketed-paste-magic:finish quote-style \ 
+zstyle :bracketed-paste-magic:finish quote-style \
        qqq)
 
 When the pasted text is inserted into tt(BUFFER), it is quoted per the
 tt(quote-style) value.  To forcibly turn off the built-in numeric prefix
 quoting of tt(bracketed-paste), use:
 
-example(zstyle :bracketed-paste-magic:finish quote-style \ 
+example(zstyle :bracketed-paste-magic:finish quote-style \
        none)
 )
 enditem()
@@ -2423,6 +2423,21 @@ history is restricted, so cursor motions, etc., may not pass outside of
 the pasted content.  Text assigned to tt(BUFFER) by the active widgets
 is copied back into tt(PASTED) before tt(paste-finish).
 )
+tindex(change-directory)
+item(tt(change-directory))(
+This function implements the widgets tt(cd-upward), tt(cd-backward) and
+tt(cd-forward).  They can be used, respectively, to change to the current
+directory's parent or the previous/next entry in the directory stack. They
+should be initialized as follows:
+
+example(autoload -Uz change-directory
+zle -N cd-upward   change-directory
+zle -N cd-backward change-directory
+zle -N cd-forward  change-directory
+bindkey '^[^[OA' cd-upward    # Alt-Up
+bindkey '^[^[OD' cd-backward  # Alt-Left
+bindkey '^[^[OC' cd-forward   # Alt-Right
+)
 tindex(copy-earlier-word)
 item(tt(copy-earlier-word))(
 This widget works like a combination of tt(insert-last-word) and
@@ -2447,7 +2462,7 @@ function based completion system may know about multiple places in
 this string where characters are missing or differ from at least one
 of the possible matches.  It will then place the cursor on the
 position it considers to be the most interesting one, i.e. the one
-where one can disambiguate between as many matches as possible with as 
+where one can disambiguate between as many matches as possible with as
 little typing as possible.
 
 This widget allows the cursor to be easily moved to the other interesting
@@ -2516,9 +2531,9 @@ the history.
 Although you tt(autoload) only one function, the commands to use it are
 slightly different because it implements two widgets.
 
-example(zle -N history-beginning-search-backward-end \ 
+example(zle -N history-beginning-search-backward-end \
        history-search-end
-zle -N history-beginning-search-forward-end \ 
+zle -N history-beginning-search-forward-end \
        history-search-end
 bindkey '\e^P' history-beginning-search-backward-end
 bindkey '\e^N' history-beginning-search-forward-end)
@@ -2543,7 +2558,7 @@ in the text typed is treated as a wildcard and can match anything (hence
 a leading space is equivalent to giving a numeric argument).  Both
 forms can be combined, for example:
 
-example(zle -N history-beginning-search-menu-space-end \ 
+example(zle -N history-beginning-search-menu-space-end \
        history-beginning-search-menu)
 )
 tindex(history-pattern-search)
@@ -2865,7 +2880,7 @@ non-zero.
 
 Here is a trivial example of a widget using this feature.
 example(local state
-narrow-to-region -p $'Editing restricted region\n' \ 
+narrow-to-region -p $'Editing restricted region\n' \
   -P '' -S state
 zle recursive-edit
 narrow-to-region -R state)
@@ -3300,11 +3315,11 @@ and for these widgets.  For example, to use completion, approximation and
 correction for normal completion, completion and correction for
 incremental completion and only completion for prediction one could use:
 
-example(zstyle ':completion:*' completer \ 
+example(zstyle ':completion:*' completer \
         _complete _correct _approximate
-zstyle ':completion:incremental:*' completer \ 
+zstyle ':completion:incremental:*' completer \
         _complete _correct
-zstyle ':completion:predict:*' completer \ 
+zstyle ':completion:predict:*' completer \
         _complete)
 
 It is a good idea to restrict the completers used in prediction, because
@@ -3324,7 +3339,7 @@ been tried.  Values are:
 startitem()
 item(tt(complete))(
 The cursor is left where it was when completion finished, but only if
-it is after a character equal to the one just inserted by the user.  If 
+it is after a character equal to the one just inserted by the user.  If
 it is after another character, this value is the same as `tt(key)'.
 )
 item(tt(key))(
@@ -3333,7 +3348,7 @@ after the var(n)th occurrence of the character just inserted, where
 var(n) is the number of times that character appeared in the word
 before completion was attempted.  In short, this has the effect of
 leaving the cursor after the character just typed even if the
-completion code found out that no other characters need to be inserted 
+completion code found out that no other characters need to be inserted
 at that position.
 )
 enditem()
@@ -3344,7 +3359,7 @@ position where the completion code left it.
 kindex(list, widget style)
 item(tt(list))(
 When using the tt(incremental-complete-word) widget, this style says
-if the matches should be listed on every key press (if they fit on the 
+if the matches should be listed on every key press (if they fit on the
 screen).  Use the context prefix `tt(:completion:incremental)'.
 
 The tt(insert-and-predict) widget uses this style to decide if the
@@ -3410,7 +3425,7 @@ Like `tt(break-keys)', this uses the `tt(:incremental)' context.
 kindex(stop-keys, widget style)
 item(tt(stop-keys))(
 This style is used by the tt(incremental-complete-word) widget.  Its value
-is treated similarly to the one for the tt(break-keys) style (and uses 
+is treated similarly to the one for the tt(break-keys) style (and uses
 the same context: `tt(:incremental)').  However, in
 this case all keys matching the pattern given as its value will stop
 incremental completion and will then execute their usual function.
@@ -4401,7 +4416,7 @@ tt(openssl),
 tt(p4),
 tt(sudo),
 tt(svk),
-and 
+and
 tt(svn),
 commands.
 )
@@ -4646,8 +4661,8 @@ This makes defining styles a bit simpler by using a single `tt(+)' as a
 special token that allows you to append a context name to the previously
 used context name.  Like this:
 
-example(zstyle+ ':foo:bar' var(style1) var(value1) \ 
-      + ':baz'     var(style2) var(value2) \ 
+example(zstyle+ ':foo:bar' var(style1) var(value1) \
+      + ':baz'     var(style2) var(value2) \
       + ':frob'    var(style3) var(value3))
 
 This defines var(style1) with var(value1) for the context tt(:foo:bar) as usual,
diff --git a/Functions/Zle/change-directory b/Functions/Zle/change-directory
new file mode 100644
index 000000000..376e4414c
--- /dev/null
+++ b/Functions/Zle/change-directory
@@ -0,0 +1,29 @@
+zle .push-line-or-edit
+case $WIDGET in
+  *-upward )
+    if [[ -o autocd ]]; then
+      BUFFER='..'
+    else
+      BUFFER='cd ..'
+    fi
+    ;;
+  *-backward )
+    if [[ -o pushdminus ]]; then
+      BUFFER='pushd -1'
+    else
+      BUFFER='pushd +1'
+    fi
+    ;;
+  *-forward )
+    if [[ -o pushdminus ]]; then
+      BUFFER='pushd +0'
+    else
+      BUFFER='pushd -0'
+    fi
+    ;;
+  * )
+    print -u2 'change-directory: widget name should end in -(up|back|for)ward'
+    return 1
+    ;;
+esac
+zle .accept-line
-- 
2.31.1


[-- Attachment #3: Type: text/plain, Size: 2 bytes --]




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

* Re: [RFC][PATCH] Add change-directory() widget function
  2021-04-20 13:36 [RFC][PATCH] Add change-directory() widget function Marlon Richert
@ 2021-04-20 19:43 ` Bart Schaefer
  2021-04-20 20:13   ` Marlon Richert
  0 siblings, 1 reply; 30+ messages in thread
From: Bart Schaefer @ 2021-04-20 19:43 UTC (permalink / raw)
  To: Marlon Richert; +Cc: Zsh hackers list

There seem to be a whole lot of spurious diff lines in contrib.yo in
this patch?  What's going on there?  If the delta is that trailing
whitespace has been removed, please don't do that.


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

* Re: [RFC][PATCH] Add change-directory() widget function
  2021-04-20 19:43 ` Bart Schaefer
@ 2021-04-20 20:13   ` Marlon Richert
  2021-04-20 21:32     ` Bart Schaefer
                       ` (2 more replies)
  0 siblings, 3 replies; 30+ messages in thread
From: Marlon Richert @ 2021-04-20 20:13 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

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

On 20 Apr 2021, at 22:43, Bart Schaefer <schaefer@brasslantern.com> wrote:
> There seem to be a whole lot of spurious diff lines in contrib.yo in
> this patch?  What's going on there?  If the delta is that trailing
> whitespace has been removed, please don't do that.

Oh, my bad. That has been unintentional. Here’s the patch again but without the whitespace changes.


[-- Attachment #2: 0001-Add-change-directory-widget-function.txt --]
[-- Type: text/plain, Size: 2322 bytes --]

From 25684843ae02bd7b57137a8e50c661b991dec23b Mon Sep 17 00:00:00 2001
From: Marlon Richert <marlon.richert@gmail.com>
Date: Tue, 20 Apr 2021 23:12:22 +0300
Subject: [PATCH] Add change-directory() widget function

---
 Doc/Zsh/contrib.yo             | 15 +++++++++++++++
 Functions/Zle/change-directory | 29 +++++++++++++++++++++++++++++
 2 files changed, 44 insertions(+)
 create mode 100644 Functions/Zle/change-directory

diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index 3c4fdded0..6ad59798c 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -2423,6 +2423,21 @@ history is restricted, so cursor motions, etc., may not pass outside of
 the pasted content.  Text assigned to tt(BUFFER) by the active widgets
 is copied back into tt(PASTED) before tt(paste-finish).
 )
+tindex(change-directory)
+item(tt(change-directory))(
+This function implements the widgets tt(cd-upward), tt(cd-backward) and
+tt(cd-forward).  They can be used, respectively, to change to the current
+directory's parent or the previous/next entry in the directory stack. They
+should be initialized as follows:
+
+example(autoload -Uz change-directory
+zle -N cd-upward   change-directory
+zle -N cd-backward change-directory
+zle -N cd-forward  change-directory
+bindkey '^[^[OA' cd-upward    # Alt-Up
+bindkey '^[^[OD' cd-backward  # Alt-Left
+bindkey '^[^[OC' cd-forward   # Alt-Right
+)
 tindex(copy-earlier-word)
 item(tt(copy-earlier-word))(
 This widget works like a combination of tt(insert-last-word) and
diff --git a/Functions/Zle/change-directory b/Functions/Zle/change-directory
new file mode 100644
index 000000000..376e4414c
--- /dev/null
+++ b/Functions/Zle/change-directory
@@ -0,0 +1,29 @@
+zle .push-line-or-edit
+case $WIDGET in
+  *-upward )
+    if [[ -o autocd ]]; then
+      BUFFER='..'
+    else
+      BUFFER='cd ..'
+    fi
+    ;;
+  *-backward )
+    if [[ -o pushdminus ]]; then
+      BUFFER='pushd -1'
+    else
+      BUFFER='pushd +1'
+    fi
+    ;;
+  *-forward )
+    if [[ -o pushdminus ]]; then
+      BUFFER='pushd +0'
+    else
+      BUFFER='pushd -0'
+    fi
+    ;;
+  * )
+    print -u2 'change-directory: widget name should end in -(up|back|for)ward'
+    return 1
+    ;;
+esac
+zle .accept-line
-- 
2.31.1


[-- Attachment #3: Type: text/plain, Size: 2 bytes --]




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

* Re: [RFC][PATCH] Add change-directory() widget function
  2021-04-20 20:13   ` Marlon Richert
@ 2021-04-20 21:32     ` Bart Schaefer
  2021-04-21  3:46       ` Bart Schaefer
  2021-04-20 21:57     ` [RFC][PATCH] Add change-directory() widget function Daniel Shahaf
  2021-04-20 22:14     ` Daniel Shahaf
  2 siblings, 1 reply; 30+ messages in thread
From: Bart Schaefer @ 2021-04-20 21:32 UTC (permalink / raw)
  To: Marlon Richert; +Cc: Zsh hackers list

On Tue, Apr 20, 2021 at 1:13 PM Marlon Richert <marlon.richert@gmail.com> wrote:
>
> Oh, my bad. That has been unintentional. Here’s the patch again but without the whitespace changes.

Thanks.

My only remark about this as-is, is that it would have potentially
dangerous side-effects if invoked at a PS2 or PS3 prompt.

Potential fixes - begin the widget function with:
[[ ${(%):-%_} = select ]] && return 1
[[ -n "$PREBUFFER$BUFFER" ]] && zle push-input


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

* Re: [RFC][PATCH] Add change-directory() widget function
  2021-04-20 20:13   ` Marlon Richert
  2021-04-20 21:32     ` Bart Schaefer
@ 2021-04-20 21:57     ` Daniel Shahaf
  2021-04-20 22:14     ` Daniel Shahaf
  2 siblings, 0 replies; 30+ messages in thread
From: Daniel Shahaf @ 2021-04-20 21:57 UTC (permalink / raw)
  To: Marlon Richert; +Cc: Zsh hackers list

> +++ b/Doc/Zsh/contrib.yo
> @@ -2423,6 +2423,21 @@ history is restricted, so cursor motions, etc., may not pass outside of
>  the pasted content.  Text assigned to tt(BUFFER) by the active widgets
>  is copied back into tt(PASTED) before tt(paste-finish).
>  )
> +tindex(change-directory)
> +item(tt(change-directory))(
> +This function implements the widgets tt(cd-upward), tt(cd-backward) and
> +tt(cd-forward).  They can be used, respectively, to change to the current
> +directory's parent or the previous/next entry in the directory stack. They
> +should be initialized as follows:
> +
> +example(autoload -Uz change-directory
> +zle -N cd-upward   change-directory
> +zle -N cd-backward change-directory
> +zle -N cd-forward  change-directory
> +bindkey '^[^[OA' cd-upward    # Alt-Up
> +bindkey '^[^[OD' cd-backward  # Alt-Left
> +bindkey '^[^[OC' cd-forward   # Alt-Right

s/Right/Right)/

> +)
>  tindex(copy-earlier-word)
>  item(tt(copy-earlier-word))(
>  This widget works like a combination of tt(insert-last-word) and
> diff --git a/Functions/Zle/change-directory b/Functions/Zle/change-directory
> new file mode 100644
> index 000000000..376e4414c
> --- /dev/null
> +++ b/Functions/Zle/change-directory
> @@ -0,0 +1,29 @@
> +zle .push-line-or-edit
> +case $WIDGET in
> +  *-upward )
> +    if [[ -o autocd ]]; then
> +      BUFFER='..'
> +    else
> +      BUFFER='cd ..'
> +    fi
> +    ;;
> +  *-backward )
> +    if [[ -o pushdminus ]]; then
> +      BUFFER='pushd -1'
> +    else
> +      BUFFER='pushd +1'
> +    fi
> +    ;;
> +  *-forward )
> +    if [[ -o pushdminus ]]; then
> +      BUFFER='pushd +0'
> +    else
> +      BUFFER='pushd -0'
> +    fi
> +    ;;
> +  * )
> +    print -u2 'change-directory: widget name should end in -(up|back|for)ward'

Suggest to add a detail:

       print -u2 $'change-directory: widget name should end in -(up|back|for)ward; review the \'zle -N ... change-directory\' commands in your configuration'

And to remove an implementation detail:

       print -u2 $'change-directory: widget name should be cd-(up|back|for)ward; review the \'zle -N ... change-directory\' commands in your configuration'

And to avoid (foo|bar|baz) constructs — not for greppability reasons as
requested in 48402#13[48408] and again in 48632, but for
user-friendliness reasons (cf. my feedback to Sebastian back in
[workers/45149, second bullet]):

       print -u2 $'change-directory: widget name should be "cd-upward", "cd-backward", or "cd-downward"; review the \'zle -N ... change-directory\' commands in your configuration'

> +    return 1
> +    ;;
> +esac
> +zle .accept-line
> -- 
> 2.31.1
> 

> 
> 



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

* Re: [RFC][PATCH] Add change-directory() widget function
  2021-04-20 20:13   ` Marlon Richert
  2021-04-20 21:32     ` Bart Schaefer
  2021-04-20 21:57     ` [RFC][PATCH] Add change-directory() widget function Daniel Shahaf
@ 2021-04-20 22:14     ` Daniel Shahaf
  2021-04-21  0:09       ` Bart Schaefer
  2021-04-21  3:18       ` Bart Schaefer
  2 siblings, 2 replies; 30+ messages in thread
From: Daniel Shahaf @ 2021-04-20 22:14 UTC (permalink / raw)
  To: Marlon Richert; +Cc: Zsh hackers list

Marlon Richert wrote on Tue, Apr 20, 2021 at 23:13:35 +0300:
> +++ b/Functions/Zle/change-directory
> @@ -0,0 +1,29 @@
> +zle .push-line-or-edit
> +case $WIDGET in
> +  *-upward )
> +    if [[ -o autocd ]]; then

Why is the «if» codepath necessary?

> +      BUFFER='..'
> +    else
> +      BUFFER='cd ..'

s/cd/builtin cd/?

Should the -q option be passed to «cd»?

Should any of the words be quoted to protect against global aliases?

Does the existence of CHASE_DOTS / CHASE_LINKS raise any concerns?

Should this be implemented in C so it can call chdir(2) directly without
gymnastics?

Regarding your first post in the thread, lines weren't wrapped,
which made it hard to read.

Daniel

> +    fi
> +    ;;
> +  *-backward )
> +    if [[ -o pushdminus ]]; then
> +      BUFFER='pushd -1'
> +    else
> +      BUFFER='pushd +1'
> +    fi
> +    ;;
> +  *-forward )
> +    if [[ -o pushdminus ]]; then
> +      BUFFER='pushd +0'
> +    else
> +      BUFFER='pushd -0'
> +    fi
> +    ;;
> +  * )
> +    print -u2 'change-directory: widget name should end in -(up|back|for)ward'
> +    return 1
> +    ;;
> +esac
> +zle .accept-line
> -- 
> 2.31.1
> 

> 
> 



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

* Re: [RFC][PATCH] Add change-directory() widget function
  2021-04-20 22:14     ` Daniel Shahaf
@ 2021-04-21  0:09       ` Bart Schaefer
  2021-04-21  3:18       ` Bart Schaefer
  1 sibling, 0 replies; 30+ messages in thread
From: Bart Schaefer @ 2021-04-21  0:09 UTC (permalink / raw)
  To: Daniel Shahaf; +Cc: Marlon Richert, Zsh hackers list

On Tue, Apr 20, 2021 at 3:14 PM Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
>
> Should this be implemented in C so it can call chdir(2) directly without
> gymnastics?

Even if implemented in C it can't call chdir directly without
gymnastics, it has to pass through the zsh internal cd helpers that
maintain PWD and the directory stack and so on.


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

* Re: [RFC][PATCH] Add change-directory() widget function
  2021-04-20 22:14     ` Daniel Shahaf
  2021-04-21  0:09       ` Bart Schaefer
@ 2021-04-21  3:18       ` Bart Schaefer
  2021-04-21 20:11         ` Daniel Shahaf
  1 sibling, 1 reply; 30+ messages in thread
From: Bart Schaefer @ 2021-04-21  3:18 UTC (permalink / raw)
  To: Daniel Shahaf; +Cc: Marlon Richert, Zsh hackers list

On Tue, Apr 20, 2021 at 3:14 PM Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
>
> Should any of the words be quoted to protect against global aliases?

This is generally speaking a fruitless task.  Rather the function
should be loaded with e.g. "autoload -U", as compinit does for all
completion functions.


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

* Re: [RFC][PATCH] Add change-directory() widget function
  2021-04-20 21:32     ` Bart Schaefer
@ 2021-04-21  3:46       ` Bart Schaefer
  2021-04-21 11:37         ` [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function) Marlon Richert
  0 siblings, 1 reply; 30+ messages in thread
From: Bart Schaefer @ 2021-04-21  3:46 UTC (permalink / raw)
  To: Marlon Richert; +Cc: Zsh hackers list

On Tue, Apr 20, 2021 at 2:32 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> [[ -n "$PREBUFFER$BUFFER" ]] && zle push-input

Oh, I missed this:

> On Tue, Apr 20, 2021 at 1:13 PM Marlon Richert <marlon.richert@gmail.com> wrote:
> > @@ -0,0 +1,29 @@
> > +zle .push-line-or-edit

That's still wrong, because in the event there's a PS2 prompt, you'll
never get past this line.  You need .push-input here.  My suggested
test for $PREBUFFER is probably not actually needed.


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

* [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)
  2021-04-21  3:46       ` Bart Schaefer
@ 2021-04-21 11:37         ` Marlon Richert
  2021-04-21 20:40           ` Bart Schaefer
  2021-04-21 21:27           ` Daniel Shahaf
  0 siblings, 2 replies; 30+ messages in thread
From: Marlon Richert @ 2021-04-21 11:37 UTC (permalink / raw)
  To: Bart Schaefer, Daniel Shahaf; +Cc: Zsh hackers list

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

On 21 Apr 2021, at 00:32, Bart Schaefer <schaefer@brasslantern.com> wrote:
> My only remark about this as-is, is that it would have potentially
> dangerous side-effects if invoked at a PS2 or PS3 prompt.
> 
> Potential fixes - begin the widget function with:
> [[ ${(%):-%_} = select ]] && return 1
> [[ -n "$PREBUFFER$BUFFER" ]] && zle push-input

On 21 Apr 2021, at 06:46, Bart Schaefer <schaefer@brasslantern.com> wrote:
> Oh, I missed this:
> 
>> On Tue, Apr 20, 2021 at 1:13 PM Marlon Richert <marlon.richert@gmail.com> wrote:
>>> @@ -0,0 +1,29 @@
>>> +zle .push-line-or-edit
> 
> That's still wrong, because in the event there's a PS2 prompt, you'll
> never get past this line.  You need .push-input here.  My suggested
> test for $PREBUFFER is probably not actually needed.

Thank you both (Bart & Daniel) for your input. I added better safeguards against the non-PS1 cases, rewrote the function to be more generic and added comments. New patch attached.


[-- Attachment #2: 0001-Add-execute-command-widget-function.txt --]
[-- Type: text/plain, Size: 4064 bytes --]

From 83da04aba0d07112f650a98fafc3f28daf75dcd1 Mon Sep 17 00:00:00 2001
From: Marlon Richert <marlon.richert@gmail.com>
Date: Wed, 21 Apr 2021 14:33:27 +0300
Subject: [PATCH] Add execute-command() widget function

---
 Doc/Zsh/contrib.yo            | 26 +++++++++++++++++
 Functions/Zle/execute-command | 54 +++++++++++++++++++++++++++++++++++
 2 files changed, 80 insertions(+)
 create mode 100644 Functions/Zle/execute-command

diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index 8bf1a208e..df02fc4d9 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -2502,6 +2502,32 @@ arguments:
 
 example(zstyle :zle:edit-command-line editor gvim -f)
 )
+tindex(execute-command)
+item(tt(execute-command))(
+This function lets you implement widgets that can execute arbitrary commands 
+without losing the current command line, in a fashion similar to the 
+tt(run-help) and tt(which-command) widgets (see 
+ifzman(the subsection Miscellaneous in zmanref(zshzle))\
+ifnzman(noderef(Miscellaneous))). More precisely, it 
+enumeration(
+myeit() pushes the buffer onto the buffer stack, 
+myeit() executes the supplied arguments, then 
+myeit() lets the ZLE pop the buffer off the top of the buffer stack and load 
+  it into the editing buffer.
+)
+
+You can use this, for example, to create key bindings that let you instantly 
+change directories, even while in the middle of typing another command: 
+
+example(autoload -Uz execute-command
+setopt autopushd pushdminus pushdsilent
+zle -N cd-upward  ; cd-upward()   { execute-command cd .. }
+zle -N cd-backward; cd-backward() { execute-command pushd -1 }
+zle -N cd-forward ; cd-forward()  { execute-command pushd +0 }
+bindkey '^[^[[A' cd-upward; bindkey '^[^[OA' cd-upward
+bindkey '^[-' cd-backward
+bindkey '^[=' cd-forward)
+)
 tindex(expand-absolute-path)
 item(tt(expand-absolute-path))(
 Expand the file name under the cursor to an absolute path, resolving
diff --git a/Functions/Zle/execute-command b/Functions/Zle/execute-command
new file mode 100644
index 000000000..04fccf176
--- /dev/null
+++ b/Functions/Zle/execute-command
@@ -0,0 +1,54 @@
+# This function lets you implement widgets that can execute arbitrary commands
+# without losing the current command line, in a fashion similar to the
+# 'run-help' and 'which-command' widgets. You can use this, for example, to
+# create key bindings that let you instantly change directories, even while in
+# the middle of typing another command:
+#
+#   autoload -Uz execute-command
+#   setopt autopushd pushdminus pushdsilent
+#   zle -N cd-upward  ; cd-upward()   { execute-command cd .. }
+#   zle -N cd-backward; cd-backward() { execute-command pushd -1 }
+#   zle -N cd-forward ; cd-forward()  { execute-command pushd +0 }
+#   bindkey '^[^[[A' cd-upward; bindkey '^[^[OA' cd-upward
+#   bindkey '^[-' cd-backward
+#   bindkey '^[=' cd-forward
+#
+
+case $CONTEXT in
+  ( start ) # PS1
+    ;;
+  ( cont )  # PS2
+    # Add a one-time hook that will re-run this widget at the top-level prompt.
+    autoload -Uz add-zle-hook-widget
+    local hook=line-init
+    local func=:$hook:$WIDGET
+    eval "$func() {
+      # Make sure we don't run twice.
+      add-zle-hook-widget -d $hook $func
+
+      # Don't leave anything behind.
+      zle -D $func
+      unfunction $func
+
+      zle $WIDGET
+    }"
+    add-zle-hook-widget $hook $func
+
+    # Move the entire current multiline construct into the editor buffer. This
+    # function is then aborted and we return to the top-level prompt, which
+    # triggers the hook above.
+    zle .push-line-or-edit
+    return  # Not actually necessary, but for clarity's sake
+    ;;
+  ( * )
+    # We don't want this to be used in a select loop or in vared.
+    return 1
+    ;;
+esac
+
+# Push the current buffer onto the buffer stack and clear the buffer. The ZLE
+# will auto-restore it at the next top-level prompt.
+zle .push-line
+
+BUFFER="$*"
+zle .accept-line
-- 
2.31.1


[-- Attachment #3: Type: text/plain, Size: 2 bytes --]




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

* Re: [RFC][PATCH] Add change-directory() widget function
  2021-04-21  3:18       ` Bart Schaefer
@ 2021-04-21 20:11         ` Daniel Shahaf
  2021-04-21 20:29           ` Bart Schaefer
  0 siblings, 1 reply; 30+ messages in thread
From: Daniel Shahaf @ 2021-04-21 20:11 UTC (permalink / raw)
  To: Zsh hackers list; +Cc: Marlon Richert

Bart Schaefer wrote on Tue, Apr 20, 2021 at 20:18:24 -0700:
> On Tue, Apr 20, 2021 at 3:14 PM Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
> >
> > Should any of the words be quoted to protect against global aliases?
> 
> This is generally speaking a fruitless task.  Rather the function
> should be loaded with e.g. "autoload -U", as compinit does for all
> completion functions.

How so?  «PM_UNALIASED» turns «noaliases» on for the duration of parsing
the function itself, not for the duration of parsing the code which the
function dumps into $BUFFER when executed.


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

* Re: [RFC][PATCH] Add change-directory() widget function
  2021-04-21 20:11         ` Daniel Shahaf
@ 2021-04-21 20:29           ` Bart Schaefer
  0 siblings, 0 replies; 30+ messages in thread
From: Bart Schaefer @ 2021-04-21 20:29 UTC (permalink / raw)
  To: Daniel Shahaf; +Cc: Zsh hackers list, Marlon Richert

On Wed, Apr 21, 2021 at 1:12 PM Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
>
> Bart Schaefer wrote on Tue, Apr 20, 2021 at 20:18:24 -0700:
> > On Tue, Apr 20, 2021 at 3:14 PM Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
> > >
> > > Should any of the words be quoted to protect against global aliases?
> >
> > This is generally speaking a fruitless task.
>
> How so?  «PM_UNALIASED» turns «noaliases» on for the duration of parsing
> the function itself, not for the duration of parsing the code which the
> function dumps into $BUFFER when executed.

Ah, I misunderstood the scope of "any of the words".

I'm of two minds on this one.  Totally agree regarding e.g. "builtin
cd".  OTOH if for some reason I have a global alias for "+1" or "-0" I
might actually want it expanded here (I'm having a hard time
visualizing a use case for such an alias in the first place).

Marlon's latest effort to generalize this as "execute-command" pushes
responsibility for all of the foregoing onto the caller.


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

* Re: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)
  2021-04-21 11:37         ` [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function) Marlon Richert
@ 2021-04-21 20:40           ` Bart Schaefer
  2021-04-21 21:27           ` Daniel Shahaf
  1 sibling, 0 replies; 30+ messages in thread
From: Bart Schaefer @ 2021-04-21 20:40 UTC (permalink / raw)
  To: Marlon Richert; +Cc: Daniel Shahaf, Zsh hackers list

On Wed, Apr 21, 2021 at 4:37 AM Marlon Richert <marlon.richert@gmail.com> wrote:
>
> Thank you both (Bart & Daniel) for your input. I added better safeguards against the non-PS1 cases, rewrote the function to be more generic and added comments. New patch attached.

I remain confused about why .push-line-or-edit rather than .push-input
?   I think you're making this more complicated than necessary.


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

* Re: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)
  2021-04-21 11:37         ` [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function) Marlon Richert
  2021-04-21 20:40           ` Bart Schaefer
@ 2021-04-21 21:27           ` Daniel Shahaf
  2021-04-21 21:58             ` Bart Schaefer
  1 sibling, 1 reply; 30+ messages in thread
From: Daniel Shahaf @ 2021-04-21 21:27 UTC (permalink / raw)
  To: Marlon Richert; +Cc: Zsh hackers list

[ Aside: does anyone use zyodl.vim?  I see I have some local mods and
not sure how much to prioritize cleaning them up and pushing them. ]

Marlon Richert wrote on Wed, Apr 21, 2021 at 14:37:28 +0300:
> Thank you both (Bart & Daniel) for your input. I added better safeguards against the non-PS1 cases, rewrote the function to be more generic and added comments. New patch attached.

Again, please wrap long lines in your prose.  This is the third time you
are being asked to do so.

> Subject: [PATCH] Add execute-command() widget function
> 
> +++ Doc/Zsh/contrib.yo
> @@ -2502,6 +2502,32 @@ arguments:
>  
>  example(zstyle :zle:edit-command-line editor gvim -f)
>  )
> +tindex(execute-command)
> +item(tt(execute-command))(

+1 on the generalization with respect to the previous iteration.

> +This function lets you implement widgets that can execute arbitrary commands 
> +without losing the current command line, in a fashion similar to the 
> +tt(run-help) and tt(which-command) widgets (see 
> +ifzman(the subsection Miscellaneous in zmanref(zshzle))\

The title "Miscellaneous" deserves markup and/or punctuation, not merely
capitalization.

Good catch on retaining the line continuation.

> +ifnzman(noderef(Miscellaneous))). More precisely, it 

s/it/it:|it+DASH()-/ ?

> +enumeration(
> +myeit() pushes the buffer onto the buffer stack, 
> +myeit() executes the supplied arguments, then 
> +myeit() lets the ZLE pop the buffer off the top of the buffer stack and load 
> +  it into the editing buffer.

Should the precise implementation be documented here?  If the
implementation strategy is seen as an API promise, it would be more
difficult to change it later.

Describing what the function actually _does_ (as distinguished from what
sort of context it's typically used in) is of course relevant;
cf. [workers/45152, fifth hunk].

«myeit()» is defined in Etc/FAQ.yo and isn't a standard yodl macro, so
it'll just cause a compile error here.  Yes, it's not ideal that the
documentation isn't all written in the same dialect.

> +)
> +
> +You can use this, for example, to create key bindings that let you instantly 
> +change directories, even while in the middle of typing another command: 
> +
> +example(autoload -Uz execute-command
> +setopt autopushd pushdminus pushdsilent

AUTO_PUSHD is set but not used.

As to PUSHD_MINUS and PUSHD_SILENT, it would be better to give an
example doesn't change them from their default values.  That's again
45149, which I mentioned just yesterday.

> +zle -N cd-upward  ; cd-upward()   { execute-command cd .. }
> +zle -N cd-backward; cd-backward() { execute-command pushd -1 }
> +zle -N cd-forward ; cd-forward()  { execute-command pushd +0 }

s/()/+LPAR()+RPAR()/ so they don't look like yodl macros (and
potentially throw build-time warnings).  (For Vim users, the custom
ftplugin sets up concealing which does the reverse replacement while
editing.)

Use a «--» guard on the second one, for forward compatibility with
future --options to execute-command?

In general, commands are either passed as a single string and then
parsed by a shell, or are passed as an array and then kept this way
until execve(3) time.  Here, the implementation uses «BUFFER="$*"» but
the command is passed as an array; this breaks that pattern.  Either the
commands should be passed as a single string, or the assignment to
BUFFER should be changed so stuff like «execute-command print -l
'foo bar'» would work as intended.  Contrast with _call_program, which
doesn't follow that pattern, and indeed «_call_program print-foo-bar
print -l 'foo bar'» is wrong and, I suspect, not uncommon.

Consider adding index entries for cd-upward, cd-backward, cd-forward here.

> +bindkey '^[^[[A' cd-upward; bindkey '^[^[OA' cd-upward

-1 on using random escape sequences without any explanation of what they
do and how to find them out.

Can the symbolic names of these escape sequences be used here?  At least
.
    % for k v in "${(@kv)terminfo}" ; [[ $v == (*\[A*|*OA*) ]] && echo $k 
    cuu1
    kcuu1
.
if we don't have anything more human-readable than those.  (I think this
was discussed somewhere in the "default zshrc" thread so I won't
elaborate here.)

> +bindkey '^[-' cd-backward
> +bindkey '^[=' cd-forward)

What makes it safe to assume the reader will know what to press to
invoke these?  There are no relevant matches of «alt» (as a whole word)
in the manual.

> +)
>  tindex(expand-absolute-path)
>  item(tt(expand-absolute-path))(
>  Expand the file name under the cursor to an absolute path, resolving
> diff --git a/Functions/Zle/execute-command b/Functions/Zle/execute-command
> new file mode 100644
> index 000000000..04fccf176
> --- /dev/null
> +++ b/Functions/Zle/execute-command
> @@ -0,0 +1,54 @@
> +# This function lets you implement widgets that can execute arbitrary commands
> +# without losing the current command line, in a fashion similar to the
> +# 'run-help' and 'which-command' widgets. You can use this, for example, to
> +# create key bindings that let you instantly change directories, even while in
> +# the middle of typing another command:
> +#
> +#   autoload -Uz execute-command
> +#   setopt autopushd pushdminus pushdsilent
> +#   zle -N cd-upward  ; cd-upward()   { execute-command cd .. }
> +#   zle -N cd-backward; cd-backward() { execute-command pushd -1 }
> +#   zle -N cd-forward ; cd-forward()  { execute-command pushd +0 }
> +#   bindkey '^[^[[A' cd-upward; bindkey '^[^[OA' cd-upward
> +#   bindkey '^[-' cd-backward
> +#   bindkey '^[=' cd-forward

DRY.  This information is in the manual, so it isn't needed here.

> +#
> +
> +case $CONTEXT in
> +  ( start ) # PS1

My $EDITOR thanks your use of balanced parentheses.

> +    ;;
> +  ( cont )  # PS2
> +    # Add a one-time hook that will re-run this widget at the top-level prompt.
> +    autoload -Uz add-zle-hook-widget
> +    local hook=line-init
> +    local func=:$hook:$WIDGET
> +    eval "$func() {

Concerned about namespace collisions here.

> +      # Make sure we don't run twice.
> +      add-zle-hook-widget -d $hook $func
> +
> +      # Don't leave anything behind.
> +      zle -D $func
> +      unfunction $func
> +
> +      zle $WIDGET
> +    }"

Use ${(q)} as needed.

> +    add-zle-hook-widget $hook $func
> +
> +    # Move the entire current multiline construct into the editor buffer. This
> +    # function is then aborted and we return to the top-level prompt, which
> +    # triggers the hook above.
> +    zle .push-line-or-edit
> +    return  # Not actually necessary, but for clarity's sake

How is it not necessary?  If control flow continued past the «esac»,
code would be executed.

> +    ;;
> +  ( * )
> +    # We don't want this to be used in a select loop or in vared.

If the user presses the bound key at the select prompt or in vared, why
should the library function disregard that?  Shouldn't this behaviour be
changed?  Or failing that, documented?  

> +    return 1
> +    ;;
> +esac
> +
> +# Push the current buffer onto the buffer stack and clear the buffer. The ZLE
> +# will auto-restore it at the next top-level prompt.
> +zle .push-line
> +
> +BUFFER="$*"
> +zle .accept-line
> -- 
> 2.31.1
> 

> 
> 



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

* Re: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)
  2021-04-21 21:27           ` Daniel Shahaf
@ 2021-04-21 21:58             ` Bart Schaefer
  2021-04-22 10:55               ` Marlon Richert
  0 siblings, 1 reply; 30+ messages in thread
From: Bart Schaefer @ 2021-04-21 21:58 UTC (permalink / raw)
  To: Daniel Shahaf; +Cc: Marlon Richert, Zsh hackers list

On Wed, Apr 21, 2021 at 2:28 PM Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
>
> [ Aside: does anyone use zyodl.vim?  I see I have some local mods and
> not sure how much to prioritize cleaning them up and pushing them. ]

I've got an emacs yodl-mode somewhere, which is what I use when I
happen to find it ... it may be lurking on a host I rarely access any
more.

> Again, please wrap long lines in your prose.  This is the third time you
> are being asked to do so.

In order of (my personal) preference ...
++ send messages to the list in plain text with 78-column-or so lines
   (except don't line-wrap code**)
++ or send in plain text with long lines to avoid wrapping code
++ or send in HTML so lines are automatically wrapped***

** which admittedly is something I can't currently enforce on my own mailer.
*** which I sometimes do by accident because gmail replies in HTML if
the replied-to message was HTML.

> As to PUSHD_MINUS and PUSHD_SILENT, it would be better to give an
> example doesn't change them from their default values.

Using "pushd -q ..." avoids the need for PUSHD_SILENT.  Sadly there's
no way to temporarily disable PUSHD_MINUS except to setopt it in the
widget body.

> [...] the implementation uses «BUFFER="$*"» but
> the command is passed as an array [... either]
> commands should be passed as a single string, or the assignment to
> BUFFER should be changed

For example, «BUFFER="${(q-)*}"»

> > @@ -0,0 +1,54 @@
> > +# This function lets you implement widgets that can execute arbitrary commands
>
> DRY.  This information is in the manual, so it isn't needed here.

There's precedent for having similar doc in function source files and
also in the manual, so that someone perusing the files doesn't have to
then look to see whether the function is also in the manual.

> > +    # Move the entire current multiline construct into the editor buffer. This
> > +    # function is then aborted and we return to the top-level prompt, which
> > +    # triggers the hook above.
> > +    zle .push-line-or-edit
> > +    return  # Not actually necessary, but for clarity's sake
>
> How is it not necessary?  If control flow continued past the «esac»,
> code would be executed.

Flow can't continue past «zle .push-line-or-edit» because it invokes
the equivalent of send-break and kills the widget.  But I still don't
understand why he wants this here.

> If the user presses the bound key at the select prompt or in vared, why
> should the library function disregard that?

This was my suggestion, because at a select prompt the command
wouldn't be "executed", it'd be fed to select as the value of the
selection, which would be surprising at the least.

In vared it would replace the contents of the variable with the
command string and then exit vared, which is probably also not wanted.
I didn't think of that case, but it's a good catch IMO.

> Shouldn't this behaviour be
> changed?  Or failing that, documented?

Documented, unless you have a suggestion for what to change it to be.


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

* Re: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)
  2021-04-21 21:58             ` Bart Schaefer
@ 2021-04-22 10:55               ` Marlon Richert
  2021-04-22 20:25                 ` Daniel Shahaf
  2021-04-22 23:27                 ` Bart Schaefer
  0 siblings, 2 replies; 30+ messages in thread
From: Marlon Richert @ 2021-04-22 10:55 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Daniel Shahaf, Zsh hackers list

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

New version of the patch attached. Follow-ups to unresolved discussion points below.


On 22 Apr 2021, at 00:58, Bart Schaefer <schaefer@brasslantern.com> wrote:
> On Wed, Apr 21, 2021 at 2:28 PM Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
>> Again, please wrap long lines in your prose.  This is the third time you
>> are being asked to do so.

I would love to, but I’m not sure when it happens (everything looks fine on my end) and I haven’t found an explicit setting for it in my email client. Please let me know if this email has long lines or not. That would help me figure out which combination of settings might be causing this.


>> As to PUSHD_MINUS and PUSHD_SILENT, it would be better to give an
>> example doesn't change them from their default values.

Why exactly?


> Using "pushd -q ..." avoids the need for PUSHD_SILENT.  

Except `pushd -q` has the side effect of "the hook function chpwd and the functions in the array $chpwd_functions are not called" (according to the manual). That’s not something that I would want when changing dirs interactively.


>>> +    # Move the entire current multiline construct into the editor buffer. This
>>> +    # function is then aborted and we return to the top-level prompt, which
>>> +    # triggers the hook above.
>>> +    zle .push-line-or-edit
>>> +    return  # Not actually necessary, but for clarity's sake
>> 
>> How is it not necessary?  If control flow continued past the «esac»,
>> code would be executed.
> 
> Flow can't continue past «zle .push-line-or-edit» because it invokes
> the equivalent of send-break and kills the widget.  But I still don't
> understand why he wants this here.

If I would use push-input instead of push-line-or-edit, the buffer would not get restored immediately after using the widget. You’d end up with a blank command line instead. I want it to work the same from PS2 as it does from PS1.


On 22 Apr 2021, at 00:27, Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
>> +ifnzman(noderef(Miscellaneous))). More precisely, it 
> 
> s/it/it:|it+DASH()-/ ?

Sorry, I’m new to Yodl. What exactly would that do and why would we need that here?


>> +enumeration(
>> +myeit() pushes the buffer onto the buffer stack, 
>> +myeit() executes the supplied arguments, then 
>> +myeit() lets the ZLE pop the buffer off the top of the buffer stack and load 
>> +  it into the editing buffer.
> 
> Should the precise implementation be documented here?  If the
> implementation strategy is seen as an API promise, it would be more
> difficult to change it later.
> 
> Describing what the function actually _does_ (as distinguished from what
> sort of context it's typically used in) is of course relevant;
> cf. [workers/45152, fifth hunk].

It’s about the same level of detail as given for run-help, which-command and push-line-or-edit.


>> +You can use this, for example, to create key bindings that let you instantly 
>> +change directories, even while in the middle of typing another command: 
>> +
>> +example(autoload -Uz execute-command
>> +setopt autopushd pushdminus pushdsilent
> 
> AUTO_PUSHD is set but not used.

If I wouldn’t set it, then the names cd-backward and cd-forward  wouldn’t make any sense anymore.


> As to PUSHD_MINUS and PUSHD_SILENT, it would be better to give an
> example doesn't change them from their default values.  That's again
> 45149, which I mentioned just yesterday.

Let’s suppose the following:
I rewrite the example to use `pushd +1` and `pushd -0`, so it works with default shell options.
A novice user has PUSHD_MINUS in their config (for whatever reason; they might not even know what it does).
They copy-paste the example.
Now the widgets won’t work at all as expected. It’s not even the reverse.

Setting PUSHD_MINUS in the example guarantees that it will work out of the box for novice users. (More advanced users, I’m sure, will be able to figure out how to modify it to suit their needs.)

As for PUSHD_SILENT, as I pointed out above, using `pushd -q` instead has unwanted side effects. It does not have an interchangeable alternative.


>> +zle -N cd-upward  ; cd-upward()   { execute-command cd .. }
>> +zle -N cd-backward; cd-backward() { execute-command pushd -1 }
>> +zle -N cd-forward ; cd-forward()  { execute-command pushd +0 }
> 
> s/()/+LPAR()+RPAR()/ so they don't look like yodl macros (and
> potentially throw build-time warnings).  (For Vim users, the custom
> ftplugin sets up concealing which does the reverse replacement while
> editing.)

Again, sorry, I’m new to Yodl. What exactly are you suggesting?


>> +bindkey '^[^[[A' cd-upward; bindkey '^[^[OA' cd-upward
> 
> -1 on using random escape sequences without any explanation of what they
> do and how to find them out.
> 
> Can the symbolic names of these escape sequences be used here?  At least
> .
>    % for k v in "${(@kv)terminfo}" ; [[ $v == (*\[A*|*OA*) ]] && echo $k 
>    cuu1
>    kcuu1
> .
> if we don't have anything more human-readable than those.  (I think this
> was discussed somewhere in the "default zshrc" thread so I won't
> elaborate here.)

For cuu1 and cuf1, that would work, but ^[[B and ^[[D do not have entries in terminfo. I don’t think we should use $terminfo[cuu1] and $terminfo[cuf1] in some examples but then hard-code ^[[B and ^[[D elsewhere. Hard-coding all of ^[[{A..D} would be more clear.

I’m fine using $terminfo for kcuu1, kcud1, kcuf1 and kcub1. Using $key[Up] would be even better, but we cannot rely on that being set.





[-- Attachment #2.1: Type: text/html, Size: 9891 bytes --]

[-- Attachment #2.2: 0001-Add-execute-command-widget-function.txt --]
[-- Type: text/plain, Size: 4667 bytes --]

From caed732b7c9015b2fb692d3c894236b76202a979 Mon Sep 17 00:00:00 2001
From: Marlon Richert <marlon.richert@gmail.com>
Date: Thu, 22 Apr 2021 13:36:56 +0300
Subject: [PATCH] Add execute-command() widget function

---
 Doc/Zsh/contrib.yo            | 34 ++++++++++++++++++++++
 Functions/Zle/execute-command | 54 +++++++++++++++++++++++++++++++++++
 2 files changed, 88 insertions(+)
 create mode 100644 Functions/Zle/execute-command

diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index 8bf1a208e..64388ed70 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -2502,6 +2502,40 @@ arguments:
 
 example(zstyle :zle:edit-command-line editor gvim -f)
 )
+tindex(execute-command)
+tindex(cd-upward)
+tindex(cd-backward)
+tindex(cd-forward)
+item(tt(execute-command))(
+This function lets you implement a widget that executes a specified command 
+(passed as a single string argument) without losing the current command line, 
+in a fashion similar to the tt(run-help) and tt(which-command) widgets (see 
+ifzman(the subsection bf(Miscellaneous) in zmanref(zshzle))\
+ifnzman(noderef(ZLE widgets standard Miscellaneous))). More precisely, it 
+enumeration(
+eit() pushes the buffer onto the buffer stack, then
+eit() executes the supplied argument string, then 
+eit() lets the ZLE pop the buffer off the top of the buffer stack and load 
+  it into the editing buffer.
+)
+
+You can use this, for example, to create key bindings that let you instantly 
+change directories, even while in the middle of typing another command: 
+
+example(autoload -Uz execute-command
+setopt autopushd pushdminus pushdsilent
+zle -N cd-upward  ; cd-upward()   { execute-command -- 'cd ..'    }
+zle -N cd-backward; cd-backward() { execute-command -- 'pushd -1' }
+zle -N cd-forward ; cd-forward()  { execute-command -- 'pushd +0' }
+bindkey             '^[[A' cd-upward   # Alt-Up in raw mode
+bindkey "$terminfo[kcuu1]" cd-upward   # Alt-Up in app mode
+bindkey              '^[-' cd-backward # Alt-Minus
+bindkey              '^[=' cd-forward  # Alt-Equals)
+
+Note that widgets created with this function cannot be used inside a tt(select) 
+loop or tt(vared). Under those circumstances, the function does nothing and 
+returns non-zero.
+)
 tindex(expand-absolute-path)
 item(tt(expand-absolute-path))(
 Expand the file name under the cursor to an absolute path, resolving
diff --git a/Functions/Zle/execute-command b/Functions/Zle/execute-command
new file mode 100644
index 000000000..4014fa854
--- /dev/null
+++ b/Functions/Zle/execute-command
@@ -0,0 +1,54 @@
+# Lets you implement widgets that can execute arbitrary commands without losing
+# the current command line, in a fashion similar to 'run-help' and
+# 'which-command' widgets. See the manual for examples.
+
+zmodload -F zsh/zutil b:zparseopts
+autoload -Uz add-zle-hook-widget
+
+case $CONTEXT in
+  ( start ) # PS1
+    ;;
+  ( cont )  # PS2
+    # Add a one-time hook that will re-run this widget at the top-level prompt.
+    local hook=line-init
+    local func=${(q):-:${(%):-%N}:$hook:$WIDGET}
+    eval "$func() {
+      # Make sure we don't run twice.
+      add-zle-hook-widget -d $hook $func
+
+      # Don't leave anything behind.
+      zle -D $func
+      unfunction $func
+
+      # Use -w to ensure \$WIDGET is set to our original widget, not the hook.
+      # This doesn't matter at present, but might matter in future or if this
+      # code gets copy-pasted elsewhere.
+      zle ${(q)WIDGET} -w
+    }"
+    add-zle-hook-widget $hook ${(Q)func}
+
+    # Move the entire current multiline construct into the editor buffer. This
+    # function is then aborted and we return to the top-level prompt, which
+    # triggers the hook above.
+    # We don't use .push-input here, because that would result in a blank
+    # buffer afterwards.
+    zle .push-line-or-edit
+    return  # Command flow never actually gets here. See above.
+    ;;
+  ( * )
+    # We don't want this to be used in a select loop or in vared:
+    # * At a select prompt, the command wouldn't be "executed"; it'd be fed to
+    #   select as the value of the selection.
+    # * In vared, it would replace the contents of the variable with the
+    #   command string and then exit vared.
+    return 75 # EX_TEMPFAIL; see `man 3 sysexits`.
+    ;;
+esac
+
+# Push the current buffer onto the buffer stack and clear the buffer. The ZLE
+# will auto-restore it at the next top-level prompt.
+zle .push-line
+
+zparseopts -D - # Remove - or -- argument.
+BUFFER="$1"
+zle .accept-line
-- 
2.31.1


[-- Attachment #2.3: Type: text/html, Size: 252 bytes --]

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

* Re: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)
  2021-04-22 10:55               ` Marlon Richert
@ 2021-04-22 20:25                 ` Daniel Shahaf
  2021-04-22 23:27                 ` Bart Schaefer
  1 sibling, 0 replies; 30+ messages in thread
From: Daniel Shahaf @ 2021-04-22 20:25 UTC (permalink / raw)
  To: Marlon Richert; +Cc: Zsh hackers list

Marlon Richert wrote on Thu, Apr 22, 2021 at 13:55:35 +0300:
> On 22 Apr 2021, at 00:58, Bart Schaefer <schaefer@brasslantern.com> wrote:
> > On Wed, Apr 21, 2021 at 2:28 PM Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
> >> Again, please wrap long lines in your prose.  This is the third time you
> >> are being asked to do so.
> 
> I would love to, but I’m not sure when it happens (everything looks fine on my end) and I haven’t found an explicit setting for it in my email client. Please let me know if this email has long lines or not. That would help me figure out which combination of settings might be causing this.

Yes, it does.

> 
> >> As to PUSHD_MINUS and PUSHD_SILENT, it would be better to give an
> >> example doesn't change them from their default values.
> 
> Why exactly?

The very next sentence (which Bart had snipped, but you should have read
anyway) points to another post of mine which gives a reason.

Moreover, that sentence was the second time in two days that I pointed
you to that specific post.  Overall, I think there have been three or
four unique pieces of feedback which you were given multiple times *this
week alone*.  We shouldn't have to repeat ourselves to you.

> On 22 Apr 2021, at 00:27, Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
> >> +ifnzman(noderef(Miscellaneous))). More precisely, it 
> > 
> > s/it/it:|it+DASH()-/ ?
> 
> Sorry, I’m new to Yodl. What exactly would that do and why would we need that here?
> 

It was a request to change «it» either to «it:» or to «it+DASH()-».

The former would append a colon to the paragraph.

The latter would append an em dash.  The manual sources denote an em
dash as «DASH()-» (which is poor encapsulation and a leaky abstraction,
yes).

> >> +You can use this, for example, to create key bindings that let you instantly 
> >> +change directories, even while in the middle of typing another command: 
> >> +
> >> +example(autoload -Uz execute-command
> >> +setopt autopushd pushdminus pushdsilent
> > 
> > AUTO_PUSHD is set but not used.
> 
> If I wouldn’t set it, then the names cd-backward and cd-forward  wouldn’t make any sense anymore.

Sorry, I was thinking of AUTO_CD.

The point below applies, though: it would be better to give an example
that works under the default options; in this case, to use «pushd»
rather than «cd» with an option.

> 
> > As to PUSHD_MINUS and PUSHD_SILENT, it would be better to give an
> > example doesn't change them from their default values.  That's again
> > 45149, which I mentioned just yesterday.
> 
> Let’s suppose the following:
> I rewrite the example to use `pushd +1` and `pushd -0`, so it works with default shell options.
> A novice user has PUSHD_MINUS in their config (for whatever reason; they might not even know what it does).
> They copy-paste the example.
> Now the widgets won’t work at all as expected. It’s not even the reverse.
> 

A user who doesn't know what their config does isn't the primary
use-case we should design for.  Besides, we should never recommend to
set an option globally if setting it locally would suffice — at which
point we might as well set the _default_ locally, to handle users who
(deliberately) set the option to a value other than the default.

> Setting PUSHD_MINUS in the example guarantees that it will work out of the box for novice users. (More advanced users, I’m sure, will be able to figure out how to modify it to suit their needs.)
> 

Yes, until they ssh to some other box where they have an empty .zshrc
file and don't understand why they get the non-PUSHD_MINUS behaviour.
(And for that not to happen, then they'll have to read up on
PUSHD_MINUS, which increases the learning curve — which is the point
I made in 45149.  Did you read that post?  I didn't look up those X-Seq
values for my own amusement.)

> As for PUSHD_SILENT, as I pointed out above, using `pushd -q` instead has unwanted side effects. It does not have an interchangeable alternative.

Isn't «pushd foo > /dev/null» an alternative?  And it has the obvious
advantage of not affecting other uses of pushd.

> 
> >> +zle -N cd-upward  ; cd-upward()   { execute-command cd .. }
> >> +zle -N cd-backward; cd-backward() { execute-command pushd -1 }
> >> +zle -N cd-forward ; cd-forward()  { execute-command pushd +0 }
> > 
> > s/()/+LPAR()+RPAR()/ so they don't look like yodl macros (and
> > potentially throw build-time warnings).  (For Vim users, the custom
> > ftplugin sets up concealing which does the reverse replacement while
> > editing.)
> 
> Again, sorry, I’m new to Yodl. What exactly are you suggesting?

That you literally change «()» to «+LPAR()+RPAR()» so yodl doesn't look
for 0-ary macros called «upward()», «backward()», etc., and warn on
stderr that it can't find them.  There's plenty of examples of that.

> >> +bindkey '^[^[[A' cd-upward; bindkey '^[^[OA' cd-upward
> > 
> > -1 on using random escape sequences without any explanation of what they
> > do and how to find them out.
> > 
> > Can the symbolic names of these escape sequences be used here?  At least
> > .
> >    % for k v in "${(@kv)terminfo}" ; [[ $v == (*\[A*|*OA*) ]] && echo $k 
> >    cuu1
> >    kcuu1
> > .
> > if we don't have anything more human-readable than those.  (I think this
> > was discussed somewhere in the "default zshrc" thread so I won't
> > elaborate here.)
> 
> For cuu1 and cuf1, that would work, but ^[[B and ^[[D do not have
> entries in terminfo.

They aren't used in the manual either, so why does it matter whether
they're in $terminfo or not?

> I don’t think we should use $terminfo[cuu1] and $terminfo[cuf1] in
> some examples but then hard-code ^[[B and ^[[D elsewhere. Hard-coding
> all of ^[[{A..D} would be more clear.

Disagree.  I would have proposed an alternative, but it's an academic
issue.

> I’m fine using $terminfo for kcuu1, kcud1, kcuf1 and kcub1. Using
> $key[Up] would be even better, but we cannot rely on that being set.


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

* Re: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)
  2021-04-22 10:55               ` Marlon Richert
  2021-04-22 20:25                 ` Daniel Shahaf
@ 2021-04-22 23:27                 ` Bart Schaefer
  2021-04-24 20:06                   ` Marlon Richert
  1 sibling, 1 reply; 30+ messages in thread
From: Bart Schaefer @ 2021-04-22 23:27 UTC (permalink / raw)
  To: Marlon Richert; +Cc: Daniel Shahaf, Zsh hackers list

On Thu, Apr 22, 2021 at 3:55 AM Marlon Richert <marlon.richert@gmail.com> wrote:
>
> If I would use push-input instead of push-line-or-edit, the buffer would not get restored immediately after using the widget. You’d end up with a blank command line instead. I want it to work the same from PS2 as it does from PS1.

I see.  (Also, I forgot that push-input is implemented via
push-line-or-edit, not the other way around.)

Then how about:

execute-command() {
  local finish="zle .reset-prompt"
  case $CONTEXT in
  (cont) finish="zle .push-line-or-edit"
    ;&
  (start)
    print -S "${${(q-)@}}"
    eval "${(q-)@}"
    ;;
  (*) return 75
    ;;
  esac
  eval "$finish"
}

> Setting PUSHD_MINUS in the example guarantees that it will work out of the box for novice users. (More advanced users, I’m sure, will be able to figure out how to modify it to suit their needs.)

As I mentioned, by turning this into a generic "execute-command"
you've made it the responsibility of the caller to pass the right
thing.  The question then is how to write the examples.

Instead of

  setopt autopushd pushdminus pushdsilent
  cd-backward() { execute-command -- 'pushd -1' }

It should be similar to

  cd-backward() {
    setopt localoptions autopushd pushdminus pushdsilent
    execute-command -- pushd -1
  }

(I removed the quotes around «pushd -1» to correspond to my use of
${(q-)@} above.)


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

* Re: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)
  2021-04-22 23:27                 ` Bart Schaefer
@ 2021-04-24 20:06                   ` Marlon Richert
  2021-04-24 21:49                     ` Bart Schaefer
  2021-04-25 17:02                     ` Hard-wrapping emails (Was: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)) Lawrence Velázquez
  0 siblings, 2 replies; 30+ messages in thread
From: Marlon Richert @ 2021-04-24 20:06 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list


[-- Attachment #1.1: Type: text/plain, Size: 2193 bytes --]

New patch attached.

(Also, I switched from macOS's Mail app back again to Gmail's web app.
Let's see how that goes for the line wrapping. I'm unable to reproduce it
when reading back my own emails in either app. I don't have to do any
horizontal scrolling when reading them.)

On Fri, Apr 23, 2021 at 2:27 AM Bart Schaefer <schaefer@brasslantern.com>
wrote:

> On Thu, Apr 22, 2021 at 3:55 AM Marlon Richert <marlon.richert@gmail.com>
> wrote:
> >
> > If I would use push-input instead of push-line-or-edit, the buffer would
> not get restored immediately after using the widget. You’d end up with a
> blank command line instead. I want it to work the same from PS2 as it does
> from PS1.
>
> I see.  (Also, I forgot that push-input is implemented via
> push-line-or-edit, not the other way around.)
>
> Then how about:
>
> execute-command() {
>   local finish="zle .reset-prompt"
>   case $CONTEXT in
>   (cont) finish="zle .push-line-or-edit"
>     ;&
>   (start)
>     print -S "${${(q-)@}}"
>     eval "${(q-)@}"
>     ;;
>   (*) return 75
>     ;;
>   esac
>   eval "$finish"
> }
>

I tried a similar approach with `eval` before, but that doesn't play nice
with all prompts, as I outlined in workers/48622:

On Tue, Apr 20, 2021 at 4:36 PM Marlon Richert <marlon.richert@gmail.com>
wrote:

> There was some discussion as to whether this should or shouldn’t leave the
> executed commands visible in the terminal and/or history. I tried a version
> of it that doesn’t put the commands in the buffer but executes them
> directly and then does zle .reset-prompt, but this can leave the prompt in
> an incorrect state if the prompt uses precmd hooks. I also tried “manually”
> running precmd_functions before zle .reset-prompt, but this causes problems
> when any precmd hook contains code that doesn’t work when executed inside a
> widget (for example, echoing an escape code that result in a response from
> the terminal). Therefore, I instead settled on pushing the current line,
> setting the buffer, and then accepting the line. This seems to me the least
> likely to break anything, but I’m open to alternatives.
>

[-- Attachment #1.2: Type: text/html, Size: 2972 bytes --]

[-- Attachment #2: 0001-Add-execute-command-widget-function.txt --]
[-- Type: text/plain, Size: 4705 bytes --]

From 9845e9d84366c7731f6e0c13c8b661e31616e7d0 Mon Sep 17 00:00:00 2001
From: Marlon Richert <marlon.richert@gmail.com>
Date: Sat, 24 Apr 2021 22:59:27 +0300
Subject: [PATCH] Add execute-command() widget function

---
 Doc/Zsh/contrib.yo            | 43 ++++++++++++++++++++++++++++
 Functions/Zle/execute-command | 54 +++++++++++++++++++++++++++++++++++
 2 files changed, 97 insertions(+)
 create mode 100644 Functions/Zle/execute-command

diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index 8bf1a208e..8142b8418 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -2502,6 +2502,49 @@ arguments:
 
 example(zstyle :zle:edit-command-line editor gvim -f)
 )
+tindex(execute-command)
+tindex(cd-upward)
+tindex(cd-backward)
+tindex(cd-forward)
+item(tt(execute-command))(
+This function helps you implement widgets that execute a specified command
+(passed to the function as a single string argument) without losing the current
+command line, in a fashion similar to the tt(run-help) and tt(which-command) 
+widgets (see
+ifzman(the subsection bf(Miscellaneous) in zmanref(zshzle))\
+ifnzman(noderef(ZLE widgets standard Miscellaneous))). The function does this
+as follows:
+enumeration(
+eit() Push the buffer onto the buffer stack.
+eit() Execute the supplied argument string.
+eit() Let the ZLE pop the buffer off the top of the buffer stack and load it 
+  into the editing buffer.
+)
+
+You can use this, for example, to create key bindings that let you instantly
+change directories, even while in the middle of typing another command:
+
+example(autoload -Uz execute-command
+zle -N cd-parent; cd-parent+LPAR()+RPAR() {
+  execute-command -- 'cd ..'
+}
+zle -N pushd-prev; pushd-prev+LPAR()+RPAR() {
+  local sign='+'; [[ -o pushdminus ]] && sign='-'
+  execute-command -- "pushd ${sign}1 > /dev/null"
+}
+zle -N pushd-next; pushd-next+LPAR()+RPAR() { 
+  local sign='-'; [[ -o pushdminus ]] && sign='+'
+  execute-command -- "pushd ${sign}0 > /dev/null" 
+}
+bindkey  "^[$terminfo[cuu1]" cd-parent  # Alt-Up in raw mode
+bindkey "^[$terminfo[kcuu1]" cd-parent  # Alt-Up in app mode
+bindkey                '^[-' pushd-prev # Alt-Minus
+bindkey                '^[=' pushd-next # Alt-Equals)
+
+Note that widgets created with this function cannot be used inside a tt(select)
+loop or tt(vared). Under those circumstances, the function does nothing and
+returns non-zero.
+)
 tindex(expand-absolute-path)
 item(tt(expand-absolute-path))(
 Expand the file name under the cursor to an absolute path, resolving
diff --git a/Functions/Zle/execute-command b/Functions/Zle/execute-command
new file mode 100644
index 000000000..4014fa854
--- /dev/null
+++ b/Functions/Zle/execute-command
@@ -0,0 +1,54 @@
+# Lets you implement widgets that can execute arbitrary commands without losing
+# the current command line, in a fashion similar to 'run-help' and
+# 'which-command' widgets. See the manual for examples.
+
+zmodload -F zsh/zutil b:zparseopts
+autoload -Uz add-zle-hook-widget
+
+case $CONTEXT in
+  ( start ) # PS1
+    ;;
+  ( cont )  # PS2
+    # Add a one-time hook that will re-run this widget at the top-level prompt.
+    local hook=line-init
+    local func=${(q):-:${(%):-%N}:$hook:$WIDGET}
+    eval "$func() {
+      # Make sure we don't run twice.
+      add-zle-hook-widget -d $hook $func
+
+      # Don't leave anything behind.
+      zle -D $func
+      unfunction $func
+
+      # Use -w to ensure \$WIDGET is set to our original widget, not the hook.
+      # This doesn't matter at present, but might matter in future or if this
+      # code gets copy-pasted elsewhere.
+      zle ${(q)WIDGET} -w
+    }"
+    add-zle-hook-widget $hook ${(Q)func}
+
+    # Move the entire current multiline construct into the editor buffer. This
+    # function is then aborted and we return to the top-level prompt, which
+    # triggers the hook above.
+    # We don't use .push-input here, because that would result in a blank
+    # buffer afterwards.
+    zle .push-line-or-edit
+    return  # Command flow never actually gets here. See above.
+    ;;
+  ( * )
+    # We don't want this to be used in a select loop or in vared:
+    # * At a select prompt, the command wouldn't be "executed"; it'd be fed to
+    #   select as the value of the selection.
+    # * In vared, it would replace the contents of the variable with the
+    #   command string and then exit vared.
+    return 75 # EX_TEMPFAIL; see `man 3 sysexits`.
+    ;;
+esac
+
+# Push the current buffer onto the buffer stack and clear the buffer. The ZLE
+# will auto-restore it at the next top-level prompt.
+zle .push-line
+
+zparseopts -D - # Remove - or -- argument.
+BUFFER="$1"
+zle .accept-line
-- 
2.31.1


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

* Re: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)
  2021-04-24 20:06                   ` Marlon Richert
@ 2021-04-24 21:49                     ` Bart Schaefer
  2021-04-24 21:58                       ` Bart Schaefer
  2021-04-25 17:02                     ` Hard-wrapping emails (Was: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)) Lawrence Velázquez
  1 sibling, 1 reply; 30+ messages in thread
From: Bart Schaefer @ 2021-04-24 21:49 UTC (permalink / raw)
  To: Marlon Richert; +Cc: Zsh hackers list

On Sat, Apr 24, 2021 at 1:07 PM Marlon Richert <marlon.richert@gmail.com> wrote:
>
> (Also, I switched from macOS's Mail app back again to Gmail's web app. Let's see how that goes for the line wrapping.

In the gmail web app, you need to pop open the three-dot menu next to
the trash can at bottom right, and select "Plain Text Mode".  Then
gmail will wrap the lines (sometimes where you don't want it to, but
that's why the patch is an attachment).

> On Fri, Apr 23, 2021 at 2:27 AM Bart Schaefer <schaefer@brasslantern.com> wrote:
>>
>> Then how about:
>
> I tried a similar approach with `eval` before, but that doesn't play nice with all prompts, as I outlined in workers/48622:

OK, then this:

execute-command () {
  case $CONTEXT in
    (cont|start)
      print -z "$PREBUFFER$BUFFER"
      print -S "${${(q-)@}}"
      eval "${(q-)@}"
      ;;
    (*) return 75 ;;
  esac
  zle .send-break
}


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

* Re: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)
  2021-04-24 21:49                     ` Bart Schaefer
@ 2021-04-24 21:58                       ` Bart Schaefer
  2021-04-26 18:08                         ` Marlon Richert
  0 siblings, 1 reply; 30+ messages in thread
From: Bart Schaefer @ 2021-04-24 21:58 UTC (permalink / raw)
  To: Marlon Richert; +Cc: Zsh hackers list

On Sat, Apr 24, 2021 at 2:49 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> OK, then this:

Let's make the sticklers happy and fix the incomplete print commands ...

execute-command () {
  case $CONTEXT in
    (cont|start) print -rz -- "$PREBUFFER$BUFFER"
      print -rS -- "${${(q-)@}}"
      eval "${(q-)@}" ;;
    (*) return 75 ;;
  esac
  zle .send-break
}


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

* Hard-wrapping emails (Was: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function))
  2021-04-24 20:06                   ` Marlon Richert
  2021-04-24 21:49                     ` Bart Schaefer
@ 2021-04-25 17:02                     ` Lawrence Velázquez
  1 sibling, 0 replies; 30+ messages in thread
From: Lawrence Velázquez @ 2021-04-25 17:02 UTC (permalink / raw)
  To: Marlon Richert; +Cc: zsh-workers

> On Apr 24, 2021, at 4:06 PM, Marlon Richert <marlon.richert@gmail.com> wrote:
> 
> (Also, I switched from macOS's Mail app back again to Gmail's web app. Let's see how that goes for the line wrapping. I'm unable to reproduce it when reading back my own emails in either app. I don't have to do any horizontal scrolling when reading them.)

You need to look at something that doesn't soft-wrap. For example:

https://www.zsh.org/mla/workers/2021/msg00886.html

FWIW, I use a trivial Service (I think they're called Quick Actions
now?) to hard-wrap as necessary before sending from Mail or my
webmail.  It's just an Automator wrapper for fmt(1).

vq

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

* Re: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)
  2021-04-24 21:58                       ` Bart Schaefer
@ 2021-04-26 18:08                         ` Marlon Richert
  2021-04-26 21:39                           ` Bart Schaefer
  0 siblings, 1 reply; 30+ messages in thread
From: Marlon Richert @ 2021-04-26 18:08 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

On Sun, Apr 25, 2021 at 12:58 AM Bart Schaefer
<schaefer@brasslantern.com> wrote:
> execute-command () {
>   case $CONTEXT in
>     (cont|start) print -rz -- "$PREBUFFER$BUFFER"
>       print -rS -- "${${(q-)@}}"
>       eval "${(q-)@}" ;;
>     (*) return 75 ;;
>   esac
>   zle .send-break
> }

I tried it and it's otherwise good, but that last line causes %? == 1
and %(? == true. Can that be fixed somehow?


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

* Re: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)
  2021-04-26 18:08                         ` Marlon Richert
@ 2021-04-26 21:39                           ` Bart Schaefer
  2021-04-27 10:46                             ` Marlon Richert
  0 siblings, 1 reply; 30+ messages in thread
From: Bart Schaefer @ 2021-04-26 21:39 UTC (permalink / raw)
  To: Marlon Richert; +Cc: Zsh hackers list

On Mon, Apr 26, 2021 at 11:09 AM Marlon Richert
<marlon.richert@gmail.com> wrote:
>
> On Sun, Apr 25, 2021 at 12:58 AM Bart Schaefer
> <schaefer@brasslantern.com> wrote:
> > execute-command () {
> >   case $CONTEXT in
> >     (cont|start) print -rz -- "$PREBUFFER$BUFFER"
> >       print -rS -- "${${(q-)@}}"
> >       eval "${(q-)@}" ;;
> >     (*) return 75 ;;
> >   esac
> >   zle .send-break
> > }
>
> I tried it and it's otherwise good, but that last line causes %? == 1
> and %(? == true. Can that be fixed somehow?

I would think that occurs with "zle .push-line-or-edit" also?

Anyway, no, there's no way to abort the current ZLE without $?
becoming nonzero.  A lot of effort was put into preserving the value
of $? across everything that comes in between the previous command
ending (even if it ended by interrupting ZLE) and the next prompt
being printed.

I believe you'll see similar things with e.g. edit-command-line from
the distribution, if it happens to take the branch that calls
send-break.


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

* Re: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)
  2021-04-26 21:39                           ` Bart Schaefer
@ 2021-04-27 10:46                             ` Marlon Richert
  2021-04-27 19:27                               ` Bart Schaefer
  0 siblings, 1 reply; 30+ messages in thread
From: Marlon Richert @ 2021-04-27 10:46 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

On Tue, Apr 27, 2021 at 12:40 AM Bart Schaefer
<schaefer@brasslantern.com> wrote:
> I would think that occurs with "zle .push-line-or-edit" also?
>
> Anyway, no, there's no way to abort the current ZLE without $?
> becoming nonzero.  A lot of effort was put into preserving the value
> of $? across everything that comes in between the previous command
> ending (even if it ended by interrupting ZLE) and the next prompt
> being printed.
>
> I believe you'll see similar things with e.g. edit-command-line from
> the distribution, if it happens to take the branch that calls
> send-break.

In my patch, the last thing done before the widget exits is to set
$BUFFER and call zle .accept-line. The only return value the user will
see in that case is that of the executed command; not that of
.push-line-or-edit. I still prefer that version. Are there drawbacks
to that approach?


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

* Re: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)
  2021-04-27 10:46                             ` Marlon Richert
@ 2021-04-27 19:27                               ` Bart Schaefer
  2021-04-30 19:16                                 ` Marlon Richert
  0 siblings, 1 reply; 30+ messages in thread
From: Bart Schaefer @ 2021-04-27 19:27 UTC (permalink / raw)
  To: Marlon Richert; +Cc: Zsh hackers list

On Tue, Apr 27, 2021 at 3:46 AM Marlon Richert <marlon.richert@gmail.com> wrote:
>
> On Tue, Apr 27, 2021 at 12:40 AM Bart Schaefer
> <schaefer@brasslantern.com> wrote:
> > Anyway, no, there's no way to abort the current ZLE without $?
> > becoming nonzero.

I dug around to refresh my memory on this.  The important detail is
that the PS1/PS2 prompts represent ZLE doing a handshake with the
input parser+lexer.  The only way to get back to the PS1 prompt is to
get the parser to abandon its current state, and the only generalized
way to do that is to cause the lexer to return an error, so send-break
simulates the shell having received a keyboard interrupt.  That aborts
the lexer, which aborts the parser, and the top-level state is
restored.  $? represents the interrupt (you can tell a real interrupt
from a simulated one because $? = 130 in the signal case).

BTW, a possible bug with this is that $ZLE_LINE_ABORTED is not
assigned when there is a simulated interrupt.

> In my patch, the last thing done before the widget exits is to set
> $BUFFER and call zle .accept-line. The only return value the user will
> see in that case is that of the executed command; not that of
> .push-line-or-edit. I still prefer that version. Are there drawbacks
> to that approach?

The main drawback is the complexity involved in
creating/installing/disposing a hook function that modifies the zle
startup.  Seeing the exit status of the executed command via %? in the
prompt doesn't "feel" important enough to me to justify the extra
gyrations.

It's also the case that your solution takes two different code paths
in the calling widget depending on $CONTEXT -- the widget continues
past execute-command in the PS1 (or the $? = 75) case, but is aborted
in the PS2 case.  My solution consistently stops the calling widget if
the command is executed.  The documentation will need to account for
whichever approach is chosen.

Perhaps execute-command could set a variable with the exit status of
the command, and then a user for whom it is important to see that
status in the prompt could implement $PS1 in such a way as to show it
in place of %? (and then clear it, perhaps in preexec).


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

* Re: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)
  2021-04-27 19:27                               ` Bart Schaefer
@ 2021-04-30 19:16                                 ` Marlon Richert
  2021-04-30 20:25                                   ` Bart Schaefer
  0 siblings, 1 reply; 30+ messages in thread
From: Marlon Richert @ 2021-04-30 19:16 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

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

Thanks for the explanations. Here's a new patch.

[-- Attachment #2: 0001-Add-execute-commands-widget-function.txt --]
[-- Type: text/plain, Size: 4944 bytes --]

From c6036cecbedc414e024049e1da3f66dadebd497f Mon Sep 17 00:00:00 2001
From: Marlon Richert <marlon.richert@gmail.com>
Date: Fri, 30 Apr 2021 21:59:07 +0300
Subject: [PATCH] Add `execute-commands` widget function

---
 Doc/Zsh/contrib.yo            | 57 +++++++++++++++++++++++++++++++++++
 Functions/Zle/execute-command | 41 +++++++++++++++++++++++++
 2 files changed, 98 insertions(+)
 create mode 100644 Functions/Zle/execute-command

diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index 8bf1a208e..7d0acc10a 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -2502,6 +2502,58 @@ arguments:
 
 example(zstyle :zle:edit-command-line editor gvim -f)
 )
+tindex(execute-commands)
+tindex(cd-upward)
+tindex(cd-backward)
+tindex(cd-forward)
+item(tt(execute-commands) [ var(options) ] [--] var(command) ...)(
+This function helps you implement widgets that execute commands (passed to the
+function as string arguments) without losing the current command line, in a
+fashion similar to the tt(run-help) and tt(which-command) widgets (see
+ifzman(the subsection bf(Miscellaneous) in zmanref(zshzle))\
+ifnzman(noderef(ZLE widgets standard Miscellaneous))). You can use this, for
+example, to create key bindings that let you instantly change directories, even
+while in the middle of typing another command:
+
+example(autoload -Uz execute-commands
+zle -N cd-parent; cd-parent+LPAR()+RPAR() {
+  execute-commands -- 'cd ..'
+}
+zle -N pushd-prev; pushd-prev+LPAR()+RPAR() {
+  local sign='+'; [[ -o pushdminus ]] && sign='-'
+  execute-commands -- "pushd ${sign}1 > /dev/null"
+}
+zle -N pushd-next; pushd-next+LPAR()+RPAR() {
+  local sign='-'; [[ -o pushdminus ]] && sign='+'
+  execute-commands -- "pushd ${sign}0 > /dev/null"
+}
+bindkey  "^[$terminfo[cuu1]" cd-parent  # Alt-Up in raw mode
+bindkey "^[$terminfo[kcuu1]" cd-parent  # Alt-Up in app mode
+bindkey                '^[-' pushd-prev # Alt-Minus
+bindkey                '^[=' pushd-next # Alt-Equals)
+
+By default, tt(execute-commands) executes the supplied commands quietly and
+without saving them to history. Its behavior can be modified by setting the
+following options:
+startsitem()
+sitem(tt(-e))(
+Echo the commands to the command line before executing them.
+)
+sitem(tt(-s))(
+Save the commands to history.
+)
+sitem(tt(-v) var(name))(
+Store the last command's exit code in parameter var(name).
+)
+endsitem()
+
+Note that calling tt(execute-commands) should always be the only or last
+statement you execute in your widget, because (after executing the supplied
+commands) tt(execute-commands) causes execution of the current widget to be
+aborted. Also note that tt(execute-commands) cannot be used when inside a
+tt(select) loop or tt(vared). Under those circumstances, it does nothing and
+returns non-zero.
+)
 tindex(expand-absolute-path)
 item(tt(expand-absolute-path))(
 Expand the file name under the cursor to an absolute path, resolving
@@ -4649,6 +4701,11 @@ See `Recompiling Functions'
 ifzman(above)\
 ifnzman((noderef(Utilities))).
 )
+findex(zrestart)
+item(tt(zrestart))(
+This function tests whether the shell is able to restart without error and, if 
+so, restarts the shell.
+)
 findex(zstyle+)
 item(tt(zstyle+) var(context) var(style) var(value) [ tt(+) var(subcontext) var(style) var(value) ... ])(
 This makes defining styles a bit simpler by using a single `tt(+)' as a
diff --git a/Functions/Zle/execute-command b/Functions/Zle/execute-command
new file mode 100644
index 000000000..f8e25f54e
--- /dev/null
+++ b/Functions/Zle/execute-command
@@ -0,0 +1,41 @@
+# Lets you implement widgets that can execute arbitrary commands without losing
+# the current command line, in a fashion similar to the 'run-help' and
+# 'which-command' widgets. See the manual for more details.
+
+zmodload -F zsh/zutil b:zparseopts
+local -A opts
+zparseopts -D -A opts - e s v:
+
+local -a err
+zle ||
+    err+=( "${0}: can only be called from zle widgets" )
+(( # )) ||
+    err+=( "${0}: not enough arguments" )
+if [[ -n $err ]]; then
+  print -lu2 -- $err[@] \
+$'Usage: execute-commands [ <options> ] [--] <command> ...
+Execute commands from zle widget, without mangling prompt or buffer.
+Options:
+  -e         echo commands before executing
+  -s         save commands to history
+  -v <name>  store last command\'s exit status in param <name>'
+  return 1
+fi
+
+case $CONTEXT in
+  ( cont | start )
+    print -rz -- "$PREBUFFER$BUFFER"  # Push all lines to buffer stack.
+    [[ -v opts[-e] ]] &&
+        BUFFER="${(F)@}"              # Echo commands to buffer.
+    [[ -v opts[-s] ]] &&
+        print -rS -- "${(F)@}"        # Save commands to history.
+    eval "${(F)@}"                    # Execute commands.
+    local -i ret=$?
+    [[ -v opts[-v] ]] &&
+        eval "$opts[-v]=$ret"         # Store exit status.
+    ;;
+  ( * )
+    return 75 # EX_TEMPFAIL; see `man 3 sysexits`.
+    ;;
+esac
+zle .send-break
-- 
2.31.1


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

* Re: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)
  2021-04-30 19:16                                 ` Marlon Richert
@ 2021-04-30 20:25                                   ` Bart Schaefer
  2021-05-01 13:30                                     ` Marlon Richert
  0 siblings, 1 reply; 30+ messages in thread
From: Bart Schaefer @ 2021-04-30 20:25 UTC (permalink / raw)
  To: Marlon Richert; +Cc: Zsh hackers list

I'm trying not to be a wet blanket here, but:

You've accidentally included the doc for zrestart in the .yo patch.

The -e option is never going to do anything useful, because send-break
will kill the buffer to which the command has just been written.  (I
feel as though I've said this before about a different proposed
widget.  Hm.)  If you wrote to the buffer before calling print -z that
might make some sense, but more so at PS1 than PS2.

Attempting to pass multiple commands, one per positional argument, and
then eval them all at once with newlines between, strikes me as
inviting all kinds of quoting problems, plus obscures the return
status if some command in the middle of the list fails.

It's not safe to use eval that way to assign to $ops[-v], the argument
passed to -v might not be a simple variable name.  E.g. if the user
forgets the variable name, the first command they intended to execute
will be stored there instead.  Using a single well-known (documented)
name instead of passing an argument would avoid this, and it's not as
though you can have two execute-commands simultaneously that would
introduce a conflict.

Other things that occurred to me not directly related to this patch:

There's nothing preventing the user from passing more "zle" commands
to execute-commands which could arbitrarily mess up your print -z ...
heck, execute-commands could even be caused to call itself
recursively.  This is not something you need to try to code around,
but it could be documented as a misuse.

Instead of throwing an error when there are no commands provided,
execute-commands could invoke read-from-minibuffer to input a command
to run, much like the builtin execute-named-cmd does for widget names.
That could make execute-commands into a widget rather than just
something callable from widgets.


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

* Re: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)
  2021-04-30 20:25                                   ` Bart Schaefer
@ 2021-05-01 13:30                                     ` Marlon Richert
  2021-05-31 17:55                                       ` Marlon Richert
  0 siblings, 1 reply; 30+ messages in thread
From: Marlon Richert @ 2021-05-01 13:30 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

OK, I give up. Someone else can finish this.

On Fri, Apr 30, 2021 at 11:25 PM Bart Schaefer
<schaefer@brasslantern.com> wrote:
>
> I'm trying not to be a wet blanket here, but:
>
> You've accidentally included the doc for zrestart in the .yo patch.
>
> The -e option is never going to do anything useful, because send-break
> will kill the buffer to which the command has just been written.  (I
> feel as though I've said this before about a different proposed
> widget.  Hm.)  If you wrote to the buffer before calling print -z that
> might make some sense, but more so at PS1 than PS2.
>
> Attempting to pass multiple commands, one per positional argument, and
> then eval them all at once with newlines between, strikes me as
> inviting all kinds of quoting problems, plus obscures the return
> status if some command in the middle of the list fails.
>
> It's not safe to use eval that way to assign to $ops[-v], the argument
> passed to -v might not be a simple variable name.  E.g. if the user
> forgets the variable name, the first command they intended to execute
> will be stored there instead.  Using a single well-known (documented)
> name instead of passing an argument would avoid this, and it's not as
> though you can have two execute-commands simultaneously that would
> introduce a conflict.
>
> Other things that occurred to me not directly related to this patch:
>
> There's nothing preventing the user from passing more "zle" commands
> to execute-commands which could arbitrarily mess up your print -z ...
> heck, execute-commands could even be caused to call itself
> recursively.  This is not something you need to try to code around,
> but it could be documented as a misuse.
>
> Instead of throwing an error when there are no commands provided,
> execute-commands could invoke read-from-minibuffer to input a command
> to run, much like the builtin execute-named-cmd does for widget names.
> That could make execute-commands into a widget rather than just
> something callable from widgets.


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

* Re: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)
  2021-05-01 13:30                                     ` Marlon Richert
@ 2021-05-31 17:55                                       ` Marlon Richert
  0 siblings, 0 replies; 30+ messages in thread
From: Marlon Richert @ 2021-05-31 17:55 UTC (permalink / raw)
  To: Paul; +Cc: Zsh hackers list

Paul, you wrote in workers/48408: "I can write a proper widget."

Any chance you might feel like continuing where I left off, below? :)

On Sat, May 1, 2021 at 4:30 PM Marlon Richert <marlon.richert@gmail.com> wrote:
>
> OK, I give up. Someone else can finish this.
>
> On Fri, Apr 30, 2021 at 11:25 PM Bart Schaefer
> <schaefer@brasslantern.com> wrote:
> >
> > I'm trying not to be a wet blanket here, but:
> >
> > You've accidentally included the doc for zrestart in the .yo patch.
> >
> > The -e option is never going to do anything useful, because send-break
> > will kill the buffer to which the command has just been written.  (I
> > feel as though I've said this before about a different proposed
> > widget.  Hm.)  If you wrote to the buffer before calling print -z that
> > might make some sense, but more so at PS1 than PS2.
> >
> > Attempting to pass multiple commands, one per positional argument, and
> > then eval them all at once with newlines between, strikes me as
> > inviting all kinds of quoting problems, plus obscures the return
> > status if some command in the middle of the list fails.
> >
> > It's not safe to use eval that way to assign to $ops[-v], the argument
> > passed to -v might not be a simple variable name.  E.g. if the user
> > forgets the variable name, the first command they intended to execute
> > will be stored there instead.  Using a single well-known (documented)
> > name instead of passing an argument would avoid this, and it's not as
> > though you can have two execute-commands simultaneously that would
> > introduce a conflict.
> >
> > Other things that occurred to me not directly related to this patch:
> >
> > There's nothing preventing the user from passing more "zle" commands
> > to execute-commands which could arbitrarily mess up your print -z ...
> > heck, execute-commands could even be caused to call itself
> > recursively.  This is not something you need to try to code around,
> > but it could be documented as a misuse.
> >
> > Instead of throwing an error when there are no commands provided,
> > execute-commands could invoke read-from-minibuffer to input a command
> > to run, much like the builtin execute-named-cmd does for widget names.
> > That could make execute-commands into a widget rather than just
> > something callable from widgets.


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

end of thread, other threads:[~2021-05-31 17:56 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-20 13:36 [RFC][PATCH] Add change-directory() widget function Marlon Richert
2021-04-20 19:43 ` Bart Schaefer
2021-04-20 20:13   ` Marlon Richert
2021-04-20 21:32     ` Bart Schaefer
2021-04-21  3:46       ` Bart Schaefer
2021-04-21 11:37         ` [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function) Marlon Richert
2021-04-21 20:40           ` Bart Schaefer
2021-04-21 21:27           ` Daniel Shahaf
2021-04-21 21:58             ` Bart Schaefer
2021-04-22 10:55               ` Marlon Richert
2021-04-22 20:25                 ` Daniel Shahaf
2021-04-22 23:27                 ` Bart Schaefer
2021-04-24 20:06                   ` Marlon Richert
2021-04-24 21:49                     ` Bart Schaefer
2021-04-24 21:58                       ` Bart Schaefer
2021-04-26 18:08                         ` Marlon Richert
2021-04-26 21:39                           ` Bart Schaefer
2021-04-27 10:46                             ` Marlon Richert
2021-04-27 19:27                               ` Bart Schaefer
2021-04-30 19:16                                 ` Marlon Richert
2021-04-30 20:25                                   ` Bart Schaefer
2021-05-01 13:30                                     ` Marlon Richert
2021-05-31 17:55                                       ` Marlon Richert
2021-04-25 17:02                     ` Hard-wrapping emails (Was: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)) Lawrence Velázquez
2021-04-20 21:57     ` [RFC][PATCH] Add change-directory() widget function Daniel Shahaf
2021-04-20 22:14     ` Daniel Shahaf
2021-04-21  0:09       ` Bart Schaefer
2021-04-21  3:18       ` Bart Schaefer
2021-04-21 20:11         ` Daniel Shahaf
2021-04-21 20:29           ` Bart Schaefer

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