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

Re: [PATCH] Add execute-command() widget function (was Re: [RFC][PATCH] Add change-directory() widget function)



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


On 22 Apr 2021, at 00:58, Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx> wrote:
On Wed, Apr 21, 2021 at 2:28 PM Daniel Shahaf <d.s@xxxxxxxxxxxxxxxxxx> 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@xxxxxxxxxxxxxxxxxx> 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.


From caed732b7c9015b2fb692d3c894236b76202a979 Mon Sep 17 00:00:00 2001
From: Marlon Richert <marlon.richert@xxxxxxxxx>
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




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