diff options
Diffstat (limited to 'Functions')
36 files changed, 718 insertions, 143 deletions
diff --git a/Functions/Chpwd/cdr b/Functions/Chpwd/cdr index 4bed88b13..43745e5aa 100644 --- a/Functions/Chpwd/cdr +++ b/Functions/Chpwd/cdr @@ -55,7 +55,7 @@ # pattern from the directory list. The match is against the fully # expanded directory path and the full string must match (use wildcards # at the ends if needed). If output is going to a terminal, the -# function will print the new list for the user to confrim; this can be +# function will print the new list for the user to confirm; this can be # skipped by giving -P instead of -p. # # Details of directory handling diff --git a/Functions/MIME/zsh-mime-handler b/Functions/MIME/zsh-mime-handler index 288a0796d..2feacf063 100644 --- a/Functions/MIME/zsh-mime-handler +++ b/Functions/MIME/zsh-mime-handler @@ -193,7 +193,7 @@ if ! zsh-mime-contexts -s $suffix flags flags; then # Same again for flags. s=$suffix while true; do - flags="${zsh_mime_flags[$suffix]}" + flags="${zsh_mime_flags[$s]}" if [[ -n $flags ]]; then break fi diff --git a/Functions/Misc/add-zle-hook-widget b/Functions/Misc/add-zle-hook-widget index 4d8049083..4293a07dd 100644 --- a/Functions/Misc/add-zle-hook-widget +++ b/Functions/Misc/add-zle-hook-widget @@ -39,7 +39,7 @@ zstyle zle-hook types ${hooktypes#zle-} # Relying on multifuncdef option here function azhw:${^hooktypes} { - local -a hook_widgets + local -a hook_widgets match mbegin mend local hook # Values of these styles look like number:name # and we run them in number order @@ -58,7 +58,7 @@ function azhw:${^hooktypes} { # Redefine ourself with the setup left out function add-zle-hook-widget { - local -a hooktypes + local -a hooktypes match mbegin mend zstyle -a zle-hook types hooktypes # This part copied from add-zsh-hook diff --git a/Functions/Misc/colors b/Functions/Misc/colors index b221e6688..8a0cec383 100644 --- a/Functions/Misc/colors +++ b/Functions/Misc/colors @@ -44,7 +44,7 @@ color=( 35 magenta 45 bg-magenta 36 cyan 46 bg-cyan 37 white 47 bg-white -# 38 iso-8316-6 # 48 bg-iso-8316-6 +# 38 iso-8613-6 # 48 bg-iso-8613-6 39 default 49 bg-default # Other codes: @@ -63,6 +63,16 @@ color=( # 63 double-overline-or-left # 64 stress # 65 no-ideogram-marking + +# Bright color codes (xterm extension) + 90 bright-gray 100 bg-bright-gray + 91 bright-red 101 bg-bright-red + 92 bright-green 102 bg-bright-green + 93 bright-yellow 103 bg-bright-yellow + 94 bright-blue 104 bg-bright-blue + 95 bright-magenta 105 bg-bright-magenta + 96 bright-cyan 106 bg-bright-cyan + 97 bright-white 107 bg-bright-white ) # A word about black and white: The "normal" shade of white is really a @@ -79,15 +89,21 @@ for k in ${(k)color}; do color[${color[$k]}]=$k; done # Add "fg-" keys for all the text colors, for clarity. -for k in ${color[(I)3?]}; do color[fg-${color[$k]}]=$k; done +for k in ${color[(I)[39]?]}; do color[fg-${color[$k]}]=$k; done # This is inaccurate, but the prompt theme system needs it. -color[grey]=${color[black]} -color[fg-grey]=${color[grey]} -color[bg-grey]=${color[bg-black]} +for k in grey gray; do + color[$k]=${color[black]} + color[fg-$k]=${color[$k]} + color[bg-$k]=${color[bg-black]} +done + +# Assistance for the colo(u)r-blind. -# Assistance for the color-blind. +for k in '' fg- bg-; do + color[${k}bright-grey]=${color[${k}bright-gray]} +done colour=(${(kv)color}) # A case where ksh namerefs would be useful ... diff --git a/Functions/Misc/is-at-least b/Functions/Misc/is-at-least index d4ff3552a..5985684be 100644 --- a/Functions/Misc/is-at-least +++ b/Functions/Misc/is-at-least @@ -24,8 +24,14 @@ emulate -L zsh local IFS=".-" min_cnt=0 ver_cnt=0 part min_ver version order +: ${2=$ZSH_VERSION} + +# sort out the easy corner cases first +[[ $1 = $2 ]] && return 0 # same version +[[ -n $2 ]] || return 1 # no version + min_ver=(${=1}) -version=(${=2:-$ZSH_VERSION} 0) +version=(${=2} 0) while (( $min_cnt <= ${#min_ver} )); do while [[ "$part" != <-> ]]; do diff --git a/Functions/Misc/mkshadow b/Functions/Misc/mkshadow new file mode 100644 index 000000000..2ae3a0f2c --- /dev/null +++ b/Functions/Misc/mkshadow @@ -0,0 +1,11 @@ +#autoload +# Front-end to the completion helper _shadow for use outside completion. +# This just forces proper autoload of _shadow/_unshadow and calls them. + +autoload _shadow +mkshadow() { unset REPLY; _shadow "$@" } +rmshadow() { unset REPLY; _unshadow } + +# Bootstrap because of autoload special case +unset REPLY +_shadow "$@" diff --git a/Functions/Misc/regexp-replace b/Functions/Misc/regexp-replace index 0d5948075..d4408f0f7 100644 --- a/Functions/Misc/regexp-replace +++ b/Functions/Misc/regexp-replace @@ -40,7 +40,7 @@ if (( $4 )); then # append offsets and computed replacement to the array # we need to perform the evaluation in a scalar assignment so that if # it generates an array, the elements are converted to string (by - # joining with the first chararacter of $IFS as usual) + # joining with the first character of $IFS as usual) 5=${(e)3} argv+=(${(s: :)ZPCRE_OP} "$5") diff --git a/Functions/Misc/run-help b/Functions/Misc/run-help index d52c1b032..462044b72 100644 --- a/Functions/Misc/run-help +++ b/Functions/Misc/run-help @@ -58,11 +58,11 @@ do case $what in (*( is an alias for (noglob|nocorrect))*) [[ ${what[(w)7]:t} != ${what[(w)1]} ]] && - run_help_orig_cmd=${what[(w)1]} run-help ${what[(w)7]:t} + run_help_orig_cmd=${what[(w)1]} run-help ${what[(w)7]:t} ${(z)${what[(w)8,-1]}} ;; (*( is an alias)*) [[ ${what[(w)6]:t} != ${what[(w)1]} ]] && - run_help_orig_cmd=${what[(w)1]} run-help ${what[(w)6]:t} + run_help_orig_cmd=${what[(w)1]} run-help ${what[(w)6]:t} ${(z)${what[(w)7,-1]}} ;; (*( is a * function)) case ${what[(w)1]} in @@ -98,13 +98,13 @@ do if whence "run-help-$1:t" >/dev/null then local cmd_args - builtin getln cmd_args + builtin getln cmd_args && builtin print -z "$cmd_args" - cmd_args=( ${(z)cmd_args} ) + cmd_args=( ${(z)${cmd_args:-"$*"}} ) # Discard the command itself & everything before it. - shift $cmd_args[(i)${run_help_orig_cmd:-$1}] cmd_args || - return + shift $cmd_args[(i)(${run_help_orig_cmd}|$1)] cmd_args 2>/dev/null || + continue # Discard options, parameter assignments & paths. cmd_args=( ${cmd_args[@]:#([-+]*|*=*|*/*|\~*)} ) diff --git a/Functions/Misc/run-help-docker b/Functions/Misc/run-help-docker new file mode 100644 index 000000000..09a8a221a --- /dev/null +++ b/Functions/Misc/run-help-docker @@ -0,0 +1,9 @@ +if [[ $# == 0 ]] +then + man docker +elif [[ $# > 1 && $1 == (builder|checkpoint|config|container|context|image|manifest|network|node|plugin|secret|service|stack|swarm|system|trust|volume) ]] +then + man docker-$1-$2 +else + man docker-$1 +fi diff --git a/Functions/Misc/run-help-ip b/Functions/Misc/run-help-ip index b811ce352..f635cce71 100644 --- a/Functions/Misc/run-help-ip +++ b/Functions/Misc/run-help-ip @@ -18,7 +18,7 @@ case $1 in (addrl*) man ip-addrlabel ;; (a*) man ip-address ;; (l2*) man ip-l2tp ;; - (li*) man ip-link ;; + (l*) man ip-link ;; (ma*) man ip-maddress ;; (mo*) man ip-monitor ;; (mr*) man ip-mroute ;; diff --git a/Functions/Misc/run-help-openssl b/Functions/Misc/run-help-openssl index c528418c8..e4e45070e 100644 --- a/Functions/Misc/run-help-openssl +++ b/Functions/Misc/run-help-openssl @@ -1,7 +1 @@ - -if [ $# -eq 0 ]; then - man openssl -else - man $1 -fi - +man openssl${1:+-$1} || man ${1:-openssl} diff --git a/Functions/Misc/run-help-perf b/Functions/Misc/run-help-perf new file mode 100644 index 000000000..2e0695af2 --- /dev/null +++ b/Functions/Misc/run-help-perf @@ -0,0 +1 @@ +man perf${1:+-$1} diff --git a/Functions/Misc/run-help-podman b/Functions/Misc/run-help-podman new file mode 100644 index 000000000..64d9cd83f --- /dev/null +++ b/Functions/Misc/run-help-podman @@ -0,0 +1,9 @@ +if [[ $# == 0 ]] +then + man podman +elif [[ $# > 1 && $1 == (container|generate|healthcheck|image|kube|machine|manifest|network|pod|secret|system|volume) ]] +then + man podman-$1-$2 +else + man podman-$1 +fi diff --git a/Functions/Misc/run-help-ssh b/Functions/Misc/run-help-ssh new file mode 100644 index 000000000..9c48596ff --- /dev/null +++ b/Functions/Misc/run-help-ssh @@ -0,0 +1,6 @@ +if [[ $# < 2 ]] +then + man ssh +else + run-help $2 +fi diff --git a/Functions/Misc/run-help-svk b/Functions/Misc/run-help-svk deleted file mode 100644 index 782538246..000000000 --- a/Functions/Misc/run-help-svk +++ /dev/null @@ -1 +0,0 @@ -svk help $1 | ${=PAGER:-more} diff --git a/Functions/Misc/run-help-svnadmin b/Functions/Misc/run-help-svnadmin new file mode 100644 index 000000000..dbddd6396 --- /dev/null +++ b/Functions/Misc/run-help-svnadmin @@ -0,0 +1 @@ +svnadmin help $1 | ${=PAGER:-more} diff --git a/Functions/Misc/zargs b/Functions/Misc/zargs index ecd69f7e4..782d6811e 100644 --- a/Functions/Misc/zargs +++ b/Functions/Misc/zargs @@ -39,18 +39,21 @@ # # "Killed by a signal" is determined by the usual shell rule that $? is # the signal number plus 128, so zargs can be fooled by a command that -# explicitly exits with 129+. Also, zsh prior to 4.1.x returns 1 rather -# than 127 for "command not found" so this function incorrectly returns -# 123 in that case if used with zsh 4.0.x. +# explicitly exits with 129+. If the command passed to zargs is a shell +# function which uses "exit" instead of "return", zsh interprets 129+ as +# a signal sent to the process group and may terminate zargs with that +# status. This is avoided when running zargs -P 2 or greater. # -# With the --max-procs option, zargs may not correctly capture the exit -# status of the backgrounded jobs, because of limitations of the "wait" -# builtin. If the zsh/parameter module is not available, the status is -# NEVER correctly returned, otherwise the status of the longest-running -# job in each batch is captured. +# ZARGS_VERSION 1.5 is the last to support zsh 4.x. Also, zsh prior to +# 4.1.x returns 1 rather than 127 for "command not found" so zargs +# incorrectly returned 123 in that case if used with zsh 4.0.x. # -# Also because of "wait" limitations, --max-procs spawns max-procs jobs, -# then waits for all of those, then spawns another batch, etc. +# Because of "wait" limitations, --max-procs spawns max-procs jobs, then +# waits for all of those, then spawns another batch, etc. +# +# The maximum number of parallel jobs for which exit status is available +# is determined by the sysconf CHILD_MAX parameter, which can't be read +# or changed from within the shell. # # Differences from POSIX xargs: # @@ -69,6 +72,13 @@ # -I/-L and implementations reportedly differ.) In zargs, -i/-I have # this behavior, as do -l/-L, but when -i/-I appear anywhere then -l/-L # are ignored (forced to 1). +# +# * The use of SIGUSR1 and SIGUSR2 to change the number of parallel jobs +# is not supported. + +{ # Begin "always" block to reset locally defined functions + +local ZARGS_VERSION="1.8" # First, capture the current setopts as "sticky emulation" if zmodload zsh/parameter @@ -83,11 +93,20 @@ else emulate $(emulate -l) -c '_zarun() { eval "$@" }' fi +local _zaTRAPS="$(trap)" +_zatraps() { + # In children, these traps may be reset to default behavior, even + # if the calling shell has traps. Restore to surrounding context, + # but assure that if zargs itself is signaled, children will exit. + [[ -o interactive ]] && + function TRAP{HUP,INT,QUIT,TERM} { exit $((128 + $1)) } + [[ -n "$_zaTRAPS" ]] && eval "$_zaTRAPS" + unset _zaTRAPS +} + emulate -L zsh || return 1 local -a opts eof n s l P i -local ZARGS_VERSION="1.5" - if zparseopts -a opts -D -- \ -eof::=eof e::=eof \ -exit x \ @@ -192,14 +211,14 @@ then (( c = $#command - 1 )) else command=( print -r -- ) fi -local wait bg -local execute=' +local bg execute=' if (( $opts[(I)-(-interactive|p)] )) then read -q "?$call?..." || continue elif (( $opts[(I)-(-verbose|t)] )) then print -u2 -r -- "$call" fi _zarun "{ + _zatraps \"\${call[@]}\" } $bg"' local ret=0 analyze=' @@ -262,19 +281,19 @@ fi if (( P != 1 && ARGC > 1 )) then - # These setopts are necessary for "wait" on multiple jobs to work. - setopt nonotify nomonitor - bg='&' - if zmodload -i zsh/parameter 2>/dev/null - then - wait='wait ${${jobstates[(R)running:*]/#*:/}/%=*/}' - else - wait='wait' - fi + setopt nonotify # Do not report each exiting job + local -a _zajobs + local j + bg='& _zajobs+=( $! )' + analyze=' + for j in $_zajobs; do + wait $j + '"$analyze"' + done; _zajobs=()' fi -# Everything has to be in a subshell just in case of backgrounding jobs, -# so that we don't unintentionally "wait" for jobs of the parent shell. +# Everything has to be in a subshell so that we don't "wait" for any +# unrelated jobs of the parent shell. ( while ((ARGC)) @@ -313,4 +332,8 @@ return $ret ) +} always { + builtin unfunction _zarun _zatraps +} + # } diff --git a/Functions/Misc/zcalc b/Functions/Misc/zcalc index 480373345..6cd2822c9 100644 --- a/Functions/Misc/zcalc +++ b/Functions/Misc/zcalc @@ -124,8 +124,10 @@ integer _rpn_mode _matched _show_stack _i _n integer _max_stack _push local -a _expressions stack + # We use our own history file with an automatic pop on exit. -history -ap "${ZDOTDIR:-$HOME}/.zcalc_history" +history -ap "${ZCALC_HISTFILE:-${ZDOTDIR:-$HOME}/.zcalc_history}" + _forms=( '%2$g' '%.*g' '%.*f' '%.*E' '') diff --git a/Functions/Misc/zed b/Functions/Misc/zed index 7d0d590db..bb075512c 100644 --- a/Functions/Misc/zed +++ b/Functions/Misc/zed @@ -14,15 +14,17 @@ local var opts zed_file_name integer TMOUT=0 okargs=1 fun hist bind local -a expand -zparseopts -D -A opts f h b x: +zparseopts -D -A opts f h b x: || return 1 fun=$+opts[-f] hist=$+opts[-h] bind=$+opts[-b] -if [[ $opts[-x] == <-> ]]; then - expand=(-x $opts[-x]) -elif (( $+opts[-x] )); then - print -r "Integer expected after -x: $opts[-x]" >&2 - return 1 +if (( $+opts[-x] )); then + if [[ $opts[-x] == <-> ]]; then + expand=(-x $opts[-x]) + else + print -r "Integer expected after -x: $opts[-x]" >&2 + return 1 + fi fi [[ $0 = fned ]] && fun=1 diff --git a/Functions/Misc/zmv b/Functions/Misc/zmv index 269fe5ba5..5c03e9ea1 100644 --- a/Functions/Misc/zmv +++ b/Functions/Misc/zmv @@ -236,12 +236,18 @@ if [[ $pat = (#b)(*)\((\*\*##/)\)(*) ]]; then else fpat=$pat fi -files=(${~fpat}) [[ -n $hasglobqual ]] && pat=$opat errs=() +() { + # (#qN) breaks bareglobqual -Q option, so: + setopt localoptions nullglob + files=(${~fpat}) +} +(( ${#files} )) || errs=( "no files matched \`$fpat'" ) + for f in $files; do if [[ $pat = (#b)(*)\(\*\*##/\)(*) ]]; then # This looks like a recursive glob. This isn't good enough, diff --git a/Functions/Misc/zslurp b/Functions/Misc/zslurp new file mode 100644 index 000000000..84df0c948 --- /dev/null +++ b/Functions/Misc/zslurp @@ -0,0 +1,31 @@ +#!/bin/zsh -f + +# Read stdin verbatim and as efficiently as possible into $REPLY, +# stopping without any change to $REPLY in the event of any error. +# Benchmarked by Roman Perepelitsa in zsh-users/29472 + +# Although this function faithfully records the input stream, later +# references to $REPLY with the multibyte option back in effect will +# (re-)interpret the content as multibyte characters. This may not be +# what is desired. + +emulate -L zsh -o no_multibyte + +### Alternate formulation, faster on bigger files +# # /dev/fd/0 is treated specially by -f so also check /dev/fd +# if [[ -d /dev/fd && -f /dev/fd/0 ]] && zmodload zsh/mapfile +# then +# local +h -Ar mapfile +# typeset -g REPLY="${mapfile[/dev/fd/0]}" && return +# fi +# # else fall through to read from pipe/socket + +zmodload zsh/system || return +local -a content +local -i i=0 +while true; do + sysread 'content[++i]' && continue + (( $? == 5 )) || return + break +done +typeset -g REPLY=${(j::)content} diff --git a/Functions/Newuser/zsh-newuser-install b/Functions/Newuser/zsh-newuser-install index 60ac16b13..9e911d07c 100644 --- a/Functions/Newuser/zsh-newuser-install +++ b/Functions/Newuser/zsh-newuser-install @@ -627,7 +627,7 @@ Type: } -# Print and despatch a submenu. +# Print and dispatch a submenu. # The first argument is the title. The remaining arguments # are pairs of descriptions and functions to execute. # There shouldn't be more than 9 entries. diff --git a/Functions/Prompts/prompt_sprint2_setup b/Functions/Prompts/prompt_sprint2_setup new file mode 100644 index 000000000..0eaea4473 --- /dev/null +++ b/Functions/Prompts/prompt_sprint2_setup @@ -0,0 +1,128 @@ +# Created by Sebastian Gniazdowski + +prompt_sprint2_help () { + cat <<EOF +This prompt is color themable. You can invoke it in following way: + +prompt sprint2 <time/line color> <braces clr.> <text clr.> <at/colon clr.> <prompt clr.> + +You can provide only N first arguments, N=1..5. + +The default colors are: 39 green blue green yellow +EOF +} + +prompt_sprint2_setup () { + local col_time_line=${1:-'39'} + local col_parens=${2:-'green'} + local col_text=${3:-'blue'} + local col_at_colon=${4:-'green'} + local col_prompt=${5:-'yellow'} + + autoload -Uz vcs_info + + typeset -gA _psvar + + local cl_time_line="%B%F{$col_time_line}" + local cl_text="%b%F{$col_text}" + local cl_parens="%B%F{$col_parens}" + local cl_at_colon="%b%F{$col_at_colon}" + local cl_prompt="%B%F{$col_prompt}" + local cl_rst="%b%f" + + local _lpar="${cl_parens}[" + local _rpar="${cl_parens}]" + + local _time="$cl_time_line%D{%H:%M}" + _psvar[user]='%n' + local _user="$cl_text"'${_psvar[user]}' + _psvar[at]="@" + _psvar[host]='%m' + local _at="$cl_at_colon"'${_psvar[at]}'"$cl_text"'${_psvar[host]}' + _psvar[colon]=':' + local _path="$cl_at_colon"'${_psvar[colon]}'"$cl_text%28<*<%/%<<" + local _line="$cl_time_line%i" + local _prompt="$cl_prompt# " + + # You can instantly shorten the prompt by setting PSSHORT=1 + if [[ "$COLUMNS" -le 94 || "$PSSHORT" = "1" ]]; then + _psvar=() + else + _psvar[user]='%n' + _psvar[at]="@" + _psvar[host]='%m' + _psvar[colon]=':' + fi + + PS1="$_time$_lpar$_user$_at$_path$_rpar$_line$_prompt$cl_rst" + + PS2="$cl_parens> $cl_rst" + + RPS1='${vcs_info_msg_0_}' + prompt_opts=(cr subst percent) + + zstyle ':vcs_info:*' enable git svn darcs bzr hg + zstyle ':vcs_info:*' stagedstr "%F{green}●$cl_rst" + zstyle ':vcs_info:*' unstagedstr "%F{yellow}●$cl_rst" + zstyle ':vcs_info:*' check-for-changes true + zstyle ':vcs_info:*' formats '(%s)-[%b%u%c]-' + + add-zsh-hook precmd prompt_sprint2_precmd +} + +prompt_sprint2_precmd () { + setopt localoptions noxtrace noksharrays + + # You can instantly shorten the prompt by setting PSSHORT=1 + if [[ "$COLUMNS" -le 94 || "$PSSHORT" = "1" ]]; then + _psvar=() + else + _psvar[user]='%n' + _psvar[at]="@" + _psvar[host]='%m' + _psvar[colon]=':' + fi + + local -a changed_files + changed_files=( ) + git diff --quiet 2>/dev/null || changed_files=( ${(f)"$( git diff --name-only 2>/dev/null )"} ) + changed_files=( $^changed_files(N) ) + if [[ "${#changed_files}" -gt 0 ]]; then + local basedir + basedir="$(git rev-parse --show-toplevel)/" + changed_files=( ${(f)"$( find "${basedir}${^changed_files[@]}" -mtime +2 )"} ) + fi + + if [[ "${#changed_files}" -eq 0 ]]; then + zstyle ':vcs_info:*' formats ' (%s)-[%b%u%c]' + else + zstyle ':vcs_info:*' formats ' (%s)-[%b%u%B%F{yellow}-OLD-%c%%b%f]' + fi + + vcs_info +} + +prompt_sprint2_preview () { + local -a colors_time_line colors_text + colors_time_line=(red yellow green blue magenta cyan) + colors_text=(red yellow green blue magenta cyan) + + local ctime_line ctext i j + + if (( ! $#* )); then + for (( i = 1; i <= ${#colors_time_line}; i++ )); do + ctime_line="${colors_time_line[$i]}" + for (( j = 1; j <= ${#colors_text}; j++ )); do + ctext="${colors_text[$j]}" + prompt_preview_theme sprint2 "$ctime_line" "red" "$ctext" + (( i != ${#colors_time_line} || j != ${#colors_text} )) && print + done + done + else + prompt_preview_theme sprint2 "$@" + fi +} + +prompt_sprint2_setup "$@" + +# vim:ft=zsh diff --git a/Functions/Prompts/promptinit b/Functions/Prompts/promptinit index 20503d78b..0c06699e8 100644 --- a/Functions/Prompts/promptinit +++ b/Functions/Prompts/promptinit @@ -73,7 +73,7 @@ set_prompt() { Options: -c Show currently selected theme and parameters -l List currently available prompt themes - -p [<themes>] Preview given themes (defaults to all) + -p [<themes>] Preview given themes (defaults to all except current theme) -h [<theme>] Display help (for given theme) -s <theme> Set and save theme <theme> Switch to new theme immediately (changes not saved) @@ -120,10 +120,9 @@ Use prompt -h <theme> for help on specific themes.' print $prompt_themes return ;; - p) preview=( $prompt_themes ) + p) preview=( ${prompt_themes:#$prompt_theme} ) (( $#* > 1 )) && preview=( "$@[2,-1]" ) for theme in $preview; do - [[ "$theme" == "$prompt_theme[*]" ]] && continue prompt_preview_safely "$=theme" done print -P "%b%f%k" diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_git b/Functions/VCS_Info/Backends/VCS_INFO_get_data_git index eb04d4b41..9a608adab 100644 --- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_git +++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_git @@ -132,7 +132,7 @@ VCS_INFO_git_handle_patches () { VCS_INFO_set-patch-format 'git_patches_applied' 'git_applied_s' \ 'git_patches_unapplied' 'git_unapplied_s' \ ":vcs_info:${vcs}:${usercontext}:${rrn}" gitmsg \ - '' '' + '' '' '' gitmisc=$REPLY } @@ -184,18 +184,12 @@ fi VCS_INFO_adjust VCS_INFO_git_getaction ${gitdir} -local patchdir=${gitdir}/patches/${gitbranch} -if [[ -d $patchdir ]] && [[ -f $patchdir/applied ]] \ - && [[ -f $patchdir/unapplied ]] -then - # stgit - git_patches_applied=(${(f)"$(< "${patchdir}/applied")"}) - git_patches_unapplied=(${(f)"$(< "${patchdir}/unapplied")"}) - VCS_INFO_git_handle_patches -elif [[ -d "${gitdir}/rebase-merge" ]]; then +local patchdir +if [[ -d "${gitdir}/rebase-merge" ]]; then # 'git rebase -i' patchdir="${gitdir}/rebase-merge" local p + (( ${+functions[VCS_INFO_git_map_rebase_line_to_hash_and_subject]} )) || VCS_INFO_git_map_rebase_line_to_hash_and_subject() { local p=$1 unset REPLY @@ -207,7 +201,7 @@ elif [[ -d "${gitdir}/rebase-merge" ]]; then # Comment line. Skip. return 0 ;; - ((p|pick|e|edit|r|reword|f|fixup|s|squash)' '*) + (''(p|pick|e|edit|r|reword|f|fixup|s|squash)' '*) # The line is of the form "pick $hash $subject". # Just strip the verb and we're good to go. p=${p#* } @@ -230,7 +224,7 @@ elif [[ -d "${gitdir}/rebase-merge" ]]; then p="${p%% *} ?" fi ;; - ((x|exec) *) + (''(x|exec) *) # The line is of the form 'exec foo bar baz' where 'foo bar # baz' is a shell command. There's no way to map _that_ to # "$hash $subject", but I hope this counts as making an effort. @@ -250,7 +244,9 @@ elif [[ -d "${gitdir}/rebase-merge" ]]; then (( $+REPLY )) && git_patches_applied+=( "$REPLY" ) done fi - if [[ -f "${patchdir}/git-rebase-todo" ]] ; then + if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-unapplied && + [[ -f "${patchdir}/git-rebase-todo" ]] + then for p in ${(f)"$(< "${patchdir}/git-rebase-todo")"}; do VCS_INFO_git_map_rebase_line_to_hash_and_subject "$p" (( $+REPLY )) && git_patches_unapplied+=( "$REPLY" ) @@ -380,7 +376,9 @@ elif [[ -f "${gitdir}/CHERRY_PICK_HEAD" ]]; then # TODO: maybe read up to the first blank line IFS='' read -r subject < "${gitdir}/MERGE_MSG" git_patches_applied=( "$(<${gitdir}/CHERRY_PICK_HEAD) ${subject}" ) - if [[ -f "${gitdir}/sequencer/todo" ]]; then + if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-unapplied && + [[ -f "${gitdir}/sequencer/todo" ]] + then # Get the next patches, and remove the one that's in CHERRY_PICK_HEAD. git_patches_unapplied=( ${${(M)${(f)"$(<"${gitdir}/sequencer/todo")"}:#pick *}#pick } ) git_patches_unapplied[1]=() @@ -388,6 +386,31 @@ elif [[ -f "${gitdir}/CHERRY_PICK_HEAD" ]]; then git_patches_unapplied=() fi VCS_INFO_git_handle_patches +elif command -v stg >/dev/null && + ${vcs_comm[cmd]} show-ref --quiet refs/stacks/${gitbranch} refs/heads/${gitbranch}.stgit 2>/dev/null && + git_patches_applied=(${${(f)"$(stg series --noprefix --applied --description 2>/dev/null)"}/ #[#]}) +then + # Testing for StGit patches is done after testing for all git-proper + # patches/states. If a StGit user's repo is in one of those states, they + # will want to see that instead of the StGit patch info. + + # Performance note: testing for the stg executable is done first because it + # is extremely cheap and there is nothing else to do if it isn't present. + # Using git to test whether a StGit stack is initialized is cheaper than + # running stg itself; especially for versions of StGit <= 2.0. Thus getting + # StGit patch info should only have a material runtime cost if StGit is + # installed and in-use for the current branch. + + # In StGit >=1.2, the existence of refs/stacks/<branch> indicates StGit is + # initialized. In StGit >=0.15, it is refs/heads/<branch>.stgit. + + # N.B. the "--noprefix" option is available in StGit 2.x as an alias for + # --no-prefix. The former is compatible with StGit versions going back to + # 2008. + if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-unapplied; then + git_patches_unapplied=(${${(f)"$(stg series --noprefix --unapplied --description 2>/dev/null)"}/ #[#]}) + fi + VCS_INFO_git_handle_patches else gitmisc='' fi diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg b/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg index 2806aee1d..ea3798b81 100644 --- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg +++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg @@ -14,7 +14,7 @@ local hgbase bmfile branchfile topicfile rebasefile dirstatefile mqseriesfile \ local -a hgid_args defrevformat defbranchformat \ hgbmarks mqpatches mqguards mqunapplied hgmisc \ - i_patchguards i_negguards i_posguards + i_patch i_patchguards i_negguards i_posguards local -A hook_com @@ -56,7 +56,6 @@ if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-revision ; then zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" \ "check-for-changes" || hgid_args+=( -r. ) - local HGPLAIN HGPLAIN=1 ${vcs_comm[cmd]} ${(z)hgid_args} 2> /dev/null \ | read -r r_csetid r_lrev fi @@ -184,6 +183,9 @@ if zstyle -T ":vcs_info:${vcs}:${usercontext}:${rrn}" get-mq \ # Skip commented lines [[ ${i_patch} == [[:space:]]#"#"* ]] && continue + # Skip applied patches + (( ${+mqpatches[(re)${i_patch}]} )) && continue + # Separate negative and positive guards to more easily find the # intersection of active guards with patch guards i_patchguards=( ${(s: :)i_patchguards} ) @@ -226,7 +228,7 @@ if zstyle -T ":vcs_info:${vcs}:${usercontext}:${rrn}" get-mq \ VCS_INFO_set-patch-format 'mqpatches' 'applied_string' \ 'mqunapplied' 'unapplied_string' \ ":vcs_info:${vcs}:${usercontext}:${rrn}" hgmqstring \ - extra_hook_com VCS_INFO_hg_extra_zformats + extra_hook_com VCS_INFO_hg_extra_zformats '' hgmqstring=$REPLY fi diff --git a/Functions/VCS_Info/VCS_INFO_formats b/Functions/VCS_Info/VCS_INFO_formats index e0e1dc738..daf169f26 100644 --- a/Functions/VCS_Info/VCS_INFO_formats +++ b/Functions/VCS_Info/VCS_INFO_formats @@ -28,7 +28,7 @@ hook_com=( vcs_orig "${vcs}" ) hook_com[base-name]="${${hook_com[base]}:t}" -hook_com[base-name_orig]="${hook_com[base_name]}" +hook_com[base-name_orig]="${hook_com[base-name]}" hook_com[subdir]="$(VCS_INFO_reposub ${hook_com[base]})" hook_com[subdir_orig]="${hook_com[subdir]}" diff --git a/Functions/VCS_Info/VCS_INFO_patch2subject b/Functions/VCS_Info/VCS_INFO_patch2subject index a467edcdb..5aa9efd23 100644 --- a/Functions/VCS_Info/VCS_INFO_patch2subject +++ b/Functions/VCS_Info/VCS_INFO_patch2subject @@ -1,3 +1,5 @@ +## vim:ft=zsh +# # This function takes as an argument a filename of a patch and sets $REPLY to # a single-line "subject", or unsets it if no subject could be extracted. { diff --git a/Functions/VCS_Info/VCS_INFO_quilt b/Functions/VCS_Info/VCS_INFO_quilt index 264dbed0e..ce5b41f24 100644 --- a/Functions/VCS_Info/VCS_INFO_quilt +++ b/Functions/VCS_Info/VCS_INFO_quilt @@ -92,7 +92,7 @@ function VCS_INFO_quilt-patch2subject() { emulate -L zsh setopt extendedglob local mode="$1" - local patches pc tmp qstring root + local patches pc qstring root local -i ret local context local -a applied unapplied applied_string unapplied_string quiltcommand quilt_env @@ -113,9 +113,12 @@ function VCS_INFO_quilt-patch2subject() { ;; esac - VCS_INFO_quilt-dirfind .pc .version - ret=$? pc=$REPLY - if (( ret == 0 )); then + # Look for the patches directory. + # + # 1. Check if there's a .pc/.version file in a parent dir. If so, use the + # patches dir from the corresponding .pc/.quilt_patches. + if VCS_INFO_quilt-dirfind .pc .version; then + pc=$REPLY [[ ${quiltmode} == 'standalone' ]] && root=${pc} pc=${pc}/.pc if [[ -e ${pc}/applied-patches ]]; then @@ -128,25 +131,27 @@ function VCS_INFO_quilt-patch2subject() { fi patches=$(<$pc/.quilt_patches) patches=`builtin cd -q "${pc:h}" && print -r - ${patches:P}` + # 2. Else, locate a patches dir using the style settings. + else + zstyle -s "${context}" quilt-patch-dir patches || patches="${QUILT_PATCHES}" + : ${patches:="patches"} + if [[ "${patches}" != /* ]]; then + if VCS_INFO_quilt-dirfind "${patches}"; then + patches=$REPLY/$patches + else + return $? + fi + else + [[ -d ${patches} ]] || return 1 + fi + quilt_env+=(QUILT_PATCHES="$patches") fi + # At this point, $patches is set and points to an existing directory. + if zstyle -t "${context}" get-unapplied; then # This zstyle call needs to be moved further up if `quilt' needs # to be run in more places than this one. zstyle -s "${context}" quiltcommand quiltcommand || quiltcommand='quilt' - quilt_env=() - if [ -z "$patches" ]; then - zstyle -s "${context}" quilt-patch-dir patches || patches="${QUILT_PATCHES}" - if [[ "${patches}" != /* ]]; then - tmp=${patches:-patches} - VCS_INFO_quilt-dirfind "${tmp}" - ret=$? patches=$REPLY - (( ret )) && return ${ret} - patches=${patches}/${tmp} - else - [[ -d ${patches} ]] || return 1 - fi - quilt_env+=(QUILT_PATCHES="$patches") - fi unapplied=( ${(f)"$(if (( $+quilt_env[1] )); then export ${quilt_env[@]}; fi $quiltcommand --quiltrc /dev/null unapplied 2> /dev/null)"} ) unapplied=( ${unapplied:#} ) @@ -154,8 +159,7 @@ function VCS_INFO_quilt-patch2subject() { unapplied=() fi - if [[ -n $patches ]]; then - () { + { local i for ((i=1; i<=$#applied; i++)); do if VCS_INFO_quilt-patch2subject "$patches/$applied[$i]" && (( $+REPLY )) @@ -173,13 +177,17 @@ function VCS_INFO_quilt-patch2subject() { unapplied[$i]+=" ?" fi done - } - fi + } + + typeset -A quilt_extra_info=( + quilt-patches-dir ${patches} + ${pc:+"quilt-pc-dir"} $pc + ) VCS_INFO_set-patch-format 'applied' 'applied_string' \ 'unapplied' 'unapplied_string' \ ${context} qstring \ - '' '' + quilt_extra_info '' quilt_extra_info qstring=$REPLY case ${mode} in diff --git a/Functions/VCS_Info/VCS_INFO_set-branch-format b/Functions/VCS_Info/VCS_INFO_set-branch-format index 8cff51b9a..cbab60e29 100644 --- a/Functions/VCS_Info/VCS_INFO_set-branch-format +++ b/Functions/VCS_Info/VCS_INFO_set-branch-format @@ -1,3 +1,5 @@ +## vim:ft=zsh +# # A function for calling the branch-format hook # # Return the value to use in REPLY diff --git a/Functions/VCS_Info/VCS_INFO_set-patch-format b/Functions/VCS_Info/VCS_INFO_set-patch-format index e387110a2..1c774a7f6 100644 --- a/Functions/VCS_Info/VCS_INFO_set-patch-format +++ b/Functions/VCS_Info/VCS_INFO_set-patch-format @@ -1,3 +1,5 @@ +## vim:ft=zsh +# # This function is the common guts of the gen-applied-string / # gen-unapplied-string / set-patch-format dance of several backends. # @@ -13,6 +15,8 @@ # $7 - name of an assoc parameter with extra $hook_com key-value pairs for the # set-patch-format hook invocation, or '' for none # $8 - name of a function that sets $reply to extra arguments for the patch-format zformat call, or '' for none +# $9 - name of an assoc parameter with extra $hook_com key-value pairs for the +# gen-applied-string & gen-unapplied-string hook invocations, or '' for none # # The expanded patch-format string is returned in $REPLY. # @@ -20,8 +24,10 @@ # - $hook_com is overwritten and the keys 'applied', 'applied-n', # 'unapplied', 'unapplied-n', 'all-n' are set. { + hook_com=() + local applied_needs_escaping='unknown' - local unapplied_needs_escaping='unknown' + hook_com+=( ${9:+"${(@kvP)9}"} ) if VCS_INFO_hook 'gen-applied-string' "${(@P)1}"; then if (( ${(P)#1} )); then REPLY=${(P)1[1]} @@ -35,6 +41,8 @@ : ${(P)2::=$REPLY} hook_com=() + local unapplied_needs_escaping='unknown' + hook_com+=( ${9:+"${(@kvP)9}"} ) if VCS_INFO_hook 'gen-unapplied-string' "${(@P)3}"; then REPLY=${(P)#3} unapplied_needs_escaping='yes' diff --git a/Functions/VCS_Info/test-repo-git-rebase-apply b/Functions/VCS_Info/test-repo-git-rebase-apply index cb4ea4f58..ce49690cd 100755 --- a/Functions/VCS_Info/test-repo-git-rebase-apply +++ b/Functions/VCS_Info/test-repo-git-rebase-apply @@ -44,6 +44,16 @@ append_lines 7 8 9 # Specify a rebase that would create the history [1,3,4,5,6,7,8,9]. # This will conflict because r7 depends on r2 which is not included. git checkout -b myref -git rebase --onto=rebase_onto_this rebase_from_this myref +case $0:P in + (*/test-repo-git-rebase-apply) + git rebase --onto=rebase_onto_this rebase_from_this myref + ;; + (*/test-repo-git-rebase-merge) + git -c core.editor=true rebase -i --onto=rebase_onto_this rebase_from_this myref + ;; + (*) + echo >&2 "$0: unrecognized basename" + ;; +esac diff --git a/Functions/VCS_Info/test-repo-git-rebase-merge b/Functions/VCS_Info/test-repo-git-rebase-merge new file mode 120000 index 000000000..fce9e9178 --- /dev/null +++ b/Functions/VCS_Info/test-repo-git-rebase-merge @@ -0,0 +1 @@ +test-repo-git-rebase-apply \ No newline at end of file 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 } } |