Index: bash_completion
===================================================================
--- bash_completion	(revision 17272)
+++ bash_completion	(working copy)
@@ -10,12 +10,18 @@
 
 shopt -s extglob
 
+# this completion guides the command/option order along the one suggested 
+# by "svn help", although other syntaxes are allowed.
+# env SVN_FILE_PROPS: other properties on files/directories
+# env SVN_REV_PROPS: other properties on revisions
 _svn()
 {
 	local cur cmds cmdOpts pOpts mOpts rOpts qOpts nOpts optsParam opt
-	local helpCmds optBase i
 
 	COMPREPLY=()
+	# remove ":" separator to handle svn:* property names properly.
+	# must be put back later, otherwise no more completion in file:// urls
+	COMP_WORDBREAKS=${COMP_WORDBREAKS/:/}
 	cur=${COMP_WORDS[COMP_CWORD]}
 
 	# Possible expansions, without pure-prefix abbreviations such as "up".
@@ -26,11 +32,6 @@
               proplist plist propset pset resolved revert \
               status switch unlock update --version'
 
-	if [[ $COMP_CWORD -eq 1 ]] ; then
-		COMPREPLY=( $( compgen -W "$cmds" -- $cur ) )
-		return 0
-	fi
-
 	# options that require a parameter
 	# note: continued lines must end '|' continuing lines must start '|'
 	optsParam="-r|--revision|--username|--password|--targets|
@@ -38,24 +39,241 @@
 	           |--diff-cmd|--diff3-cmd|--editor-cmd|--old|--new|
 	           |--config-dir|--native-eol|--limit"
 
-	# if not typing an option, or if the previous option required a
-	# parameter, then fallback on ordinary filename expansion
-	helpCmds='help|--help|h|\?'
-	if [[ ${COMP_WORDS[1]} != @($helpCmds) ]] && \
-	   [[ "$cur" != -* ]] || \
-	   [[ ${COMP_WORDS[COMP_CWORD-1]} == @($optsParam) ]] ; then
+	# svn:* and other (env SVN_FILE_PROPS and SVN_REV_PROPS) properties
+	local svnProps revProps allProps psCmds propCmds
+
+	# svn file properties
+	svnProps="svn:keywords svn:executable svn:needs-lock svn:externals"
+	svnProps="svn:ignore svn:eol-style svn:mime-type $svnProps"
+	[ "$SVN_FILE_PROPS" ] && svnProps="$svnProps $SVN_FILE_PROPS"
+
+	# svn revision properties
+	revProps="svn:author svn:log svn:date"
+	[ "$SVN_REV_PROPS" ] && revProps="$revProps $SVN_REV_PROPS"
+
+	# all properties
+	allProps="$svnProps $revProps"
+
+	# subcommands that expect property names
+	psCmds='propset|pset|ps'
+	propCmds="$psCmds|propget|pget|pg|propedit|pedit|pe|propdel|pdel|pd"
+	
+	# commands which only expect two arguments
+	# diff has some variant with two arguments, but not all of them.
+	# merge/switch have two or three args variants.
+	twoArgsCmds='copy|cp|move|mv|export|import'
+
+	# do parse arguments and set various variables about what was found
+	local cmd= isPropCmd= isPsCmd= isHelpCmd= isTwoArgsCmd=
+	local prev= help= prop= val= isRevProp= last='none' nargs=0
+	local options= hasRevPropOpt= hasRevisionOpt= 
+
+	for opt in "${COMP_WORDS[@]}"
+	do
+	    # FIRST must be the "svn" command
+	  [ $last = 'none' ] && { last='first'; continue ; }
+
+	    # SKIP option arguments
+	    if [[ $prev == @($optsParam) ]] ; then
+		prev=''
+		last='skip'
+		continue ;
+	    fi
+	    prev=$opt
+
+	    # get the subCoMmanD
+	    if [[ ! $cmd && $opt && ( $opt != -* || $opt = '--version' ) ]]
+            then
+		cmd=$opt
+		[[ $cmd == @($propCmds) ]] && isPropCmd=1
+		[[ $cmd == @($psCmds) ]] && isPsCmd=1
+		[[ $cmd == @($twoArgsCmds) ]] && isTwoArgsCmd=1
+		[[ $cmd = 'help' ]] && isHelpCmd=1
+		last='cmd'
+	        # HELP about a command asked with an option
+		if [[ $isHelpCmd && $cmd && $cmd != 'help' && ! $help ]]
+		then 
+		    help=$cmd
+		    cmd='help'
+		fi
+		continue
+	    fi
+
+	    # HELP about a command
+	    if [[ $isHelpCmd && ! $help && $opt && $opt != -* ]]
+	    then
+		help=$opt
+		last='help'
+		continue
+	    fi
+	    
+	    # PROPerty name
+	    if [[ $isPropCmd && ! $prop && $opt && $opt != -* ]] 
+	    then
+		prop=$opt
+		[[ $prop == @(${allProps// /|}) ]] && isSvnProp=1
+		[[ $prop == @(${revProps// /|}) ]] && isRevProp=1
+		last='prop'
+		continue
+	    fi
+
+	    # property VALue
+	    if [[ $isPsCmd && $prop && ! $val && $opt != -* ]] ;
+	    then
+		val=$opt
+		last='val'
+		continue
+	    fi
+
+	    if [[ $last != 'arg' ]]
+	    then
+	      # more OPTions
+	      case $opt in
+		  -r|--revision|--revision=*) 
+		      hasRevisionOpt=1 
+		      ;;
+		  --revprop) 
+		      hasRevPropOpt=1
+		    # restrict to revision properties
+		      allProps=$revProps
+		      ;;
+		  -h|--help)
+		      isHelpCmd=1
+		      ;;
+		  -F|--file)
+		      val='-F'
+		      ;;
+	      esac
+	      
+	      # no more options?
+	      if [[ $opt = '--' ]] ; then
+		  last='arg'
+		  break
+	      fi
+	      
+	      # options are recorded...
+	      if [[ $opt == -* ]] ; then
+		  options="$options $opt "
+		  last='opt'
+		  continue
+	      fi
+	    fi
+
+	    # then we have only arguments
+	    last='arg'
+	    let nargs++
+	done
+
+	# suggest all subcommands
+	if [[ ! $cmd || $last = 'cmd' || $last = 'help' ||
+	      ( $isHelpCmd && ! $help ) ]]
+	then
+	    COMPREPLY=( $( compgen -W "$cmds" -- $cur ) )
+	    COMP_WORDBREAKS="${COMP_WORDBREAKS}:"
+	    return 0
+	fi
+
+	# help about option arguments
+	if [[ $last = 'skip' ]]
+	then
+            # some special partial help about --revision option.
+	    if [[ ${COMP_WORDS[COMP_CWORD-1]} = '--revision' ]]
+	    then
+		COMPREPLY=( $( compgen -W "HEAD BASE PREV COMMITTED 0 {" \
+			       -- $cur ) )
+		COMP_WORDBREAKS="${COMP_WORDBREAKS}:"
 		return 0
+	    fi
+
+	    # TODO: provide help about other option arguments, such as:
+	    # --encoding --author --old --new --native-eol --limit
+
+	    # if the previous option required a parameter,
+	    # then fallback on ordinary filename expansion
+	    COMP_WORDBREAKS="${COMP_WORDBREAKS}:"
+	    return 0
 	fi
 
+	# provide allowed property names after property commands
+	if [[ $isPropCmd && ( ! $prop || $last = 'prop' ) && $cur != -* ]]
+	then
+	    COMPREPLY=( $( compgen -W "$allProps" -- $cur ) )
+	    # DO NOT PUT ":" BACK IN COMP_WORDBREAKS HERE
+	    return 0
+	fi
+
+	# force --revprop option on revision properties
+	if [[ $isRevProp && ! $hasRevPropOpt ]]
+	then
+	    COMPREPLY=( $( compgen -W '--revprop' -- $cur ) )
+	    COMP_WORDBREAKS="${COMP_WORDBREAKS}:"
+	    return 0
+	fi
+
+	# force --revision option on revision properties
+	if [[ $isRevProp && $hasRevPropOpt && ! $hasRevisionOpt ]]
+	then
+	    COMPREPLY=( $( compgen -W '--revision' -- $cur ) )
+	    COMP_WORDBREAKS="${COMP_WORDBREAKS}:"
+	    return 0
+	fi
+
+	# possible completion values for properties
+	if [[ $isPsCmd && $prop && ( ! $val || $last = 'val' ) && $cur != -* ]] 
+	then
+	    # ' is a reminder for an arbitrary value
+	    local values="\' --file"
+	    case $prop in
+		svn:keywords)
+		    # just a subset?
+		    values="Id Rev URL Date Author \'"
+		    ;;
+		svn:executable|svn:needs-lock)
+		    # hmmm... canonical value * is special to the shell.
+		    values='\\*'
+		    ;;
+		svn:eol-style) 
+		    values='native LF CR CRLF'
+		    ;;
+		svn:mime-type) 
+		    # could read /etc/mime.types if available. overkill.
+		    values='text/ text/plain text/html text/xml text/rtf 
+                       image/ image/png image/gif image/jpeg image/tiff
+                       audio/ audio/midi audio/mpeg
+                       video/ video/mpeg video/mp4 
+                       application/ application/octet-stream'
+		    ;;
+	    esac
+
+	    COMPREPLY=( $( compgen -W "$values" -- $cur ) )
+	    COMP_WORDBREAKS="${COMP_WORDBREAKS}:"
+	    return 0
+	fi
+
+	# copy expects two arguments
+	if [[ $isTwoArgsCmd && $nargs -gt 2 ]] ; then
+	    # some way to tell 'no completion'... is there a better one?
+	    COMPREPLY=( '' )
+	    COMP_WORDBREAKS="${COMP_WORDBREAKS}:"
+	    return 0
+	fi
+
+	# if not typing an option,
+	# then fallback on ordinary filename expansion
+	if [[ $cur != -* ]]  ; then
+	        COMP_WORDBREAKS="${COMP_WORDBREAKS}:"
+		return 0
+	fi
+
+	# otherwise build possible options for the command
 	pOpts="--username --password --no-auth-cache --non-interactive"
 	mOpts="-m --message -F --file --encoding --force-log"
 	rOpts="-r --revision"
 	qOpts="-q --quiet"
 	nOpts="-N --non-recursive"
 
-	# possible options for the command
 	cmdOpts=
-	case ${COMP_WORDS[1]} in
+	case $cmd in
 	--version)
 		cmdOpts="$qOpts"
 		;;
@@ -94,7 +312,7 @@
                          --ignore-externals"
 		;;
 	help|h|\?)
-		cmdOpts="$cmds $qOpts"
+		cmdOpts="$qOpts"
 		;;
 	import)
 		cmdOpts="--auto-props --no-auto-props $mOpts $qOpts $nOpts \
@@ -126,22 +344,25 @@
 		cmdOpts="$mOpts $rOpts $qOpts --force --editor-cmd $pOpts"
 		;;
 	propdel|pdel|pd)
-		cmdOpts="$qOpts -R --recursive $rOpts --revprop $pOpts"
+		cmdOpts="$qOpts -R --recursive $rOpts $pOpts"
+		[[ $isRevProp || ! $prop ]] && cmdOpts="$cmdOpts --revprop"
 		;;
 	propedit|pedit|pe)
-		cmdOpts="$rOpts --revprop --encoding --editor-cmd $pOpts \
-		         --force"
+		cmdOpts="$rOpts --encoding --editor-cmd $pOpts --force"
+		[[ $isRevProp || ! $prop ]] && cmdOpts="$cmdOpts --revprop"
 		;;
 	propget|pget|pg)
-		cmdOpts="-R --recursive $rOpts --revprop --strict $pOpts"
+	        cmdOpts="-R --recursive $rOpts --strict $pOpts"
+		[[ $isRevProp || ! $prop ]] && cmdOpts="$cmdOpts --revprop"
 		;;
 	proplist|plist|pl)
 		cmdOpts="-v --verbose -R --recursive $rOpts --revprop $qOpts \
 		         $pOpts"
 		;;
 	propset|pset|ps)
-		cmdOpts="-F --file $qOpts --targets -R --recursive --revprop \
+		cmdOpts="-F --file $qOpts --targets -R --recursive \
 		         --encoding $pOpts $rOpts --force"
+		[[ $isRevProp || ! $prop ]] && cmdOpts="$cmdOpts --revprop"
 		;;
 	resolved)
 		cmdOpts="--targets -R --recursive $qOpts"
@@ -170,9 +391,11 @@
 	cmdOpts="$cmdOpts --help -h --config-dir"
 
 	# take out options already given
-	for (( i=2; i<=$COMP_CWORD-1; ++i )) ; do
-		opt=${COMP_WORDS[$i]}
+	for opt in $options
+	do
+	        local optBase
 
+		# remove leading dashes and arguments
 		case $opt in
 		--*)    optBase=${opt/=*/} ;;
 		-*)     optBase=${opt:0:2} ;;
@@ -208,14 +431,16 @@
 			;;
 		esac
 
-		# skip next option if this one requires a parameter
-		if [[ $opt == @($optsParam) ]] ; then
-			((++i))
+		# remove help options within help subcommand
+		if [ $isHelpCmd ] ; then
+		    cmdOpts=${cmdOpts/ -h / }
+		    cmdOpts=${cmdOpts/ --help / }
 		fi
 	done
 
+	# provide help about available options
 	COMPREPLY=( $( compgen -W "$cmdOpts" -- $cur ) )
-
+	COMP_WORDBREAKS="${COMP_WORDBREAKS}:"
 	return 0
 }
 complete -F _svn -o default svn
