From caed732b7c9015b2fb692d3c894236b76202a979 Mon Sep 17 00:00:00 2001 From: Marlon Richert 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