| 1 | # devtools/completion.bash: bash (and OSH!) completion for TASK FILES.
|
| 2 | #
|
| 3 | # Task are simply shell functions:
|
| 4 | #
|
| 5 | # build-foo() {
|
| 6 | # rm foo; make foo
|
| 7 | # }
|
| 8 | #
|
| 9 | # run-task "$@" # dispatch to task; in devtools/run-task.sh
|
| 10 |
|
| 11 | # This script also supports completing unit tests files and
|
| 12 | # TestClass.testMethod names.
|
| 13 | #
|
| 14 | # $ test/unit.sh unit <TAB>
|
| 15 | # $ test/unit.sh unit frontend/args_test.py <TAB>
|
| 16 | #
|
| 17 | # TODO:
|
| 18 | #
|
| 19 | # - Remove dep on ~/git/oilshell/bash-completion/osh_completion, see below
|
| 20 | #
|
| 21 | # - Query the binary for more advanced completions? (e.g. flag completions)
|
| 22 | # Maybe it could a --completions flag.
|
| 23 | #
|
| 24 | # Most binaries will response with a exit code 1 in that case. But if it
|
| 25 | # prints a spec, then you could use that to find flags.
|
| 26 |
|
| 27 | # Note: Bash completion is bizarre.
|
| 28 | #
|
| 29 | # - Use -X '!*_test.py' to remove everything EXCEPT *_test.py. -G for glob
|
| 30 | # does NOT do what you want. This confusing and bizarrely undocumented.
|
| 31 |
|
| 32 | # Test for default distro completion:
|
| 33 | #
|
| 34 | # bash --norc --noprofile
|
| 35 | # . /etc/bash_completion
|
| 36 | # apt-get <TAB> -> You see actions.
|
| 37 |
|
| 38 | log() {
|
| 39 | echo "$@" >&2
|
| 40 | }
|
| 41 |
|
| 42 | _debug() {
|
| 43 | log "$COMP_CWORD - ${COMP_WORDS[@]}"
|
| 44 | }
|
| 45 |
|
| 46 | readonly THIS_DIR=$(dirname ${BASH_SOURCE[0]})
|
| 47 |
|
| 48 | _completion_py() {
|
| 49 | "$THIS_DIR/completion.py" "$@"
|
| 50 | }
|
| 51 |
|
| 52 | # default completion
|
| 53 | # if $0 ends with .sh, then try scanning with completion.py
|
| 54 | # otherwise, do the default filename/dir completion
|
| 55 | _my_default_completion() {
|
| 56 | # This seems be what the default completion is, for the -1, 0, and positive
|
| 57 | # cases
|
| 58 |
|
| 59 | case "$COMP_CWORD" in
|
| 60 |
|
| 61 | # Fall back if there's nothing there
|
| 62 | -1) ;;
|
| 63 | # Fall back to complete a partial command ($0)
|
| 64 | # NOTE: not getting this to happen?
|
| 65 | 0) ;;
|
| 66 |
|
| 67 | *)
|
| 68 | local cur="${COMP_WORDS[COMP_CWORD]}"
|
| 69 | local script="${COMP_WORDS[0]}"
|
| 70 |
|
| 71 | case $script in
|
| 72 | # Special completion for run.sh/test.sh scripts. Auto is also supported.
|
| 73 | # unit action, and then unit tests
|
| 74 | # test.sh: new convention for test runner setting PYTHONPATH, etc.
|
| 75 | *run.sh|*test.sh|*unit.sh|*Auto)
|
| 76 | case "$COMP_CWORD" in
|
| 77 | # Complete the action first
|
| 78 | 1)
|
| 79 | local script="${COMP_WORDS[0]}"
|
| 80 | local actions=$(_completion_py bash "$script")
|
| 81 | COMPREPLY=( $(compgen -W "$actions" -- "$cur") )
|
| 82 | return
|
| 83 | ;;
|
| 84 | # Complete *_test.py files
|
| 85 | 2)
|
| 86 | local word1="${COMP_WORDS[1]}"
|
| 87 | if test "$word1" = 'unit' || test "$word1" = 'py-unit'; then
|
| 88 | # BUG: dirs don't have slashes here?
|
| 89 | COMPREPLY=( $(compgen -A file -o plusdirs -X '!*_test.py' -- "$cur") )
|
| 90 | return
|
| 91 | fi
|
| 92 | ;;
|
| 93 | # Complete Class.testMethod within the foo_test.py file
|
| 94 | 3)
|
| 95 | local word1="${COMP_WORDS[1]}"
|
| 96 | if test "$word1" = 'unit' || test "$word1" = 'py-unit'; then
|
| 97 | local test_file="${COMP_WORDS[2]}"
|
| 98 | local tests=$(_completion_py pyunit "$test_file")
|
| 99 | if test -z "$tests"; then
|
| 100 | COMPREPLY=( NOTESTS )
|
| 101 | else
|
| 102 | COMPREPLY=( $(compgen -W "$tests" -- "$cur") )
|
| 103 | fi
|
| 104 | return
|
| 105 | fi
|
| 106 | ;;
|
| 107 | esac
|
| 108 | ;;
|
| 109 |
|
| 110 | *.sh)
|
| 111 | # For the first word, try to complete actions in shell scripts
|
| 112 | case "$COMP_CWORD" in
|
| 113 | 1)
|
| 114 | local actions=$(_completion_py bash "$script")
|
| 115 | COMPREPLY=( $(compgen -W "$actions" -- "$cur") )
|
| 116 | return
|
| 117 | ;;
|
| 118 | esac
|
| 119 | ;;
|
| 120 |
|
| 121 | *_test.py)
|
| 122 | case "$COMP_CWORD" in
|
| 123 | # Complete Class.testMethod within the foo_test.py file
|
| 124 | 1)
|
| 125 | local test_file="${COMP_WORDS[0]}"
|
| 126 | local tests=$(_completion_py pyunit "$test_file")
|
| 127 | # Show a dummy error result, so we aren't confused by the
|
| 128 | # directory name completion
|
| 129 | if test -z "$tests"; then
|
| 130 | COMPREPLY=( NOTESTS )
|
| 131 | else
|
| 132 | COMPREPLY=( $(compgen -W "$tests" -- "$cur") )
|
| 133 | fi
|
| 134 | return
|
| 135 | ;;
|
| 136 | esac
|
| 137 | ;;
|
| 138 | esac # script
|
| 139 | ;;
|
| 140 | esac # $COMP_CWORD
|
| 141 |
|
| 142 | # Need this for for ./run.sh action <filename> There is an "if" in that clause.
|
| 143 | test -n "$_comp_fallback" && "$_comp_fallback" "$@"
|
| 144 | }
|
| 145 |
|
| 146 | # global that is mutated
|
| 147 | _comp_fallback=''
|
| 148 |
|
| 149 | # _comp_fallback is invoked by my _my_default_completion, with the same 3 args
|
| 150 | # as a completion function, i.e. -- "$@".
|
| 151 | _maybe_set_comp_fallback() {
|
| 152 | local _distro_script
|
| 153 | if test -n "$BASH_VERSION"; then
|
| 154 | # running under bash
|
| 155 | _distro_script='/etc/bash_completion'
|
| 156 | else
|
| 157 | # running under OSH
|
| 158 | _distro_script=~/git/oilshell/bash-completion/osh_completion
|
| 159 | fi
|
| 160 | local _distro_function=_completion_loader
|
| 161 |
|
| 162 | if test -f $_distro_script; then
|
| 163 | source $_distro_script
|
| 164 | if test $(type -t $_distro_function) = 'function'; then
|
| 165 | _comp_fallback=$_distro_function
|
| 166 | fi
|
| 167 | else
|
| 168 | # log "Warning: $_distro_script not found; no completion fallback"
|
| 169 | _comp_fallback=''
|
| 170 | fi
|
| 171 | }
|
| 172 |
|
| 173 | _install_completion() {
|
| 174 | # Fallback on distro completion so we don't clobber it.
|
| 175 | _maybe_set_comp_fallback
|
| 176 |
|
| 177 | # Fix: add "-o bashdefault" to fix completion of variable names (e.g. $HO ->
|
| 178 | # HOME). When there is no completion produced by my function, bash will fall
|
| 179 | # back on its defaults.
|
| 180 | # -o filenames: it makes it so that directories get a trailing slash.
|
| 181 | #
|
| 182 | # Formula for completing a subset of filenames:
|
| 183 | # 1) complete -o filenames ...
|
| 184 | # 2) compgen -A file -o plusdirs -X '!*.sh'
|
| 185 |
|
| 186 | complete -F _my_default_completion -o bashdefault -o filenames -D
|
| 187 | }
|
| 188 |
|
| 189 | _install_completion
|
| 190 |
|