diff options
author | Axel Beckert <abe@deuxchevaux.org> | 2022-04-11 00:17:48 +0200 |
---|---|---|
committer | Axel Beckert <abe@deuxchevaux.org> | 2022-04-11 00:17:48 +0200 |
commit | b09f4483416c54c1782824633dfabaf2ec0265b6 (patch) | |
tree | 304bc82642862525ae680c7fbaa249663b10ad57 /Functions | |
parent | 12eb3e5356f2fc3351eed58ef1cef1b8fb83b504 (diff) | |
parent | 6e55c920503071e917619b8cb1a188cd35d772db (diff) | |
download | zsh-b09f4483416c54c1782824633dfabaf2ec0265b6.tar.gz zsh-b09f4483416c54c1782824633dfabaf2ec0265b6.zip |
New upstream version 5.8.1.2-test
Diffstat (limited to 'Functions')
41 files changed, 749 insertions, 313 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/Chpwd/zsh_directory_name_cdr b/Functions/Chpwd/zsh_directory_name_cdr index cb72e4600..b653e7c38 100644 --- a/Functions/Chpwd/zsh_directory_name_cdr +++ b/Functions/Chpwd/zsh_directory_name_cdr @@ -16,8 +16,10 @@ elif [[ $1 = c ]]; then typeset -a keys values values=(${${(f)"$(cdr -l)"}/ ##/:}) keys=(${values%%:*}) + local addsuffix + [[ $ISUFFIX = *\]* ]] || addsuffix='-S]' _describe -t dir-index 'recent directory index' \ - values -V unsorted -S']' + values -V unsorted $addsuffix return fi fi diff --git a/Functions/Example/zpgrep b/Functions/Example/zpgrep index 8b1edaa1c..556e58cd6 100644 --- a/Functions/Example/zpgrep +++ b/Functions/Example/zpgrep @@ -2,24 +2,31 @@ # zpgrep() { -local file pattern +local file pattern ret pattern=$1 shift +ret=1 if ((! ARGC)) then set -- - fi -pcre_compile $pattern +zmodload zsh/pcre || return +pcre_compile -- "$pattern" pcre_study for file do if [[ "$file" == - ]] then - while read -u0 buf; do pcre_match $buf && print $buf; done + while IFS= read -ru0 buf; do + pcre_match -- "$buf" && ret=0 && print -r -- "$buf" + done else - while read -u0 buf; do pcre_match $buf && print $buf; done < "$file" + while IFS= read -ru0 buf; do + pcre_match -- "$buf" && ret=0 && print -r -- "$buf" + done < "$file" fi done +return "$ret" } diff --git a/Functions/Math/zmathfunc b/Functions/Math/zmathfunc index 4ff40700d..12d2c2f3d 100644 --- a/Functions/Math/zmathfunc +++ b/Functions/Math/zmathfunc @@ -1,34 +1,50 @@ #autoload zsh_math_func_min() { + emulate -L zsh local result=$1 shift local arg for arg ; do - (( $arg < result )) && result=$arg + (( arg < result )) + case $? in + (0) (( result = arg ));; + (1) ;; + (*) return $?;; + esac done - (( result )) # return + (( result )) + true # Careful here: `return 0` evaluates an arithmetic expression } functions -M min 1 -1 zsh_math_func_min # at least one argument zsh_math_func_max() { + emulate -L zsh local result=$1 shift local arg for arg ; do - (( $arg > result )) && result=$arg + (( arg > result )) + case $? in + (0) (( result = arg ));; + (1) ;; + (*) return $?;; + esac done - (( result )) # return + (( result )) + true # Careful here: `return 0` evaluates an arithmetic expression } functions -M max 1 -1 zsh_math_func_max # at least one argument zsh_math_func_sum() { + emulate -L zsh local sum local arg for arg ; do - (( sum += $arg )) + (( sum += arg )) done (( sum )) + true # Careful here: `return 0` evaluates an arithmetic expression } functions -M sum 0 -1 zsh_math_func_sum diff --git a/Functions/Misc/add-zle-hook-widget b/Functions/Misc/add-zle-hook-widget index 9cc35496f..4d8049083 100644 --- a/Functions/Misc/add-zle-hook-widget +++ b/Functions/Misc/add-zle-hook-widget @@ -47,9 +47,9 @@ function azhw:${^hooktypes} { for hook in "${(@)${(@on)hook_widgets[@]}#<->:}"; do if [[ "$hook" = user:* ]]; then # Preserve $WIDGET within the renamed widget - zle "$hook" -N -- "$@" + zle "$hook" -f "nolast" -N -- "$@" else - zle "$hook" -Nw -- "$@" + zle "$hook" -f "nolast" -Nw -- "$@" fi || return done return 0 diff --git a/Functions/Misc/colors b/Functions/Misc/colors index 027ca9a14..5e9d77d10 100644 --- a/Functions/Misc/colors +++ b/Functions/Misc/colors @@ -14,11 +14,12 @@ color=( 00 none # 20 gothic 01 bold # 21 double-underline 02 faint 22 normal - 03 standout 23 no-standout + 03 italic 23 no-italic # no-gothic 04 underline 24 no-underline 05 blink 25 no-blink # 06 fast-blink # 26 proportional 07 reverse 27 no-reverse +# 07 standout 27 no-standout 08 conceal 28 no-conceal # 09 strikethrough # 29 no-strikethrough @@ -82,9 +83,11 @@ for k in ${color[(I)3?]}; 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 color-blind. diff --git a/Functions/Misc/regexp-replace b/Functions/Misc/regexp-replace index dec105524..d4408f0f7 100644 --- a/Functions/Misc/regexp-replace +++ b/Functions/Misc/regexp-replace @@ -8,36 +8,84 @@ # $ and backtick substitutions; in particular, $MATCH will be replaced # by the portion of the string matched by the regular expression. -integer pcre +# we use positional parameters instead of variables to avoid +# clashing with the user's variable. Make sure we start with 3 and only +# 3 elements: +argv=("$1" "$2" "$3") -[[ -o re_match_pcre ]] && pcre=1 +# $4 records whether pcre is enabled as that information would otherwise +# be lost after emulate -L zsh +4=0 +[[ -o re_match_pcre ]] && 4=1 emulate -L zsh -(( pcre )) && setopt re_match_pcre - -# $4 is the string to be matched -4=${(P)1} -# $5 is the final string -5= -# 6 indicates if we made a change -6= + + local MATCH MBEGIN MEND local -a match mbegin mend -while [[ -n $4 ]]; do - if [[ $4 =~ $2 ]]; then - # append initial part and subsituted match - 5+=${4[1,MBEGIN-1]}${(e)3} - # truncate remaining string - 4=${4[MEND+1,-1]} - # indicate we did something - 6=1 - else - break - fi -done -5+=$4 - -eval ${1}=${(q)5} -# status 0 if we did something, else 1. -[[ -n $6 ]] +if (( $4 )); then + # if using pcre, we're using pcre_match and a running offset + # That's needed for ^, \A, \b, and look-behind operators to work + # properly. + + zmodload zsh/pcre || return 2 + pcre_compile -- "$2" && pcre_study || return 2 + + # $4 is the current *byte* offset, $5, $6 reserved for later use + 4=0 6= + + local ZPCRE_OP + while pcre_match -b -n $4 -- "${(P)1}"; do + # 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 character of $IFS as usual) + 5=${(e)3} + argv+=(${(s: :)ZPCRE_OP} "$5") + + # for 0-width matches, increase offset by 1 to avoid + # infinite loop + 4=$((argv[-2] + (argv[-3] == argv[-2]))) + done + + (($# > 6)) || return # no match + + set +o multibyte + + # $5 contains the result, $6 the current offset + 5= 6=1 + for 2 3 4 in "$@[7,-1]"; do + 5+=${(P)1[$6,$2]}$4 + 6=$(($3 + 1)) + done + 5+=${(P)1[$6,-1]} +else + # in ERE, we can't use an offset so ^, (and \<, \b, \B, [[:<:]] where + # available) won't work properly. + + # $4 is the string to be matched + 4=${(P)1} + + while [[ -n $4 ]]; do + if [[ $4 =~ $2 ]]; then + # append initial part and substituted match + 5+=${4[1,MBEGIN-1]}${(e)3} + # truncate remaining string + if ((MEND < MBEGIN)); then + # zero-width match, skip one character for the next match + ((MEND++)) + 5+=${4[1]} + fi + 4=${4[MEND+1,-1]} + # indicate we did something + 6=1 + else + break + fi + done + [[ -n $6 ]] || return # no match + 5+=$4 +fi + +eval $1=\$5 diff --git a/Functions/Misc/run-help b/Functions/Misc/run-help index e351dd6a6..d52c1b032 100644 --- a/Functions/Misc/run-help +++ b/Functions/Misc/run-help @@ -101,12 +101,15 @@ do builtin getln cmd_args builtin print -z "$cmd_args" cmd_args=( ${(z)cmd_args} ) - # Discard environment assignments, etc. - while [[ $cmd_args[1] != ${run_help_orig_cmd:-$1} ]] - do - shift cmd_args || return 1 - done - eval "run-help-$1:t ${(q@)cmd_args[2,-1]}" + + # Discard the command itself & everything before it. + shift $cmd_args[(i)${run_help_orig_cmd:-$1}] cmd_args || + return + + # Discard options, parameter assignments & paths. + cmd_args=( ${cmd_args[@]:#([-+]*|*=*|*/*|\~*)} ) + + eval "run-help-$1:t ${(@q)cmd_args}" else POSIXLY_CORRECT=1 man $@:t fi diff --git a/Functions/Misc/run-help-btrfs b/Functions/Misc/run-help-btrfs new file mode 100644 index 000000000..cb139e9b7 --- /dev/null +++ b/Functions/Misc/run-help-btrfs @@ -0,0 +1,18 @@ +case $1 in + (b*) man btrfs-balance ;; + (c*) man btrfs-check ;; + (d*) man btrfs-device ;; + (f*) man btrfs-filesystem ;; + (i*) man btrfs-inspect-internal ;; + (p*) man btrfs-property ;; + (qg*) man btrfs-qgroup ;; + (qu*) man btrfs-quota ;; + (rec*) man btrfs-receive ;; + (rep*) man btrfs-replace ;; + (resc*) man btrfs-rescue ;; + (rest*) man btrfs-restore ;; + (sc*) man btrfs-scrub ;; + (se*) man btrfs-send ;; + (su*) man btrfs-subvolume ;; + (*) man btrfs ;; +esac diff --git a/Functions/Misc/run-help-git b/Functions/Misc/run-help-git index ce94d0d02..a841f89d6 100644 --- a/Functions/Misc/run-help-git +++ b/Functions/Misc/run-help-git @@ -1,9 +1 @@ -if [ $# -eq 0 ]; then - man git -else - local al - if al=$(git config --get "alias.$1"); then - 1=${al%% *} - fi - man git-$1 -fi +git help ${1:-git} diff --git a/Functions/Misc/run-help-ip b/Functions/Misc/run-help-ip index 8807f9ef1..b811ce352 100644 --- a/Functions/Misc/run-help-ip +++ b/Functions/Misc/run-help-ip @@ -14,10 +14,6 @@ if ! man -w ip-address >/dev/null 2>&1; then return fi -while [[ $# != 0 && $1 == -* ]]; do - shift -done - case $1 in (addrl*) man ip-addrlabel ;; (a*) man ip-address ;; diff --git a/Functions/Misc/run-help-p4 b/Functions/Misc/run-help-p4 index 662ce94fe..e48a4d068 100644 --- a/Functions/Misc/run-help-p4 +++ b/Functions/Misc/run-help-p4 @@ -2,4 +2,4 @@ if (( ! $# )); then p4 help commands else p4 help $1 -fi | ${=PAGER:-less} +fi | ${=PAGER:-more} diff --git a/Functions/Misc/run-help-sudo b/Functions/Misc/run-help-sudo index f38696e19..a8fa7aa0f 100644 --- a/Functions/Misc/run-help-sudo +++ b/Functions/Misc/run-help-sudo @@ -2,6 +2,6 @@ if [ $# -eq 0 ]; then man sudo else - man $1 + run-help $1 fi diff --git a/Functions/Misc/run-help-svk b/Functions/Misc/run-help-svk index 92438a53f..782538246 100644 --- a/Functions/Misc/run-help-svk +++ b/Functions/Misc/run-help-svk @@ -1 +1 @@ -svk help ${${@:#-*}[1]} | ${=PAGER:-more} +svk help $1 | ${=PAGER:-more} diff --git a/Functions/Misc/run-help-svn b/Functions/Misc/run-help-svn index 5d1068588..d55a493a6 100644 --- a/Functions/Misc/run-help-svn +++ b/Functions/Misc/run-help-svn @@ -1 +1 @@ -svn help ${${@:#-*}[1]} | ${=PAGER:-more} +svn help $1 | ${=PAGER:-more} diff --git a/Functions/Misc/zargs b/Functions/Misc/zargs index 28ebca78f..81916a3ac 100644 --- a/Functions/Misc/zargs +++ b/Functions/Misc/zargs @@ -43,14 +43,12 @@ # than 127 for "command not found" so this function incorrectly returns # 123 in that case if used with zsh 4.0.x. # -# 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. +# Because of "wait" limitations, --max-procs spawns max-procs jobs, then +# waits for all of those, then spawns another batch, etc. # -# Also 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,11 +67,27 @@ # -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. + +# First, capture the current setopts as "sticky emulation" +if zmodload zsh/parameter +then + emulate $(emulate -l) -c "\ + _zarun() { + options=( ${(j: :kv)options[@]} monitor off zle off )"' + eval "$@" + }' +else + # Warning? + emulate $(emulate -l) -c '_zarun() { eval "$@" }' +fi emulate -L zsh || return 1 local -a opts eof n s l P i -local ZARGS_VERSION="1.5" +local ZARGS_VERSION="1.7" if zparseopts -a opts -D -- \ -eof::=eof e::=eof \ @@ -186,8 +200,8 @@ local execute=' elif (( $opts[(I)-(-verbose|t)] )) then print -u2 -r -- "$call" fi - eval "{ - \"\${(@)call}\" + _zarun "{ + \"\${call[@]}\" } $bg"' local ret=0 analyze=' case $? in @@ -251,17 +265,19 @@ 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 + local -a _zajobs + local j + bg='& _zajobs+=( $! )' + wait='wait' + 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)) diff --git a/Functions/Misc/zed b/Functions/Misc/zed index 9eb4b2d93..7d0d590db 100644 --- a/Functions/Misc/zed +++ b/Functions/Misc/zed @@ -5,16 +5,18 @@ # Edit small files with the command line editor. # Use ^X^W to save (or ZZ in vicmd mode), ^C to abort. # Option -f: edit shell functions. (Also if called as fned.) +# Option -h: edit shell history. (Also if called as histed.) setopt localoptions noksharrays local var opts zed_file_name # We do not want timeout while we are editing a file -integer TMOUT=0 okargs=1 fun bind +integer TMOUT=0 okargs=1 fun hist bind local -a expand -zparseopts -D -A opts f b x: +zparseopts -D -A opts f h b x: fun=$+opts[-f] +hist=$+opts[-h] bind=$+opts[-b] if [[ $opts[-x] == <-> ]]; then expand=(-x $opts[-x]) @@ -24,23 +26,28 @@ elif (( $+opts[-x] )); then fi [[ $0 = fned ]] && fun=1 +[[ $0 = histed ]] && hist=1 +(( hist && $# <= 2 )) && okargs=$# (( bind )) && okargs=0 -if (( $# != okargs )); then +if (( $# != okargs || bind + fun + hist > 1 )); then echo 'Usage: zed filename zed -f [ -x N ] function +zed -h [ filename [ size ] ] zed -b' >&2 return 1 fi local curcontext=zed::: -# Matching used in zstyle -m: hide result from caller. -# Variables not used directly here. -local -a match mbegin mend -zstyle -m ":completion:zed:*" insert-tab '*' || - zstyle ":completion:zed:*" insert-tab yes +() { + # Matching used in zstyle -m: hide result from caller. + # Variables not used directly here. + local -a match mbegin mend + zstyle -m ":completion:zed:*" insert-tab '*' || + zstyle ":completion:zed:*" insert-tab yes +} zmodload zsh/terminfo 2>/dev/null @@ -124,22 +131,51 @@ fi setopt localoptions nobanghist if ((fun)) then - var="$(functions $expand -- $1)" + var="$(functions $expand -- "$1")" # If function is undefined but autoloadable, load it if [[ $var = *\#\ undefined* ]] then - var="$(autoload +X $1; functions -- $1)" + var="$(autoload +X "$1"; functions -- "$1")" elif [[ -z $var ]] then var="${(q-)1} () { }" fi vared -M zed -m zed-vicmd -i __zed_init var && eval function "$var" +elif ((hist)) then + if [[ -n $1 ]]; then + { fc -p -a "$1" ${2:-$({ wc -l <"$1" } 2>/dev/null)} || return } + let HISTSIZE++ + print -s "" # Work around fc -p limitation + fi + # When editing the current shell history, the "zed -h" command is not + # itself included because the current event is not added to the ring + # until the next prompt is printed. This means "zed -h" is prepended + # to the result of the edit, because of the way "print -s" is defined. + var=( "${(@Oav)history}" ) + IFS=$'\n' vared -M zed -m zed-vicmd -i __zed_init var + if (( ? )); then + [[ -n $1 ]] && unset HISTFILE + else + local HISTSIZE=0 savehist=$#var + fc -R /dev/null # Remove entries other than those added here + HISTSIZE=$savehist # Resets on function exit because local + [[ -n $1 ]] && SAVEHIST=$savehist # Resets via foregoing fc -a + for (( hist=1; hist <= savehist; hist++ )) + do print -rs -- "$var[hist]" + done + if [[ -n $zed_file_name ]]; then + fc -W "$zed_file_name" + [[ -n $1 ]] && unset HISTFILE + fi + # Note prepend effect when global HISTSIZE greater than $savehist. + # This does not affect file editing. + fi else - zed_file_name=$1 - [[ -f $1 ]] && var="$(<$1)" + zed_file_name="$1" + [[ -f $1 ]] && var="$(<"$1")" while vared -M zed -m zed-vicmd -i __zed_init var do { - print -r -- "$var" >| $zed_file_name + print -r -- "$var" >| "$zed_file_name" } always { (( TRY_BLOCK_ERROR = 0 )) } && break diff --git a/Functions/Misc/zmathfuncdef b/Functions/Misc/zmathfuncdef index e5692e769..5ed991f68 100644 --- a/Functions/Misc/zmathfuncdef +++ b/Functions/Misc/zmathfuncdef @@ -78,7 +78,7 @@ if ! zmodload -e zsh/mathfunc; then fi { - eval "$fname() { (( $body )) }" + eval "$fname() { (( $body )); true }" } always { # Remove math function if shell function definition failed. if (( TRY_BLOCK_ERROR )); then 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_restore_setup b/Functions/Prompts/prompt_restore_setup index 54c4adbf9..b77dbe815 100644 --- a/Functions/Prompts/prompt_restore_setup +++ b/Functions/Prompts/prompt_restore_setup @@ -1,2 +1,3 @@ # Damn that was easy zstyle -t :prompt-theme cleanup +zstyle -t :prompt-theme restore diff --git a/Functions/Prompts/promptinit b/Functions/Prompts/promptinit index e27b8779a..0c06699e8 100644 --- a/Functions/Prompts/promptinit +++ b/Functions/Prompts/promptinit @@ -14,6 +14,8 @@ prompt_themes=() promptinit () { emulate -L zsh setopt extendedglob + autoload -Uz add-zsh-hook add-zle-hook-widget + local ppath='' name theme local -a match mbegin mend @@ -32,9 +34,6 @@ promptinit () { fi done - # To manipulate precmd and preexec hooks... - autoload -Uz add-zsh-hook - # Variables common to all prompt styles prompt_newline=$'\n%{\r%}' } @@ -47,36 +46,23 @@ prompt_preview_safely() { return fi - # This handles all the stuff from the default :prompt-theme cleanup - local +h PS1=$PS1 PS2=$PS2 PS3=$PS3 PS4=$PS4 RPS1=$RPS1 RPS2=$RPS2 - local +h PROMPT=$PROMPT RPROMPT=$RPOMPT RPROMPT2=$RPROMPT2 PSVAR=$PSVAR - local -a precmd_functions preexec_functions prompt_preview_cleanup - local -aLl +h zle_highlight + # Run this in a subshell, so we don't need to clean up afterwards. + ( + # Execute current theme's cleanup sequence, if any. + zstyle -t :prompt-theme cleanup - { - # Save and clear current restore-point if any - zstyle -g prompt_preview_cleanup :prompt-theme cleanup - { - zstyle -d :prompt-theme cleanup - - # The next line is a bit ugly. It (perhaps unnecessarily) - # runs the prompt theme setup function to ensure that if - # the theme has a _preview function that it's been autoloaded. + # If we can't find a _preview function, run the _setup function to see if + # it will create one. + typeset +f prompt_${1}_preview >&/dev/null || prompt_${1}_setup - if typeset +f prompt_${1}_preview >&/dev/null; then - prompt_${1}_preview "$@[2,-1]" - else - prompt_preview_theme "$@" - fi - } always { - # Run any theme-specific cleanup, then reset restore point - zstyle -t :prompt-theme cleanup - } - } always { - (( $#prompt_preview_cleanup )) && - zstyle -e :prompt-theme cleanup "${prompt_preview_cleanup[@]}" - } + # ...then try again. + if typeset +f prompt_${1}_preview >&/dev/null; then + prompt_${1}_preview "$@[2,-1]" + else + prompt_preview_theme "$@" + fi + ) } set_prompt() { @@ -87,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) @@ -96,19 +82,6 @@ Use prompt -h <theme> for help on specific themes.' getopts "chlps:" opt case "$opt" in - (h|p) - setopt localtraps - if [[ -z "$prompt_theme[1]" ]]; then - # Not using a prompt theme; save settings - local +h PS1=$PS1 PS2=$PS2 PS3=$PS3 PS4=$PS4 RPS1=$RPS1 RPS2=$RPS2 - local +h PROMPT=$PROMPT RPROMPT=$RPOMPT RPROMPT2=$RPROMPT2 PSVAR=$PSVAR - local -a precmd_functions preexec_functions - else - trap 'prompt_${prompt_theme[1]}_setup "${(@)prompt_theme[2,-1]}"' 0 - fi - ;; - esac - case "$opt" in c) if [[ -n $prompt_theme ]]; then print -n "Current prompt theme" (( $#prompt_theme > 1 )) && print -n " with parameters" @@ -119,20 +92,26 @@ Use prompt -h <theme> for help on specific themes.' return ;; h) if [[ -n "$2" && -n $prompt_themes[(r)$2] ]]; then - if functions prompt_$2_setup >/dev/null; then - # The next line is a bit ugly. It (perhaps unnecessarily) - # runs the prompt theme setup function to ensure that if - # the theme has a _help function that it's been autoloaded. - prompt_$2_setup - fi - if functions prompt_$2_help >/dev/null; then - print "Help for $2 theme:\n" - prompt_$2_help - else - print "No help available for $2 theme." - fi - print "\nType \`prompt -p $2' to preview the theme, \`prompt $2'" - print "to try it out, and \`prompt -s $2' to use it in future sessions." + # Run this in a subshell, so we don't need to clean up afterwards. + ( + # Execute current theme's cleanup sequence, if any. + zstyle -t :prompt-theme cleanup + + # If we can't find a _help function, run the _setup function to see + # if it will create one. + typeset +f prompt_$2_help >/dev/null || + prompt_$2_setup + + # ...then try again. + if typeset +f prompt_$2_help >/dev/null; then + print "Help for $2 theme:\n" + prompt_$2_help + else + print "No help available for $2 theme." + fi + print "\nType \`prompt -p $2' to preview the theme, \`prompt $2'" + print "to try it out, and \`prompt -s $2' to use it in future sessions." + ) else print "$usage" fi @@ -141,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" @@ -173,34 +151,46 @@ Use prompt -h <theme> for help on specific themes.' # Reset some commonly altered bits to the default local hook - for hook in chpwd precmd preexec periodic zshaddhistory zshexit; do - add-zsh-hook -D "${hook}" "prompt_*_${hook}" + for hook in chpwd precmd preexec periodic zshaddhistory zshexit \ + zsh_directory_name; do + add-zsh-hook -D "$hook" "prompt_*_$hook" + done + for hook in isearch-exit isearch-update line-pre-redraw line-init \ + line-finish history-line-set keymap-select; do + add-zle-hook-widget -D "$hook" "prompt_*_$hook" done typeset -ga zle_highlight=( ${zle_highlight:#default:*} ) (( ${#zle_highlight} )) || unset zle_highlight + zstyle -t :prompt-theme cleanup prompt_$1_setup "$@[2,-1]" && prompt_theme=( "$@" ) ;; esac } prompt_cleanup () { - local -a cleanup_hooks - if zstyle -g cleanup_hooks :prompt-theme cleanup - then - cleanup_hooks+=(';' "$@") - zstyle -e :prompt-theme cleanup "${cleanup_hooks[@]}" - elif (( $+prompt_preview_cleanup == 0 )) - then - print -u2 "prompt_cleanup: no prompt theme active" - return 1 + local -a cleanup_hooks theme_active + if ! zstyle -g cleanup_hooks :prompt-theme cleanup; then + if ! zstyle -g theme_active :prompt-theme restore; then + print -u2 "prompt_cleanup: no prompt theme active" + return 1 + fi + + # Set the cleanup sequence up. + zstyle -e :prompt-theme cleanup \ + 'zstyle -d :prompt-theme cleanup;' \ + 'reply=(yes)' + zstyle -g cleanup_hooks :prompt-theme cleanup fi + + cleanup_hooks+=(';' "$@") + zstyle -e :prompt-theme cleanup "${cleanup_hooks[@]}" } prompt () { local -a prompt_opts theme_active - zstyle -g theme_active :prompt-theme cleanup || { + zstyle -g theme_active :prompt-theme restore || { # This is done here rather than in set_prompt so that it # is safe and sane for set_prompt to setopt localoptions, # which will be cleared before we arrive back here again. @@ -210,22 +200,21 @@ prompt () { [[ -o promptpercent ]] && prompt_opts+=(percent) [[ -o promptsp ]] && prompt_opts+=(sp) [[ -o promptsubst ]] && prompt_opts+=(subst) - zstyle -e :prompt-theme cleanup \ - 'zstyle -d :prompt-theme cleanup;' \ - 'prompt_default_setup;' \ - ${PS1+PS1="${(q)PS1}"} \ - ${PS2+PS2="${(q)PS2}"} \ - ${PS3+PS3="${(q)PS3}"} \ - ${PS4+PS4="${(q)PS4}"} \ - ${RPS1+RPS1="${(q)RPS1}"} \ - ${RPS2+RPS2="${(q)RPS2}"} \ - ${RPROMPT+RPROMPT="${(q)RPROMPT}"} \ - ${RPROMPT2+RPROMPT2="${(q)RPROMPT2}"} \ - ${PSVAR+PSVAR="${(q)PSVAR}"} \ - "precmd_functions=(${(q)precmd_functions[@]})" \ - "preexec_functions=(${(q)preexec_functions[@]})" \ - "prompt_opts=( ${prompt_opts[*]} )" \ - 'reply=(yes)' + zstyle -e :prompt-theme restore " + zstyle -d :prompt-theme restore + prompt_default_setup + ${PS1+PS1=${(q+)PS1}} + ${PS2+PS2=${(q+)PS2}} + ${PS3+PS3=${(q+)PS3}} + ${PS4+PS4=${(q+)PS4}} + ${RPS1+RPS1=${(q+)RPS1}} + ${RPS2+RPS2=${(q+)RPS2}} + ${RPROMPT+RPROMPT=${(q+)RPROMPT}} + ${RPROMPT2+RPROMPT2=${(q+)RPROMPT2}} + ${PSVAR+PSVAR=${(q+)PSVAR}} + prompt_opts=( $prompt_opts[*] ) + reply=( yes ) + " } set_prompt "$@" @@ -238,26 +227,29 @@ prompt () { prompt_preview_theme () { emulate -L zsh - # Check for proper state handling - (( $+prompt_preview_cleanup )) || { - prompt_preview_safely "$@" - return - } - # Minimal preview for prompts that don't supply one local -a prompt_opts print -n "$1 theme" (( $#* > 1 )) && print -n " with parameters \`$*[2,-1]'" print ":" + zstyle -t :prompt-theme cleanup prompt_${1}_setup "$@[2,-1]" (( ${#prompt_opts} )) && setopt noprompt{bang,cr,percent,sp,subst} "prompt${^prompt_opts[@]}" + + [[ -n ${chpwd_functions[(r)prompt_${1}_chpwd]} ]] && + prompt_${1}_chpwd [[ -n ${precmd_functions[(r)prompt_${1}_precmd]} ]] && - prompt_${1}_precmd - [[ -o promptcr ]] && print -n $'\r'; : - print -P "${PS1}command arg1 arg2 ... argn" + prompt_${1}_precmd + + # We'd like to call zle-line-init/finish hooks, too, but that's not possible + # while the ZLE is not active. + + [[ -o promptcr ]] && print -n $'\r' + :; print -P -- "${PS1}command arg1 arg2 ... argn" + [[ -n ${preexec_functions[(r)prompt_${1}_preexec]} ]] && - prompt_${1}_preexec + prompt_${1}_preexec } [[ -o kshautoload ]] || promptinit "$@" diff --git a/Functions/VCS_Info/Backends/VCS_INFO_detect_cvs b/Functions/VCS_Info/Backends/VCS_INFO_detect_cvs index 7a5ee1eef..a57959ec9 100644 --- a/Functions/VCS_Info/Backends/VCS_INFO_detect_cvs +++ b/Functions/VCS_Info/Backends/VCS_INFO_detect_cvs @@ -7,5 +7,18 @@ setopt localoptions NO_shwordsplit [[ $1 == '--flavours' ]] && return 1 VCS_INFO_check_com ${vcs_comm[cmd]} || return 1 -[[ -d "./CVS" ]] && [[ -r "./CVS/Repository" ]] && return 0 -return 1 +if ! [[ -d "./CVS" ]] || ! [[ -r "./CVS/Repository" ]] ; then + return 1 +fi + +# Look for the most distant parent that still has a CVS subdirectory. +local cvsbase="." +cvsbase=${cvsbase:P} +while [[ -d "${cvsbase:h}/CVS" ]]; do + cvsbase="${cvsbase:h}" + if [[ $cvsbase == '/' ]]; then + break + fi +done + +vcs_comm[basedir]="${cvsbase}" diff --git a/Functions/VCS_Info/Backends/VCS_INFO_detect_git b/Functions/VCS_Info/Backends/VCS_INFO_detect_git index e4191f474..b7955de38 100644 --- a/Functions/VCS_Info/Backends/VCS_INFO_detect_git +++ b/Functions/VCS_Info/Backends/VCS_INFO_detect_git @@ -7,6 +7,7 @@ setopt localoptions NO_shwordsplit [[ $1 == '--flavours' ]] && { print -l git-p4 git-svn; return 0 } if VCS_INFO_check_com ${vcs_comm[cmd]} && vcs_comm[gitdir]="$(${vcs_comm[cmd]} rev-parse --git-dir 2> /dev/null)" ; then + vcs_comm[basedir]="$( ${vcs_comm[cmd]} rev-parse --show-toplevel 2> /dev/null )" if [[ -d ${vcs_comm[gitdir]}/svn ]] ; then vcs_comm[overwrite_name]='git-svn' elif [[ -d ${vcs_comm[gitdir]}/refs/remotes/p4 ]] ; then vcs_comm[overwrite_name]='git-p4' ; fi return 0 diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_bzr b/Functions/VCS_Info/Backends/VCS_INFO_get_data_bzr index b30e0e12b..f1f5527e8 100644 --- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_bzr +++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_bzr @@ -100,14 +100,7 @@ else fi rrn=${bzrbase:t} -zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" branchformat bzrbr || bzrbr="%b:%r" -hook_com=( branch "${bzrinfo[2]}" revision "${bzrinfo[1]}" ) -if VCS_INFO_hook 'set-branch-format' "${bzrbr}"; then - zformat -f bzrbr "${bzrbr}" "b:${hook_com[branch]}" "r:${hook_com[revision]}" -else - bzrbr=${hook_com[branch-replace]} -fi -hook_com=() - +VCS_INFO_set-branch-format "${bzrinfo[2]}" "${bzrinfo[1]}" && + bzrbr="${REPLY}" VCS_INFO_formats '' "${bzrbr}" "${bzrbase}" '' "${bzr_changes}" "${bzrinfo[1]}" "${bzr_changes}" return 0 diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_cvs b/Functions/VCS_Info/Backends/VCS_INFO_get_data_cvs index 9b828bd11..bc0d5cfe5 100644 --- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_cvs +++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_cvs @@ -5,17 +5,8 @@ setopt localoptions NO_shwordsplit local cvsbranch cvsbase -# Look for the most distant parent that still has a CVS subdirectory. +cvsbase="${vcs_comm[basedir]}" # VCS_INFO_detect_cvs ensured that ./CVS/Repository exists. -cvsbase="." -cvsbase=${cvsbase:P} -while [[ -d "${cvsbase:h}/CVS" ]]; do - cvsbase="${cvsbase:h}" - if [[ $cvsbase == '/' ]]; then - break - fi -done - cvsbranch=$(< ./CVS/Repository) rrn=${cvsbase:t} cvsbranch=${cvsbranch##${rrn}/} diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_fossil b/Functions/VCS_Info/Backends/VCS_INFO_get_data_fossil index fd0f8389e..e84b3abc0 100644 --- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_fossil +++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_fossil @@ -20,5 +20,6 @@ if [ -n "$merging" ]; then action="merging" fi +rrn=${fsinfo[local_root]:t} VCS_INFO_formats "$action" "${fsbranch}" "${fsinfo[local_root]}" '' "$changed" "${fshash}" "${fsinfo[repository]}" return 0 diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_git b/Functions/VCS_Info/Backends/VCS_INFO_get_data_git index ceb4f978a..e45eebc8e 100644 --- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_git +++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_git @@ -132,13 +132,17 @@ 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 } gitdir=${vcs_comm[gitdir]} VCS_INFO_git_getbranch ${gitdir} -gitbase=$( ${vcs_comm[cmd]} rev-parse --show-toplevel ) +gitbase=${vcs_comm[basedir]} +if [[ -z ${gitbase} ]]; then + # Bare repository + gitbase=${gitdir:P} +fi rrn=${gitbase:t} if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-revision ; then gitsha1=$(${vcs_comm[cmd]} rev-parse --quiet --verify HEAD) @@ -192,13 +196,19 @@ elif [[ -d "${gitdir}/rebase-merge" ]]; then # 'git rebase -i' patchdir="${gitdir}/rebase-merge" local p - [[ -f "${patchdir}/done" ]] && - for p in ${(f)"$(< "${patchdir}/done")"}; do + (( ${+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 # pick/edit/fixup/squash/reword: Add "$hash $subject" to $git_patches_applied. # exec: Add "exec ${command}" to $git_patches_applied. # (anything else): As 'exec'. case $p in - ((p|pick|e|edit|r|reword|f|fixup|s|squash)' '*) + ([#]*) + # Comment line. Skip. + return 0 + ;; + (''(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#* } @@ -221,11 +231,11 @@ elif [[ -d "${gitdir}/rebase-merge" ]]; then p="${p%% *} ?" fi ;; - (x *) + (''(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. - p=${p/x /exec } + p=${p/#x /exec } ;; (*) # Forward compatibility with not-yet-existing 'git rebase -i' verbs. @@ -233,30 +243,74 @@ elif [[ -d "${gitdir}/rebase-merge" ]]; then p+=" ?" fi esac - git_patches_applied+=("$p") - done + REPLY=$p + } + if [[ -f "${patchdir}/done" ]] ; then + for p in ${(f)"$(< "${patchdir}/done")"}; do + VCS_INFO_git_map_rebase_line_to_hash_and_subject "$p" + (( $+REPLY )) && git_patches_applied+=( "$REPLY" ) + done + fi if [[ -f "${patchdir}/git-rebase-todo" ]] ; then - # TODO: Process ${patchdir}/git-rebase-todo lines like ${patchdir}/done lines are - git_patches_unapplied=( ${${(f)${"$(<"${patchdir}/git-rebase-todo")"}}:#[#]*} ) + 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" ) + done fi VCS_INFO_git_handle_patches elif [[ -d "${gitdir}/rebase-apply" ]]; then - # 'git rebase' without -i + # 'git rebase' without -i, or 'git am' patchdir="${gitdir}/rebase-apply" local next="${patchdir}/next" + local this_patch_file if [[ -f $next ]]; then local cur=$(< $next) local p subject - # Fake patch names for all but current patch - for ((p = 1; p < cur; p++)); do - printf -v "git_patches_applied[$p]" "%04d ?" "$p" - done + # Compute patch names for patches "before" the current patch + if [[ -r ${patchdir}/rewritten ]]; then + if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" use-simple; then + git_patches_applied=( ${${(f)"$(<${patchdir}/rewritten)"}// */' ?'} ) + else + git_patches_applied=( + ${(f)"$(${vcs_comm[cmd]} log --no-walk=unsorted --pretty='%h %s' ${${(f)"$(<${patchdir}/rewritten)"}%% *} --)"} + ) + fi + else + # Compatibility with old git. In 2.11 and 2.24, at least, + # (( cur == 1 )), so the loop body would never run. However, both + # of these versions have original-commit and orig-head and would + # take the 'if' branch, rather than this 'else' branch. + for ((p = 1; p < cur; p++)); do + printf -v this_patch_file "%s/%04d" "${patchdir}" "${p}" + if [[ -f $this_patch_file ]]; then + VCS_INFO_patch2subject "${this_patch_file}" + git_patches_applied+=( "$p $REPLY" ) + else + git_patches_applied+=( "$p ?" ) + fi + done + fi + # Set $subject to the info for the current patch if [[ -f "${patchdir}/msg-clean" ]]; then subject="${$(< "${patchdir}/msg-clean")[(f)1]}" - elif local this_patch_file - printf -v this_patch_file "%s/%04d" "${patchdir}" "${cur}" + elif [[ -f "${patchdir}/final-commit" ]]; then + # This value is not rfc2047-encoded. It's also available via + # "${patchdir}/info". + subject="${$(< "${patchdir}/final-commit")[(f)1]}" + elif printf -v this_patch_file "%s/%04d" "${patchdir}" "${cur}" [[ -f $this_patch_file ]] then + # This branch is last for several reasons: + # + # - The "Subject" header will be MIME-encoded (rfc2047). + # + # - If the mail has full rfc822 headers (including "Received" and + # so on), we won't find the "Subject:" header, since + # VCS_INFO_patch2subject only checks the first few lines. + # + # - In --scissors mode, we may find the outer "Subject:" header, + # whereas the inner one (after the scissors line) will be used, + # if present. () { local REPLY VCS_INFO_patch2subject "${this_patch_file}" @@ -266,14 +320,32 @@ elif [[ -d "${gitdir}/rebase-apply" ]]; then subject=${subject:-'?'} if [[ -f "${patchdir}/original-commit" ]]; then git_patches_applied+=("$(< ${patchdir}/original-commit) $subject") + elif [[ -f "${patchdir}/next" ]]; then + git_patches_applied+=("$(< ${patchdir}/next) $subject") else git_patches_applied+=("? $subject") fi - local last="$(< "${patchdir}/last")" - if (( cur+1 <= last )); then - git_patches_unapplied=( {$((cur+1))..$last} ) + # Handle patches scheduled for after the current patch, if instructed to. + if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-unapplied; then + local last="$(< "${patchdir}/last")" + if [[ -r ${patchdir}/original-commit && -r ${patchdir}/orig-head ]] && + ! zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" use-simple + then + git_patches_unapplied+=( ${(f)"$(${vcs_comm[cmd]} log --reverse --pretty="%h %s" "$(<${patchdir}/original-commit)..$(<${patchdir}/orig-head)")"} ) + else + for ((p = cur+1; p <= last; p++)); do + printf -v this_patch_file "%s/%04d" "${patchdir}" "${p}" + if [[ -f $this_patch_file ]]; then + VCS_INFO_patch2subject "${this_patch_file}" + git_patches_unapplied+=( "$p $REPLY" ) + else + git_patches_unapplied+=( "$p ?" ) + fi + done + fi fi fi + unset this_patch_file VCS_INFO_git_handle_patches elif [[ -f "${gitdir}/MERGE_HEAD" ]]; then diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg b/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg index cd5ef321d..ea3798b81 100644 --- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg +++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg @@ -5,7 +5,7 @@ setopt localoptions extendedglob NO_shwordsplit -local hgbase bmfile branchfile rebasefile dirstatefile mqseriesfile \ +local hgbase bmfile branchfile topicfile rebasefile dirstatefile mqseriesfile \ curbmfile curbm \ mqstatusfile mqguardsfile patchdir mergedir \ r_csetid r_lrev r_branch i_bmhash i_bmname \ @@ -14,7 +14,7 @@ local hgbase bmfile branchfile 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 @@ -27,6 +27,7 @@ mergedir="${hgbase}/.hg/merge/" bmfile="${hgbase}/.hg/bookmarks" curbmfile="${hgbase}/.hg/bookmarks.current" branchfile="${hgbase}/.hg/branch" +topicfile="${hgbase}/.hg/topic" rebasefile="${hgbase}/.hg/rebasestate" dirstatefile="${hgbase}/.hg/dirstate" mqstatusfile="${patchdir}/status" # currently applied patches @@ -48,27 +49,34 @@ if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-revision ; then # Settling for a short (but unique!) hash because getting the full # 40-char hash in addition to all the other info we want isn't # available in a single hg invocation - hgid_args=( id -i -n -b ) + hgid_args=( id -i -n ) # Looking for changes is a tad bit slower since the dirstate cache must # first be refreshed before being read 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 r_branch + | read -r r_csetid r_lrev fi fi # If the user doesn't opt to invoke hg we can still get the current branch -if [[ -z ${r_branch} && -r ${branchfile} ]] ; then +if [[ -r ${branchfile} ]] ; then r_branch=$(< ${branchfile}) fi # If we still don't know the branch it's safe to assume default [[ -n ${r_branch} ]] || r_branch="default" +# Show topic if there is any (the UI for this experimental concept is not yet +# final, but for a long time the convention has been to join the branch name +# and the topic name by a colon) +if [[ -f ${topicfile} && -r ${topicfile} && -s ${topicfile} ]] ; then + IFS= read -r REPLY < ${topicfile} + r_branch=${r_branch}:${REPLY} +fi + # The working dir has uncommitted-changes if the revision ends with a + if [[ $r_lrev[-1] == + ]] ; then hgchanges=1 @@ -175,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} ) @@ -208,12 +219,16 @@ if zstyle -T ":vcs_info:${vcs}:${usercontext}:${rrn}" get-mq \ fi local -A extra_hook_com=( guards "${guards_string}" guards-n ${#mqguards} ) - local -a extra_zformats=( "g:${extra_hook_com[guards]}" "G:${#mqguards}" ) + + (( ${+functions[VCS_INFO_hg_extra_zformats]} )) || + VCS_INFO_hg_extra_zformats() { + reply=( "g:${hook_com[guards]}" "G:${#mqguards}" ) + } VCS_INFO_set-patch-format 'mqpatches' 'applied_string' \ 'mqunapplied' 'unapplied_string' \ ":vcs_info:${vcs}:${usercontext}:${rrn}" hgmqstring \ - extra_hook_com extra_zformats + extra_hook_com VCS_INFO_hg_extra_zformats '' hgmqstring=$REPLY fi diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_p4 b/Functions/VCS_Info/Backends/VCS_INFO_get_data_p4 index 329884982..e8a08a663 100644 --- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_p4 +++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_p4 @@ -17,13 +17,8 @@ local p4branch change # here down is synced as the revision. # I suppose the following might be slow on a tortuous client view. change="${${$(${vcs_comm[cmd]} changes -m 1 ...\#have)##Change }%% *}" -zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" branchformat p4branch || p4branch="%b:%r" -hook_com=( branch "${p4info[Client_name]}" revision "${change}" ) -if VCS_INFO_hook 'set-branch-format' "${p4branch}"; then - zformat -f p4branch "${p4branch}" "b:${hook_com[branch]}" "r:${hook_com[revision]}" -else - p4branch=${hook_com[branch-replace]} -fi -hook_com=() +rrn=${p4base:t} +VCS_INFO_set-branch-format "${p4info[Client_name]}" "${change}" && + p4branch="${REPLY}" VCS_INFO_formats '' "${p4branch}" "${p4base}" '' '' "$change" '' return 0 diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_svk b/Functions/VCS_Info/Backends/VCS_INFO_get_data_svk index 1d2d22ffb..149e30222 100644 --- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_svk +++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_svk @@ -8,13 +8,7 @@ local -A hook_com svkbase=${vcs_comm[basedir]} rrn=${svkbase:t} -zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" branchformat svkbranch || svkbranch="%b:%r" -hook_com=( branch "${vcs_comm[branch]}" revision "${vcs_comm[revision]}" ) -if VCS_INFO_hook 'set-branch-format' "${svkbranch}"; then - zformat -f svkbranch "${svkbranch}" "b:${hook_com[branch]}" "r:${hook_com[revision]}" -else - svkbranch=${hook_com[branch-replace]} -fi -hook_com=() +VCS_INFO_set-branch-format "${vcs_comm[branch]}" "${vcs_comm[revision]}" && + svkbranch="${REPLY}" VCS_INFO_formats '' "${svkbranch}" "${svkbase}" '' '' "${vcs_comm[revision]}" '' return 0 diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_svn b/Functions/VCS_Info/Backends/VCS_INFO_get_data_svn index 21590addd..b33efc2fb 100644 --- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_svn +++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_svn @@ -10,6 +10,7 @@ local -i rc local -A svninfo parentinfo cwdinfo local -A hook_com integer -r SVN_ERR_WC_UPGRADE_REQUIRED=155036 # from /usr/local/include/subversion-1/svn_error_codes.h +integer -r SVN_ERR_WC_UNSUPPORTED_FORMAT=155021 svnbase="."; svninfo=() @@ -22,7 +23,14 @@ rc=$? if (( rc != 0 )) ; then if (( rc == 1 )) && [[ -n ${(M)dat:#"svn: E${SVN_ERR_WC_UPGRADE_REQUIRED}: "*} ]]; then hook_com=() - VCS_INFO_formats '' '?' '?' '' '' '?' 'upgrade required' + # User should run 'svn upgrade' + VCS_INFO_formats '' '?' '?' '' '' '?' 'working copy upgrade required' + return $? + elif (( rc == 1 )) && [[ -n ${(M)dat:#"svn: E${SVN_ERR_WC_UNSUPPORTED_FORMAT}: "*} ]]; then + hook_com=() + # User probably needs to install a newer svn, but we're not sure, so point + # the user to svn's error message. + VCS_INFO_formats '' '?' '?' '' '' '?' 'svn error' return $? else return 1 @@ -60,13 +68,7 @@ else fi rrn=${svnbase:t} -zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" branchformat svnbranch || svnbranch="%b:%r" -hook_com=( branch "${svninfo[URL]##*/}" revision "${cwdinfo[Revision]}" ) -if VCS_INFO_hook 'set-branch-format' "${svnbranch}"; then - zformat -f svnbranch "${svnbranch}" "b:${hook_com[branch]}" "r:${hook_com[revision]}" -else - svnbranch=${hook_com[branch-replace]} -fi -hook_com=() +VCS_INFO_set-branch-format "${svninfo[URL]##*/}" "${cwdinfo[Revision]}" && + svnbranch="${REPLY}" VCS_INFO_formats '' "${svnbranch}" "${svnbase}" '' '' "${cwdinfo[Revision]}" '' return 0 diff --git a/Functions/VCS_Info/VCS_INFO_bydir_detect b/Functions/VCS_Info/VCS_INFO_bydir_detect index 29b261413..89b4d6503 100644 --- a/Functions/VCS_Info/VCS_INFO_bydir_detect +++ b/Functions/VCS_Info/VCS_INFO_bydir_detect @@ -36,4 +36,6 @@ done [[ ${basedir} == "/" ]] && return 1 vcs_comm[basedir]=${basedir} +# TODO: Would the following be correct? --- +# rrn=${vcs_comm[basedir]:t} return 0 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 fef85964a..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,33 +131,35 @@ 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=(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)"$(${quilt_env[@]} $quiltcommand --quiltrc /dev/null unapplied 2> /dev/null)"} ) + unapplied=( ${(f)"$(if (( $+quilt_env[1] )); then export ${quilt_env[@]}; fi + $quiltcommand --quiltrc /dev/null unapplied 2> /dev/null)"} ) unapplied=( ${unapplied:#} ) else unapplied=() fi - if [[ -n $patches ]]; then - () { + { local i for ((i=1; i<=$#applied; i++)); do if VCS_INFO_quilt-patch2subject "$patches/$applied[$i]" && (( $+REPLY )) @@ -172,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 new file mode 100644 index 000000000..cbab60e29 --- /dev/null +++ b/Functions/VCS_Info/VCS_INFO_set-branch-format @@ -0,0 +1,24 @@ +## vim:ft=zsh +# +# A function for calling the branch-format hook +# +# Return the value to use in REPLY +# +# Parameters: +readonly branch=$1 +readonly revision=$2 +# + +[[ -n $rrn ]] || return 1 +local -A hook_com +local branchformat + +zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" branchformat branchformat || branchformat="%b:%r" +hook_com=( branch "${branch}" revision "${revision}" ) +if VCS_INFO_hook 'set-branch-format' "${branchformat}"; then + zformat -f REPLY "${branchformat}" "b:${hook_com[branch]}" "r:${hook_com[revision]}" +else + REPLY=${hook_com[branch-replace]} +fi +hook_com=() +return 0 diff --git a/Functions/VCS_Info/VCS_INFO_set-patch-format b/Functions/VCS_Info/VCS_INFO_set-patch-format index 917ebf6bf..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. # @@ -12,7 +14,9 @@ # $6 - name of a parameter to store a patch-format format string in # $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 an array parameter with extra arguments for the patch-format zformat call, or '' for empty +# $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' @@ -68,10 +76,12 @@ hook_com[unapplied]=${hook_com[unapplied]//'%'/%%} fi + reply=() + [[ -n $8 ]] && "$8" zformat -f REPLY "${(P)6}" "p:${hook_com[applied]}" "u:${hook_com[unapplied]}" \ "n:${hook_com[applied-n]}" "c:${hook_com[unapplied-n]}" \ "a:${hook_com[all-n]}" \ - ${8:+"${(@P)8}"} + "${reply[@]}" else unset applied_needs_escaping unapplied_needs_escaping # the hook deals with escaping REPLY=${hook_com[patch-replace]} diff --git a/Functions/VCS_Info/test-repo-git-rebase-apply b/Functions/VCS_Info/test-repo-git-rebase-apply new file mode 100755 index 000000000..ce49690cd --- /dev/null +++ b/Functions/VCS_Info/test-repo-git-rebase-apply @@ -0,0 +1,59 @@ +#!/usr/local/bin/zsh -f +# +# This script creates a test repository for testing the git backend's behaviour during rebase-apply operations. +# +# It works in ./vcs-test/. +# +# Suggested zshrc settings: +# +# autoload vcs_info +# precmd() { vcs_info; typeset -pm vcs\* } +# zstyle ':vcs_info:*' actionformats %m +# zstyle ':vcs_info:*' patch-format '[%n+%c=%a] [%p] [%u]' +# zstyle :vcs_info:\*gen-applied-string\* hooks f1 +# +vi-f1() { typeset -p argv hook_com } +# zstyle :vcs_info:\*gen-unapplied-string\* hooks f2 +# +vi-f2() { typeset -p argv hook_com } +# zstyle :vcs_info:\* get-unapplied true +# +mkdir "vcs-test" +cd "vcs-test" +git init + +append_lines() { for 1; do echo line from r$1 >> iota && git commit -am "r$1: Append a line"; done } + +echo "This is the file 'iota'." > iota +git add iota +git commit -m "r1: Add iota" +git tag rebase_onto_this HEAD + +# Make another change to iota +append_lines 2 +git tag rebase_from_this HEAD + +# Make non-conflicting changes +for 1 in 3 4 5 6; do + touch kappa$1 + git add kappa$1 + git commit -m "r${1}: non-conflicting change" +done + +# Make more changes to iota +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 +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 100755 index 000000000..ce49690cd --- /dev/null +++ b/Functions/VCS_Info/test-repo-git-rebase-merge @@ -0,0 +1,59 @@ +#!/usr/local/bin/zsh -f +# +# This script creates a test repository for testing the git backend's behaviour during rebase-apply operations. +# +# It works in ./vcs-test/. +# +# Suggested zshrc settings: +# +# autoload vcs_info +# precmd() { vcs_info; typeset -pm vcs\* } +# zstyle ':vcs_info:*' actionformats %m +# zstyle ':vcs_info:*' patch-format '[%n+%c=%a] [%p] [%u]' +# zstyle :vcs_info:\*gen-applied-string\* hooks f1 +# +vi-f1() { typeset -p argv hook_com } +# zstyle :vcs_info:\*gen-unapplied-string\* hooks f2 +# +vi-f2() { typeset -p argv hook_com } +# zstyle :vcs_info:\* get-unapplied true +# +mkdir "vcs-test" +cd "vcs-test" +git init + +append_lines() { for 1; do echo line from r$1 >> iota && git commit -am "r$1: Append a line"; done } + +echo "This is the file 'iota'." > iota +git add iota +git commit -m "r1: Add iota" +git tag rebase_onto_this HEAD + +# Make another change to iota +append_lines 2 +git tag rebase_from_this HEAD + +# Make non-conflicting changes +for 1 in 3 4 5 6; do + touch kappa$1 + git add kappa$1 + git commit -m "r${1}: non-conflicting change" +done + +# Make more changes to iota +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 +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/vcs_info b/Functions/VCS_Info/vcs_info index 9f48bee75..786b61918 100644 --- a/Functions/VCS_Info/vcs_info +++ b/Functions/VCS_Info/vcs_info @@ -22,6 +22,7 @@ static_functions=( VCS_INFO_hexdump VCS_INFO_hook VCS_INFO_set-patch-format + VCS_INFO_set-branch-format VCS_INFO_maxexports VCS_INFO_nvcsformats VCS_INFO_patch2subject diff --git a/Functions/Zle/edit-command-line b/Functions/Zle/edit-command-line index 991775ea5..5f7ea321f 100644 --- a/Functions/Zle/edit-command-line +++ b/Functions/Zle/edit-command-line @@ -7,6 +7,23 @@ # except that it will handle multi-line buffers properly. emulate -L zsh +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 (( CURSOR < MARK )); then + left=$CURSOR right=$MARK + lbuffer= + else + left=$MARK right=$CURSOR + lbuffer[right-left,-1]= + fi + (( left++ )) + buffer=$BUFFER[left,right] +elif (( ! ZLE_RECURSIVE )); then + prebuffer=$PREBUFFER +fi () { exec </dev/tty @@ -17,13 +34,17 @@ emulate -L zsh (( $+zle_bracketed_paste )) && print -r -n - $zle_bracketed_paste[2] # Open the editor, placing the cursor at the right place if we know how. - local editor=( "${(@Q)${(z)${VISUAL:-${EDITOR:-vi}}}}" ) - case $editor in + local -a editor + zstyle -a :zle:$WIDGET editor editor + if (( ! $#editor )); then + editor=( "${(@Q)${(z)${VISUAL:-${EDITOR:-vi}}}}" ) + fi + case $editor in (*vim*) - integer byteoffset=$(( $#PREBUFFER + $#LBUFFER + 1 )) + integer byteoffset=$(( $#prebuffer + $#lbuffer + 1 )) "${(@)editor}" -c "normal! ${byteoffset}go" -- $1;; (*emacs*) - local lines=( "${(@f):-"$PREBUFFER$LBUFFER"}" ) + local lines=( "${(@f):-"$prebuffer$lbuffer"}" ) "${(@)editor}" +${#lines}:$((${#lines[-1]} + 1)) $1;; (*) "${(@)editor}" $1;; esac @@ -31,7 +52,32 @@ emulate -L zsh (( $+zle_bracketed_paste )) && print -r -n - $zle_bracketed_paste[1] # Replace the buffer with the editor output. - print -Rz - "$(<$1)" -} =(<<<"$PREBUFFER$BUFFER") + # avoid drawing a new prompt when we can: + # - in recursive-edit, the send-break will just cancel the recursive-edit + # rather than reload the line from print -z so in that case we want to + # just set $BUFFER (unfortunately, recursive-edit doesn't reset CONTEXT + # or PREBUFFER so we have to explicitly handle this case, which overrides + # the following point) + # - when we are at PS2 (CONTEXT == cont && ! ZLE_RECURSIVE) we do want the + # break or otherwise the text from PREBUFFER will be inserted twice + # - when the region is active, we only want to change the parts of BUFFER + # covered by the region, and any PREBUFFER stays as PREBUFFER + # - in all other cases (that I can think of) we also just want to set + # $BUFFER directly. + if (( REGION_ACTIVE )); then + # adjust the length of the region to the length of the edited text + local prelen=$#BUFFER + BUFFER[left,right]="$(<$1)" + if (( MARK > CURSOR )); then + (( MARK += $#BUFFER - prelen )) + else + (( CURSOR += $#BUFFER - prelen )) + fi + elif [[ $CONTEXT != cont ]] || (( ZLE_RECURSIVE )); then + BUFFER="$(<$1)" + else + print -Rz - "$(<$1)" + zle send-break + fi -zle send-break # Force reload from the buffer stack +} =(<<<"$prebuffer$buffer") diff --git a/Functions/Zle/zed-set-file-name b/Functions/Zle/zed-set-file-name index da3421e71..745610660 100644 --- a/Functions/Zle/zed-set-file-name +++ b/Functions/Zle/zed-set-file-name @@ -2,8 +2,25 @@ emulate -L zsh autoload -Uz read-from-minibuffer -zle -K zed-normal-keymap - -local REPLY -read-from-minibuffer "File name: " -zed_file_name=$REPLY +case $curcontext in + (zed:::) + local curcontext=zed-set-file-name::: + # The call to vared from zed does the equivalent of + # bindkey -A zed main + # which confuses read-from-minibuffer. Fix it. + bindkey -A zed-normal-keymap main;; + (zed-set-file-name:::) + zle -M "zed-set-file-name: may not be called recursively" + return 1;; + (*) + zle -M "zed-set-file-name: not called from within zed" + return 1;; +esac +{ + local REPLY + read-from-minibuffer "File name: " + zed_file_name=$REPLY +} always { + # Re-install the zed keymap in the way vared should have all along + zle -K zed +} |