source: dasscm/trunk/etc/bash_completion.d/dasscm.sh @ 881

Last change on this file since 881 was 881, checked in by joergs, 11 years ago

initial, from /usr/share/subversion/tools/client-side/bash_completion

File size: 29.0 KB
Line 
1# Programmable completion for the Subversion svn command under bash. Source
2# this file (or on some systems add it to ~/.bash_completion and start a new
3# shell) and bash's completion mechanism will know all about svn's options!
4# Provides completion for the svnadmin, svndumpfilter, svnlook and svnsync
5# commands as well.  Who wants to read man pages/help text...
6
7# Known to work with bash 3.* with programmable completion and extended
8# pattern matching enabled (use 'shopt -s extglob progcomp' to enable
9# these if they are not already enabled).
10
11shopt -s extglob
12
13# Tree helper functions which only use bash, to ease readability.
14
15# look for value associated to key from stdin in K/V hash file format
16# val=$(_svn_read_hashfile svn:realmstring < some/file)
17function _svn_read_hashfile()
18{
19  local tkey=$1 key= val=
20  while true; do
21    read tag len
22    [ $tag = 'END' ] && break
23    [ $tag != 'K' ] && {
24      #echo "unexpected tag '$tag' instead of 'K'" >&2
25      return
26    }
27    read -r -n $len key ; read
28    read tag len
29    [ $tag != 'V' ] && {
30      #echo "unexpected tag '$tag' instead of 'V'" >&2
31      return
32    }
33    read -r -n $len val ; read
34    if [[ $key = $tkey ]] ; then
35      echo "$val"
36      return
37    fi
38  done
39  #echo "target key '$tkey' not found" >&2
40}
41
42# _svn_grcut shell-regular-expression
43# extract filenames from 'svn status' output
44function _svn_grcut()
45{
46    local re=$1 line= old_IFS
47    # fix IFS, so that leading spaces are not ignored by next read.
48    # (there is a leading space in svn status output if only a prop is changed)
49    old_IFS="$IFS"
50    IFS=$'\n'
51    while read -r line ; do
52        [[ ! $re || $line == $re ]] && echo "${line/????????/}"
53    done
54    IFS="$old_IFS"
55}
56
57# extract stuff from svn info output
58# _svn_info (URL|Repository Root)
59function _svn_info()
60{
61  local what=$1 line=
62  LANG=C LC_MESSAGES=C svn info --non-interactive 2> /dev/null | \
63  while read line ; do
64    [[ $line == *"$what: "* ]] && echo ${line#*: }
65  done
66}
67
68# _svn_lls (dir|file|all) files...
69# list svn-managed files from list
70# some 'svn status --all-files' would be welcome here?
71function _svn_lls()
72{
73    local opt=$1 f=
74    shift
75    for f in "$@" ; do
76        # could try to check in .svn/entries? hmmm...
77        if [[ $opt == @(dir|all) && -d "$f" ]] ; then
78            echo "$f/"
79        elif [[ $opt == @(file|all) ]] ; then
80            # split f in directory/file names
81            local dn= fn="$f"
82            [[ "$f" == */* ]] && dn=${f%\/*}/ fn=${f##*\/}
83            # ??? this does not work for just added files, because they
84            # do not have a content reference yet...
85            [ -f "${dn}.svn/text-base/${fn}.svn-base" ] && echo "$f"
86        fi
87    done
88}
89
90# This completion guides the command/option order along the one suggested
91# by "svn help", although other syntaxes are allowed.
92#
93# - there is a "real" parser to check for what is available and deduce what
94#   can be suggested further.
95# - the syntax should be coherent with subversion/svn/{cl.h,main.c}
96# - although it is not a good practice, mixed options and arguments
97#   is supported by the completion as it is by the svn command.
98# - the completion works in the middle of a line,
99#   but not really in the middle of an argument or option.
100# - property names are completed: see comments about issues related to handling
101#   ":" within property names although it is a word completion separator.
102# - unknown properties are assumed to be simple file properties.
103# - --revprop and --revision options are forced to revision properties
104#   as they are mandatory in this case.
105# - argument values are suggested to some other options, eg directory names
106#   for --config-dir.
107# - values for some options can be extended with environment variables:
108#   SVN_BASH_FILE_PROPS: other properties on files/directories
109#   SVN_BASH_REV_PROPS: other properties on revisions
110#   SVN_BASH_ENCODINGS: encodings to be suggested
111#   SVN_BASH_MIME_TYPE: mime types to be suggested
112#   SVN_BASH_KEYWORDS: "svn:keywords" substitutions to be suggested
113#   SVN_BASH_USERNAME: usernames suggested for --username
114#   SVN_BASH_COMPL_EXT: completion extensions for file arguments, based on the
115#      current subcommand, so that for instance only modified files are
116#      suggested for 'revert', only not svn-managed files for 'add', and so on.
117#      Possible values are:
118#      - username: guess usernames from ~/.subversion/auth/...
119#      - svnstatus: use 'svn status' for completion
120#      - recurse: allow recursion (expensive)
121#      - externals: recurse into externals (very expensive)
122#     Both former options are reasonable, but beware that both later options
123#     may be unadvisable if used on large working copies.
124#     None of these costly completions are activated by default.
125#     Argument completion outside a working copy results in an error message.
126#     Filenames with spaces are not completed properly.
127#
128# TODO
129# - other options?
130# - obsolete options could be removed from auto-comp? (e.g. -N)
131# - obsolete commands could be removed? (e.g. resolved)
132# - completion does not work properly when editing in the middle of the line
133#   status/previous are those at the end of the line, not at the entry position
134_svn()
135{
136        local cur cmds cmdOpts pOpts mOpts rOpts qOpts nOpts optsParam opt
137
138        COMPREPLY=()
139        cur=${COMP_WORDS[COMP_CWORD]}
140
141        # Possible expansions, without pure-prefix abbreviations such as "up".
142        cmds='add blame annotate praise cat changelist cl checkout co cleanup'
143        cmds="$cmds commit ci copy cp delete remove rm diff export help import"
144        cmds="$cmds info list ls lock log merge mergeinfo mkdir move mv rename"
145        cmds="$cmds propdel pdel propedit pedit propget pget proplist plist"
146        cmds="$cmds propset pset resolve resolved revert status switch unlock"
147        cmds="$cmds update"
148
149        # help options have a strange command status...
150        local helpOpts='--help -h'
151        # all special options that have a command status
152        local specOpts="--version $helpOpts"
153
154        # options that require a parameter
155        # note: continued lines must end '|' continuing lines must start '|'
156        optsParam="-r|--revision|--username|--password|--targets"
157        optsParam="$optsParam|-x|--extensions|-m|--message|-F|--file"
158        optsParam="$optsParam|--encoding|--diff-cmd|--diff3-cmd|--editor-cmd"
159        optsParam="$optsParam|--old|--new|--config-dir|--config-option"
160        optsParam="$optsParam|--native-eol|-l|--limit|-c|--change"
161        optsParam="$optsParam|--depth|--set-depth|--with-revprop"
162        optsParam="$optsParam|--changelist|--accept|--show-revs"
163
164        # svn:* and other (env SVN_BASH_*_PROPS) properties
165        local svnProps revProps allProps psCmds propCmds
166
167        # svn and user configured file properties
168        svnProps="svn:keywords svn:executable svn:needs-lock svn:externals
169                  svn:ignore svn:eol-style svn:mime-type $SVN_BASH_FILE_PROPS"
170
171        # svn and user configured revision properties
172        revProps="svn:author svn:log svn:date $SVN_BASH_REV_PROPS"
173
174        # all properties as an array variable
175        allProps=( $svnProps $revProps )
176
177        # subcommands that expect property names
178        psCmds='propset|pset|ps'
179        propCmds="$psCmds|propget|pget|pg|propedit|pedit|pe|propdel|pdel|pd"
180
181        # Parse arguments and set various variables about what was found.
182        #
183        # cmd: the current command if available
184        #    isPropCmd: whether it expects a property name argument
185        #    isPsCmd: whether it also expects a property value argument
186        #    isHelpCmd: whether it is about help
187        #    nExpectArgs: how many arguments are expected by the command
188        # help: help requested about this command (if cmd=='help')
189        # prop: property name (if appropriate)
190        #    isRevProp: is it a special revision property
191        # val: property value (if appropriate, under pset)
192        # options: all options encountered
193        #    hasRevPropOpt: is --revprop set
194        #    hasRevisionOpt: is --revision set
195        #    hasRelocateOpt: is --relocate set
196        #    hasReintegrateOpt: is --reintegrate set
197        #    acceptOpt: the value of --accept
198        # nargs: how many arguments were found
199        # stat: status of parsing at the 'current' word
200        #
201        # prev: previous command in the loop
202        # last: status of last parameter analyzed
203        # i: index
204        local cmd= isPropCmd= isPsCmd= isHelpCmd= nExpectArgs= isCur= i=0
205        local prev= help= prop= val= isRevProp= last='none' nargs=0 stat=
206        local options= hasRevPropOpt= hasRevisionOpt= hasRelocateOpt=
207        local acceptOpt= URL= hasReintegrateOpt=
208
209        for opt in "${COMP_WORDS[@]}"
210        do
211            # get status of current word (from previous iteration)
212            [[ $isCur ]] && stat=$last
213
214            # are we processing the current word
215            isCur=
216            [[ $i -eq $COMP_CWORD ]] && isCur=1
217            let i++
218
219            # FIRST must be the "svn" command
220            [ $last = 'none' ] && { last='first'; continue ; }
221
222            # SKIP option arguments
223            if [[ $prev == @($optsParam) ]] ; then
224
225                # record accept value
226                [[ $prev = '--accept' ]] && acceptOpt=$opt
227
228                prev=''
229                last='skip'
230                continue ;
231            fi
232
233            # Argh...  This looks like a bash bug...
234            # Redirections are passed to the completion function
235            # although it is managed by the shell directly...
236            # It matters because we want to tell the user when no more
237            # completion is available, so it does not necessary
238            # fallback to the default case.
239            if [[ $prev == @(<|>|>>|[12]>|[12]>>) ]] ; then
240                prev=''
241                last='skip'
242                continue ;
243            fi
244            prev=$opt
245
246            # get the subCoMmanD
247            if [[ ! $cmd && $opt \
248               && ( $opt != -* || $opt == @(${specOpts// /|}) ) ]]
249            then
250                cmd=$opt
251                [[ $cmd == @($propCmds) ]] && isPropCmd=1
252                [[ $cmd == @($psCmds) ]] && isPsCmd=1
253                [[ $cmd == @(${helpOpts// /|}) ]] && cmd='help'
254                [[ $cmd = 'help' ]] && isHelpCmd=1
255                # HELP about a command asked with an option
256                if [[ $isHelpCmd && $cmd && $cmd != 'help' && ! $help ]]
257                then
258                    help=$cmd
259                    cmd='help'
260                fi
261                last='cmd'
262                continue
263            fi
264
265            # HELP about a command
266            if [[ $isHelpCmd && ! $help && $opt && $opt != -* ]]
267            then
268                help=$opt
269                last='help'
270                continue
271            fi
272
273            # PROPerty name
274            if [[ $isPropCmd && ! $prop && $opt && $opt != -* ]]
275            then
276                prop=$opt
277                [[ $prop == @(${revProps// /|}) ]] && isRevProp=1
278                last='prop'
279                continue
280            fi
281
282            # property VALue
283            if [[ $isPsCmd && $prop && ! $val && $opt != -* ]] ;
284            then
285                val=$opt
286                last='val'
287                continue
288            fi
289
290            if [[ $last != 'onlyarg' ]]
291            then
292              # more OPTions
293              case $opt in
294                  -r|--revision|--revision=*)
295                      hasRevisionOpt=1
296                      ;;
297                  --revprop)
298                      hasRevPropOpt=1
299                      # restrict to revision properties!
300                      allProps=( $revProps )
301                      # on revprops, only one URL is expected
302                      nExpectArgs=1
303                      ;;
304                  -h|--help)
305                      isHelpCmd=1
306                      ;;
307                  -F|--file)
308                      val='-F'
309                      ;;
310                  --relocate)
311                      hasRelocateOpt=1
312                      ;;
313                  --reintegrate)
314                      hasReintegrateOpt=1
315                      ;;
316              esac
317
318              # no more options, only arguments, whatever they look like.
319              if [[ $opt = '--' && ! $isCur ]] ; then
320                  last='onlyarg'
321                  continue
322              fi
323
324              # options are recorded...
325              if [[ $opt == -* ]] ; then
326                  # but not the current one!
327                  [[ ! $isCur ]] && options="$options $opt "
328                  last='opt'
329                  continue
330              fi
331            else
332                # onlyarg
333                let nargs++
334                continue
335            fi
336
337            # then we have an argument
338            if [[ $cmd = 'merge' && ! $URL ]] ; then
339              # fist argument is the source URL for the merge
340              URL=$opt
341            fi
342
343            last='arg'
344            let nargs++
345        done
346        # end opt option processing...
347        [[ $stat ]] || stat=$last
348
349        # suggest all subcommands, including special help
350        if [[ ! $cmd || $stat = 'cmd' ]]
351        then
352            COMPREPLY=( $( compgen -W "$cmds $specOpts" -- $cur ) )
353            return 0
354        fi
355
356        # suggest all subcommands
357        if [[ $stat = 'help' || ( $isHelpCmd && ! $help ) ]]
358        then
359            COMPREPLY=( $( compgen -W "$cmds" -- $cur ) )
360            return 0
361        fi
362
363        if [[ $cmd = 'merge' || $cmd = 'mergeinfo' ]]
364        then
365          local here=$(_svn_info URL)
366          # suggest a possible URL for merging
367          if [[ ! $URL && $stat = 'arg' ]] ; then
368            # we assume a 'standard' repos with branches and trunk
369            if [[ "$here" == */branches/* ]] ; then
370              # we guess that it is a merge from the trunk
371              COMPREPLY=( $(compgen -W ${here/\/branches\/*/\/trunk} -- $cur ) )
372              return 0
373            elif [[ "$here" == */trunk* ]] ; then
374              # we guess that it is a merge from a branch
375              COMPREPLY=( $(compgen -W ${here/\/trunk*/\/branches\/} -- $cur ) )
376              return 0
377            else
378              # no se, let us suggest the repository root...
379              COMPREPLY=( $(compgen -W $(_svn_info Root) -- $cur ) )
380              return 0
381            fi
382          elif [[ $URL == */branches/* && $here == */trunk* && \
383                ! $hasReintegrateOpt && $cur = '' && $stat = 'arg' ]] ; then
384            # force --reintegrate only if the current word is empty
385            COMPREPLY=( $(compgen -W '--reintegrate' -- $cur ) )
386            return 0
387          fi
388        fi
389
390        # help about option arguments
391        if [[ $stat = 'skip' ]]
392        then
393            local previous=${COMP_WORDS[COMP_CWORD-1]}
394            local values= dirs= beep= exes=
395
396            [[ $previous = '--config-dir' ]] && dirs=1
397
398            # external editor, diff, diff3...
399            [[ $previous = --*-cmd ]] && exes=1
400
401            [[ $previous = '--native-eol' ]] && values='LF CR CRLF'
402
403            # just to suggest that a number is expected. hummm.
404            [[ $previous = '--limit' ]] && values='0 1 2 3 4 5 6 7 8 9'
405
406            # some special partial help about --revision option.
407            [[ $previous = '--revision' || $previous = '-r' ]] && \
408                values='HEAD BASE PREV COMMITTED 0 {'
409
410            [[ $previous = '--encoding' ]] && \
411                values="latin1 utf8 $SVN_BASH_ENCODINGS"
412
413            [[ $previous = '--extensions' || $previous = '-x' ]] && \
414                values="--unified --ignore-space-change \
415                        --ignore-all-space --ignore-eol-style"
416
417            [[ $previous = '--depth' ]] && \
418                values='empty files immediates infinity'
419
420            [[ $previous = '--set-depth' ]] && \
421                values='empty exclude files immediates infinity'
422
423            [[ $previous = '--accept' ]] && \
424            {
425                # the list is different for 'resolve'
426                if [[ $cmd = 'resolve' ]] ; then
427                    # from svn help resolve
428                    values='base working mine-full theirs-full'
429                else # checkout merge switch update
430                    # not implemented yet: mine-conflict theirs-conflict
431                    values='postpone base mine-full theirs-full edit launch'
432                fi
433            }
434
435            [[ $previous = '--show-revs' ]] && values='merged eligible'
436
437            if [[ $previous = '--username' ]] ; then
438              values="$SVN_BASH_USERNAME"
439              if [[ $SVN_BASH_COMPL_EXT == *username* ]] ; then
440                local file=
441                # digest? others?
442                for file in ~/.subversion/auth/svn.simple/* ; do
443                  if [ -r $file ] ; then
444                    values="$values $(_svn_read_hashfile username < $file)"
445                  fi
446                done
447              fi
448              [[ ! "$values" ]] && beep=1
449            fi
450
451            # could look at ~/.subversion/ ?
452            # hmmm... this option should not exist
453            [[ $previous = '--password' ]] && beep=1
454
455            # TODO: provide help about other options such as:
456            # --old --new --with-revprop
457
458            # if the previous option required a parameter, do something
459            # or fallback on ordinary filename expansion
460            [[ $values ]] && COMPREPLY=( $( compgen -W "$values" -- $cur ) )
461            [[ $dirs ]] && COMPREPLY=( $( compgen -o dirnames -- $cur ) )
462            [[ $exes ]] && COMPREPLY=( $( compgen -c -- $cur ) )
463            [[ $beep ]] &&
464            {
465                # 'no known completion'. hummm.
466                echo -en "\a"
467                COMPREPLY=( '' )
468            }
469            return 0
470        fi
471
472        # provide allowed property names after property commands
473        if [[ $isPropCmd && ( ! $prop || $stat = 'prop' ) && $cur != -* ]]
474        then
475            #
476            # Ok, this part is pretty ugly.
477            #
478            # The issue is that ":" is a completion word separator,
479            # which is a good idea for file:// urls but not within
480            # property names...
481            #
482            # The first idea was to remove locally ":" from COMP_WORDBREAKS
483            # and then put it back in all cases but in property name
484            # completion.  It does not always work.  There is a strange bug
485            # where one may get "svn:svn:xxx" in some unclear cases.
486            #
487            # Thus the handling is reprogrammed here...
488            # The code assumes that property names look like *:*,
489            # but it also works reasonably well with simple names.
490            local choices=
491
492            if [[ $cur == *:* ]]
493            then
494                # only suggest/show possible suffixes
495                local prefix=${cur%:*} suffix=${cur#*:} c=
496                for c in ${allProps[@]} ; do
497                    [[ $c == $prefix:* ]] && choices="$choices ${c#*:}"
498                done
499                # everything will be appended to the prefix because ':' is
500                # a separator, so cur is restricted to the suffix part.
501                cur=$suffix
502            else
503                # only one choice is fine
504                COMPREPLY=( $( compgen -W "${allProps[*]}" -- $cur ) )
505                [ ${#COMPREPLY[@]} -eq 1 ] && return 0
506
507                # no ':' so only suggest prefixes?
508                local seen= n=0 last= c=
509                for c in ${allProps[@]%:*} ; do
510                    # do not put the same prefix twice...
511                    if [[ $c == $cur* && ( ! $seen || $c != @($seen) ) ]]
512                    then
513                        let n++
514                        last=$c
515                        choices="$choices $c:"
516                        if [[ $seen ]]
517                        then
518                            seen="$seen|$c*"
519                        else
520                            seen="$c*"
521                        fi
522                    fi
523                done
524
525                # supply two choices to force a partial completion and a beep
526                [[ $n -eq 1 ]] && choices="$last:1 $last:2"
527            fi
528
529            COMPREPLY=( $( compgen -W "$choices" -- $cur ) )
530            return 0
531        fi
532
533        # force mandatory --revprop option on revision properties
534        if [[ $isRevProp && ! $hasRevPropOpt ]]
535        then
536            COMPREPLY=( $( compgen -W '--revprop' -- $cur ) )
537            return 0
538        fi
539
540        # force mandatory --revision option on revision properties
541        if [[ $isRevProp && $hasRevPropOpt && ! $hasRevisionOpt ]]
542        then
543            COMPREPLY=( $( compgen -W '--revision' -- $cur ) )
544            return 0
545        fi
546
547        # possible completion when setting property values
548        if [[ $isPsCmd && $prop && ( ! $val || $stat = 'val' ) ]]
549        then
550            # ' is a reminder for an arbitrary value
551            local values="\' --file"
552            case $prop in
553                svn:keywords)
554                    # just a subset?
555                    values="Id Rev URL Date Author Header \' $SVN_BASH_KEYWORDS"
556                    ;;
557                svn:executable|svn:needs-lock)
558                    # hmmm... canonical value * is special to the shell.
559                    values='\\*'
560                    ;;
561                svn:eol-style)
562                    values='native LF CR CRLF'
563                    ;;
564                svn:mime-type)
565                    # could read /etc/mime.types if available. overkill.
566                    values="text/ text/plain text/html text/xml text/rtf
567                       image/ image/png image/gif image/jpeg image/tiff
568                       audio/ audio/midi audio/mpeg
569                       video/ video/mpeg video/mp4
570                       application/ application/octet-stream
571                       $SVN_BASH_MIME_TYPE"
572                    ;;
573            esac
574
575            COMPREPLY=( $( compgen -W "$values" -- $cur ) )
576            # special case for --file... return even if within an option
577            [[ ${COMPREPLY} ]] && return 0
578        fi
579
580        # force mandatory --accept option for 'resolve' command
581        if [[ $cmd = 'resolve' && ! $acceptOpt ]]
582        then
583            COMPREPLY=( $( compgen -W '--accept' -- $cur ) )
584            # force option now! others will be available on later completions
585            return 0
586        fi
587
588        # maximum number of additional arguments expected in various forms
589        case $cmd in
590            merge)
591                nExpectArgs=3
592                ;;
593            mergeinfo)
594                nExpectArgs=1
595                ;;
596            copy|cp|move|mv|rename|ren|export|import)
597                nExpectArgs=2
598                ;;
599            switch|sw)
600                [[ ! $hasRelocateOpt ]] && nExpectArgs=2
601                ;;
602            help|h)
603                nExpectArgs=0
604                ;;
605            --version)
606                nExpectArgs=0
607                ;;
608        esac
609
610        # the maximum number of arguments is reached for a command
611        if [[ $nExpectArgs && $nargs -gt $nExpectArgs ]]
612        then
613            # some way to tell 'no completion at all'... is there a better one?
614            # Do not say 'file completion' here.
615            echo -en "\a"
616            COMPREPLY=( '' )
617            return 0
618        fi
619
620        # if not typing an option,
621        # then fallback on filename expansion...
622        if [[ $cur != -* || $stat = 'onlyarg' ]]  ; then
623
624            # do we allow possible expensive completion here?
625            if [[ $SVN_BASH_COMPL_EXT == *svnstatus* ]] ; then
626
627                # build status command and options
628                # "--quiet" removes 'unknown' files
629                local status='svn status --non-interactive'
630
631                [[ $SVN_BASH_COMPL_EXT == *recurse* ]] || \
632                    status="$status --non-recursive"
633
634                # I'm not sure that it can work with externals in call cases
635                # the output contains translatable sentences (even with quiet)
636                [[ $SVN_BASH_COMPL_EXT == *externals* ]] || \
637                    status="$status --ignore-externals"
638
639                local cs= files=
640                # subtlety: must not set $cur* if $cur is empty in some cases
641                [[ $cur ]] && cs=$cur*
642
643                # 'files' is set according to the current subcommand
644                case $cmd in
645                    st*) # status completion must include all files
646                        files=$cur*
647                        ;;
648                    ci|commit|revert|di*) # anything edited
649                        files=$($status $cs| _svn_grcut '@([MADR!]*| M*|_M*)')
650                        ;;
651                    add) # unknown files
652                        files=$($status $cs| _svn_grcut '\?*')
653                        ;;
654                    unlock) # unlock locked files
655                        files=$($status $cs| _svn_grcut '@(??L*|?????[KOTB]*)')
656                        ;;
657                    resolve*) # files in conflict
658                        files=$($status $cs| _svn_grcut '@(?C*|C*)')
659                        ;;
660                    praise|blame|ann*) # any svn file but added
661                        files=$( _svn_lls all $cur* )
662                        ;;
663                    p*) # prop commands
664                        if [[ $cmd == @($propCmds) && \
665                              $prop == @(svn:ignore|svn:externals) ]] ; then
666                            # directory specific props
667                            files=$( _svn_lls dir . $cur* )
668                        else
669                            # ??? added directories appear twice: foo foo/
670                            files="$( _svn_lls all $cur* )
671                                   $($status $cs | _svn_grcut 'A*' )"
672                        fi
673                        ;;
674                    info) # information on any file
675                        files="$( _svn_lls all $cur* )
676                               $($status $cs | _svn_grcut 'A*' )"
677                        ;;
678                    remove|rm|del*|move|mv|rename) # changing existing files
679                        files=$( _svn_lls all $cur* )
680                        ;;
681                    mkdir) # completion in mkdir can only be for subdirs?
682                        files=$( _svn_lls dir $cur* )
683                        ;;
684                    log|lock|up*|cl*|switch) # misc, all but added files
685                        files=$( _svn_lls all $cur* )
686                        ;;
687                    merge) # may do a better job? URL/WCPATH
688                        files=$( _svn_lls all $cur* )
689                        ;;
690                    ls|list) # better job? what about URLs?
691                        files=$( _svn_lls all $cur* )
692                        ;;
693                    *) # other commands: changelist export import cat mergeinfo
694                        local fallback=1
695                        ;;
696                esac
697
698                # when not recursive, some relevant files may exist
699                # within subdirectories, so they are added here.
700                # should it be restricted to svn-managed subdirs? no??
701                if [[ $SVN_BASH_COMPL_EXT != *recurse* ]] ; then
702                    files="$files $( _svn_lls dir $cur* )"
703                fi
704
705                # set completion depending on computed 'files'
706                if [[ $files ]] ; then
707                    COMPREPLY=( $( compgen -W "$files" -- $cur ) )
708                    # if empty, set to nope?
709                    [[ "${COMPREPLY[*]}" ]] || COMPREPLY=( '' )
710                elif [[ ! $fallback ]] ; then
711                    # this suggests no completion...
712                    echo -en "\a"
713                    COMPREPLY=( '' )
714                fi
715            fi
716            # else fallback to ordinary filename completion...
717            return 0
718        fi
719
720        # otherwise build possible options for the command
721        pOpts="--username --password --no-auth-cache --non-interactive"
722        mOpts="-m --message -F --file --encoding --force-log --with-revprop"
723        rOpts="-r --revision"
724        qOpts="-q --quiet"
725        nOpts="-N --non-recursive --depth"
726        gOpts="-g --use-merge-history"
727
728        cmdOpts=
729        case $cmd in
730        --version)
731                cmdOpts="$qOpts"
732                ;;
733        add)
734                cmdOpts="--auto-props --no-auto-props --force --targets \
735                         --no-ignore --parents $nOpts $qOpts $pOpts"
736                ;;
737        blame|annotate|ann|praise)
738                cmdOpts="$rOpts $pOpts -v --verbose --incremental --xml \
739                         -x --extensions --force $gOpts"
740                ;;
741        cat)
742                cmdOpts="$rOpts $pOpts"
743                ;;
744        changelist|cl)
745                cmdOpts="--targets $pOpts $qOpts --changelist \
746                         -R --recursive --depth --remove"
747                ;;
748        checkout|co)
749                cmdOpts="$rOpts $qOpts $nOpts $pOpts --ignore-externals \
750                         --force --accept"
751                ;;
752        cleanup)
753                cmdOpts="--diff3-cmd $pOpts"
754                ;;
755        commit|ci)
756                cmdOpts="$mOpts $qOpts $nOpts --targets --editor-cmd $pOpts \
757                         --no-unlock --changelist --keep-changelists"
758                ;;
759        copy|cp)
760                cmdOpts="$mOpts $rOpts $qOpts --editor-cmd $pOpts --parents"
761                ;;
762        delete|del|remove|rm)
763                cmdOpts="--force $mOpts $qOpts --targets --editor-cmd $pOpts \
764                         --keep-local"
765                ;;
766        diff|di)
767                cmdOpts="$rOpts -x --extensions --diff-cmd --no-diff-deleted \
768                         $nOpts $pOpts --force --old --new --notice-ancestry \
769                         -c --change --summarize --changelist --xml"
770                ;;
771        export)
772                cmdOpts="$rOpts $qOpts $pOpts $nOpts --force --native-eol \
773                         --ignore-externals"
774                ;;
775        help|h|\?)
776                cmdOpts=
777                ;;
778        import)
779                cmdOpts="--auto-props --no-auto-props $mOpts $qOpts $nOpts \
780                         --no-ignore --editor-cmd $pOpts --force"
781                ;;
782        info)
783                cmdOpts="$pOpts $rOpts --targets -R --recursive --depth \
784                         --incremental --xml --changelist"
785                ;;
786        list|ls)
787                cmdOpts="$rOpts -v --verbose -R --recursive $pOpts \
788                         --incremental --xml --depth"
789                ;;
790        lock)
791                cmdOpts="-m --message -F --file --encoding --force-log \
792                         --targets --force $pOpts"
793                ;;
794        log)
795                cmdOpts="$rOpts -v --verbose --targets $pOpts --stop-on-copy \
796                         --incremental --xml $qOpts -l --limit -c --change \
797                         $gOpts --with-all-revprops --with-revprop"
798                ;;
799        merge)
800                cmdOpts="$rOpts $nOpts $qOpts --force --dry-run --diff3-cmd \
801                         $pOpts --ignore-ancestry -c --change -x --extensions \
802                         --record-only --accept --reintegrate"
803                ;;
804        mergeinfo)
805                cmdOpts="$rOpts $pOpts --show-revs"
806                ;;
807        mkdir)
808                cmdOpts="$mOpts $qOpts --editor-cmd $pOpts --parents"
809                ;;
810        move|mv|rename|ren)
811                cmdOpts="$mOpts $rOpts $qOpts --force --editor-cmd $pOpts \
812                         --parents"
813                ;;
814        propdel|pdel|pd)
815                cmdOpts="$qOpts -R --recursive $rOpts $pOpts --changelist \
816                         --depth"
817                [[ $isRevProp || ! $prop ]] && cmdOpts="$cmdOpts --revprop"
818                ;;
819        propedit|pedit|pe)
820                cmdOpts="--editor-cmd $pOpts $mOpts --force"
821                [[ $isRevProp || ! $prop ]] && \
822                    cmdOpts="$cmdOpts --revprop $rOpts"
823                ;;
824        propget|pget|pg)
825                cmdOpts="-v --verbose -R --recursive $rOpts --strict $pOpts --changelist \
826                         --depth --xml"
827                [[ $isRevProp || ! $prop ]] && cmdOpts="$cmdOpts --revprop"
828                ;;
829        proplist|plist|pl)
830                cmdOpts="-v --verbose -R --recursive $rOpts --revprop $qOpts \
831                         $pOpts --changelist --depth --xml"
832                ;;
833        propset|pset|ps)
834                cmdOpts="$qOpts --targets -R --recursive \
835                         --encoding $pOpts --force --changelist --depth"
836                [[ $isRevProp || ! $prop ]] && \
837                    cmdOpts="$cmdOpts --revprop $rOpts"
838                [[ $val ]] || cmdOpts="$cmdOpts -F --file"
839                ;;
840        resolve)
841                cmdOpts="--targets -R --recursive $qOpts $pOpts --accept \
842                         --depth"
843                ;;
844        resolved)
845                cmdOpts="--targets -R --recursive $qOpts $pOpts --depth"
846                ;;
847        revert)
848                cmdOpts="--targets -R --recursive $qOpts --changelist \
849                         --depth $pOpts"
850                ;;
851        status|stat|st)
852                cmdOpts="-u --show-updates -v --verbose $nOpts $qOpts $pOpts \
853                         --no-ignore --ignore-externals --incremental --xml \
854                         --changelist"
855                ;;
856        switch|sw)
857                cmdOpts="--relocate $rOpts $nOpts $qOpts $pOpts --diff3-cmd \
858                         --force --accept --ignore-externals --set-depth"
859                ;;
860        unlock)
861                cmdOpts="--targets --force $pOpts"
862                ;;
863        update|up)
864                cmdOpts="$rOpts $nOpts $qOpts $pOpts --diff3-cmd \
865                         --ignore-externals --force --accept --changelist \
866                         --editor-cmd --set-depth"
867                ;;
868        *)
869                ;;
870        esac
871
872        # add options that are nearly always available
873        [[ "$cmd" != "--version" ]] && cmdOpts="$cmdOpts $helpOpts"
874        cmdOpts="$cmdOpts --config-dir --config-option"
875
876        # --accept (edit|launch) incompatible with --non-interactive
877        if [[ $acceptOpt == @(edit|launch) ]] ;
878        then
879            cmdOpts=${cmdOpts/ --non-interactive / }
880        fi
881
882        # take out options already given
883        for opt in $options
884        do
885                local optBase
886
887                # remove leading dashes and arguments
888                case $opt in
889                --*)    optBase=${opt/=*/} ;;
890                -*)     optBase=${opt:0:2} ;;
891                esac
892
893                cmdOpts=" $cmdOpts "
894                cmdOpts=${cmdOpts/ ${optBase} / }
895
896                # take out alternatives and mutually exclusives
897                case $optBase in
898                -v)              cmdOpts=${cmdOpts/ --verbose / } ;;
899                --verbose)       cmdOpts=${cmdOpts/ -v / } ;;
900                -N)              cmdOpts=${cmdOpts/ --non-recursive / } ;;
901                --non-recursive) cmdOpts=${cmdOpts/ -N / } ;;
902                -R)              cmdOpts=${cmdOpts/ --recursive / } ;;
903                --recursive)     cmdOpts=${cmdOpts/ -R / } ;;
904                -x)              cmdOpts=${cmdOpts/ --extensions / } ;;
905                --extensions)    cmdOpts=${cmdOpts/ -x / } ;;
906                -q)              cmdOpts=${cmdOpts/ --quiet / } ;;
907                --quiet)         cmdOpts=${cmdOpts/ -q / } ;;
908                -h)              cmdOpts=${cmdOpts/ --help / } ;;
909                --help)          cmdOpts=${cmdOpts/ -h / } ;;
910                -l)              cmdOpts=${cmdOpts/ --limit / } ;;
911                --limit)         cmdOpts=${cmdOpts/ -l / } ;;
912                -r)              cmdOpts=${cmdOpts/ --revision / } ;;
913                --revision)      cmdOpts=${cmdOpts/ -r / } ;;
914                -c)              cmdOpts=${cmdOpts/ --change / } ;;
915                --change)        cmdOpts=${cmdOpts/ -c / } ;;
916                --auto-props)    cmdOpts=${cmdOpts/ --no-auto-props / } ;;
917                --no-auto-props) cmdOpts=${cmdOpts/ --auto-props / } ;;
918                -g)              cmdOpts=${cmdOpts/ --use-merge-history / } ;;
919                --use-merge-history)
920                                 cmdOpts=${cmdOpts/ -g / } ;;
921                -m|--message|-F|--file)
922                        cmdOpts=${cmdOpts/ --message / }
923                        cmdOpts=${cmdOpts/ -m / }
924                        cmdOpts=${cmdOpts/ --file / }
925                        cmdOpts=${cmdOpts/ -F / }
926                        ;;
927                esac
928
929                # remove help options within help subcommand
930                if [ $isHelpCmd ] ; then
931                    cmdOpts=${cmdOpts/ -h / }
932                    cmdOpts=${cmdOpts/ --help / }
933                fi
934        done
935
936        # provide help about available options
937        COMPREPLY=( $( compgen -W "$cmdOpts" -- $cur ) )
938        return 0
939}
940complete -F _svn -o default -X '@(*/.svn|*/.svn/|.svn|.svn/)' dasscm
941
Note: See TracBrowser for help on using the repository browser.