Remove a stray line from cabal-install changelog
[cabal.git] / cabal-install / bash-completion / cabal
blob800579ad5aff00814896d6974a542e659b30dc7f
1 #!/bin/bash
3 ##################################################
5 # cabal command line completion
7 # Copyright 2007-2008 "Lennart Kolmodin" <kolmodin@gentoo.org>
8 # "Duncan Coutts" <dcoutts@gentoo.org>
9 # Copyright 2019- "Sam Boosalis" <samboosalis@gmail.com>
12 # Compatibility — Bash 3.
14 # OSX won't update Bash 3 (last updated circa 2009) to Bash 4,
15 # and we'd like this completion script to work on both Linux and Mac.
17 # For example, OSX Yosemite (released circa 2014) ships with Bash 3:
19 # $ echo $BASH_VERSION
20 # 3.2
22 # While Ubuntu LTS 14.04 (a.k.a. Trusty, also released circa 2016)
23 # ships with the latest version, Bash 4 (updated circa 2016):
25 # $ echo $BASH_VERSION
26 # 4.3
29 # Testing
31 # (1) Invoke « shellcheck »
33 # * source: « https://github.com/koalaman/shellcheck »
34 # * run: « shellcheck ./cabal-install/bash-completion/cabal »
36 # (2) Interpret via Bash 3
38 # * source: « https://ftp.gnu.org/gnu/bash/bash-3.2.tar.gz »
39 # * run: « bash --noprofile --norc --posix ./cabal-install/bash-completion/cabal »
43 ##################################################
44 # Dependencies:
46 command -v cabal >/dev/null
47 command -v grep >/dev/null
48 command -v sed >/dev/null
50 ##################################################
52 # List project-specific (/ internal) packages:
56 function _cabal_list_packages ()
58 shopt -s nullglob
60 local CabalFiles
61 CabalFiles=( ./*.cabal ./*/*.cabal ./*/*/*.cabal )
63 for FILE in "${CabalFiles[@]}"
66 BASENAME=$(basename "$FILE")
67 PACKAGE="${BASENAME%.cabal}"
69 echo "$PACKAGE"
71 done | sort | uniq
74 # NOTES
76 # [1] « "${string%suffix}" » strips « suffix » from « string »,
77 # in pure Bash.
79 # [2] « done | sort | uniq » removes duplicates from the output of the for-loop.
82 ##################################################
84 # List cabal targets by type, pass:
86 # - ‹test-suite› for test suites
87 # - ‹benchmark› for benchmarks
88 # - ‹executable› for executables
89 # - ‹library› for internal libraries
90 # - ‹foreign-library› for foreign libraries
91 # - nothing for all components.
94 function _cabal_list_targets ()
96 shopt -s nullglob
98 # ^ NOTE « _cabal_list_targets » must be a subshell to temporarily enable « nullglob ».
99 # hence, « function _ () ( ... ) » over « function _ () { ... } ».
100 # without « nullglob », if a glob-pattern fails, it becomes a literal
101 # (i.e. the string with an asterix, rather than an empty string).
103 CabalComponent=${1:-library|executable|test-suite|benchmark|foreign-library}
105 local CabalFiles
106 CabalFiles=( ./*.cabal ./*/*.cabal ./*/*/*.cabal )
108 for FILE in "${CabalFiles[@]}"
111 grep -E -i "^[[:space:]]*($CabalComponent)[[:space:]]" "$FILE" 2>/dev/null | sed -e "s/.* \([^ ]*\).*/\1/" | sed -e '/^$/d'
113 done | sort | uniq
116 # NOTES
118 # [1] in « sed '/^$/d' »:
120 # * « d » is the sed command to delete a line.
121 # * « ^$ » is a regular expression matching only a blank line
122 # (i.e. a line start followed by a line end).
124 # dropping blank lines is necessary to ignore public « library » stanzas,
125 # while still matching private « library _ » stanzas.
127 # [2]
130 #TODO# rm duplicate components and qualify with « PACKAGE: » (from basename):
132 # $ .. | sort | uniq
134 ##################################################
136 # List possible targets depending on the command supplied as parameter. The
137 # ideal option would be to implement this via --list-options on cabal directly.
138 # This is a temporary workaround.
140 function _cabal_targets ()
143 local Completion
145 for Completion in "$@"; do
147 [ "$Completion" == new-build ] && _cabal_list_targets && break
148 [ "$Completion" == new-repl ] && _cabal_list_targets && break
149 [ "$Completion" == new-run ] && _cabal_list_targets "executable" && break
150 [ "$Completion" == new-test ] && _cabal_list_targets "test-suite" && break
151 [ "$Completion" == new-bench ] && _cabal_list_targets "benchmark" && break
152 [ "$Completion" == new-haddock ] && _cabal_list_targets && break
154 [ "$Completion" == new-install ] && _cabal_list_targets "executable" && break
155 # ^ Only complete for local packages (not all 1000s of remote packages).
157 [ "$Completion" == build ] && _cabal_list_targets "executable|test-suite|benchmark" && break
158 [ "$Completion" == repl ] && _cabal_list_targets "executable|test-suite|benchmark" && break
159 [ "$Completion" == run ] && _cabal_list_targets "executable" && break
160 [ "$Completion" == test ] && _cabal_list_targets "test-suite" && break
161 [ "$Completion" == bench ] && _cabal_list_targets "benchmark" && break
163 done
166 # NOTES
168 # [1] « $@ » will be the full command-line (so far).
170 # [2]
173 ##################################################
175 # List possible subcommands of a cabal subcommand.
177 # In example "sandbox" is a cabal subcommand that itself has subcommands. Since
178 # "cabal --list-options" doesn't work in such cases we have to get the list
179 # using other means.
181 function _cabal_subcommands ()
184 local word
185 for word in "$@"; do
186 case "$word" in
187 sandbox)
188 # Get list of "cabal sandbox" subcommands from its help message.
189 "$1" help sandbox |
190 sed -n '1,/^Subcommands:$/d;/^Flags for sandbox:$/,$d;/^ /d;s/^\(.*\):/\1/p'
191 break # Terminate for loop.
193 esac
194 done
197 ##################################################
199 function __cabal_has_doubledash ()
202 local c=1
203 # Ignore the last word, because it is replaced anyways.
204 # This allows expansion for flags on "cabal foo --",
205 # but does not try to complete after "cabal foo -- ".
206 local n=$((${#COMP_WORDS[@]} - 1))
207 while [ $c -lt $n ]; do
208 if [ "--" = "${COMP_WORDS[c]}" ]; then
209 return 0
211 ((c++))
212 done
213 return 1
217 ##################################################
219 function _cabal ()
222 # no completion past cabal arguments.
223 __cabal_has_doubledash && return
225 # get the word currently being completed
226 local CurrentWord
227 CurrentWord=${COMP_WORDS[$COMP_CWORD]}
229 # create a command line to run
230 local CommandLine
231 # copy all words the user has entered
232 CommandLine=( "${COMP_WORDS[@]}" )
234 # replace the current word with --list-options
235 CommandLine[${COMP_CWORD}]="--list-options"
237 # the resulting completions should be put into this array
238 COMPREPLY=( $( compgen -W "$( eval "${CommandLine[@]}" 2>/dev/null ) $( _cabal_targets "${CommandLine[@]}" ) $( _cabal_subcommands "${COMP_WORDS[@]}" )" -- "$CurrentWord" ) )
241 # abc="a b c"
242 # { IFS=" " read -a ExampleArray <<< "$abc"; echo ${ExampleArray[@]}; echo ${!ExampleArray[@]}; }
244 ##################################################
246 complete -F _cabal -o default cabal