diff options
Diffstat (limited to 'Functions/Zle/bracketed-paste-magic')
-rw-r--r-- | Functions/Zle/bracketed-paste-magic | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/Functions/Zle/bracketed-paste-magic b/Functions/Zle/bracketed-paste-magic new file mode 100644 index 000000000..da106d1ac --- /dev/null +++ b/Functions/Zle/bracketed-paste-magic @@ -0,0 +1,192 @@ +# Starting with zsh-5.0.9, ZLE began to recognize the "bracketed paste" +# capability of terminal emulators, that is, the sequences $'\e[200~' to +# start a paste and $'\e[201~' to indicate the end of the pasted text. +# Pastes are handled by the bracketed-paste widget and insert literally +# into the editor buffer rather than being interpreted as keystrokes. +# +# This disables some common usages where the self-insert widget has been +# replaced in order to accomplish some extra processing. An example is +# the contributed url-quote-magic widget. The bracketed-paste-magic +# widget replaces bracketed-paste with a wrapper that re-enables these +# self-insert actions, and other actions as selected by the zstyles +# described below. +# +# Setup: +# autoload -Uz bracketed-paste-magic +# zle -N bracketed-paste bracketed-paste-magic + +# The following zstyles may be set to control processing of pasted text. +# +# active-widgets +# Looked up in the context :bracketed-paste-magic to obtain a list of +# patterns that match widget names that should be activated during the +# paste. All other key sequences are processed as self-insert-unmeta. +# The default is 'self-*' so any user-defined widgets named with that +# prefix are active along with the builtin self-insert. If this style is +# not set (note: it must be explicitly deleted after loading this +# function, otherwise it becomes set by default) or has no value, no +# widgets are active and the pasted text is inserted literally. If the +# value includes undefined-key, any unknown sequences are discarded from +# the pasted text. +# +# inactive-keys +# This is the inverse of active-widgets, it lists key sequences that +# always use self-insert-unmeta even when bound to an active-widget. +# Note that this is a list of literal key sequences, not patterns. +# This style is in context :bracketed-paste-magic and has no default. +# +# paste-init +# paste-finish +# Also looked up in the context :bracketed-paste-magic, these styles +# each are a list of function names. They are executed in widget +# context but are called as functions (NOT as widgets with "zle name"). +# They also run in zsh emulation context set by bracketed-paste-magic. +# As with hooks, the functions are called in order until one of them +# returns a nonzero exit status. The parameter PASTED contains the +# current state of the pasted text, other ZLE parameters are as usual. +# Although a nonzero status stops each list of functions, it does NOT +# abort the entire paste operation; use "zle send-break" for that. + +# IMPORTANT: During processing of the paste (after paste-init and +# before paste-finish), BUFFER starts empty and history is restricted, +# so cursor motions etc. may not pass outside of the pasted content. +# However, the paste-init functions have access to the full history and +# the original BUFFER, so they may for example move words from BUFFER +# into PASTED to make those words visible to the active-widgets. + +# Establish default values for styles, but only if not already set +zstyle -m :bracketed-paste-magic active-widgets '*' || + zstyle ':bracketed-paste-magic' active-widgets 'self-*' + +# Helper/example paste-init for exposing a word prefix inside PASTED. +# Useful with url-quote-magic if you have http://... on the line and +# are pasting additional text on the end of the URL. +# +# Usage: +# zstyle :bracketed-paste-magic paste-init backward-extend-paste +# +# TODO: rewrite this using match-words-by-style +# +backward-extend-paste() { + : emulate -LR zsh # Already set by bracketed-paste-magic + integer bep_mark=$MARK bep_region=$REGION_ACTIVE + if (( REGION_ACTIVE && MARK < CURSOR )); then + zle .exchange-point-and-mark + fi + if (( CURSOR )); then + local -a bep_words=( ${(z)LBUFFER} ) + if [[ -n $bep_words[-1] && $LBUFFER = *$bep_words[-1] ]]; then + PASTED=$bep_words[-1]$PASTED + LBUFFER=${LBUFFER%${bep_words[-1]}} + fi + fi + if (( MARK > bep_mark )); then + zle .exchange-point-and-mark + fi + REGION_ACTIVE=$bep_region +} + +# Example paste-finish for quoting the pasted text. +# +# Usage e.g.: +# zstyle :bracketed-paste-magic paste-finish quote-paste +# zstyle :bracketed-paste-magic:finish quote-style qqq +# +# Using "zstyle -e" to examine $PASTED lets you choose different quotes +# depending on context. +# +# To forcibly turn off numeric prefix quoting, use e.g.: +# zstyle :bracketed-paste-magic:finish quote-style none +# +quote-paste() { + : emulate -LR zsh # Already set by bracketed-paste-magic + local qstyle + # If there's a quoting style, be sure .bracketed-paste leaves it alone + zstyle -s :bracketed-paste-magic:finish quote-style qstyle && NUMERIC=1 + case $qstyle in + (b) PASTED=${(b)PASTED};; + (q-) PASTED=${(q-)PASTED};; + (\\|q) PASTED=${(q)PASTED};; + (\'|qq) PASTED=${(qq)PASTED};; + (\"|qqq) PASTED=${(qqq)PASTED};; + (\$|qqqq) PASTED=${(qqqq)PASTED};; + (Q) PASTED=${(Q)PASTED};; + esac +} + +# Now the actual function + +bracketed-paste-magic() { + emulate -LR zsh + local -a bpm_hooks bpm_inactive + local PASTED bpm_func bpm_active + + # Set PASTED and run the paste-init functions + zle .bracketed-paste PASTED + if zstyle -a :bracketed-paste-magic paste-init bpm_hooks; then + for bpm_func in $bpm_hooks; do + if (( $+functions[$bpm_func] )); then + $bpm_func || break + fi + done + fi + + # Save context, create a clean slate for the paste + integer bpm_mark=$MARK bpm_cursor=$CURSOR bpm_region=$REGION_ACTIVE + integer bpm_numeric=${NUMERIC:-1} + local bpm_buffer=$BUFFER + fc -p -a /dev/null 0 0 + BUFFER= + + zstyle -a :bracketed-paste-magic inactive-keys bpm_inactive + if zstyle -s :bracketed-paste-magic active-widgets bpm_active '|'; then + # There are active widgets. Reprocess $PASTED as keystrokes. + NUMERIC=1 + zle -U - $PASTED + while [[ -n $PASTED ]] && zle .read-command; do + PASTED=${PASTED#$KEYS} + if [[ $KEYS = ${(~j:|:)${(b)bpm_inactive}} ]]; then + zle .self-insert-unmeta + else + case $REPLY in + (${~bpm_active}) zle $REPLY;; + (*) zle .self-insert-unmeta;; + esac + fi + done + PASTED=$BUFFER + fi + + # Restore state + BUFFER=$bpm_buffer + MARK=$bpm_mark + CURSOR=$bpm_cursor + REGION_ACTIVE=$bpm_region + NUMERIC=$bpm_numeric + fc -P + + # PASTED has been updated, run the paste-finish functions + if zstyle -a :bracketed-paste-magic paste-finish bpm_hooks; then + for bpm_func in $bpm_hooks; do + if (( $+functions[$bpm_func] )); then + $bpm_func || break + fi + done + fi + + # Reprocess $PASTED as an actual paste this time + zle -U - $PASTED$'\e[201~' # append paste-end marker + zle .bracketed-paste + zle .split-undo + + # Arrange to display highlighting if necessary + if [[ -n ${(M)zle_highlight:#paste:*} ]]; then + zle -R + zle .read-command && zle -U - $KEYS + fi +} + +# Handle zsh autoloading conventions +if [[ $zsh_eval_context = *loadautofunc && ! -o kshautoload ]]; then + bracketed-paste-magic "$@" +fi |