mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-11-19 21:44:11 +01:00
506 lines
16 KiB
Plaintext
506 lines
16 KiB
Plaintext
#compdef darcs
|
|
|
|
# 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() {
|
|
# Based on section 6.8 of _A User's Guide to the Z-Shell_ by Peter Stephenson.
|
|
# Also based on the tla completion module by Jason McCarty. How do I credit
|
|
# this?
|
|
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]<=<spaces>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="$abs_root/_darcs/current/$in_tree_head"
|
|
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 "$@"
|
|
|
|
|
|
|