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