Follow upstream changes -- rest
[git-darcs-import.git] / tools / zsh_completion_new
blobf7d1f38f5d7bc3d510d17bfa0e0ab7d25c1405e3
1 #compdef darcs
3 # The Z Shell is copyright (c) 1992-2004 Paul Falstad, Richard Coleman,
4 # Zoltán Hidvégi, Andrew Main, Peter Stephenson, Sven Wischnowsky, and
5 # others.  All rights reserved.  Individual authors, whether or not
6 # specifically named, retain copyright in all changes; in what follows, they
7 # are referred to as `the Zsh Development Group'.  This is for convenience
8 # only and this body has no legal status.  The Z shell is distributed under
9 # the following licence; any provisions made in individual files take
10 # precedence.
12 # Permission is hereby granted, without written agreement and without
13 # licence or royalty fees, to use, copy, modify, and distribute this
14 # software and to distribute modified versions of this software for any
15 # purpose, provided that the above copyright notice and the following
16 # two paragraphs appear in all copies of this software.
18 # In no event shall the Zsh Development Group be liable to any party for
19 # direct, indirect, special, incidental, or consequential damages arising out
20 # of the use of this software and its documentation, even if the Zsh
21 # Development Group have been advised of the possibility of such damage.
23 # The Zsh Development Group specifically disclaim any warranties, including,
24 # but not limited to, the implied warranties of merchantability and fitness
25 # for a particular purpose.  The software provided hereunder is on an "as is"
26 # basis, and the Zsh Development Group have no obligation to provide
27 # maintenance, support, updates, enhancements, or modifications.
29 # This completion module is based on section 6.8 of 'A User's Guide to the Z-Shell' 
30 # by Peter Stephenson and on the tla completion module by Jason McCarty.
32 # EXTENDED_GLOB is required fr pattern backreferences.
33 setopt EXTENDED_GLOB
35 local DARCS=$words[1]
37 # test whether to hide short options from completion
38 autoload is-at-least
39 local hide_short
40 if zstyle -s ":completion:${curcontext}" hide-shortopts hide_short; then
41   case $hide_short in
42     true|yes|on|1) hide_short='!' ;;
43     *) hide_short='' ;;
44   esac
45 else
46   is-at-least 4.1 || hide_short='!'
51 _darcs_main() {
52 local DARCS=$words[1]
53 local arguments
54 local curcontext="$curcontext"
56 if (( CURRENT > 2 )); then
57     local cmd=${words[2]}
58     local var_cmd=cmd_${cmd//-/_}
59     curcontext="${curcontext%:*:*}:darcs-${cmd}:"
60     (( CURRENT-- ))
61     shift words
63     local short long arg desc action
64     short=()
65     long=()
66     arg=()
67     desc=()
68     action=()   
69     arguments=()
70     
71     # Collect all help lines which have a leading space.
72     local input
73     input=(${(f)"$($DARCS $cmd -h)"})
74     input=(${input:#[^\ ]*})
75     local i
76     for (( i=1 ; i <= ${#input} ; i++ )); do
77         # Assumption: the the argument descriptions from `darcs cmd -h` 
78         # have the following format:
79         # [spaces]<-f>[spaces][--flag]<=<spaces>argument>[spaces][description]
80         [[ "$input[i]" = (#b)' '#(-?|)' '#([^' ']#|)' '#(--[^=' ']#)(=([^' ']#)|)' '#(*) ]] \
81                 || _message -e messages "cannot parse command help output." || return 1
83         short[i]="$match[1]"
84         long[i]="$match[3]"
85         arg[i]="$match[5]"
86         desc[i]="$match[6]"
87         desc[i]="${${desc[i]//\[/\\[}//\]/\\]}" # escape brackets       
89         case $arg[i] in
90         DIRECTORY)
91           action[i]='_files -/' ;;
92         FILE|FILENAME|IDFILE|KEYS)
93           action[i]='_files' ;;
94         USERNAME)
95           action[i]='_users' ;;
96         EMAIL|FROM)
97           action[i]='_email_addresses' ;;
98         *)
99           action[i]='' ;;
100         esac
101     done
102     
103     # Compute the exludes for _arguments
105     local excluded short_excluded long_excluded k
107     for (( i=1 ; i <= ${#input} ; i++ )); do
108         excluded=()
109         for opt (${=excludes[$long[i]]}); do
110             k=$long[(i)$opt]
111             excluded=($excluded $short[k] $long[k])
112         done
114         # Generate arguments for _arguments.
115         # Make long and short options mutually exclusive.
116         short_excluded=($long[i] $excluded)
117         long_excluded=($short[i] $excluded)
118         [ $short[i] ] && arguments=("$arguments[@]"
119             "${hide_short}(${short_excluded})${short[i]}[${desc[i]}]${arg[i]:+:$arg[i]:$action[i]}")
120         [ $long[i] ] && arguments=("$arguments[@]"
121             "(${long_excluded})${long[i]}${arg[i]:+=}[${desc[i]}]${arg[i]:+:$arg[i]:$action[i]}")
122     done
124     arguments=("$arguments[@]" "${(@P)var_cmd-*:FILE:_files}")
125 else
126     local hline
127     local -a cmdlist
128     _call_program help-commands darcs --help | while read -A hline; do
129         (( ${#hline} < 2 )) && continue
130         [[ $hline[1] == darcs ]] && continue
131         [[ $hline[1] == Usage: ]] && continue
132         [[ $hline[1] == Use ]] && continue
133         cmdlist=( $cmdlist "${hline[1]}:${hline[2,-1]}" )
134      done
135     arguments=(':commands:(($cmdlist))')
138 _arguments -S -A '-*' \
139     "$arguments[@]"
145 # Command argument definitions
147 local -a cmd_initialize cmd_get
148 cmd_initialize=()
149 cmd_get=(':repository:_files -/' ':new repository name:_files -/')
151 local -a cmd_add cmd_remove cmd_move cmd_replace
152 cmd_add=('*:new files:_darcs_new_file_or_tree')
153 cmd_remove=('*:existing controlled files:_darcs_controlled_files -e')
154 cmd_move=('*:existing controlled files:_darcs_controlled_files -e')
155 cmd_replace=(':old token:' ':new token:' '*:existing controlled files:_darcs_controlled_files -e')
157 local -a cmd_record cmd_pull cmd_push cmd_send cmd_apply
158 cmd_record=('*:controlled files:_darcs_controlled_files')
159 cmd_pull=(':repository:_darcs_repository_or_tree')
160 cmd_push=(':repository:_darcs_repository_or_tree')
161 cmd_send=(':repository:_darcs_repository_or_tree')
162 cmd_apply=(':patch file:_files')
164 local -a cmd_whatsnew cmd_changes
165 cmd_whatsnew=('*:controlled files:_darcs_controlled_files')
166 cmd_changes=('*:controlled files:_darcs_controlled_files')
168 local -a cmd_tag cmd_setpref cmd_check cmd_optimize
169 cmd_tag=()
170 cmd_setpref=(':preference key:(test predist boringfile binaries)' ':value:_files')
171 cmd_check=()
172 cmd_optimize=()
174 local -a cmd_amend_record cmd_rollback cmd_unrecord cmd_unpull cmd_revert cmd_unrevert
175 cmd_amend_record=('*:controlled files:_darcs_controlled_files')
176 cmd_rollback=()
177 cmd_unrecord=()
178 cmd_unpull=()
179 cmd_revert=('*:controlled files:_darcs_controlled_files')
180 cmd_unrevert=()
182 local -a cmd_diff cmd_annotate
183 cmd_diff=('*:controlled files:_darcs_controlled_files')
184 cmd_annotate=('*:controlled files:_darcs_controlled_files')
186 local -a cmd_resolve cmd_dist cmd_trackdown cmd_repair
187 cmd_resolve=()
188 cmd_dist=()
189 cmd_trackdown=(':initialization:' ':command:')
190 cmd_repair=()
194 # Completion functions
197 (( $+functions[_darcs_new_files] )) ||
198 _darcs_new_files () {
199     local -a new_files
200     local -a local_files
201     local in_tree_head in_tree_tail
202     _darcs_make_tree_path in_tree_head in_tree_tail || return 1
203     new_files=(${(f)"$(cd $(_darcs_absolute_tree_root)/$in_tree_head; $DARCS whatsnew -sl .)"})  
204     new_files=(${${new_files:#[^a]*}//a /})
206     local_files=()
207     for file ($new_files); do
208         [[ $file:h = $in_tree_head && $file:t = ${in_tree_tail}* ]] && local_files+=$file:t
209     done
211     compset -P '*/'
212     _description new_files expl "new files"
213     compadd "$expl[@]" "$local_files[@]"
219 # _darcs_controlled_files [-e|r] [-f|d]
221 # Adds controlled files to the completion. Can take either
222 # -e or -r as flags which respectively only add the existing
223 # files or the deleted files. Can take either -f or -d which
224 # respectively add only the files or directories.
225 (( $+functions[_darcs_controlled_files] )) ||
226 _darcs_controlled_files() {
227     local abs_root=$(_darcs_absolute_tree_root)
228     local only_removed only_existing only_files only_dirs
229     zparseopts -E \
230         'r=only_removed' 'e=only_existing' \
231         'f=only_files' 'd=only_dirs'
233     local in_tree_head in_tree_tail
234     _darcs_make_tree_path in_tree_head in_tree_tail
235     local recorded_dir
236     if [[ -d $abs_root/_darcs/current ]]; then
237         recorded_dir="$abs_root/_darcs/current/$in_tree_head"
238     else
239         recorded_dir="$abs_root/_darcs/pristine/$in_tree_head"
240     fi
241     local -a controlled_files controlled_dirs existing_files existing_dirs 
242     local -a dir_display_strs removed_dir_display_strs
243     controlled_files=${(z)$(print $recorded_dir/$in_tree_tail*(.:t))}
244     controlled_dirs=${(z)$(print $recorded_dir/$in_tree_tail*(/:t))}
245     existing_files=() existing_dirs=()
246     removed_files=() removed_dirs=() 
247     dir_display_strs=() removed_dir_display_strs=()
248     local dir file
249     for dir ($controlled_dirs); do
250         if [[ -e $abs_root/$in_tree_head/$dir ]]; then
251             existing_dirs+="$dir"
252             dir_display_strs+="$dir/"   
253         else
254             removed_dirs+="$dir"
255             removed_dir_display_strs+="$dir/"
256         fi
257     done
258     for file ($controlled_files); do
259         if [[ -e $abs_root/$in_tree_head/$file ]]; then
260             existing_files+="$file"
261         else
262             removed_files+="$file"
263         fi
264     done
266     compset -P '*/'
267     if (( ! ${#only_removed} )); then 
268         _description controlled_files expl "existing revision controlled files"
269         (( ! ${#only_dirs} )) && compadd "$expl[@]" $existing_files
270         (( ! ${#only_files} )) \
271             && compadd "$expl[@]" -q -S / -d dir_display_strs -a -- existing_dirs
272     fi
273     if (( ! ${#only_existing} )); then
274         _description removed_files expl "removed revision controlled files"
275         (( ! ${#only_dirs} )) && compadd "$expl[@]" $removed_files
276         (( ! ${#only_files} )) \
277             && compadd "$expl[@]" -q -S / -d removed_dir_display_strs -a -- removed_dirs
278     fi
281 (( $+functions[_darcs_repositories] )) ||
282 _darcs_repositories() {
283     local local_repos_path="$(_darcs_absolute_tree_root)/_darcs/prefs/repos"
284     local global_repos_path="$HOME/.darcs/repos"
285     local -a local_repos global_repos
286     local -a global_repos
287     [[ -e $local_repos_path ]] && cat $local_repos_path | read -A local_repos
288     [[ -e $global_repos_path ]] && cat $global_repos_path | read -A global_repos
289     local_repos=${local_repos:# #}
290     global_repos=${global_repos:# #}
291     _description repositories expl "repositories"
292     (( ${#local_repos} )) && compadd "$expl[@]" -- "$local_repos[@]"
293     (( ${#global_repos} )) && compadd "$expl[@]" -- "$global_repos[@]"
298 # Combination completion functions
300 (( $+functions[_darcs_new_file_or_tree] )) ||
301 _darcs_new_file_or_tree() {
302     local base_dir=$( cd ${$(_darcs_repodir):-.}; pwd -P)
303     [[ -z $(_darcs_absolute_tree_root $base_dir) ]] && return 1
304     local -a ignored_files
305     ignored_files=(_darcs)
306     _alternative 'newfiles:new file:_darcs_new_files' \
307                  "directories:tree:_path_files -/ -W$base_dir -Fignored_files"
310 (( $+functions[_darcs_repository_or_tree] )) ||
311 _darcs_repository_or_tree() {
312     local -a ignored_files
313     ignored_files=(_darcs)
314     _alternative 'repository:repository:_darcs_repositories' \
315                  "directories:directories:_path_files -/ -Fignored_files"
320 # Mutually exclusive options 
323 typeset -A excludes
324 excludes=(
325 # Output
326     --summary                     '--no-summary'
327     --no-summary                  '--summary'
328     --context                     '          --xml-output --human-readable --unified'
329     --xml-output                  '--context              --human-readable --unified'
330     --human-readable              '--context --xml-output                  --unified'
331     --unified                     '--context --xml-output --human-readable          '
333 # Verbosity
334     --verbose                     '          --quiet --standard-verbosity'
335     --quiet                       '--verbose         --standard-verbosity'
336     --standard-verbosity          '--verbose --quiet                     '
338 # Traversal
339     --recursive                   '--not-recursive'
340     --not-recursive               '--recursive'
341     --look-for-adds               '--dont-look-for-adds'
342     --dont-look-for-adds          '--look-for-adds'
344 # Pattern
345     --from-match                  '             --from-patch --from-tag'
346     --from-patch                  '--from-match              --from-tag'
347     --from-tag                    '--from-patch --from-match           '
348     --to-match                    '           --to-patch -to-tag'
349     --to-patch                    '--to-match            -to-tag'
350     --to-tag                      '--to-match --to-patch        '
352 # Repository Properties
353     --plain-pristine-tree         '--no-pristine-tree'
354     --no-pristine-tree            '--plain-pristine-tree'
355     --parial                      '--complete'
356     --complete                    '--partial'
357     --compress                    '--dont-compress'
358     --dont-compress               '--compress'
359     --set-default                 '--no-set-default'
360     --no-set-default              '--set-default'
362 # Logs
363     --edit-long-comment           '--skip-long-comment --leave-test-directory'
364     --skip-long-comment           '--edit-long-comment --leave-test-directory'
365     --prompt-long-comment         '--edit-long-comment --skip-long-comment'
367 # Security
368     --sign                        '       --sign-as --sign-ssl --dont-sign'
369     --sign-as                     '--sign           --sign-ssl --dont-sign'
370     --sign-ssl                    '--sign --sign-as            --dont-sign'
371     --dont-sign                   '--sign --sign-as --sign-ssl            '
372     --verify                      '         --verify-ssl --no-verify'
373     --verify-ssl                  '--verify              --no-verify'
374     --no-verify                   '--verify --verify-ssl            '
375     --apply-as                    '--apply-as-myself'
376     --apply-as-myself             '--apply-as'
378 # Conflicts
379     --mark-conflicts              '--allow-conflicts --no-resolve-conflicts --dont-allow-conflicts'
380     --allow-conflicts             '--mark-conflicts --no-resolve-conflicts --dont-allow-conflicts'
381     --no-resolve-conflicts        '--mark-conflicts --allow-conflicts --dont-allow-conflicts'
382     --dont-allow-conflicts        '--mark-conflicts --allow-conflicts --no-resolve-conflicts '
384 # Test
385     --test                        '--no-test'
386     --no-test                     '--test'
387     --leave-test-directory        '--remove-test-directory'
388     --remove-test-directory       '--leave-test-directory'
390 # Misc
391     --force                       '--no-force'
392     --no-force                    '--force'
393     --ask-deps                    '--no-ask-deps'
394     --no-ask-deps                 '--ask-deps'
395     --date-trick                  '--no-date-trick'
396     --no-date-trick               '--date-trick'
397     --set-scripts-executable      '--dont-set-scripts-executable'
398     --dont-set-scripts-executable '--set-scripts-executable'
404 # Utility functions
407 # _darcs_make_tree_path in_tree_head_name in_tree_tail_name path
408 # Set in_tree_head_name in_tree_tail_name to the corresponding path
409 # parts from inside the current darcs tree.
410 _darcs_make_tree_path () {
411     [[ -z $3 || $3 = '.' ]] && 3=${PREFIX:-./}
412     local _in_tree=$(_darcs_path_from_root ${$(_darcs_repodir):-.}/$3)
413     [[ -z $_in_tree ]] && return 1
414     4='' 5=''
415     if [[ ${3[-1]} = / ]]; then 
416         4=$_in_tree
417     else
418         4=$_in_tree:h
419         [[ $_in_tree:t != . ]] && 5=$_in_tree:t
420     fi
421     set -A "$1" "$4"
422     set -A "$2" "$5"
425 _darcs_repodir () {
426     local index=$words[(i)--repodir*]
427     if (( index < CURRENT )); then
428         if [[ $words[$index] = --repodir ]]; then
429             (( index++ ))
430             print $words[$index]
431         else
432             print ${words[$index]#*=}
433         fi
434     fi
437 _darcs_absolute_tree_root () {
438     local root=$(_darcs_repodir)
439     [[ -z $root ]] && root=$(pwd -P)
440     while [[ ! $root -ef / ]]; do
441         [[ -d $root/_darcs ]] && break
442         root="$root/.."
443     done
444     [[ $root -ef / ]] || print $(cd $root; pwd -P)
447 _darcs_tree_root () {
448     local abs=$(_darcs_absolute_tree_root)
449     local rel=$(_darcs_relative_path $abs $(pwd -P))
450     [[ -n $abs ]] && print $rel
453 # _darcs_paths_from_root name paths
454 # Sets name to the paths relative to the darcs tree root.
455 # If no argument is given then the current directory
456 # is assumed.
457 _darcs_paths_from_root () {
458     local name=$1
459     abs=$(_darcs_absolute_tree_root)
460     [[ -z $abs ]] && set -A "$name" && return 1
461     shift
462     1=${1:=$PWD}
463     local -a subpaths
464     _darcs_filter_for_subpaths subpaths $abs $*
465     local i
466     for (( i=1; i<=${#subpaths}; i++ )); do
467         [[ $subpaths[$i] != '.' ]] && subpaths[$i]="./$subpaths[$i]"
468     done
469     set -A "$name" "$subpaths[@]"
472 _darcs_path_from_root() {
473     local path
474     _darcs_paths_from_root path $1
475     [[ -n $path ]] && print "$path"
478 # _darcs_filter_for_in_dir name dir paths
479 # Sets name to the relative paths from dir to the given paths which 
480 # traverse dir. It ignores paths which are not in dir.
481 _darcs_filter_for_subpaths () {
482     local name=$1 dir=$2 
483     shift 2
484     local p rel
485     local -a accepted_paths 
486     accepted_paths=()
487     for p; do
488         rel=$(_darcs_path_difference $p $dir)
489         [[ -n $rel ]] && accepted_paths+="$rel"
490     done
491     set -A "$name" "$accepted_paths[@]"
494 # _darcs_path_difference p1 p2
495 # Print the path from p2 to p1. If p2 is not an ancestor of p1 then it 
496 # prints a blank string. If they point to the same directory then it returns
497 # a single period. p2 needs to be a directory path.
498 _darcs_path_difference () {
499     local diff=$(_darcs_relative_path $1 $2)
500     [[ ${diff%%/*} != .. ]] && print $diff || return 1
504 # Print the a relative path from the second directory to the first,
505 # defaulting the second directory to $PWD if none is specified.
506 # Taken from the zsh mailing list.
507 _darcs_relative_path () {
508     2=${2:=$PWD}
509     [[ -d $2 && -d $1:h ]] || return 1
510     [[ ! -d $1 ]] && 3=$1:t 1=$1:h
511     1=$(cd $1; pwd -P)
512     2=$(cd $2; pwd -P)
513     [[ $1 -ef $2 ]] && print ${3:-.} && return
515     local -a cur abs
516     cur=(${(s:/:)2})        # Split 'current' directory into cur
517     abs=(${(s:/:)1} $3)     # Split target directory into abs
519     # Compute the length of the common prefix, or discover a subdiretory:
520     integer i=1
521     while [[ i -le $#abs && $abs[i] == $cur[i] ]]
522     do
523         ((++i > $#cur)) && print ${(j:/:)abs[i,-1]} && return
524     done
526     2=${(j:/:)cur[i,-1]/*/..}       # Up to the common prefix directory and
527     1=${(j:/:)abs[i,-1]}            # down to the target directory or file
529     print $2${1:+/$1}
532 # Code to make sure _darcs is run when we load it
533 _darcs_main "$@"