summaryrefslogtreecommitdiff
path: root/Functions/Zle/bracketed-paste-magic
diff options
context:
space:
mode:
Diffstat (limited to 'Functions/Zle/bracketed-paste-magic')
-rw-r--r--Functions/Zle/bracketed-paste-magic192
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