#compdef svn svnadmin svnadmin-static=svnadmin _svn () { local curcontext="$curcontext" state line expl ret=1 typeset -A opt_args local update_policy zstyle -s ":completion:*:*:$service:*" cache-policy update_policy if [[ -z "$update_policy" ]]; then zstyle ":completion:*:*:$service:*" cache-policy _svn_caching_policy fi _arguments -C \ '(-)--help[print help information]' \ '(- *)--version[print client version information]' \ '1: :->cmds' \ '*:: :->args' && ret=0 if [[ -n $state ]] && (( ! $+_svn_cmds )); then typeset -gHA _svn_cmds if _cache_invalid svn-cmds || ! _retrieve_cache svn-cmds; then _svn_cmds=( ${=${(f)${${"$(_comp_locale; _call_program commands svn help)"#l#*Available subcommands:}%%Subversion is a tool*}}/(#s)[[:space:]]#(#b)([a-z]##)[[:space:]]#(\([a-z, ?]##\))#/$match[1] :$match[1]${match[2]:+:${${match[2]//[(),]}// /:}}:} ) _store_cache svn-cmds _svn_cmds fi fi case $state in cmds) _wanted commands expl 'svn command' _svn_commands && ret=0 ;; args) local cmd args usage idx typeset -gHA _cache_svn_status _cache_svn_mtime cmd="${${(k)_svn_cmds[(R)*:$words[1]:*]}:-${(k)_svn_cmds[(i):$words[1]:]}}" if (( $#cmd )); then curcontext="${curcontext%:*:*}:svn-${cmd}:" if _cache_invalid svn-${cmd}-usage || \ ! _retrieve_cache svn-${cmd}-usage; then usage=${${(M)${(f)"$(_comp_locale; _call_program options svn help $cmd)"}:#usage:*}#usage:*$cmd] } _store_cache svn-${cmd}-usage usage fi if _cache_invalid svn-${cmd}-usage || \ ! _retrieve_cache svn-${cmd}-args; then args=( ${=${${${(M)${(f)"$(_comp_locale; _call_program options svn help $cmd)"#(*Valid options:|(#e))}:#* :*}%% #:*}/ (arg|ARG)/:arg:}/(#b)(-##)([[:alpha:]]##) \[--([a-z-]##)\](:arg:)#/(--$match[3])$match[1]$match[2]$match[4] ($match[1]$match[2])--$match[3]$match[4]} ) while (( idx=$args[(I)*--c(l|hangelist):arg:] )); do args[(I)*--c(l|hangelist):arg:]=( \*{--cl,--changelist}':change list:_svn_changelists' ) done _store_cache svn-${cmd}-args args fi case $cmd in; (add) args+=( '*:file:_files -g "*(^e:_svn_controlled:)"' ) ;; (commit) args=( ${args/(#b)(*--file*):arg:/$match[1]:file:_files} '*:file:_files -g "*(e:_svn_status:)"' ) ;; (delete) args+=( '*:file:_files -g ".svn(/e:_svn_deletedfiles:)"' ) ;; (diff) args+=( '*: : _alternative "files:file:_files -g \*\(e:_svn_status:\)" "urls:URL:_svn_urls"' ) ;; (help) args+=( '*::sub command:_svn_commands' ) ;; (import) args+=( '1:project directory or import location: _alternative "files:file:_files" "urls:URL:_svn_urls"' '2:import location: _alternative "files:file:_files" "urls:URL:_svn_urls"' ) ;; (log) args+=( '1: : _alternative "files:file:_files -g \*\(e:_svn_controlled:\)" "urls:URL:_svn_urls"' '*:file:_files -g "*(e:_svn_controlled:)"' ) ;; (mergeinfo) args[(r)--show-revs:arg:]=( '--show-revs:revisions:(merged eligible)' ) ;; (propget|propedit) args+=( '1:property name:_svn_props' '2:target: _alternative "files:file:_files" "urls:URL:_svn_urls"' ) ;; (propset) args=( ':propname:(svn:ignore svn:keywords svn:executable svn:eol-style svn:mime-type svn:externals svn:needs-lock)' ${args/(#b)(*--file*):arg:/$match[1]:file:_files} '*:path or url: _alternative "files:file:_files" "urls:URL:_svn_urls"' ) ;; (resolved) args+=( '*:file:_files -g "*(e:_svn_conflicts:)"' ) ;; (revert) args+=( '*:file:_files -g "(.svn|*)(/e:_svn_deletedfiles:,e:_svn_status:)"' ) ;; (*) case $usage in *(SRC|DST|TARGET|URL*PATH)*) args+=( '*: : _alternative "files:file:_files" "urls:URL:_svn_urls"' ) ;; *URL*) args+=( ':URL:_svn_urls' ) ;; *PATH*) args+=( '*:file:_files' ) ;; esac ;; esac _arguments "$args[@]" && ret=0 else _message "unknown svn command: $words[1]" fi ;; esac return ret } _svnadmin () { local curcontext="$curcontext" state line ret=1 _arguments -C \ '(-)--help[print help information]' \ '(- *)--version[print client version information]' \ '1: :->cmds' \ '*:: :->args' && ret=0 if [[ -n $state ]] && (( ! $+_svnadmin_cmds )); then typeset -gHA _svnadmin_cmds _svnadmin_cmds=( ${=${(f)${${"$(_comp_locale; _call_program commands svnadmin help)"#l#*Available subcommands:}}}/(#s)[[:space:]]#(#b)([-a-z]##)[[:space:]]#(\([a-z, ?]##\))#/$match[1] :$match[1]${match[2]:+:${${match[2]//[(),]}// /:}}:} ) fi case $state in cmds) _wanted commands expl 'svnadmin command' _svnadmin_commands && ret=0 ;; args) local cmd args usage cmd="${${(k)_svnadmin_cmds[(R)*:$words[1]:*]}:-${(k)_svnadmin_cmds[(i):$words[1]:]}}" if (( $#cmd )); then curcontext="${curcontext%:*:*}:svnadmin-${cmd}:" usage=${${(M)${(f)"$(_comp_locale; _call_program options svnadmin help $cmd)"}:#$cmd: usage:*}#$cmd: usage: svnadmin $cmd } args=( ${=${${${(M)${(f)"$(_comp_locale; _call_program options svnadmin help $cmd)"#(*Valid options:|(#e))}:#* :*}%% #:*}/ (arg|ARG)/:arg:}/(#b)-([[:alpha:]]) \[--([a-z-]##)\](:arg:)#/(--$match[2])-$match[1]$match[3] (-$match[1])--$match[2]$match[3]} ) if [[ $usage == *REPOS_PATH* ]]; then args+=( ":path:_files -/" ) elif [[ $cmd = help ]]; then args+=( "*:subcommand:_svnadmin_commands" ) fi _arguments "$args[@]" && ret=0 else _message "unknown svnadmin command: $words[1]" fi ;; esac return ret } (( $+functions[_svn_controlled] )) || _svn_controlled() { [[ -f ${(M)REPLY##*/}.svn/text-base/${REPLY##*/}.svn-base ]] } (( $+functions[_svn_conflicts] )) || _svn_conflicts() { [ -n $REPLY.(mine|r<->)(N[1]) ] } (( $+functions[_svn_deletedfiles] )) || _svn_deletedfiles() { # Typical usage would be _files -g '.svn(/e:_svn_deletedfiles:)' local cont controlled reply=( ) [[ $REPLY = (*/|).svn ]] || return controlled=( $REPLY/text-base/*.svn-base(N:r:t) ) for cont in ${controlled}; do [[ -e $REPLY:h/$cont ]] || reply+=( ${REPLY%.svn}$cont ) done } (( $+functions[_svn_status] )) || _svn_status() { local dir=$REPLY:h local pat="${1:-([ADMR~]|?M)}" zmodload -F zsh/stat b:zstat 2>/dev/null local key="$(zstat +device $dir):$(zstat +inode $dir)" local mtime="$(zstat +mtime $dir/.svn/entries)" if (( ! $+_cache_svn_status[$key] || _cache_svn_mtime[$key] != mtime )); then _cache_svn_status[$key]="$(_call_program files svn status -N $dir)" _cache_svn_mtime[$key]="$mtime" fi (( ${(M)#${(f)_cache_svn_status[$key]}:#(#s)${~pat}*$REPLY} )) } (( $+functions[_svn_remote_paths] )) || _svn_remote_paths() { local expl remfiles remdispf remdispd suf ret=1 pfx='\^/' sub='^/' # prefix must match a valid repository path format, either standard style # schema://host/path/.. or ^/path/.. specifying a path relative to the # root of the working directory repository. In the second form, allow the # leading '^' be escaped in case the user has the extendedglob option set. [[ -prefix *://*/ ]] || [[ -f .svn/entries && ( -prefix '^/' || -prefix '\^/' ) ]] || return 1 # return if remote access is not permitted zstyle -T ":completion:${curcontext}:" remote-access || return 1 remfiles=( ${(f)"$(svn list $IPREFIX${${PREFIX%%[^/]#}/#$pfx/$sub} 2>/dev/null)"} ) (( $? == 0 )) || return 1 # you might consider trying to return early if $#remfiles is zero, # but for whatever reason remfiles will always contain at least a # single empty string; that case is handled correctly below. compset -P '*/' compset -S '/*' || suf=file remdispf=(${remfiles:#*/}) remdispd=(${(M)remfiles:#*/}) _tags files while _tags; do while _next_label files expl ${suf:-directory}; do # add files, unless there is a '/' immediately to the right [[ -n $suf ]] && compadd -S ' ' -q "$@" "$expl[@]" -d remdispf $remdispf && ret=0 # add directories; use empty suffix if there is a '/' immediately to the right compadd -S "${suf:+/}" -q "$@" "$expl[@]" -d remdispd ${remdispd%/} && ret=0 done (( ret )) || return 0 done return 1 } (( $+functions[_svn_urls] )) || _svn_urls() { local urlsch expl ret=1 # first try completing a remote path; if successful, we are all done.. _svn_remote_paths && return 0 # allow configuring svn repository locations using the 'urls' zstyle. # always attempt completion of these because then matcher-list styles # which do substring matching will work correctly. _urls -S/ && ret=0 if [[ ! -prefix *://? ]] ; then zstyle -a ":completion:${curcontext}:" url-schemas urlsch \ || urlsch=( file:// http:// https:// svn:// svn+ssh:// ) if (( $#urlsch )) ; then compset -S '[^:]*' _wanted url-schemas expl 'URL schema' compadd -S '' - $urlsch[@] && ret=0 fi fi return ret } (( $+functions[_svn_commands] )) || _svn_commands() { compadd "$@" -k _svn_cmds || compadd "$@" ${(s.:.)_svn_cmds} } (( $+functions[_svnadmin_command] )) || _svnadmin_commands() { compadd "$@" -k _svnadmin_cmds || compadd "$@" ${(s.:.)_svnadmin_cmds} } (( $+functions[_svn_props] )) || _svn_props() { local properties properties=( ${${(M)${(f)"$(svn proplist 2>/dev/null)"}:# [^ ]*}# } ) compadd "$@" -a properties && return 0 } (( $+functions[_svn_changelists] )) || _svn_changelists() { local cls cls=( ${${${(M)${(f)"$(_comp_locale; _call_program changelists svn status 2>/dev/null)"}:#--- Changelist*}%??}##*\'} ) compadd "$@" -a cls && return 0 } _subversion () { case $service in (svn) _svn "$@" ;; (svnadmin) _svnadmin "$@" ;; esac } _svn_caching_policy() { [[ =$service -nt $1 ]] } _subversion "$@"