summaryrefslogtreecommitdiff
path: root/Functions/Zle
diff options
context:
space:
mode:
Diffstat (limited to 'Functions/Zle')
-rw-r--r--Functions/Zle/edit-command-line23
-rw-r--r--Functions/Zle/incarg322
-rw-r--r--Functions/Zle/keeper3
3 files changed, 312 insertions, 36 deletions
diff --git a/Functions/Zle/edit-command-line b/Functions/Zle/edit-command-line
index 5f7ea321f..d4b405eaf 100644
--- a/Functions/Zle/edit-command-line
+++ b/Functions/Zle/edit-command-line
@@ -11,15 +11,30 @@ local left right prebuffer buffer=$BUFFER lbuffer=$LBUFFER
local TMPSUFFIX=.zsh
# set up parameters depending on which context we are called from,
# see below comment for more details
-if (( REGION_ACTIVE )); then
+if (( REGION_ACTIVE == 1 )); then
if (( CURSOR < MARK )); then
left=$CURSOR right=$MARK
- lbuffer=
else
left=$MARK right=$CURSOR
- lbuffer[right-left,-1]=
fi
- (( left++ ))
+ lbuffer=$lbuffer[++left,-1]
+ buffer=$BUFFER[left,++right]
+elif (( REGION_ACTIVE == 2 )); then
+ local nl=$'\n'
+ if (( CURSOR < MARK )); then
+ left=${${BUFFER[1,CURSOR]}[(I)$nl]}
+ right=${${BUFFER[MARK+1,-1]}[(i)$nl]}
+ (( right += MARK ))
+ else
+ left=${${BUFFER[1,MARK]}[(I)$nl]}
+ right=${${BUFFER[CURSOR+1,-1]}[(i)$nl]}
+ (( right += CURSOR ))
+ fi
+ lbuffer=$lbuffer[++left,-1]
+ if [[ $BUFFER[right] = $nl ]]; then
+ # Keep the newline because "$(<$1)" below trims it
+ (( --right ))
+ fi
buffer=$BUFFER[left,right]
elif (( ! ZLE_RECURSIVE )); then
prebuffer=$PREBUFFER
diff --git a/Functions/Zle/incarg b/Functions/Zle/incarg
index cff0cfe4c..0cfaf9ff5 100644
--- a/Functions/Zle/incarg
+++ b/Functions/Zle/incarg
@@ -1,43 +1,303 @@
-# Shell function to increment an integer either under the cursor or just
-# to the left of it. Use
+emulate -L zsh
+
+# A ZLE widget to increment an integer.
+#
+# In addition to decimals, it can handle hexadecimals prefixed with "0x",
+# binaries with "0b", and octals with "0o".
+#
+# By default, the target integer will be incremented by one. With a numeric
+# argument, the integer is incremented by the amount of the argument. The shell
+# parameter "incarg" may be set to change the default increment to something
+# other than one.
+#
+# The behavior of this widget changes depending on how it is named.
+#
+# - incarg / decarg
+#
+# incarg will increment an integer either under the cursor or just to the left
+# of it. decarg, on the other hand, will decrement it.
+#
+# For example,
+#
+# echo 41
+# ^^^ cursor anywhere here
+#
+# with incarg gives
+#
+# echo 42
+# ^ cursor will move here
+#
+# - sync-incarg / sync-decarg
+#
+# The sync- variant is used for creating a sequence of numbers on split
+# terminals with synchronized key input. The first pane won't be incremented
+# at all, but each pane after that will have the number incremented once more
+# than the previous pane.
+#
+# Currently supports tmux and iTerm2.
+#
+# - vim-incarg / vim-decarg
+#
+# This behaves like Vim's CTRL-A / CTRL-X. It moves the cursor to the nearest
+# number after the cursor and increments or decrements it.
+#
+# - vim-backward-incarg / vim-backward-decarg
+#
+# This behaves like vim-incarg & vim-decarg, but it searches backwards for a
+# number.
+#
+# - vim-sync-incarg / vim-sync-decarg
+#
+# This combines the behavior of the vim- and sync- variants. It's inspired by
+# Vim's g_CTRL-A / g_CTRL-X.
+#
+# - vim-backward-sync-incarg / vim-backward-sync-decarg
+#
+# This combines the behavior of the vim-backward- and sync- variants.
+#
+# Example Usage:
+#
# autoload -Uz incarg
-# zle -N incarg
-# bindkey "..." incarg
-# to define it. For example,
-# echo 41
-# ^^^ cursor anywhere here
-# with incarg gives
-# echo 42
-# with the cursor in the same place.
-#
-# A numeric argument gives a number other than 1 to add (may be negative).
-# If you're going to do it a lot with one particular number, you can set
-# the parameter incarg to that number (a numeric argument still takes
-# precedence).
+# for widget in vim-{,sync-}{inc,dec}arg; do
+# zle -N "$widget" incarg
+# done
+# bindkey -a \
+# '^A' vim-incarg \
+# '^X' vim-decarg \
+# 'g^A' vim-sync-incarg \
+# 'g^X' vim-sync-decarg
-emulate -L zsh
-setopt extendedglob
+zle -f vichange
-local rrest lrest num
+setopt localoptions extended_glob
+local match mbegin mend MATCH MBEGIN MEND i
-rrest=${RBUFFER##[0-9]#}
-if [[ $RBUFFER = [0-9]* ]]; then
- if [[ -z $rrest ]]; then
- num=$RBUFFER
- else
- num=${RBUFFER[1,-$#rrest-1]}
+[[ -z "$BUFFER" ]] && return 1
+
+# find the number and determine the base
+integer pos=$(( CURSOR + 1 )) base=0
+
+# avoid miscalculating positions when cursor is at the end of the line
+while (( pos > 0 )) && [[ "$BUFFER[pos]" == '' ]]; do
+ (( pos-- ))
+done
+
+# check for a prefix (e.g., 0x) before the cursor
+for (( i = 0; i < 2; i++ )); do
+ case "$BUFFER[1,pos]" in
+ *0[xX][0-9a-fA-F]##) base=16 ;;
+ *0[oO][0-7]##) base=8 ;;
+ *0[bB][01]##) base=2 ;;
+ *[1-9]) base=10 ;;
+ *0) ;; # there may be a prefix right after the cursor
+ *)
+ # the non-Vim variant looks right before the cursor too, but not after it
+ if [[ "$WIDGET" != vi* ]]; then
+ if (( i == 0 )); then
+ (( pos-- ))
+ continue
+ else
+ return 1
+ fi
+ fi
+ ;;
+ esac
+
+ break
+done
+
+# check for a prefix on the cursor
+if (( base == 0 && pos < $#BUFFER )); then
+ case "$BUFFER[1,pos+1]" in
+ *0[xX][0-9a-fA-F]) base=16; (( pos++ )) ;;
+ *0[oO][0-7]) base=8; (( pos++ )) ;;
+ *0[bB][01]) base=2; (( pos++ )) ;;
+ esac
+fi
+
+if (( base == 0 )); then
+ if [[ "$WIDGET" == vi* ]]; then
+ if [[ "$WIDGET" == *backward-* ]]; then
+ # search backwards for a number
+ while true; do
+ case "$BUFFER[1,pos]" in
+ *0[xX][0-9a-fA-F]##) base=16 ;;
+ *0[oO][0-7]##) base=8 ;;
+ *0[bB][01]##) base=2 ;;
+ *[0-9]) base=10 ;;
+ *-)
+ case "$BUFFER[pos,-1]" in
+ -0[xX][0-9a-fA-F]*) ;;
+ -0[oO][0-7]*) ;;
+ -0[bB][01]*) ;;
+ -[0-9]*) base=10 ;;
+ esac
+ ;;
+ esac
+ (( base != 0 )) && break
+
+ (( pos-- ))
+ (( pos <= 0 )) && return 1
+ done
+ else
+ # jump to the nearest number after the cursor
+ while [[ "$BUFFER[pos]" == [^0-9] ]]; do
+ (( pos++ ))
+ (( pos > $#BUFFER )) && return 1
+ done
+ fi
+ fi
+
+ # check for a prefix right after the cursor and jump right after it, if any
+ if (( pos <= 1 )) || [[ "$BUFFER[pos-1]" == [^0-9] ]]; then
+ case "$BUFFER[pos,-1]" in
+ 0[xX][0-9a-fA-F]*) base=16; (( pos += 2 )) ;;
+ 0[oO][0-7]*) base=8; (( pos += 2 )) ;;
+ 0[bB][01]*) base=2; (( pos += 2 )) ;;
+ esac
fi
fi
-lrest=${LBUFFER%%[0-9]#}
-if [[ $LBUFFER = *[0-9] ]]; then
- if [[ -z $lrest ]]; then
- num="$LBUFFER$num"
+if (( base == 0 )); then
+ base=10
+fi
+
+# find the start of the number
+integer first="$pos"
+case "$base" in
+ 10)
+ while [[ "$BUFFER[first-1]" == [0-9] ]]; do
+ (( first-- ))
+ done
+ if [[ $BUFFER[first-1] = - ]]; then
+ (( first-- ))
+ fi
+ ;;
+ 2)
+ while [[ "$BUFFER[first-1]" == [01] ]]; do
+ (( first-- ))
+ done
+ ;;
+ 8)
+ while [[ "$BUFFER[first-1]" == [0-7] ]]; do
+ (( first-- ))
+ done
+ ;;
+ 16)
+ while [[ "$BUFFER[first-1]" == [0-9a-fA-F] ]]; do
+ (( first-- ))
+ done
+ ;;
+esac
+
+# find the end of the number
+integer last="$pos"
+case "$base" in
+ 10)
+ while [[ "$BUFFER[last+1]" == [0-9] ]]; do
+ (( last++ ))
+ done
+ ;;
+ 2)
+ while [[ "$BUFFER[last+1]" == [01] ]]; do
+ (( last++ ))
+ done
+ ;;
+ 8)
+ while [[ "$BUFFER[last+1]" == [0-7] ]]; do
+ (( last++ ))
+ done
+ ;;
+ 16)
+ while [[ "$BUFFER[last+1]" == [0-9a-fA-F] ]]; do
+ (( last++ ))
+ done
+ ;;
+esac
+
+# calculate the number of digits
+integer ndigits=0
+case "$BUFFER[first,first+1]" in
+ 0*|-0) ndigits=$(( last - first + 1 )) ;;
+esac
+
+# determine the amount to increment
+integer delta=${NUMERIC:-${incarg:-1}}
+if [[ "$WIDGET" = *decarg ]]; then
+ (( delta = -delta ))
+fi
+if [[ "$WIDGET" = *sync-* ]]; then
+ integer pane_index=0
+ if [[ -n "$TMUX_PANE" ]]; then
+ pane_index="$(tmux display-message -pt "$TMUX_PANE" '#{pane_index}')"
+ elif [[ "$ITERM_SESSION_ID" =~ '^w[0-9]+t[0-9]+p([0-9]+)' ]]; then
+ pane_index="$match[1]"
else
- num="${LBUFFER[$#lrest+1,-1]}$num"
+ zle -M "[$WIDGET] unsupported terminal"
+ return 1
fi
+ (( delta *= pane_index ))
fi
-[[ -n $num ]] && (( num += ${NUMERIC:-${incarg:-1}} ))
+local old="$BUFFER[first,last]"
+integer oldlen=$#BUFFER
+integer oldnum="$base#$old" 2> /dev/null
+
+# -00 should increment to 01 instead of 001
+if [[ "$BUFFER[first]" == '-' ]] && (( oldnum == 0 )); then
+ (( ndigits-- ))
+fi
+
+local fmt1 fmt2
+case "$base" in
+ 10) fmt1=d; fmt2='#10' ;;
+ 2) fmt1=s; fmt2='##2' ;;
+ 8) fmt1=s; fmt2='##8' ;;
+ 16) fmt1="$BUFFER[first-1]"; fmt2='#16' ;;
+esac
+
+local raw_result padded
+# $(( )) outputs an error message to stderr when integer truncation occurs
+printf -v raw_result "%0$ndigits$fmt1" $(( [$fmt2] "$base#$old" + delta )) 2> /dev/null
+padded="${raw_result// /0}"
+integer newnum="$base#$padded" 2> /dev/null
+
+# try to detect integer truncation
+if (( base != 10 && newnum < 0
+ || delta > 0 && newnum < oldnum
+ || delta < 0 && newnum > oldnum )); then
+ zle -M "[$WIDGET] The resulting number is either too big or too small."
+ return 1
+fi
+
+# adjust the number of leading zeros if the sign of the integer changed
+local new
+if (( base == 10 && ndigits == $#padded )); then
+ if (( oldnum < 0 && newnum >= 0 )); then
+ new="${padded#0}"
+ elif (( oldnum >= 0 && newnum < 0 )); then
+ new="-0${padded#-}"
+ fi
+fi
+if [[ -z "$new" ]]; then
+ new="$padded"
+fi
+
+if zstyle -t ":zle:$WIDGET" debug; then
+ zle -M "[$WIDGET] base: $base delta: $delta old: '$old' new: '$new'"
+fi
+
+if (( 0 < first && first <= last && last <= $#BUFFER )); then
+ BUFFER[first,last]="$new"
+else
+ zle -M "[$WIDGET] The detected location of the integer was invalid. [location=BUFFER[$first,$last]]"
+ return 1
+fi
+
+integer offset=0
+if [[ "$WIDGET" == vi* ]]; then
+ offset=-1
+fi
+(( CURSOR = last + $#BUFFER - oldlen + offset ))
-BUFFER="$lrest$num$rrest"
+return 0
diff --git a/Functions/Zle/keeper b/Functions/Zle/keeper
index a40125771..1570eb94a 100644
--- a/Functions/Zle/keeper
+++ b/Functions/Zle/keeper
@@ -73,6 +73,7 @@ zstyle ':completion:expand-kept-result:*' completer _insert_kept
_expand_word_and_keep() {
{
+ _shadow compadd
function compadd {
local -A args
zparseopts -E -A args J:
@@ -85,7 +86,7 @@ _expand_word_and_keep() {
}
_expand_word
} always {
- unfunction compadd
+ _unshadow compadd
}
}