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

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



On 21 Apr 2021, at 00:32, Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx> 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@xxxxxxxxxxxxxxxx> wrote:
> Oh, I missed this:
> 
>> On Tue, Apr 20, 2021 at 1:13 PM Marlon Richert <marlon.richert@xxxxxxxxx> 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.

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




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