#compdef darcs # The Z Shell is copyright (c) 1992-2004 Paul Falstad, Richard Coleman, # Zoltán Hidvégi, Andrew Main, Peter Stephenson, Sven Wischnowsky, and # others. All rights reserved. Individual authors, whether or not # specifically named, retain copyright in all changes; in what follows, they # are referred to as `the Zsh Development Group'. This is for convenience # only and this body has no legal status. The Z shell is distributed under # the following licence; any provisions made in individual files take # precedence. # # Permission is hereby granted, without written agreement and without # licence or royalty fees, to use, copy, modify, and distribute this # software and to distribute modified versions of this software for any # purpose, provided that the above copyright notice and the following # two paragraphs appear in all copies of this software. # # In no event shall the Zsh Development Group be liable to any party for # direct, indirect, special, incidental, or consequential damages arising out # of the use of this software and its documentation, even if the Zsh # Development Group have been advised of the possibility of such damage. # # The Zsh Development Group specifically disclaim any warranties, including, # but not limited to, the implied warranties of merchantability and fitness # for a particular purpose. The software provided hereunder is on an "as is" # basis, and the Zsh Development Group have no obligation to provide # maintenance, support, updates, enhancements, or modifications. # This completion module is based on section 6.8 of 'A User's Guide to the Z-Shell' # by Peter Stephenson and on the tla completion module by Jason McCarty. # EXTENDED_GLOB is required fr pattern backreferences. setopt EXTENDED_GLOB local DARCS=$words[1] # test whether to hide short options from completion autoload is-at-least local hide_short if zstyle -s ":completion:${curcontext}" hide-shortopts hide_short; then case $hide_short in true|yes|on|1) hide_short='!' ;; *) hide_short='' ;; esac else is-at-least 4.1 || hide_short='!' fi _darcs_main() { local DARCS=$words[1] local arguments local curcontext="$curcontext" if (( CURRENT > 2 )); then local cmd=${words[2]} local var_cmd=cmd_${cmd//-/_} curcontext="${curcontext%:*:*}:darcs-${cmd}:" (( CURRENT-- )) shift words local short long arg desc action short=() long=() arg=() desc=() action=() arguments=() # Collect all help lines which have a leading space. local input input=(${(f)"$($DARCS $cmd -h)"}) input=(${input:#[^\ ]*}) local i for (( i=1 ; i <= ${#input} ; i++ )); do # Assumption: the the argument descriptions from `darcs cmd -h` # have the following format: # [spaces]<-f>[spaces][--flag]<=argument>[spaces][description] [[ "$input[i]" = (#b)' '#(-?|)' '#([^' ']#|)' '#(--[^=' ']#)(=([^' ']#)|)' '#(*) ]] \ || _message -e messages "cannot parse command help output." || return 1 short[i]="$match[1]" long[i]="$match[3]" arg[i]="$match[5]" desc[i]="$match[6]" desc[i]="${${desc[i]//\[/\\[}//\]/\\]}" # escape brackets case $arg[i] in DIRECTORY) action[i]='_files -/' ;; FILE|FILENAME|IDFILE|KEYS) action[i]='_files' ;; USERNAME) action[i]='_users' ;; EMAIL|FROM) action[i]='_email_addresses' ;; *) action[i]='' ;; esac done # Compute the exludes for _arguments local excluded short_excluded long_excluded k for (( i=1 ; i <= ${#input} ; i++ )); do excluded=() for opt (${=excludes[$long[i]]}); do k=$long[(i)$opt] excluded=($excluded $short[k] $long[k]) done # Generate arguments for _arguments. # Make long and short options mutually exclusive. short_excluded=($long[i] $excluded) long_excluded=($short[i] $excluded) [ $short[i] ] && arguments=("$arguments[@]" "${hide_short}(${short_excluded})${short[i]}[${desc[i]}]${arg[i]:+:$arg[i]:$action[i]}") [ $long[i] ] && arguments=("$arguments[@]" "(${long_excluded})${long[i]}${arg[i]:+=}[${desc[i]}]${arg[i]:+:$arg[i]:$action[i]}") done arguments=("$arguments[@]" "${(@P)var_cmd-*:FILE:_files}") else local hline local -a cmdlist _call_program help-commands darcs --help | while read -A hline; do (( ${#hline} < 2 )) && continue [[ $hline[1] == darcs ]] && continue [[ $hline[1] == Usage: ]] && continue [[ $hline[1] == Use ]] && continue cmdlist=( $cmdlist "${hline[1]}:${hline[2,-1]}" ) done arguments=(':commands:(($cmdlist))') fi _arguments -S -A '-*' \ "$arguments[@]" } # # Command argument definitions # local -a cmd_initialize cmd_get cmd_initialize=() cmd_get=(':repository:_files -/' ':new repository name:_files -/') local -a cmd_add cmd_remove cmd_move cmd_replace cmd_add=('*:new files:_darcs_new_file_or_tree') cmd_remove=('*:existing controlled files:_darcs_controlled_files -e') cmd_move=('*:existing controlled files:_darcs_controlled_files -e') cmd_replace=(':old token:' ':new token:' '*:existing controlled files:_darcs_controlled_files -e') local -a cmd_record cmd_pull cmd_push cmd_send cmd_apply cmd_record=('*:controlled files:_darcs_controlled_files') cmd_pull=(':repository:_darcs_repository_or_tree') cmd_push=(':repository:_darcs_repository_or_tree') cmd_send=(':repository:_darcs_repository_or_tree') cmd_apply=(':patch file:_files') local -a cmd_whatsnew cmd_changes cmd_whatsnew=('*:controlled files:_darcs_controlled_files') cmd_changes=('*:controlled files:_darcs_controlled_files') local -a cmd_tag cmd_setpref cmd_check cmd_optimize cmd_tag=() cmd_setpref=(':preference key:(test predist boringfile binaries)' ':value:_files') cmd_check=() cmd_optimize=() local -a cmd_amend_record cmd_rollback cmd_unrecord cmd_unpull cmd_revert cmd_unrevert cmd_amend_record=('*:controlled files:_darcs_controlled_files') cmd_rollback=() cmd_unrecord=() cmd_unpull=() cmd_revert=('*:controlled files:_darcs_controlled_files') cmd_unrevert=() local -a cmd_diff cmd_annotate cmd_diff=('*:controlled files:_darcs_controlled_files') cmd_annotate=('*:controlled files:_darcs_controlled_files') local -a cmd_resolve cmd_dist cmd_trackdown cmd_repair cmd_resolve=() cmd_dist=() cmd_trackdown=(':initialization:' ':command:') cmd_repair=() # # Completion functions # (( $+functions[_darcs_new_files] )) || _darcs_new_files () { local -a new_files local -a local_files local in_tree_head in_tree_tail _darcs_make_tree_path in_tree_head in_tree_tail || return 1 new_files=(${(f)"$(cd $(_darcs_absolute_tree_root)/$in_tree_head; $DARCS whatsnew -sl .)"}) new_files=(${${new_files:#[^a]*}//a /}) local_files=() for file ($new_files); do [[ $file:h = $in_tree_head && $file:t = ${in_tree_tail}* ]] && local_files+=$file:t done compset -P '*/' _description new_files expl "new files" compadd "$expl[@]" "$local_files[@]" } # _darcs_controlled_files [-e|r] [-f|d] # # Adds controlled files to the completion. Can take either # -e or -r as flags which respectively only add the existing # files or the deleted files. Can take either -f or -d which # respectively add only the files or directories. (( $+functions[_darcs_controlled_files] )) || _darcs_controlled_files() { local abs_root=$(_darcs_absolute_tree_root) local only_removed only_existing only_files only_dirs zparseopts -E \ 'r=only_removed' 'e=only_existing' \ 'f=only_files' 'd=only_dirs' local in_tree_head in_tree_tail _darcs_make_tree_path in_tree_head in_tree_tail local recorded_dir if [[ -d $abs_root/_darcs/current ]]; then recorded_dir="$abs_root/_darcs/current/$in_tree_head" else recorded_dir="$abs_root/_darcs/pristine/$in_tree_head" fi local -a controlled_files controlled_dirs existing_files existing_dirs local -a dir_display_strs removed_dir_display_strs controlled_files=${(z)$(print $recorded_dir/$in_tree_tail*(.:t))} controlled_dirs=${(z)$(print $recorded_dir/$in_tree_tail*(/:t))} existing_files=() existing_dirs=() removed_files=() removed_dirs=() dir_display_strs=() removed_dir_display_strs=() local dir file for dir ($controlled_dirs); do if [[ -e $abs_root/$in_tree_head/$dir ]]; then existing_dirs+="$dir" dir_display_strs+="$dir/" else removed_dirs+="$dir" removed_dir_display_strs+="$dir/" fi done for file ($controlled_files); do if [[ -e $abs_root/$in_tree_head/$file ]]; then existing_files+="$file" else removed_files+="$file" fi done compset -P '*/' if (( ! ${#only_removed} )); then _description controlled_files expl "existing revision controlled files" (( ! ${#only_dirs} )) && compadd "$expl[@]" $existing_files (( ! ${#only_files} )) \ && compadd "$expl[@]" -q -S / -d dir_display_strs -a -- existing_dirs fi if (( ! ${#only_existing} )); then _description removed_files expl "removed revision controlled files" (( ! ${#only_dirs} )) && compadd "$expl[@]" $removed_files (( ! ${#only_files} )) \ && compadd "$expl[@]" -q -S / -d removed_dir_display_strs -a -- removed_dirs fi } (( $+functions[_darcs_repositories] )) || _darcs_repositories() { local local_repos_path="$(_darcs_absolute_tree_root)/_darcs/prefs/repos" local global_repos_path="$HOME/.darcs/repos" local -a local_repos global_repos local -a global_repos [[ -e $local_repos_path ]] && cat $local_repos_path | read -A local_repos [[ -e $global_repos_path ]] && cat $global_repos_path | read -A global_repos local_repos=${local_repos:# #} global_repos=${global_repos:# #} _description repositories expl "repositories" (( ${#local_repos} )) && compadd "$expl[@]" -- "$local_repos[@]" (( ${#global_repos} )) && compadd "$expl[@]" -- "$global_repos[@]" } # Combination completion functions (( $+functions[_darcs_new_file_or_tree] )) || _darcs_new_file_or_tree() { local base_dir=$( cd ${$(_darcs_repodir):-.}; pwd -P) [[ -z $(_darcs_absolute_tree_root $base_dir) ]] && return 1 local -a ignored_files ignored_files=(_darcs) _alternative 'newfiles:new file:_darcs_new_files' \ "directories:tree:_path_files -/ -W$base_dir -Fignored_files" } (( $+functions[_darcs_repository_or_tree] )) || _darcs_repository_or_tree() { local -a ignored_files ignored_files=(_darcs) _alternative 'repository:repository:_darcs_repositories' \ "directories:directories:_path_files -/ -Fignored_files" } # # Mutually exclusive options # typeset -A excludes excludes=( # Output --summary '--no-summary' --no-summary '--summary' --context ' --xml-output --human-readable --unified' --xml-output '--context --human-readable --unified' --human-readable '--context --xml-output --unified' --unified '--context --xml-output --human-readable ' # Verbosity --verbose ' --quiet --standard-verbosity' --quiet '--verbose --standard-verbosity' --standard-verbosity '--verbose --quiet ' # Traversal --recursive '--not-recursive' --not-recursive '--recursive' --look-for-adds '--dont-look-for-adds' --dont-look-for-adds '--look-for-adds' # Pattern --from-match ' --from-patch --from-tag' --from-patch '--from-match --from-tag' --from-tag '--from-patch --from-match ' --to-match ' --to-patch -to-tag' --to-patch '--to-match -to-tag' --to-tag '--to-match --to-patch ' # Repository Properties --plain-pristine-tree '--no-pristine-tree' --no-pristine-tree '--plain-pristine-tree' --parial '--complete' --complete '--partial' --compress '--dont-compress' --dont-compress '--compress' --set-default '--no-set-default' --no-set-default '--set-default' # Logs --edit-long-comment '--skip-long-comment --leave-test-directory' --skip-long-comment '--edit-long-comment --leave-test-directory' --prompt-long-comment '--edit-long-comment --skip-long-comment' # Security --sign ' --sign-as --sign-ssl --dont-sign' --sign-as '--sign --sign-ssl --dont-sign' --sign-ssl '--sign --sign-as --dont-sign' --dont-sign '--sign --sign-as --sign-ssl ' --verify ' --verify-ssl --no-verify' --verify-ssl '--verify --no-verify' --no-verify '--verify --verify-ssl ' --apply-as '--apply-as-myself' --apply-as-myself '--apply-as' # Conflicts --mark-conflicts '--allow-conflicts --no-resolve-conflicts --dont-allow-conflicts' --allow-conflicts '--mark-conflicts --no-resolve-conflicts --dont-allow-conflicts' --no-resolve-conflicts '--mark-conflicts --allow-conflicts --dont-allow-conflicts' --dont-allow-conflicts '--mark-conflicts --allow-conflicts --no-resolve-conflicts ' # Test --test '--no-test' --no-test '--test' --leave-test-directory '--remove-test-directory' --remove-test-directory '--leave-test-directory' # Misc --force '--no-force' --no-force '--force' --ask-deps '--no-ask-deps' --no-ask-deps '--ask-deps' --date-trick '--no-date-trick' --no-date-trick '--date-trick' --set-scripts-executable '--dont-set-scripts-executable' --dont-set-scripts-executable '--set-scripts-executable' ) # # Utility functions # # _darcs_make_tree_path in_tree_head_name in_tree_tail_name path # Set in_tree_head_name in_tree_tail_name to the corresponding path # parts from inside the current darcs tree. _darcs_make_tree_path () { [[ -z $3 || $3 = '.' ]] && 3=${PREFIX:-./} local _in_tree=$(_darcs_path_from_root ${$(_darcs_repodir):-.}/$3) [[ -z $_in_tree ]] && return 1 4='' 5='' if [[ ${3[-1]} = / ]]; then 4=$_in_tree else 4=$_in_tree:h [[ $_in_tree:t != . ]] && 5=$_in_tree:t fi set -A "$1" "$4" set -A "$2" "$5" } _darcs_repodir () { local index=$words[(i)--repodir*] if (( index < CURRENT )); then if [[ $words[$index] = --repodir ]]; then (( index++ )) print $words[$index] else print ${words[$index]#*=} fi fi } _darcs_absolute_tree_root () { local root=$(_darcs_repodir) [[ -z $root ]] && root=$(pwd -P) while [[ ! $root -ef / ]]; do [[ -d $root/_darcs ]] && break root="$root/.." done [[ $root -ef / ]] || print $(cd $root; pwd -P) } _darcs_tree_root () { local abs=$(_darcs_absolute_tree_root) local rel=$(_darcs_relative_path $abs $(pwd -P)) [[ -n $abs ]] && print $rel } # _darcs_paths_from_root name paths # Sets name to the paths relative to the darcs tree root. # If no argument is given then the current directory # is assumed. _darcs_paths_from_root () { local name=$1 abs=$(_darcs_absolute_tree_root) [[ -z $abs ]] && set -A "$name" && return 1 shift 1=${1:=$PWD} local -a subpaths _darcs_filter_for_subpaths subpaths $abs $* local i for (( i=1; i<=${#subpaths}; i++ )); do [[ $subpaths[$i] != '.' ]] && subpaths[$i]="./$subpaths[$i]" done set -A "$name" "$subpaths[@]" } _darcs_path_from_root() { local path _darcs_paths_from_root path $1 [[ -n $path ]] && print "$path" } # _darcs_filter_for_in_dir name dir paths # Sets name to the relative paths from dir to the given paths which # traverse dir. It ignores paths which are not in dir. _darcs_filter_for_subpaths () { local name=$1 dir=$2 shift 2 local p rel local -a accepted_paths accepted_paths=() for p; do rel=$(_darcs_path_difference $p $dir) [[ -n $rel ]] && accepted_paths+="$rel" done set -A "$name" "$accepted_paths[@]" } # _darcs_path_difference p1 p2 # Print the path from p2 to p1. If p2 is not an ancestor of p1 then it # prints a blank string. If they point to the same directory then it returns # a single period. p2 needs to be a directory path. _darcs_path_difference () { local diff=$(_darcs_relative_path $1 $2) [[ ${diff%%/*} != .. ]] && print $diff || return 1 } # Print the a relative path from the second directory to the first, # defaulting the second directory to $PWD if none is specified. # Taken from the zsh mailing list. _darcs_relative_path () { 2=${2:=$PWD} [[ -d $2 && -d $1:h ]] || return 1 [[ ! -d $1 ]] && 3=$1:t 1=$1:h 1=$(cd $1; pwd -P) 2=$(cd $2; pwd -P) [[ $1 -ef $2 ]] && print ${3:-.} && return local -a cur abs cur=(${(s:/:)2}) # Split 'current' directory into cur abs=(${(s:/:)1} $3) # Split target directory into abs # Compute the length of the common prefix, or discover a subdiretory: integer i=1 while [[ i -le $#abs && $abs[i] == $cur[i] ]] do ((++i > $#cur)) && print ${(j:/:)abs[i,-1]} && return done 2=${(j:/:)cur[i,-1]/*/..} # Up to the common prefix directory and 1=${(j:/:)abs[i,-1]} # down to the target directory or file print $2${1:+/$1} } # Code to make sure _darcs is run when we load it _darcs_main "$@"