-
Notifications
You must be signed in to change notification settings - Fork 696
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5928 from sboosali/master
fix « cabal »'s bash-completion (see issue #5927)
- Loading branch information
Showing
1 changed file
with
167 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,170 @@ | ||
#!/bin/bash | ||
|
||
### -*- mode: shell-script; -*- | ||
|
||
################################################## | ||
|
||
# cabal command line completion | ||
# | ||
# Copyright 2007-2008 "Lennart Kolmodin" <[email protected]> | ||
# "Duncan Coutts" <[email protected]> | ||
# "Duncan Coutts" <[email protected]> | ||
# "Sam Boosalis" <[email protected]> | ||
# | ||
|
||
################################################## | ||
|
||
# List project-specific (/ internal) packages: | ||
# | ||
# | ||
|
||
function _cabal_list_packages () | ||
( | ||
shopt -s nullglob | ||
|
||
local CabalFiles | ||
CabalFiles=( ./*.cabal ./*/*.cabal ./*/*/*.cabal ) | ||
|
||
for FILE in "${CabalFiles[@]}" | ||
do | ||
|
||
BASENAME=$(basename "$FILE") | ||
PACKAGE="${BASENAME%.cabal}" | ||
|
||
echo "$PACKAGE" | ||
|
||
done | sort | uniq | ||
) | ||
|
||
# NOTES | ||
# | ||
# [1] « "${string%suffix}" » strips « suffix » from « string », | ||
# in pure Bash. | ||
# | ||
# [2] « done | sort | uniq » removes duplicates from the output of the for-loop. | ||
# | ||
|
||
################################################## | ||
|
||
|
||
# List cabal targets by type, pass: | ||
# - test-suite for test suites | ||
# - benchmark for benchmarks | ||
# - executable for executables | ||
# - executable|test-suite|benchmark for the three | ||
_cabal_list() | ||
# | ||
# - ‹test-suite› for test suites | ||
# - ‹benchmark› for benchmarks | ||
# - ‹executable› for executables | ||
# - ‹library› for internal libraries | ||
# - ‹foreign-library› for foreign libraries | ||
# - nothing for all components. | ||
# | ||
|
||
function _cabal_list_targets () | ||
( | ||
shopt -s nullglob | ||
|
||
# ^ NOTE « _cabal_list_targets » must be a subshell to temporarily enable « nullglob ». | ||
# hence, « function _ () ( ... ) » over « function _ () { ... } ». | ||
# without « nullglob », if a glob-pattern fails, it becomes a literal | ||
# (i.e. the string with an asterix, rather than an empty string). | ||
|
||
CabalComponent=${1:-library|executable|test-suite|benchmark|foreign-library} | ||
|
||
local CabalFiles | ||
CabalFiles=( ./*.cabal ./*/*.cabal ./*/*/*.cabal ) | ||
|
||
for FILE in "${CabalFiles[@]}" | ||
do | ||
|
||
grep -E -i "^[[:space:]]*($CabalComponent)[[:space:]]" "$FILE" 2>/dev/null | sed -e "s/.* \([^ ]*\).*/\1/" | sed -e '/^$/d' | ||
|
||
done | sort | uniq | ||
) | ||
|
||
# NOTES | ||
# | ||
# [1] in « sed '/^$/d' »: | ||
# | ||
# * « d » is the sed command to delete a line. | ||
# * « ^$ » is a regular expression matching only a blank line | ||
# (i.e. a line start followed by a line end). | ||
# | ||
# dropping blank lines is necessary to ignore public « library » stanzas, | ||
# while still matching private « library _ » stanzas. | ||
# | ||
# [2] | ||
# | ||
|
||
#TODO# rm duplicate components and qualify with « PACKAGE: » (from basename): | ||
# | ||
# $ .. | sort | uniq | ||
|
||
################################################## | ||
|
||
# List known (/ external) packages: | ||
# | ||
function _cabal_list_external_packages () | ||
{ | ||
for f in ./*.cabal; do | ||
grep -Ei "^[[:space:]]*($1)[[:space:]]" "$f" | | ||
sed -e "s/.* \([^ ]*\).*/\1/" | ||
done | ||
|
||
cabal list --simple-output | ||
|
||
} | ||
|
||
# NOTES | ||
# | ||
# [1] this is slow. | ||
# e.g. « cabal list --simple-output » output ~100,000 lines, | ||
# given multiple versions and different « repository »'s | ||
# (which took several seconds to print out). | ||
# | ||
# [2] | ||
# | ||
|
||
################################################## | ||
|
||
# List possible targets depending on the command supplied as parameter. The | ||
# ideal option would be to implement this via --list-options on cabal directly. | ||
# This is a temporary workaround. | ||
_cabal_targets() | ||
|
||
function _cabal_targets () | ||
|
||
{ | ||
# If command ($*) contains build, repl, test or bench completes with | ||
# targets of according type. | ||
local comp | ||
for comp in "$@"; do | ||
[ "$comp" == new-build ] && _cabal_list "executable|test-suite|benchmark" && break | ||
[ "$comp" == build ] && _cabal_list "executable|test-suite|benchmark" && break | ||
[ "$comp" == repl ] && _cabal_list "executable|test-suite|benchmark" && break | ||
[ "$comp" == run ] && _cabal_list "executable" && break | ||
[ "$comp" == test ] && _cabal_list "test-suite" && break | ||
[ "$comp" == bench ] && _cabal_list "benchmark" && break | ||
local Completion | ||
|
||
for Completion in "$@"; do | ||
|
||
[ "$Completion" == new-build ] && _cabal_list_targets && break | ||
[ "$Completion" == new-repl ] && _cabal_list_targets && break | ||
[ "$Completion" == new-run ] && _cabal_list_targets "executable" && break | ||
[ "$Completion" == new-test ] && _cabal_list_targets "test-suite" && break | ||
[ "$Completion" == new-bench ] && _cabal_list_targets "benchmark" && break | ||
[ "$Completion" == new-haddock ] && _cabal_list_targets && break | ||
|
||
[ "$Completion" == new-install ] && _cabal_list_external_packages && break | ||
|
||
[ "$Completion" == build ] && _cabal_list_targets "executable|test-suite|benchmark" && break | ||
[ "$Completion" == repl ] && _cabal_list_targets "executable|test-suite|benchmark" && break | ||
[ "$Completion" == run ] && _cabal_list_targets "executable" && break | ||
[ "$Completion" == test ] && _cabal_list_targets "test-suite" && break | ||
[ "$Completion" == bench ] && _cabal_list_targets "benchmark" && break | ||
|
||
done | ||
} | ||
|
||
# NOTES | ||
# | ||
# [1] « $@ » will be the full command-line (so far). | ||
# | ||
# [2] | ||
# | ||
|
||
################################################## | ||
|
||
# List possible subcommands of a cabal subcommand. | ||
# | ||
# In example "sandbox" is a cabal subcommand that itself has subcommands. Since | ||
# "cabal --list-options" doesn't work in such cases we have to get the list | ||
# using other means. | ||
_cabal_subcommands() | ||
|
||
function _cabal_subcommands () | ||
|
||
{ | ||
local word | ||
for word in "$@"; do | ||
|
@@ -54,7 +179,10 @@ _cabal_subcommands() | |
done | ||
} | ||
|
||
__cabal_has_doubledash () | ||
################################################## | ||
|
||
function __cabal_has_doubledash () | ||
|
||
{ | ||
local c=1 | ||
# Ignore the last word, because it is replaced anyways. | ||
|
@@ -70,25 +198,34 @@ __cabal_has_doubledash () | |
return 1 | ||
} | ||
|
||
_cabal() | ||
|
||
################################################## | ||
|
||
function _cabal () | ||
|
||
{ | ||
# no completion past cabal arguments. | ||
__cabal_has_doubledash && return | ||
|
||
# get the word currently being completed | ||
local cur | ||
cur=${COMP_WORDS[$COMP_CWORD]} | ||
local CurrentWord | ||
CurrentWord=${COMP_WORDS[$COMP_CWORD]} | ||
|
||
# create a command line to run | ||
local cmd | ||
local CommandLine | ||
# copy all words the user has entered | ||
cmd=( ${COMP_WORDS[@]} ) | ||
CommandLine=( "${COMP_WORDS[@]}" ) | ||
|
||
# replace the current word with --list-options | ||
cmd[${COMP_CWORD}]="--list-options" | ||
CommandLine[${COMP_CWORD}]="--list-options" | ||
|
||
# the resulting completions should be put into this array | ||
COMPREPLY=( $( compgen -W "$( eval "${cmd[@]}" 2>/dev/null ) $( _cabal_targets "${cmd[@]}" ) $( _cabal_subcommands "${COMP_WORDS[@]}" )" -- "$cur" ) ) | ||
COMPREPLY=( $( compgen -W "$( eval "${CommandLine[@]}" 2>/dev/null ) $( _cabal_targets "${CommandLine[@]}" ) $( _cabal_subcommands "${COMP_WORDS[@]}" )" -- "$CurrentWord" ) ) | ||
} | ||
|
||
# abc="a b c" | ||
# { IFS=" " read -a ExampleArray <<< "$abc"; echo ${ExampleArray[@]}; echo ${!ExampleArray[@]}; } | ||
|
||
################################################## | ||
|
||
complete -F _cabal -o default cabal |