From baae0305b6ded4b7e709ace3755ee0c424239f3b Mon Sep 17 00:00:00 2001 From: Konstantin Gredeskoul Date: Sat, 17 Nov 2018 17:45:44 -0800 Subject: [PATCH 01/50] Speed up bash-it Search & support exact matches This commit improves Bash-It search functionality in a couple of ways: * bash-it search (with no arguments) will print detailed help. * bash-it search now accepts terms prefixed with '@' sign, indicating an exact match. * bash-it search now performs smarter caching of the component listings/status New search syntax is as follows: bash-it search [-|@]term1 [-|@]term2 [ --enable | --disable | --help ] --- lib/helpers.bash | 8 +- lib/search.bash | 453 ++++++++++++++++++++++++++++++++---------- test/lib/helpers.bats | 1 + test/lib/search.bats | 92 ++++++--- 4 files changed, 419 insertions(+), 135 deletions(-) diff --git a/lib/helpers.bash b/lib/helpers.bash index 565173ed..cbcdd42d 100644 --- a/lib/helpers.bash +++ b/lib/helpers.bash @@ -70,7 +70,7 @@ bash-it () example '$ bash-it disable alias hg [tmux]...' example '$ bash-it migrate' example '$ bash-it update' - example '$ bash-it search ruby [[-]rake]... [--enable | --disable]' + example '$ bash-it search [-|@]term1 [-|@]term2 ... [--enable | --disable | --help | --refresh | --no-color ]' example '$ bash-it version' example '$ bash-it reload' typeset verb=${1:-} @@ -393,6 +393,9 @@ _disable-thing () fi fi + local flag="DEFER_CACHE_CLEANUP_FOR_${file_type}" + [[ -z ${!flag} ]] && _bash_it_search_cache_clean "${file_type}" + if [ -n "$BASH_IT_AUTOMATIC_RELOAD_AFTER_CONFIG_CHANGE" ]; then exec ${0/-/} fi @@ -488,6 +491,9 @@ _enable-thing () ln -s ../$subdirectory/available/$to_enable "${BASH_IT}/enabled/${use_load_priority}${BASH_IT_LOAD_PRIORITY_SEPARATOR}${to_enable}" fi + local flag="DEFER_CACHE_CLEANUP_FOR_${file_type}" + [[ -z ${!flag} ]] && _bash_it_search_cache_clean "${file_type}" + if [ -n "$BASH_IT_AUTOMATIC_RELOAD_AFTER_CONFIG_CHANGE" ]; then exec ${0/-/} fi diff --git a/lib/search.bash b/lib/search.bash index f643ad12..a54971f5 100644 --- a/lib/search.bash +++ b/lib/search.bash @@ -1,3 +1,4 @@ +#!/usr/bin/env bash # # Search by Konstantin Gredeskoul «github.com/kigster» #——————————————————————————————————————————————————————————————————————————————— @@ -5,46 +6,172 @@ # whose name or description matches one of the search terms provided as arguments. # # Usage: -# ❯ bash-it search term1 [[-]term2] ... [[-]termN] [ --enable | --disable ] +# ❯ bash-it search [-|@]term1 [-|@]term2 ... \ +# [ --enable | -e ] \ +# [ --disable | -d ] \ +# [ --refresh | -r ] +# [ --help | -h ] # -# Exmplanation: # Single dash, as in "-chruby", indicates a negative search term. # Double dash indicates a command that is to be applied to the search result. -# At the moment only --enable and --disable are supported. +# At the moment only --help, --enable and --disable are supported. +# An '@' sign indicates an exact (not partial) match. # # Examples: # ❯ bash-it search ruby rbenv rvm gem rake -# aliases : bundler -# plugins : chruby chruby-auto rbenv ruby rvm -# completions : gem rake - +# aliases: bundler +# plugins: chruby chruby-auto ruby rbenv rvm ruby +# completions: rvm gem rake +# # ❯ bash-it search ruby rbenv rvm gem rake -chruby -# aliases : bundler -# plugins : rbenv ruby rvm -# completions : gem rake +# aliases: bundler +# plugins: ruby rbenv rvm ruby +# completions: rvm gem rake # # Examples of enabling or disabling results of the search: # # ❯ bash-it search ruby -# aliases => bundler -# plugins => chruby chruby-auto ruby +# aliases: bundler +# plugins: chruby chruby-auto ruby # # ❯ bash-it search ruby -chruby --enable -# aliases => ✓bundler -# plugins => ✓ruby +# aliases: bundler +# plugins: ruby # +# Examples of using exact match: + +# ❯ bash-it search @git @ruby +# aliases: git +# plugins: git ruby +# completions: git # + _bash-it-search() { _about 'searches for given terms amongst bash-it plugins, aliases and completions' _param '1: term1' _param '2: [ term2 ]...' - _example '$ _bash-it-search ruby rvm rake bundler' + _example '$ _bash-it-search @git ruby -rvm rake bundler' - declare -a _components=(aliases plugins completions) + local component + export BASH_IT_SEARCH_USE_COLOR=true + export BASH_IT_GREP=${BASH_IT_GREP:-$(which egrep)} + declare -a BASH_IT_COMPONENTS=(aliases plugins completions) - for _component in "${_components[@]}" ; do - _bash-it-search-component "${_component}" "$@" + if [[ -z "$*" ]] ; then + _bash-it-search-help + return 0 + fi + + local -a args=() + for word in $@; do + if [[ ${word} == "--help" || ${word} == "-h" ]]; then + _bash-it-search-help + return 0 + elif [[ ${word} == "--refresh" || ${word} == "-r" ]]; then + _bash_it_search_cache_clean + elif [[ ${word} == "--no-color" ]]; then + export BASH_IT_SEARCH_USE_COLOR=false + else + args=(${args[@]} ${word}) + fi done + + for component in "${BASH_IT_COMPONENTS[@]}" ; do + _bash-it-search-component "${component}" "${args[@]}" + done + + return 0 +} + +_bash-it-search-help() { + printf "${echo_normal} +${echo_underline_yellow}USAGE${echo_normal} + + bash-it search [-|@]term1 [-|@]term2 ... \\ + [ --enable | --disable | --help | --refresh | --no-color ] + +${echo_underline_yellow}DESCRIPTION${echo_normal} + + Use ${echo_bold_green}search${echo_normal} bash-it command to search for a list of terms or term negations + across all components: aliases, completions and plugins. Components that are + enabled are shown in green (or with a check box if --no-color option is used). + + In addition to simply finding the right component, you can use the results + of the search to enable or disable all components that the search returns. + + When search is used to enable/disable components it becomes clear that + you must be able to perform not just a partial match, but an exact match, + as well as be able to exclude some components. + + * To exclude a component (or all components matching a substring) use + a search term with minus as a prefix, eg '-flow' + + * To perform an exact match, use character '@' in front of the term, + eg. '@git' would only match aliases, plugins and completions named 'git'. + +${echo_underline_yellow}FLAGS${echo_normal} + --enable ${echo_purple}Enable all matching componenents.${echo_normal} + --disable ${echo_purple}Disable all matching componenents.${echo_normal} + --help ${echo_purple}Print this help.${echo_normal} + --refresh ${echo_purple}Force a refresh of the search cache.${echo_normal} + --no-color ${echo_purple}Disable color output and use monochrome text.${echo_normal} + +${echo_underline_yellow}EXAMPLES${echo_normal} + + For example, ${echo_bold_green}bash-it search git${echo_normal} would match any alias, completion + or plugin that has the word 'git' in either the module name or + it's description. You should see something like this when you run this + command: + + ${echo_bold_green}❯ bash-it search git${echo_bold_blue} + ${echo_bold_yellow}aliases: ${echo_bold_green}git ${echo_normal}gitsvn + ${echo_bold_yellow}plugins: ${echo_normal}autojump fasd ${echo_bold_green}git ${echo_normal}git-subrepo jgitflow jump + ${echo_bold_yellow}completions: ${echo_bold_green}git ${echo_normal}git_flow git_flow_avh${echo_normal} + + You can exclude some terms by prefixing a term with a minus, eg: + + ${echo_bold_green}❯ bash-it search git -flow -svn${echo_bold_blue} + ${echo_bold_yellow}aliases: ${echo_normal}git + ${echo_bold_yellow}plugins: ${echo_normal}autojump fasd git git-subrepo jump + ${echo_bold_yellow}completions: ${echo_normal}git${echo_normal} + + Finally, if you prefix a term with '@' symbol, that indicates an exact + match. Note, that we also pass the '--enable' flag, which would ensure + that all matches are automatically enabled. The example is below: + + ${echo_bold_green}❯ bash-it search @git --enable${echo_bold_blue} + ${echo_bold_yellow}aliases: ${echo_normal}git + ${echo_bold_yellow}plugins: ${echo_normal}git + ${echo_bold_yellow}completions: ${echo_normal}git${echo_normal} + +${echo_underline_yellow}SUMMARY${echo_normal} + + Take advantage of the search functionality to discover what Bash-It can do + for you. Try searching for partial term matches, mix and match with the + negative terms, or specify an exact matches of any number of terms. Once + you created the search command that returns ONLY the modules you need, + simply append '--enable' or '--disable' at the end to activate/deactivate + each module. + +" +} + +_bash-it-cache-file() { + local component="${1}" + local file="/tmp/bash_it/${component}.status" + mkdir -p $(dirname ${file}) + printf ${file} +} + +_bash_it_search_cache_clean() { + local component="$1" + if [[ -z ${component} ]] ; then + for component in "${BASH_IT_COMPONENTS[@]}" ; do + _bash_it_search_cache_clean "${component}" + done + else + rm -f $(_bash-it-cache-file ${component}) + fi } #——————————————————————————————————————————————————————————————————————————————— @@ -58,137 +185,261 @@ _bash-it-array-contains-element () { echo -n $r } +_bash-it-array-dedup() { + echo "$*" | tr ' ' '\n' | sort -u | tr '\n' ' ' +} + +_bash-it-grep() { + if [[ -z "${BASH_IT_GREP}" ]] ; then + export BASH_IT_GREP="$(which egrep || which grep || '/usr/bin/grep')" + fi + printf "%s " "${BASH_IT_GREP}" +} + +_bash-it-is-partial-match() { + local component="$1" + local term="$2" + _bash-it-component-help "${component}" | $(_bash-it-grep) -E -i -q -- "${term}" +} + +_bash-it-component-term-matches-negation() { + local match="$1"; shift + local negative + for negative in "$@"; do + [[ "${match}" =~ "${negative}" ]] && return 0 + done + + return 1 +} + +_bash-it-component-help() { + local component="$1" + local file=$(_bash-it-cache-file ${component}) + if [[ ! -s "${file}" || -z $(find "${file}" -mmin -2) ]] ; then + rm -f "${file}" 2>/dev/null + local func="_bash-it-${component}" + ${func} | $(_bash-it-grep) -E ' \[' | cat > ${file} + fi + cat "${file}" +} + +_bash-it-component-list() { + local component="$1" + _bash-it-component-help "${component}" | awk '{print $1}' | uniq | sort | tr '\n' ' ' +} + +_bash-it-component-list-matching() { + local component="$1"; shift + local term="$1" + _bash-it-component-help "${component}" | $(_bash-it-grep) -E -- "${term}" | awk '{print $1}' | sort | uniq +} + +_bash-it-component-list-enabled() { + local component="$1" + _bash-it-component-help "${component}" | $(_bash-it-grep) -E '\[x\]' | awk '{print $1}' | uniq | sort | tr '\n' ' ' +} + +_bash-it-component-list-disabled() { + local component="$1" + _bash-it-component-help "${component}" | $(_bash-it-grep) -E -v '\[x\]' | awk '{print $1}' | uniq | sort | tr '\n' ' ' +} + +_bash-it-component-item-is-enabled() { + local component="$1" + local item="$2" + _bash-it-component-help "${component}" | $(_bash-it-grep) -E '\[x\]' | $(_bash-it-grep) -E -q -- "^${item}\s" +} + +_bash-it-component-item-is-disabled() { + local component="$1" + local item="$2" + _bash-it-component-help "${component}" | $(_bash-it-grep) -E -v '\[x\]' | $(_bash-it-grep) -E -q -- "^${item}\s" +} + + _bash-it-search-component() { - _about 'searches for given terms amongst a given component' - _param '1: component type, one of: [ aliases | plugins | completions ]' - _param '2: term1 ' - _param '3: [-]term2 [-]term3 ...' - _example '$ _bash-it-search-component aliases rake bundler -chruby' - - _component=$1 - - local func=_bash-it-${_component} - local help=$($func) + local component="$1" shift + _about 'searches for given terms amongst a given component' + _param '1: component type, one of: [ aliases | plugins | completions ]' + _param '2: term1 term2 @term3' + _param '3: [-]term4 [-]term5 ...' + _example '$ _bash-it-search-component aliases @git rake bundler -chruby' + # if one of the search terms is --enable or --disable, we will apply - # this action to the matches further down. - local action action_func component_singular - declare -a _search_commands=(enable disable) - for _search_command in "${_search_commands[@]}"; do - if [[ $(_bash-it-array-contains-element "--${_search_command}" "$@") == "true" ]]; then - action=$_search_command - component_singular=${_component} + # this action to the matches further ` down. + local component_singular action action_func + local -a search_commands=(enable disable) + for search_command in "${search_commands[@]}"; do + if [[ $(_bash-it-array-contains-element "--${search_command}" "$@") == "true" ]]; then + component_singular=${component} component_singular=${component_singular/es/} # aliases -> alias component_singular=${component_singular/ns/n} # plugins -> plugin + + action="${search_command}" action_func="_${action}-${component_singular}" break fi done - local _grep=$((which --skip-alias grep 2> /dev/null || which grep) | tail -n 1) + local -a terms=($@) # passed on the command line - declare -a terms=($@) # passed on the command line - declare -a matches=() # results that we found - declare -a negative_terms=() # terms that began with a dash + unset exact_terms + unset partial_terms + unset negative_terms + + local -a exact_terms=() # terms that should be included only if they match exactly + local -a partial_terms=() # terms that should be included if they match partially + local -a negative_terms=() # negated partial terms that should be excluded + + unset component_list + local -a component_list=( $(_bash-it-component-list "${component}") ) + local term for term in "${terms[@]}"; do - # -- can only be used for the actions: enable/disable - [[ "${term:0:2}" == "--" ]] && continue - [[ "${term:0:1}" == "-" ]] && negative_terms=(${negative_terms[@]} ${term:1}) && continue - - # print asterisk next to each result that is already enabled by the user - local term_match=($(echo "${help}"| ${_grep} -i -- ${term} | ${_grep} -E '\[( |x)\]' | cut -b -30 | sed 's/ *\[ \]//g;s/ *\[x\]/*/g;' )) - [[ "${#term_match[@]}" -gt 0 ]] && { - matches=(${matches[@]} ${term_match[@]}) # append to the list of results - } + local search_term="${term:1}" + if [[ "${term:0:2}" == "--" ]] ; then + continue + elif [[ "${term:0:1}" == "-" ]] ; then + negative_terms=(${negative_terms[@]} "${search_term}") + elif [[ "${term:0:1}" == "@" ]] ; then + if [[ $(_bash-it-array-contains-element "${search_term}" "${component_list[@]}") == "true" ]]; then + exact_terms=(${exact_terms[@]} "${search_term}") + fi + else + partial_terms=(${partial_terms[@]} $(_bash-it-component-list-matching "${component}" "${term}") ) + fi done - # now check if we found any negative terms, and subtract them - [[ ${#negative_terms} -gt 0 ]] && { - declare -a filtered_matches=() - for match in "${matches[@]}"; do - local negations=0 - for nt in "${negative_terms[@]}"; do - [[ "${match}" =~ "${nt}" ]] && negations=$(($negations+1)) - done - [[ $negations -eq 0 ]] && filtered_matches=(${filtered_matches[@]} ${match}) - done - matches=(${filtered_matches[@]}) - } + local -a total_matches=( $(_bash-it-array-dedup ${exact_terms[@]} ${partial_terms[@]}) ) - _bash-it-search-result $action $action_func - - unset matches filtered_matches terms + unset matches + declare -a matches=() + for match in ${total_matches[@]}; do + local include_match=true + if [[ ${#negative_terms[@]} -gt 0 ]]; then + ( _bash-it-component-term-matches-negation "${match}" "${negative_terms[@]}" ) && include_match=false + fi + ( ${include_match} ) && matches=(${matches[@]} "${match}") + done + _bash-it-search-result "${component}" "${action}" "${action_func}" "${matches[@]}" + unset matches final_matches terms } _bash-it-search-result() { - local action=$1; shift - local action_func=$1; shift + local component="$1"; shift + local action="$1"; shift + local action_func="$1"; shift + local -a matches=($@) + local color_component color_enable color_disable color_off - [[ -z "$NO_COLOR" ]] && { + color_sep=':' + + ( ${BASH_IT_SEARCH_USE_COLOR} ) && { color_component='\e[1;34m' color_enable='\e[1;32m' + suffix_enable='' + suffix_disable='' color_disable='\e[0;0m' color_off='\e[0;0m' - color_sep=':' } - [[ -n "$NO_COLOR" ]] && { + ( ${BASH_IT_SEARCH_USE_COLOR} ) || { color_component='' - color_sep=' => ' - color_enable='✓' + suffix_enable=' ✓ ︎' + suffix_disable=' ' + color_enable='' color_disable='' color_off='' } - if [[ "${#matches[*]}" -gt 0 ]] ; then - printf "${color_component}%13s${color_sep} ${color_off}" "${_component}" + local match + local modified=0 - sorted_matches=($(echo "${matches[*]}" | tr ' ' '\n' | sort | uniq)) + local flag="DEFER_CACHE_CLEANUP_FOR_${component}" + eval "export ${flag}=true" - for match in "${sorted_matches[@]}"; do - local match_color compatible_action - if [[ $match =~ "*" ]]; then - match_color=$color_enable + if [[ "${#matches[@]}" -gt 0 ]] ; then + printf "${color_component}%13s${color_sep} ${color_off}" "${component}" + + for match in "${matches[@]}"; do + local enabled=0 + ( _bash-it-component-item-is-enabled "${component}" "${match}" ) && enabled=1 + + local match_color compatible_action suffix opposite_suffix + + (( ${enabled} )) && { + match_color=${color_enable} + suffix=${suffix_enable} + opposite_suffix=${suffix_disable} compatible_action="disable" - else - match_color=$color_disable - compatible_action="enable" - fi + } - match_value=${match/\*/} # remove asterisk - len=${#match_value} - if [[ -n $NO_COLOR ]]; then - local m="${match_color}${match_value}" + (( ${enabled} )) || { + match_color=${color_disable} + suffix=${suffix_disable} + opposite_suffix=${suffix_enable} + compatible_action="enable" + } + + local len + if ( ${BASH_IT_SEARCH_USE_COLOR} ); then + local m="${match_color}${match}${suffix}" + len=${#m} + else + local m="${match}${suffix}" len=${#m} fi - printf " ${match_color}${match_value}" # print current state - + printf " ${match_color}${match}${suffix}" # print current state if [[ "${action}" == "${compatible_action}" ]]; then - # oh, i see – we need to either disable enabled, or enable disabled - # component. Let's start with the most important part: redrawing - # the search result backwards. Because style. - - printf "\033[${len}D" - for a in {0..30}; do - [[ $a -gt $len ]] && break - printf "%.*s" $a " " - sleep 0.07 # who knew you could sleep for fraction of the cost :) - done - printf "\033[${len}D" - result=$(${action_func} ${match_value}) + if [[ ${action} == "enable" && ${BASH_IT_SEARCH_USE_COLOR} == false ]]; then + _bash-it-flash-term ${len} "${match}${suffix}" + else + _bash-it-erase-term ${len} + fi + modified=1 + result=$(${action_func} ${match}) local temp="color_${compatible_action}" match_color=${!temp} - printf "${match_color}${match_value}" + _bash-it-rewind ${len} + printf "${match_color}${match}${opposite_suffix}" fi printf "${color_off}" done + [[ ${modified} -gt 0 ]] && _bash_it_search_cache_clean ${component} printf "\n" fi - +} + +_bash-it-rewind() { + local len="$1" + printf "\033[${len}D" +} + +_bash-it-flash-term() { + local len="$1" + local match="$2" + local delay=0.1 + local color + + for color in ${text_black} ${echo_bold_blue} ${bold_yellow} ${bold_red} ${echo_bold_green} ; do + sleep ${delay} + _bash-it-rewind "${len}" + printf "${color}${match}" + done +} + +_bash-it-erase-term() { + local len="$1" + _bash-it-rewind ${len} + for a in {0..30}; do + [[ ${a} -gt ${len} ]] && break + printf "%.*s" $a " " + sleep 0.05 + done } diff --git a/test/lib/helpers.bats b/test/lib/helpers.bats index 7720b930..563bc380 100644 --- a/test/lib/helpers.bats +++ b/test/lib/helpers.bats @@ -2,6 +2,7 @@ load ../test_helper load ../../lib/composure +load ../../lib/search load ../../plugins/available/base.plugin cite _about _param _example _group _author _version diff --git a/test/lib/search.bats b/test/lib/search.bats index 1043c1a4..29e861d7 100644 --- a/test/lib/search.bats +++ b/test/lib/search.bats @@ -1,16 +1,20 @@ #!/usr/bin/env bats load ../test_helper - load ../../lib/composure +load ../../lib/helpers +load ../../lib/search load ../../plugins/available/base.plugin +load ../../aliases/available/git.aliases +load ../../plugins/available/ruby.plugin +load ../../plugins/available/rails.plugin +load ../../completion/available/bundler.completion +load ../../completion/available/gem.completion +load ../../completion/available/rake.completion cite _about _param _example _group _author _version load ../../lib/helpers -load ../../lib/search - -NO_COLOR=true function local_setup { mkdir -p "$BASH_IT" @@ -23,46 +27,68 @@ function local_setup { rm -rf "$BASH_IT"/aliases/enabled rm -rf "$BASH_IT"/completion/enabled rm -rf "$BASH_IT"/plugins/enabled + + mkdir -p "$BASH_IT"/enabled + mkdir -p "$BASH_IT"/aliases/enabled + mkdir -p "$BASH_IT"/completion/enabled + mkdir -p "$BASH_IT"/plugins/enabled + + export OLD_PATH="$PATH" + export PATH="/usr/bin:/bin:/usr/sbin" +} + +function local_teardown { + export PATH="$OLD_PATH" + unset OLD_PATH } @test "search: plugin base" { + export BASH_IT_SEARCH_USE_COLOR=false run _bash-it-search-component 'plugins' 'base' - [[ "${lines[0]}" =~ 'plugins' && "${lines[0]}" =~ 'base' ]] + assert_line -n 0 ' plugins: base ' +} + +@test "search: git" { + run _bash-it-search 'git' --no-color + assert_line -n 0 ' aliases: git gitsvn ' + assert_line -n 1 ' plugins: autojump fasd git git-subrepo jgitflow jump ' + assert_line -n 2 ' completions: git git_flow git_flow_avh ' } @test "search: ruby gem bundle rake rails" { - # first disable them all, so that the output does not appear with a checkbox - # and we can compare the result - run _bash-it-search 'ruby' 'gem' 'bundle' 'rake' 'rails' '--disable' - # Now perform the search - run _bash-it-search 'ruby' 'gem' 'bundle' 'rake' 'rails' - # And verify - assert [ "${lines[0]/✓/}" == ' aliases => bundler rails' ] - assert [ "${lines[1]/✓/}" == ' plugins => chruby chruby-auto rails ruby' ] - assert [ "${lines[2]/✓/}" == ' completions => bundler gem rake' ] + run _bash-it-search rails ruby gem bundler rake --no-color + + assert_line -n 0 ' aliases: bundler rails ' + assert_line -n 1 ' plugins: chruby chruby-auto rails ruby ' + assert_line -n 2 ' completions: bundler gem rake ' } -@test "search: ruby gem bundle -chruby rake rails" { - run _bash-it-search 'ruby' 'gem' 'bundle' 'rake' 'rails' '--disable' - run _bash-it-search 'ruby' 'gem' 'bundle' '-chruby' 'rake' 'rails' - assert [ "${lines[0]/✓/}" == ' aliases => bundler rails' ] - assert [ "${lines[1]/✓/}" == ' plugins => rails ruby' ] - assert [ "${lines[2]/✓/}" == ' completions => bundler gem rake' ] +@test "search: rails ruby gem bundler rake -chruby" { + run _bash-it-search rails ruby gem bundler rake -chruby --no-color + + assert_line -n 0 ' aliases: bundler rails ' + assert_line -n 1 ' plugins: rails ruby ' + assert_line -n 2 ' completions: bundler gem rake ' } -@test "search: (rails enabled) ruby gem bundle rake rails" { - run _bash-it-search 'ruby' 'gem' 'bundle' 'rake' 'rails' '--disable' - run _enable-alias 'rails' - run _bash-it-search 'ruby' 'gem' 'bundle' 'rake' 'rails' - assert_line -n 0 ' aliases => bundler ✓rails' - assert_line -n 1 ' plugins => chruby chruby-auto rails ruby' - assert_line -n 2 ' completions => bundler gem rake' +@test "search: @git" { + run _bash-it-search '@git' --no-color + assert_line -n 0 ' aliases: git ' + assert_line -n 1 ' plugins: git ' + assert_line -n 2 ' completions: git ' } -@test "search: (all enabled) ruby gem bundle rake rails" { - run _bash-it-search 'ruby' 'gem' 'bundle' 'rake' '-chruby' 'rails' '--enable' - run _bash-it-search 'ruby' 'gem' 'bundle' 'rake' '-chruby' 'rails' - assert_line -n 0 ' aliases => ✓bundler ✓rails' - assert_line -n 1 ' plugins => ✓rails ✓ruby' - assert_line -n 2 ' completions => ✓bundler ✓gem ✓rake' +@test "search: @git --enable / --disable" { + set -e + run _bash-it-search '@git' --enable --no-color + run _bash-it-search '@git' --no-color + + [[ "${lines[0]}" =~ '✓' ]] + + run _bash-it-search '@git' --disable --no-color + run _bash-it-search '@git' --no-color + + assert_line -n 0 ' aliases: git ' + assert_line -n 0 ' aliases: git ' + assert_line -n 2 ' completions: git ' } From 09c2be9d6bec0d0d6b917203e3c222970ea8ebe2 Mon Sep 17 00:00:00 2001 From: Konstantin Gredeskoul Date: Fri, 23 Nov 2018 16:14:12 -0800 Subject: [PATCH 02/50] =?UTF-8?q?Refactor=20(extract=20utilities=20+=20add?= =?UTF-8?q?=20tests)=20&=20more=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Extracting common utilities into utilities.bash * Adding new tests for utilities * Relocating the cache file to be under $BASH_IT * Removing cache cleanup deferral code for now * Wiping the cache in local_setup in tests. --- .gitignore | 1 + lib/helpers.bash | 9 ++- lib/search.bash | 142 ++++++++------------------------------- lib/utilities.bash | 144 ++++++++++++++++++++++++++++++++++++++++ test/lib/helpers.bats | 1 + test/lib/search.bats | 2 + test/lib/utilities.bats | 108 ++++++++++++++++++++++++++++++ 7 files changed, 289 insertions(+), 118 deletions(-) mode change 100644 => 100755 lib/search.bash create mode 100644 lib/utilities.bash create mode 100644 test/lib/utilities.bats diff --git a/.gitignore b/.gitignore index 2e6fb7c0..6fad6279 100755 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ bats *.sublime-workspace *.sublime-project enabled/* +tmp/ diff --git a/lib/helpers.bash b/lib/helpers.bash index cbcdd42d..bec603f1 100644 --- a/lib/helpers.bash +++ b/lib/helpers.bash @@ -70,7 +70,7 @@ bash-it () example '$ bash-it disable alias hg [tmux]...' example '$ bash-it migrate' example '$ bash-it update' - example '$ bash-it search [-|@]term1 [-|@]term2 ... [--enable | --disable | --help | --refresh | --no-color ]' + example '$ bash-it search [-|@]term1 [-|@]term2 ... [ -e/--enable ] [ -d/--disable ] [ -r/--refresh ] [ -c/--no-color ]' example '$ bash-it version' example '$ bash-it reload' typeset verb=${1:-} @@ -78,6 +78,7 @@ bash-it () typeset component=${1:-} shift typeset func + case $verb in show) func=_bash-it-$component;; @@ -393,8 +394,7 @@ _disable-thing () fi fi - local flag="DEFER_CACHE_CLEANUP_FOR_${file_type}" - [[ -z ${!flag} ]] && _bash_it_search_cache_clean "${file_type}" + _bash-it-clean-component-cache "${file_type}" if [ -n "$BASH_IT_AUTOMATIC_RELOAD_AFTER_CONFIG_CHANGE" ]; then exec ${0/-/} @@ -491,8 +491,7 @@ _enable-thing () ln -s ../$subdirectory/available/$to_enable "${BASH_IT}/enabled/${use_load_priority}${BASH_IT_LOAD_PRIORITY_SEPARATOR}${to_enable}" fi - local flag="DEFER_CACHE_CLEANUP_FOR_${file_type}" - [[ -z ${!flag} ]] && _bash_it_search_cache_clean "${file_type}" + _bash-it-clean-component-cache "${file_type}" if [ -n "$BASH_IT_AUTOMATIC_RELOAD_AFTER_CONFIG_CHANGE" ]; then exec ${0/-/} diff --git a/lib/search.bash b/lib/search.bash old mode 100644 new mode 100755 index a54971f5..1c487a3e --- a/lib/search.bash +++ b/lib/search.bash @@ -7,10 +7,11 @@ # # Usage: # ❯ bash-it search [-|@]term1 [-|@]term2 ... \ -# [ --enable | -e ] \ -# [ --disable | -d ] \ -# [ --refresh | -r ] -# [ --help | -h ] +# [ --enable | -e ] \ +# [ --disable | -d ] \ +# [ --no-color | -c ] \ +# [ --refresh | -r ] \ +# [ --help | -h ] # # Single dash, as in "-chruby", indicates a negative search term. # Double dash indicates a command that is to be applied to the search result. @@ -52,6 +53,8 @@ _bash-it-search() { _param '2: [ term2 ]...' _example '$ _bash-it-search @git ruby -rvm rake bundler' + [[ -z "$(type _bash-it-array-contains-element 2>/dev/null)" ]] && source "${BASH_IT}/lib/utilities.bash" + local component export BASH_IT_SEARCH_USE_COLOR=true export BASH_IT_GREP=${BASH_IT_GREP:-$(which egrep)} @@ -68,17 +71,19 @@ _bash-it-search() { _bash-it-search-help return 0 elif [[ ${word} == "--refresh" || ${word} == "-r" ]]; then - _bash_it_search_cache_clean - elif [[ ${word} == "--no-color" ]]; then + _bash-it-clean-component-cache + elif [[ ${word} == "--no-color" || ${word} == '-c' ]]; then export BASH_IT_SEARCH_USE_COLOR=false else args=(${args[@]} ${word}) fi done - for component in "${BASH_IT_COMPONENTS[@]}" ; do - _bash-it-search-component "${component}" "${args[@]}" - done + if [[ ${#args} -gt 0 ]]; then + for component in "${BASH_IT_COMPONENTS[@]}" ; do + _bash-it-search-component "${component}" "${args[@]}" + done + fi return 0 } @@ -88,7 +93,11 @@ _bash-it-search-help() { ${echo_underline_yellow}USAGE${echo_normal} bash-it search [-|@]term1 [-|@]term2 ... \\ - [ --enable | --disable | --help | --refresh | --no-color ] + [ --enable | -e ] \\ + [ --disable | -d ] \\ + [ --no-color | -c ] \\ + [ --refresh | -r ] \\ + [ --help | -h ] ${echo_underline_yellow}DESCRIPTION${echo_normal} @@ -110,11 +119,11 @@ ${echo_underline_yellow}DESCRIPTION${echo_normal} eg. '@git' would only match aliases, plugins and completions named 'git'. ${echo_underline_yellow}FLAGS${echo_normal} - --enable ${echo_purple}Enable all matching componenents.${echo_normal} - --disable ${echo_purple}Disable all matching componenents.${echo_normal} - --help ${echo_purple}Print this help.${echo_normal} - --refresh ${echo_purple}Force a refresh of the search cache.${echo_normal} - --no-color ${echo_purple}Disable color output and use monochrome text.${echo_normal} + --enable | -e ${echo_purple}Enable all matching componenents.${echo_normal} + --disable | -d ${echo_purple}Disable all matching componenents.${echo_normal} + --help | -h ${echo_purple}Print this help.${echo_normal} + --refresh | -r ${echo_purple}Force a refresh of the search cache.${echo_normal} + --no-color | -c ${echo_purple}Disable color output and use monochrome text.${echo_normal} ${echo_underline_yellow}EXAMPLES${echo_normal} @@ -156,46 +165,6 @@ ${echo_underline_yellow}SUMMARY${echo_normal} " } -_bash-it-cache-file() { - local component="${1}" - local file="/tmp/bash_it/${component}.status" - mkdir -p $(dirname ${file}) - printf ${file} -} - -_bash_it_search_cache_clean() { - local component="$1" - if [[ -z ${component} ]] ; then - for component in "${BASH_IT_COMPONENTS[@]}" ; do - _bash_it_search_cache_clean "${component}" - done - else - rm -f $(_bash-it-cache-file ${component}) - fi -} - -#——————————————————————————————————————————————————————————————————————————————— -# array=("something to search for" "a string" "test2000") -# _bash-it-array-contains-element "a string" "${array[@]}" -# ( prints "true" or "false" ) -_bash-it-array-contains-element () { - local e - local r=false - for e in "${@:2}"; do [[ "$e" == "$1" ]] && r=true; done - echo -n $r -} - -_bash-it-array-dedup() { - echo "$*" | tr ' ' '\n' | sort -u | tr '\n' ' ' -} - -_bash-it-grep() { - if [[ -z "${BASH_IT_GREP}" ]] ; then - export BASH_IT_GREP="$(which egrep || which grep || '/usr/bin/grep')" - fi - printf "%s " "${BASH_IT_GREP}" -} - _bash-it-is-partial-match() { local component="$1" local term="$2" @@ -212,51 +181,6 @@ _bash-it-component-term-matches-negation() { return 1 } -_bash-it-component-help() { - local component="$1" - local file=$(_bash-it-cache-file ${component}) - if [[ ! -s "${file}" || -z $(find "${file}" -mmin -2) ]] ; then - rm -f "${file}" 2>/dev/null - local func="_bash-it-${component}" - ${func} | $(_bash-it-grep) -E ' \[' | cat > ${file} - fi - cat "${file}" -} - -_bash-it-component-list() { - local component="$1" - _bash-it-component-help "${component}" | awk '{print $1}' | uniq | sort | tr '\n' ' ' -} - -_bash-it-component-list-matching() { - local component="$1"; shift - local term="$1" - _bash-it-component-help "${component}" | $(_bash-it-grep) -E -- "${term}" | awk '{print $1}' | sort | uniq -} - -_bash-it-component-list-enabled() { - local component="$1" - _bash-it-component-help "${component}" | $(_bash-it-grep) -E '\[x\]' | awk '{print $1}' | uniq | sort | tr '\n' ' ' -} - -_bash-it-component-list-disabled() { - local component="$1" - _bash-it-component-help "${component}" | $(_bash-it-grep) -E -v '\[x\]' | awk '{print $1}' | uniq | sort | tr '\n' ' ' -} - -_bash-it-component-item-is-enabled() { - local component="$1" - local item="$2" - _bash-it-component-help "${component}" | $(_bash-it-grep) -E '\[x\]' | $(_bash-it-grep) -E -q -- "^${item}\s" -} - -_bash-it-component-item-is-disabled() { - local component="$1" - local item="$2" - _bash-it-component-help "${component}" | $(_bash-it-grep) -E -v '\[x\]' | $(_bash-it-grep) -E -q -- "^${item}\s" -} - - _bash-it-search-component() { local component="$1" shift @@ -272,7 +196,7 @@ _bash-it-search-component() { local component_singular action action_func local -a search_commands=(enable disable) for search_command in "${search_commands[@]}"; do - if [[ $(_bash-it-array-contains-element "--${search_command}" "$@") == "true" ]]; then + if $(_bash-it-array-contains-element "--${search_command}" "$@"); then component_singular=${component} component_singular=${component_singular/es/} # aliases -> alias component_singular=${component_singular/ns/n} # plugins -> plugin @@ -304,7 +228,7 @@ _bash-it-search-component() { elif [[ "${term:0:1}" == "-" ]] ; then negative_terms=(${negative_terms[@]} "${search_term}") elif [[ "${term:0:1}" == "@" ]] ; then - if [[ $(_bash-it-array-contains-element "${search_term}" "${component_list[@]}") == "true" ]]; then + if $(_bash-it-array-contains-element "${search_term}" "${component_list[@]}"); then exact_terms=(${exact_terms[@]} "${search_term}") fi else @@ -358,9 +282,6 @@ _bash-it-search-result() { local match local modified=0 - local flag="DEFER_CACHE_CLEANUP_FOR_${component}" - eval "export ${flag}=true" - if [[ "${#matches[@]}" -gt 0 ]] ; then printf "${color_component}%13s${color_sep} ${color_off}" "${component}" @@ -384,14 +305,9 @@ _bash-it-search-result() { compatible_action="enable" } + local m="${match}${suffix}" local len - if ( ${BASH_IT_SEARCH_USE_COLOR} ); then - local m="${match_color}${match}${suffix}" - len=${#m} - else - local m="${match}${suffix}" - len=${#m} - fi + len=${#m} printf " ${match_color}${match}${suffix}" # print current state if [[ "${action}" == "${compatible_action}" ]]; then @@ -411,7 +327,7 @@ _bash-it-search-result() { printf "${color_off}" done - [[ ${modified} -gt 0 ]] && _bash_it_search_cache_clean ${component} + [[ ${modified} -gt 0 ]] && _bash-it-clean-component-cache ${component} printf "\n" fi } diff --git a/lib/utilities.bash b/lib/utilities.bash new file mode 100644 index 00000000..93e96606 --- /dev/null +++ b/lib/utilities.bash @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +# +# A collection of reusable functions. + +########################################################################### +# Component-specific functions (component is either an alias, a plugin, or a +# completion). +########################################################################### + +_bash-it-component-help() { + local component=$(_bash-it-pluralize-component "${1}") + local file=$(_bash-it-component-cache-file ${component}) + if [[ ! -s "${file}" || -z $(find "${file}" -mmin -300) ]] ; then + rm -f "${file}" 2>/dev/null + local func="_bash-it-${component}" + ${func} | $(_bash-it-grep) -E ' \[' | cat > ${file} + fi + cat "${file}" +} + +_bash-it-component-cache-file() { + local component=$(_bash-it-pluralize-component "${1}") + local file="${BASH_IT}/tmp/cache/${component}" + [[ -f ${file} ]] || mkdir -p $(dirname ${file}) + printf "${file}" +} + +_bash-it-pluralize-component() { + local component="${1}" + local len=$(( ${#component} - 1 )) + # pluralize component name for consistency + [[ ${component:${len}:1} != 's' ]] && component="${component}s" + [[ ${component} == "alias" ]] && component="aliases" + printf ${component} +} + +_bash-it-clean-component-cache() { + local component="$1" + local cache + local -a BASH_IT_COMPONENTS=(aliases plugins completions) + if [[ -z ${component} ]] ; then + for component in "${BASH_IT_COMPONENTS[@]}" ; do + _bash-it-clean-component-cache "${component}" + done + else + cache="$(_bash-it-component-cache-file ${component})" + [[ -f "${cache}" ]] && rm -f "${cache}" + fi +} + +########################################################################### +# Generic utilies +########################################################################### + +# This function searches an array for an exact match against the term passed +# as the first argument to the function. This function exits as soon as +# a match is found. +# +# Returns: +# 0 when a match is found, otherwise 1. +# +# Examples: +# $ declare -a fruits=(apple orange pear mandarin) +# +# $ _bash-it-array-contains-element apple "@{fruits[@]}" && echo 'contains apple' +# contains apple +# +# $ if $(_bash-it-array-contains-element pear "${fruits[@]}"); then +# echo "contains pear!" +# fi +# contains pear! +# +# +_bash-it-array-contains-element() { + local e + for e in "${@:2}"; do + [[ "$e" == "$1" ]] && return 0 + done + return 1 +} + +# Dedupe a simple array of words without spaces. +_bash-it-array-dedup() { + echo "$*" | tr ' ' '\n' | sort -u | tr '\n' ' ' +} + +# Outputs a full path of the gre found on the filesystem +_bash-it-grep() { + if [[ -z "${BASH_IT_GREP}" ]] ; then + export BASH_IT_GREP="$(which egrep || which grep || '/usr/bin/grep')" + fi + printf "%s " "${BASH_IT_GREP}" +} + + +# Returns an array of items within each compoenent. +_bash-it-component-list() { + local component="$1" + _bash-it-component-help "${component}" | awk '{print $1}' | uniq | sort | tr '\n' ' ' +} + +_bash-it-component-list-matching() { + local component="$1"; shift + local term="$1" + _bash-it-component-help "${component}" | $(_bash-it-grep) -E -- "${term}" | awk '{print $1}' | sort | uniq +} + +_bash-it-component-list-enabled() { + local component="$1" + _bash-it-component-help "${component}" | $(_bash-it-grep) -E '\[x\]' | awk '{print $1}' | uniq | sort | tr '\n' ' ' +} + +_bash-it-component-list-disabled() { + local component="$1" + _bash-it-component-help "${component}" | $(_bash-it-grep) -E -v '\[x\]' | awk '{print $1}' | uniq | sort | tr '\n' ' ' +} + +# Checks if a given item is enabled for a particular component/file-type. +# Uses the component cache if available. +# +# Returns: +# 0 if an item of the component is enabled, 1 otherwise. +# +# Examples: +# _bash-it-component-item-is-enabled alias git && echo "git alias is enabled" +_bash-it-component-item-is-enabled() { + local component="$1" + local item="$2" + _bash-it-component-help "${component}" | $(_bash-it-grep) -E '\[x\]' | $(_bash-it-grep) -E -q -- "^${item}\s" +} + +# Checks if a given item is disabled for a particular component/file-type. +# Uses the component cache if available. +# +# Returns: +# 0 if an item of the component is enabled, 1 otherwise. +# +# Examples: +# _bash-it-component-item-is-disabled alias git && echo "git aliases are disabled" +_bash-it-component-item-is-disabled() { + local component="$1" + local item="$2" + _bash-it-component-help "${component}" | $(_bash-it-grep) -E -v '\[x\]' | $(_bash-it-grep) -E -q -- "^${item}\s" +} diff --git a/test/lib/helpers.bats b/test/lib/helpers.bats index 563bc380..6c4f706a 100644 --- a/test/lib/helpers.bats +++ b/test/lib/helpers.bats @@ -2,6 +2,7 @@ load ../test_helper load ../../lib/composure +load ../../lib/utilities load ../../lib/search load ../../plugins/available/base.plugin diff --git a/test/lib/search.bats b/test/lib/search.bats index 29e861d7..56cda073 100644 --- a/test/lib/search.bats +++ b/test/lib/search.bats @@ -3,6 +3,7 @@ load ../test_helper load ../../lib/composure load ../../lib/helpers +load ../../lib/utilities load ../../lib/search load ../../plugins/available/base.plugin load ../../aliases/available/git.aliases @@ -27,6 +28,7 @@ function local_setup { rm -rf "$BASH_IT"/aliases/enabled rm -rf "$BASH_IT"/completion/enabled rm -rf "$BASH_IT"/plugins/enabled + rm -rf "$BASH_IT"/tmp/cache mkdir -p "$BASH_IT"/enabled mkdir -p "$BASH_IT"/aliases/enabled diff --git a/test/lib/utilities.bats b/test/lib/utilities.bats new file mode 100644 index 00000000..be21fa40 --- /dev/null +++ b/test/lib/utilities.bats @@ -0,0 +1,108 @@ +#!/usr/bin/env bats + +load ../test_helper +load ../../lib/composure +load ../../lib/helpers +load ../../lib/utilities +load ../../lib/search + +cite _about _param _example _group _author _version + +function local_setup { + mkdir -p "$BASH_IT" + lib_directory="$(cd "$(dirname "$0")" && pwd)" + # Use rsync to copy Bash-it to the temp folder + # rsync is faster than cp, since we can exclude the large ".git" folder + rsync -qavrKL -d --delete-excluded --exclude=.git $lib_directory/../../.. "$BASH_IT" + + rm -rf "$BASH_IT"/enabled + rm -rf "$BASH_IT"/aliases/enabled + rm -rf "$BASH_IT"/completion/enabled + rm -rf "$BASH_IT"/plugins/enabled + rm -rf "$BASH_IT"/tmp/cache + + mkdir -p "$BASH_IT"/enabled + mkdir -p "$BASH_IT"/aliases/enabled + mkdir -p "$BASH_IT"/completion/enabled + mkdir -p "$BASH_IT"/plugins/enabled +} + +function has_match() { + $(_bash-it-array-contains-element ${@}) && echo "has" "$1" +} + +function item_enabled() { + $(_bash-it-component-item-is-enabled ${@}) && echo "$1" "$2" "is enabled" +} + +function item_disabled() { + $(_bash-it-component-item-is-disabled ${@}) && echo "$1" "$2" "is disabled" +} + +@test "_bash-it-component-item-is-enabled() - for a disabled item" { + run item_enabled aliases svn + assert_line -n 0 '' +} + +@test "_bash-it-component-item-is-enabled() - for an enabled/disabled item" { + run bash-it enable alias svn + assert_line -n 0 'svn enabled with priority 150.' + + run item_enabled alias svn + assert_line -n 0 'alias svn is enabled' + + run bash-it disable alias svn + assert_line -n 0 'svn disabled.' + + run item_enabled alias svn + assert_line -n 0 '' +} + +@test "_bash-it-component-item-is-disabled() - for a disabled item" { + run item_disabled alias svn + assert_line -n 0 'alias svn is disabled' +} + +@test "_bash-it-component-item-is-disabled() - for an enabled/disabled item" { + run bash-it enable alias svn + assert_line -n 0 'svn enabled with priority 150.' + + run item_disabled alias svn + assert_line -n 0 '' + + run bash-it disable alias svn + assert_line -n 0 'svn disabled.' + + run item_disabled alias svn + assert_line -n 0 'alias svn is disabled' +} + +@test "_bash-it-array-contains-element() - when match is found, and is the first" { + declare -a fruits=(apple pear orange mandarin) + run has_match apple "${fruits[@]}" + assert_line -n 0 'has apple' +} + +@test "_bash-it-array-contains-element() - when match is found, and is the last" { + declare -a fruits=(apple pear orange mandarin) + run has_match mandarin "${fruits[@]}" + assert_line -n 0 'has mandarin' +} + +@test "_bash-it-array-contains-element() - when match is found, and is in the middle" { + declare -a fruits=(apple pear orange mandarin) + run has_match pear "${fruits[@]}" + assert_line -n 0 'has pear' +} + +@test "_bash-it-array-contains-element() - when match is found, and it has spaces" { + declare -a fruits=(apple pear orange mandarin "yellow watermelon") + run has_match "yellow watermelon" "${fruits[@]}" + assert_line -n 0 'has yellow watermelon' +} + +@test "_bash-it-array-contains-element() - when match is not found" { + declare -a fruits=(apple pear orange mandarin) + run has_match xyz "${fruits[@]}" + assert_line -n 0 '' +} From 7136604b57edfc3f9fb3c77f9262ebca47a7bb03 Mon Sep 17 00:00:00 2001 From: con-f-use Date: Mon, 10 Dec 2018 15:03:10 +0100 Subject: [PATCH 03/50] Fix dependency of docker aliases on docker plugin Some of the docker aliases use functions found in the docker plugin. If the plugin is not enabled, these aliases do not work. This commit only defines the aliases if the plugin is enabled. Also changed: `dkelc` now creates an explicit login shell, has the synonym `dkbash` and uses no backticks. Reason: on some docker images the status as login-shell is not set automatically, which causes e.g. `/etc/bash.bashrc` and `/etc/profile` not to be loaded in these images. Note: There should be a convenience function in `lib/helpers.bash` that checks if a plugin is available/enabled/disabled. The if-statement if rather clonky. --- aliases/available/docker.aliases.bash | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/aliases/available/docker.aliases.bash b/aliases/available/docker.aliases.bash index 5606b619..59322b39 100644 --- a/aliases/available/docker.aliases.bash +++ b/aliases/available/docker.aliases.bash @@ -9,7 +9,6 @@ alias dkps='docker ps' # List running Docker containers alias dkpsa='docker ps -a' # List all Docker containers alias dki='docker images' # List Docker images alias dkrmac='docker rm $(docker ps -a -q)' # Delete all Docker containers -alias dkrmlc='docker-remove-most-recent-container' # Delete most recent (i.e., last) Docker container case $OSTYPE in darwin*|*bsd*|*BSD*) @@ -20,12 +19,17 @@ case $OSTYPE in ;; esac -alias dkrmall='docker-remove-stale-assets' # Delete all untagged images and exited containers -alias dkrmli='docker-remove-most-recent-image' # Delete most recent (i.e., last) Docker image -alias dkrmi='docker-remove-images' # Delete images for supplied IDs or all if no IDs are passed as arguments -alias dkideps='docker-image-dependencies' # Output a graph of image dependencies using Graphiz -alias dkre='docker-runtime-environment' # List environmental variables of the supplied image ID -alias dkelc='docker exec -it `dklcid` bash' # Enter last container (works with Docker 1.3 and above) +if [ ! -z "$(command ls "${BASH_IT}/$subdirectory/"{[0-9][0-9][0-9]${BASH_IT_LOAD_PRIORITY_SEPARATOR}docker,docker}.plugin.bash 2>/dev/null | head -1)" ]; then +# Function aliases from docker plugin: + alias dkrmlc='docker-remove-most-recent-container' # Delete most recent (i.e., last) Docker container + alias dkrmall='docker-remove-stale-assets' # Delete all untagged images and exited containers + alias dkrmli='docker-remove-most-recent-image' # Delete most recent (i.e., last) Docker image + alias dkrmi='docker-remove-images' # Delete images for supplied IDs or all if no IDs are passed as arguments + alias dkideps='docker-image-dependencies' # Output a graph of image dependencies using Graphiz + alias dkre='docker-runtime-environment' # List environmental variables of the supplied image ID +fi +alias dkelc='docker exec -it $(dklcid) bash --login' # Enter last container (works with Docker 1.3 and above) +alias dkbash='dkelc' alias dkex='docker exec -it ' # Useful to run any commands into container without leaving host alias dkri='docker run --rm -i ' alias dkrit='docker run --rm -it ' From 60b3572f7c4ce08a37dcfc27703f6c4b0aab9457 Mon Sep 17 00:00:00 2001 From: con-f-use Date: Tue, 11 Dec 2018 21:10:52 +0100 Subject: [PATCH 04/50] Purge `$subdirectory` leftover in docker aliases --- aliases/available/docker.aliases.bash | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aliases/available/docker.aliases.bash b/aliases/available/docker.aliases.bash index 59322b39..52ccc4c6 100644 --- a/aliases/available/docker.aliases.bash +++ b/aliases/available/docker.aliases.bash @@ -19,7 +19,7 @@ case $OSTYPE in ;; esac -if [ ! -z "$(command ls "${BASH_IT}/$subdirectory/"{[0-9][0-9][0-9]${BASH_IT_LOAD_PRIORITY_SEPARATOR}docker,docker}.plugin.bash 2>/dev/null | head -1)" ]; then +if [ ! -z "$(command ls "${BASH_IT}/enabled/"{[0-9][0-9][0-9]${BASH_IT_LOAD_PRIORITY_SEPARATOR}docker,docker}.plugin.bash 2>/dev/null | head -1)" ]; then # Function aliases from docker plugin: alias dkrmlc='docker-remove-most-recent-container' # Delete most recent (i.e., last) Docker container alias dkrmall='docker-remove-stale-assets' # Delete all untagged images and exited containers @@ -29,6 +29,7 @@ if [ ! -z "$(command ls "${BASH_IT}/$subdirectory/"{[0-9][0-9][0-9]${BASH_IT_LOA alias dkre='docker-runtime-environment' # List environmental variables of the supplied image ID fi alias dkelc='docker exec -it $(dklcid) bash --login' # Enter last container (works with Docker 1.3 and above) +alias dkrmflast='docker rm -f $(dklcid)' alias dkbash='dkelc' alias dkex='docker exec -it ' # Useful to run any commands into container without leaving host alias dkri='docker run --rm -i ' From ca5c512a35b79676f4478251445650fc6fdac3a0 Mon Sep 17 00:00:00 2001 From: Nils Winkler Date: Tue, 18 Dec 2018 09:26:25 +0100 Subject: [PATCH 05/50] Removed 'local' keyword --- plugins/available/percol.plugin.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/available/percol.plugin.bash b/plugins/available/percol.plugin.bash index 27b98708..66610662 100644 --- a/plugins/available/percol.plugin.bash +++ b/plugins/available/percol.plugin.bash @@ -27,7 +27,7 @@ _replace_by_history() { if command -v percol>/dev/null; then - local current_version=${BASH_VERSION%%[^0-9]*} + current_version=${BASH_VERSION%%[^0-9]*} if [ $current_version -lt 4 ]; then echo -e "\033[91mWarning: You have to upgrade Bash to Bash v4.x to use the 'percol' plugin.\033[m" echo -e "\033[91m Your current Bash version is $BASH_VERSION.\033[m" From 2bc990269ab503268f8e8cff9781b96d0e0bf077 Mon Sep 17 00:00:00 2001 From: Li Zou Date: Sat, 22 Dec 2018 15:25:08 -0800 Subject: [PATCH 06/50] Add alias for listing untracked files --- aliases/available/git.aliases.bash | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aliases/available/git.aliases.bash b/aliases/available/git.aliases.bash index 169fe7b8..75b7997a 100644 --- a/aliases/available/git.aliases.bash +++ b/aliases/available/git.aliases.bash @@ -77,6 +77,8 @@ alias gcsam="git commit -S -am" alias gstd="git stash drop" alias gstl="git stash list" alias gh='cd "$(git rev-parse --show-toplevel)"' +# Show untracked files +alias gu='git ls-files . --exclude-standard --others' case $OSTYPE in darwin*) From 7b41736d1747939cb9978d3537744f578cab0651 Mon Sep 17 00:00:00 2001 From: Dan Beste Date: Sun, 22 Jul 2018 16:44:06 -0500 Subject: [PATCH 07/50] New theme: essential A simple, console-compatible Bash-it theme. Features: * Reactive coloring for: * Privileged and standard users. * Local and remote hosts. * Exit status for prompt strings 1 and 2. * Console-compatible! * No unicode "tofu" in console sessions. * Matching alignment for prompt strings 1 and 2. Preview: $PS1| $TIME $USER@$HOST:$PWD $PS1| + function foo() { $PS2| | bar $PS2| | baz $PS2| | } --- themes/essential/essential.theme.bash | 96 +++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 themes/essential/essential.theme.bash diff --git a/themes/essential/essential.theme.bash b/themes/essential/essential.theme.bash new file mode 100644 index 00000000..c0b1a38e --- /dev/null +++ b/themes/essential/essential.theme.bash @@ -0,0 +1,96 @@ +#!/usr/bin/env bash + +# https://github.com/koalaman/shellcheck/wiki/Sc2154 +# shellcheck disable=SC2154 + +function _user-prompt() { + local -r user='\\u' + + if [[ "${EUID}" -eq 0 ]]; then + # Privileged users: + local -r user_color="${bold_red}" + else + # Standard users: + local -r user_color="${bold_green}" + fi + + # Print the current user's name (colored according to their current EUID): + echo -e "${user_color}${user}${normal}" +} + +function _host-prompt() { + local -r host='\\h' + + # Check whether or not $SSH_TTY is set: + if [[ -z "${SSH_TTY}" ]]; then + # For local hosts, set the host's prompt color to blue: + local -r host_color="${bold_blue}" + else + # For remote hosts, set the host's prompt color to red: + local -r host_color="${bold_red}" + fi + + # Print the current hostname (colored according to $SSH_TTY's status): + echo -e "${host_color}${host}${normal}" +} + +function _user-at-host-prompt() { + # Concatenate the user and host prompts into: user@host: + echo -e "$(_user-prompt)${bold_white}@$(_host-prompt)" +} + +function _exit-status-prompt() { + local -r prompt_string="${1}" + local -r exit_status="${2}" + + # Check the exit status of the last command captured by $exit_status: + if [[ "${exit_status}" -eq 0 ]]; then + # For commands that return an exit status of zero, set the exit status's + # notifier to green: + local -r exit_status_color="${bold_green}" + else + # For commands that return a non-zero exit status, set the exit status's + # notifier to red: + local -r exit_status_color="${bold_red}" + fi + + echo -ne "${exit_status_color}" + if [[ "${prompt_string}" -eq 1 ]]; then + # $PS1: + echo -e " +${normal} " + elif [[ "${prompt_string}" -eq 2 ]]; then + # $PS2: + echo -e " |${normal} " + else + # Default: + echo -e " ?${normal} " + fi +} + +function _ps1() { + local -r time='\\t' + + echo -ne "${bold_white}${time} " + echo -ne "$(_user-at-host-prompt)" + echo -e "${bold_white}:${normal}${PWD}" + echo -e "$(_exit-status-prompt 1 "${exit_status}")" +} + +function _ps2() { + echo -e "$(_exit-status-prompt 2 "${exit_status}")" +} + +function prompt_command() { + # Capture the exit status of the last command: + local -r exit_status="${?}" + + # Build the $PS1 prompt: + PS1="$(_ps1)" + + # Build the $PS2 prompt: + PS2="$(_ps2)" +} + +safe_append_prompt_command prompt_command + +# vim: sw=2 ts=2 et: From 0fe631ea7cda5b2c1e5ad73e2a83a0770a57e1bf Mon Sep 17 00:00:00 2001 From: Claudia Date: Wed, 2 Jan 2019 17:45:43 +0100 Subject: [PATCH 08/50] Make `bash-it reload` preserve working directory In e5b6869 (part of #1283), a regression was introduced, which caused `bash-it reload` to return with the working directory set to `.bash_it` and the original working directory discarded. The root cause is the function wrapper of `bash-it reload`, which has always relied on the working directory not to change during execution of the wrapped code. This assumption no longer holds with the changes introduced in #1283. This commit fixes the regression by using `pushd`/`popd` in `bash-it reload`. --- lib/helpers.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/helpers.bash b/lib/helpers.bash index e632b80b..4d11e657 100644 --- a/lib/helpers.bash +++ b/lib/helpers.bash @@ -256,7 +256,7 @@ _bash-it-reload() { _about 'reloads a profile file' _group 'lib' - cd "${BASH_IT}" || return + pushd "${BASH_IT}" &> /dev/null || return case $OSTYPE in darwin*) @@ -267,7 +267,7 @@ _bash-it-reload() { ;; esac - cd - &> /dev/null || return + popd &> /dev/null || return } _bash-it-describe () From fe8f5e42d09abd78051df82829227b37996135e7 Mon Sep 17 00:00:00 2001 From: tbhaxor Date: Mon, 7 Jan 2019 01:10:21 +0530 Subject: [PATCH 09/50] added wpscan completions --- completion/available/wpscan.completion.bash | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 completion/available/wpscan.completion.bash diff --git a/completion/available/wpscan.completion.bash b/completion/available/wpscan.completion.bash new file mode 100644 index 00000000..ec517aec --- /dev/null +++ b/completion/available/wpscan.completion.bash @@ -0,0 +1,16 @@ +#!/usr/bin/bash + +if command -v wpscan > /dev/null; then + __wpscan_completion() { + local OPTS=("--help --hh --version --ignore-main-redirect --verbose --output --format --detection-mode --scope --headers --user-agent --vhost --random-user-agent --user-agents-list --http-auth --max-threads --throttle --request-timeout --connect-timeout --disable-tlc-checks --proxy --proxy-auth --cookie-string --cookie-jar --cache-ttl --clear-cache --server --cache-dir --update --no-update --wp-content-dir --wp-plugins-dir --wp-version-detection --main-theme-detection --enumrate --exclude-content-based --plugins-list --plugins-detection --plugins-version-all --plugins-version-detection --themes-list --themes-detection --themes-version-all --themes-version-detection --timthumbs-list --timthumbs-detection --config-backups-list --config-backups-detection --db-exports-list --db-exports-detection --medias-detection --users-list --users-detection --passwords --usernames --multicall-max-passwords --password-attack --stealthy") + COMPREPLY=() + for _opt_ in ${OPTS[@]}; do + if [[ "$_opt_" == "$2"* ]]; then + COMPREPLY+=("$_opt_") + fi + done + } + + complete -F __wpscan_completion wpscan +fi + From 508f82eb95d8aa3d04f3bf722af244cf97ae0379 Mon Sep 17 00:00:00 2001 From: tbhaxor Date: Mon, 7 Jan 2019 01:18:37 +0530 Subject: [PATCH 10/50] added vuejs completions --- completion/available/vuejs.completion.bash | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 completion/available/vuejs.completion.bash diff --git a/completion/available/vuejs.completion.bash b/completion/available/vuejs.completion.bash new file mode 100644 index 00000000..2959f29b --- /dev/null +++ b/completion/available/vuejs.completion.bash @@ -0,0 +1,15 @@ +#!/usr/bin/bash + +if command -v vue > /dev/null; then + __vuejs_completion() { + local OPTS=("--version --help create add invoke inspect serve build ui init config upgrade info") + COMPREPLY=() + for _opt_ in ${OPTS[@]}; do + if [[ "$_opt_" == "$2"* ]]; then + COMPREPLY+=("$_opt_") + fi + done + } + + complete -F __vuejs_completion vue +fi From a636f602c84985528e4b02e326adb32e26645a9b Mon Sep 17 00:00:00 2001 From: tbhaxor Date: Mon, 7 Jan 2019 01:23:00 +0530 Subject: [PATCH 11/50] added main option --- completion/available/wpscan.completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/completion/available/wpscan.completion.bash b/completion/available/wpscan.completion.bash index ec517aec..9f21a0d8 100644 --- a/completion/available/wpscan.completion.bash +++ b/completion/available/wpscan.completion.bash @@ -2,7 +2,7 @@ if command -v wpscan > /dev/null; then __wpscan_completion() { - local OPTS=("--help --hh --version --ignore-main-redirect --verbose --output --format --detection-mode --scope --headers --user-agent --vhost --random-user-agent --user-agents-list --http-auth --max-threads --throttle --request-timeout --connect-timeout --disable-tlc-checks --proxy --proxy-auth --cookie-string --cookie-jar --cache-ttl --clear-cache --server --cache-dir --update --no-update --wp-content-dir --wp-plugins-dir --wp-version-detection --main-theme-detection --enumrate --exclude-content-based --plugins-list --plugins-detection --plugins-version-all --plugins-version-detection --themes-list --themes-detection --themes-version-all --themes-version-detection --timthumbs-list --timthumbs-detection --config-backups-list --config-backups-detection --db-exports-list --db-exports-detection --medias-detection --users-list --users-detection --passwords --usernames --multicall-max-passwords --password-attack --stealthy") + local OPTS=("--help --hh --version --url --ignore-main-redirect --verbose --output --format --detection-mode --scope --headers --user-agent --vhost --random-user-agent --user-agents-list --http-auth --max-threads --throttle --request-timeout --connect-timeout --disable-tlc-checks --proxy --proxy-auth --cookie-string --cookie-jar --cache-ttl --clear-cache --server --cache-dir --update --no-update --wp-content-dir --wp-plugins-dir --wp-version-detection --main-theme-detection --enumrate --exclude-content-based --plugins-list --plugins-detection --plugins-version-all --plugins-version-detection --themes-list --themes-detection --themes-version-all --themes-version-detection --timthumbs-list --timthumbs-detection --config-backups-list --config-backups-detection --db-exports-list --db-exports-detection --medias-detection --users-list --users-detection --passwords --usernames --multicall-max-passwords --password-attack --stealthy") COMPREPLY=() for _opt_ in ${OPTS[@]}; do if [[ "$_opt_" == "$2"* ]]; then From ec088d4271de855e0c173f9df5b3623c38f3b26b Mon Sep 17 00:00:00 2001 From: LanikSJ Date: Tue, 8 Jan 2019 14:01:54 -0800 Subject: [PATCH 12/50] Remove Deprecated Brew Command --- aliases/available/homebrew.aliases.bash | 1 - 1 file changed, 1 deletion(-) diff --git a/aliases/available/homebrew.aliases.bash b/aliases/available/homebrew.aliases.bash index 239d884b..15907518 100644 --- a/aliases/available/homebrew.aliases.bash +++ b/aliases/available/homebrew.aliases.bash @@ -4,7 +4,6 @@ cite 'about-alias' about-alias 'homebrew abbreviations' alias bup='brew update && brew upgrade' -alias bupc='brew update && brew upgrade --cleanup' alias bout='brew outdated' alias bin='brew install' alias brm='brew uninstall' From 56945e3bdfe2b8d00db7ec86dfbbc53388790874 Mon Sep 17 00:00:00 2001 From: Kyle Welsby Date: Wed, 16 Jan 2019 17:15:23 +0000 Subject: [PATCH 13/50] Fixes #1309 _bash-it-clean-component-cache: command not found Silence errors when installing ``` Would you like to enable the ag aliases? [y/N] y ~/.bash_it/lib/helpers.bash: line 488: _bash-it-clean-component-cache: command not found ag enabled with priority 150. ``` --- install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install.sh b/install.sh index c0848cdb..8250c3df 100755 --- a/install.sh +++ b/install.sh @@ -155,6 +155,7 @@ fi # Load dependencies for enabling components source "$BASH_IT/lib/composure.bash" +source "$BASH_IT/lib/utilities.bash" cite _about _param _example _group _author _version source "$BASH_IT/lib/helpers.bash" From fedb72b34f148e9b3dd2c3aa3164c4d58feed2ef Mon Sep 17 00:00:00 2001 From: Kyle Welsby Date: Wed, 16 Jan 2019 17:35:44 +0000 Subject: [PATCH 14/50] Fixes exit 1 issue when tmp cache doesn't exist Found if this `${cache}` file doesn't exist, the single line conditional command would return false causing a `exit 1`. This little change makes the conditional verbose and not cause a false return failing the function. --- lib/utilities.bash | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/utilities.bash b/lib/utilities.bash index 93e96606..d7cf6f3f 100644 --- a/lib/utilities.bash +++ b/lib/utilities.bash @@ -44,7 +44,9 @@ _bash-it-clean-component-cache() { done else cache="$(_bash-it-component-cache-file ${component})" - [[ -f "${cache}" ]] && rm -f "${cache}" + if [[ -f "${cache}" ]] ; then + rm -f "${cache}" + fi fi } From ca58587217661357663967667ff8b2a57df0671a Mon Sep 17 00:00:00 2001 From: Kyle Welsby Date: Wed, 16 Jan 2019 18:16:08 +0000 Subject: [PATCH 15/50] Fixes #1203 convert alias to aliases A ungraceful patch but fixes this one case where the command is singular `alias` while the file are plural with `aliases. --- lib/helpers.bash | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/helpers.bash b/lib/helpers.bash index 7e9ff2ab..3a47cd4a 100644 --- a/lib/helpers.bash +++ b/lib/helpers.bash @@ -452,6 +452,9 @@ _enable-thing () for f in "${BASH_IT}/$subdirectory/available/"*.bash do to_enable=$(basename $f .$file_type.bash) + if [ "$file_type" = "alias" ]; then + to_enable=$(basename $f ".aliases.bash") + fi _enable-thing $subdirectory $file_type $to_enable $load_priority done else From af1bf11748c16fa6bdff83cf1f20ba76caa62181 Mon Sep 17 00:00:00 2001 From: Kaiming Guo Date: Tue, 22 Jan 2019 14:38:58 +0800 Subject: [PATCH 16/50] add MSYS2 aliases Ignore NTUSER.DAT files in Git/MSYS2 Bash. --- aliases/available/msys2.aliases.bash | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 aliases/available/msys2.aliases.bash diff --git a/aliases/available/msys2.aliases.bash b/aliases/available/msys2.aliases.bash new file mode 100644 index 00000000..83b14a13 --- /dev/null +++ b/aliases/available/msys2.aliases.bash @@ -0,0 +1,16 @@ +#!/bin/bash + +cite 'about-alias' +about-alias 'MSYS2 aliases' + +LS_COMMON="-hG" +LS_COMMON="$LS_COMMON --color=auto" +LS_COMMON="$LS_COMMON -I NTUSER.DAT\* -I ntuser.dat\*" + +test -n "$LS_COMMON" && + +# alias +alias ls="command ls $LS_COMMON" +alias ll="ls -l" +alias la="ls -a" +alias lal="ll -a" From 212eb6636eb659b5390a987675c0604d11f60a6d Mon Sep 17 00:00:00 2001 From: Kai-Ming Guo Date: Tue, 22 Jan 2019 17:19:35 +0800 Subject: [PATCH 17/50] complete condition command `test` command to check `$LS_COMMON` argument exists. and merged the next line. --- aliases/available/msys2.aliases.bash | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/aliases/available/msys2.aliases.bash b/aliases/available/msys2.aliases.bash index 83b14a13..a309a5b2 100644 --- a/aliases/available/msys2.aliases.bash +++ b/aliases/available/msys2.aliases.bash @@ -7,10 +7,9 @@ LS_COMMON="-hG" LS_COMMON="$LS_COMMON --color=auto" LS_COMMON="$LS_COMMON -I NTUSER.DAT\* -I ntuser.dat\*" -test -n "$LS_COMMON" && - # alias -alias ls="command ls $LS_COMMON" +# setup the main ls alias if we've established common args +test -n "$LS_COMMON" && alias ls="command ls $LS_COMMON" alias ll="ls -l" alias la="ls -a" alias lal="ll -a" From 34487076ba506437c97ecf796c4560dfda810d37 Mon Sep 17 00:00:00 2001 From: NiilShon <51centimos@gmail.com> Date: Wed, 23 Jan 2019 00:29:11 +0100 Subject: [PATCH 18/50] Update general.aliases.bash --- aliases/available/general.aliases.bash | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aliases/available/general.aliases.bash b/aliases/available/general.aliases.bash index c3455525..542b76cd 100644 --- a/aliases/available/general.aliases.bash +++ b/aliases/available/general.aliases.bash @@ -99,8 +99,9 @@ alias bshenc="bash-it enable completion" # Shorten extract alias xt="extract" -# sudo vim +# sudo editors alias svim="sudo vim" +alias snano="sudo nano" # Display whatever file is regular file or folder catt() { From cb74bd63b9158072a7b59c223ee284918586a02b Mon Sep 17 00:00:00 2001 From: jrab66 Date: Fri, 25 Jan 2019 15:00:21 -0600 Subject: [PATCH 19/50] squash screenshot --- themes/metal/metal.theme.bash | 81 +++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 themes/metal/metal.theme.bash diff --git a/themes/metal/metal.theme.bash b/themes/metal/metal.theme.bash new file mode 100644 index 00000000..e0eddd26 --- /dev/null +++ b/themes/metal/metal.theme.bash @@ -0,0 +1,81 @@ +#!/usr/bin/env bash + +# Emoji-based theme to display source control management and +# virtual environment info beside the ordinary bash prompt. + +# Theme inspired by: +# - Naming your Terminal tabs in OSX Lion - http://thelucid.com/2012/01/04/naming-your-terminal-tabs-in-osx-lion/ +# - Bash_it sexy theme + +# inspired by previous bash_it theme : cupcake + +# Demo: +# ┌ⓔ virtualenv 🐲🤘user @ 💻 host in 🗂️ directory on 🌵 branch {1} ↑1 ↓1 +1 •1 ⌀1 ✗ +# └❯ cd .bash-it/themes/cupcake + +# virtualenv prompts +VIRTUALENV_CHAR="ⓔ " +VIRTUALENV_THEME_PROMPT_PREFIX="" +VIRTUALENV_THEME_PROMPT_SUFFIX="" + +# SCM prompts +SCM_NONE_CHAR="" +SCM_GIT_CHAR="[±] " +SCM_GIT_BEHIND_CHAR="${red}↓${normal}" +SCM_GIT_AHEAD_CHAR="${bold_green}↑${normal}" +SCM_GIT_UNTRACKED_CHAR="⌀" +SCM_GIT_UNSTAGED_CHAR="${bold_yellow}•${normal}" +SCM_GIT_STAGED_CHAR="${bold_green}+${normal}" + +SCM_THEME_PROMPT_DIRTY="" +SCM_THEME_PROMPT_CLEAN="" +SCM_THEME_PROMPT_PREFIX="" +SCM_THEME_PROMPT_SUFFIX="" + +# Git status prompts +GIT_THEME_PROMPT_DIRTY=" ${red}✗${normal}" +GIT_THEME_PROMPT_CLEAN=" ${bold_green}✓${normal}" +GIT_THEME_PROMPT_PREFIX="" +GIT_THEME_PROMPT_SUFFIX="" + +# ICONS ======================================================================= + +icon_start="┌" +icon_user="🤘-🐧" +icon_host="@ 💻 " +icon_directory=" - 🧱 " +icon_branch="🌵" +icon_end="└🤘-> " + +# extra spaces ensure legiblity in prompt + +# FUNCTIONS =================================================================== + +# Display virtual environment info +function virtualenv_prompt { + if [[ -n "$VIRTUAL_ENV" ]]; then + virtualenv=`basename "$VIRTUAL_ENV"` + echo -e "$VIRTUALENV_CHAR$virtualenv " + fi +} + +# Rename tab +function tabname { + printf "\e]1;$1\a" +} + +# Rename window +function winname { + printf "\e]2;$1\a" +} + +# PROMPT OUTPUT =============================================================== + +# Displays the current prompt +function prompt_command() { + PS1="\n${icon_start}$(virtualenv_prompt)${icon_user}${bold_green}\u${normal}${icon_host}${bold_cyan}\h${normal}${icon_directory}${bold_purple}\W${normal}\$([[ -n \$(git branch 2> /dev/null) ]] && echo \" on ${icon_branch} \")${white}$(scm_prompt_info)${normal}\n${icon_end}" + PS2="${icon_end}" +} + +# Runs prompt (this bypasses bash_it $PROMPT setting) +safe_append_prompt_command prompt_command From 55cf918918fb1650eb33bc9a5b706c6e8ceb1201 Mon Sep 17 00:00:00 2001 From: Alex Thiessen Date: Sat, 2 Feb 2019 10:28:24 +0100 Subject: [PATCH 20/50] editorconfig: Respect user rules Editorconfig provides the concept of cascading rules, refer to https://editorconfig.org/#file-location. For instance, I use a rule which limits line length in git commit messages which enables `vim` to re-flow the text by a simple command. Setting `root=true` in LT's own `.editorconfig` file prevents any such user-defined rules from being propagated. Remove `root=true` to enable user-defined rules. --- .editorconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index 4dc96bd5..87e50155 100755 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,5 @@ # EditorConfig is awesome: http://EditorConfig.org -root = true - [*] indent_style = space indent_size = 2 From 83c44fac646ef92e70694f1528d63a6f66b99cfd Mon Sep 17 00:00:00 2001 From: Alex Thiessen Date: Mon, 4 Feb 2019 23:32:39 +0100 Subject: [PATCH 21/50] template/profile: Require interactive shell Bash is run either in interactive mode (e.g. in a terminal) or non-interactive mode (e.g. on an SSH server handling an SCP request). In the latter example, any output performed by bash-it's actions will be interpreted as SCP reply on the client side, typically leading to failed transfers. This is a well-known limitation of the SCP: https://bugzilla.redhat.com/show_bug.cgi?id=20527. In bash's own bashrc example, the first lines of code query for the mode and return early if it's non-interactive: http://git.savannah.gnu.org/cgit/bash.git/tree/examples/startup-files/bashrc?h=bash-5.0#n1. This practice is adopted by Linux distributions (e.g. Ubuntu) and probably other systems. Current mode can be queried as described here: https://www.gnu.org/software/bash/manual/html_node/Is-this-Shell-Interactive_003f.html. Copy the according lines from Debian stretch's default .bashrc (as found in https://packages.debian.org/stretch/bash under /etc/skel/.bashrc) to the bash-it profile template to disable bash-it for non-interactive shells. As a side effect, this change makes SCP faster since the server doesn't need to run any bash-it code in context of SCP handling. --- template/bash_profile.template.bash | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/template/bash_profile.template.bash b/template/bash_profile.template.bash index 5e877900..e7f92dd3 100755 --- a/template/bash_profile.template.bash +++ b/template/bash_profile.template.bash @@ -1,5 +1,11 @@ #!/usr/bin/env bash +# If not running interactively, don't do anything +case $- in + *i*) ;; + *) return;; +esac + # Path to the bash it configuration export BASH_IT="{{BASH_IT}}" From f14d4fcc3c7228b0eb6d905421407112d7c594cf Mon Sep 17 00:00:00 2001 From: markusdd Date: Fri, 22 Feb 2019 14:41:01 +0100 Subject: [PATCH 22/50] turn coloured cat and less into ccat and cless old approach would override systems cat, which is malicious when used in conjunction with shell piping or redirection --- plugins/available/less-pretty-cat.plugin.bash | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/available/less-pretty-cat.plugin.bash b/plugins/available/less-pretty-cat.plugin.bash index 9221dde6..841122ca 100644 --- a/plugins/available/less-pretty-cat.plugin.bash +++ b/plugins/available/less-pretty-cat.plugin.bash @@ -6,8 +6,9 @@ if $(command -v pygmentize &> /dev/null) ; then CAT_BIN=$(which cat) LESS_BIN=$(which less) - # pigmentize cat and less outputs - function cat() + # pigmentize cat and less outputs - call them ccat and cless to avoid that + # especially cat'ed output in scripts gets mangled with pygemtized meta characters + function ccat() { about 'runs either pygmentize or cat on each file passed in' param '*: files to concatenate (as normally passed to cat)' @@ -18,7 +19,7 @@ if $(command -v pygmentize &> /dev/null) ; then done } - function less() + function cless() { about 'it pigments the file passed in and passes it to less for pagination' param '$1: the file to paginate with less' From 1e17fa1fd2f442836ccbe41b15e376fcb6159894 Mon Sep 17 00:00:00 2001 From: tbhaxor Date: Sun, 24 Feb 2019 21:24:13 +0530 Subject: [PATCH 23/50] added flutter completions --- completion/available/flutter.completion.bash | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 completion/available/flutter.completion.bash diff --git a/completion/available/flutter.completion.bash b/completion/available/flutter.completion.bash new file mode 100644 index 00000000..a274b5f3 --- /dev/null +++ b/completion/available/flutter.completion.bash @@ -0,0 +1,5 @@ +#!/usr/bin/bash + +if command -v flutter > /dev/null; then + eval "$(flutter bash-completion)" +fi \ No newline at end of file From 50aebc5c6e09d324e9a1463ba022db5e6be644a8 Mon Sep 17 00:00:00 2001 From: tbhaxor Date: Mon, 25 Feb 2019 16:44:54 +0530 Subject: [PATCH 24/50] fixed code style --- completion/available/flutter.completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/completion/available/flutter.completion.bash b/completion/available/flutter.completion.bash index a274b5f3..6372dfe4 100644 --- a/completion/available/flutter.completion.bash +++ b/completion/available/flutter.completion.bash @@ -1,5 +1,5 @@ #!/usr/bin/bash if command -v flutter > /dev/null; then - eval "$(flutter bash-completion)" -fi \ No newline at end of file + eval "$(flutter bash-completion)" +fi From 29f79dfa75f305e3322e8f46bac1af5a8607be58 Mon Sep 17 00:00:00 2001 From: tbhaxor Date: Mon, 25 Feb 2019 16:45:52 +0530 Subject: [PATCH 25/50] :bug: fix --- completion/available/flutter.completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/completion/available/flutter.completion.bash b/completion/available/flutter.completion.bash index 6372dfe4..62befc82 100644 --- a/completion/available/flutter.completion.bash +++ b/completion/available/flutter.completion.bash @@ -1,5 +1,5 @@ #!/usr/bin/bash -if command -v flutter > /dev/null; then +if _command_exists flutter; then eval "$(flutter bash-completion)" fi From 81c3f709007e190cf71558c88324d6ff6fa6dc79 Mon Sep 17 00:00:00 2001 From: Anthony Wharton Date: Thu, 7 Mar 2019 20:12:50 +0000 Subject: [PATCH 26/50] Add eval to SCM_BRANCH in powerline theme --- themes/powerline/powerline.base.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/powerline/powerline.base.bash b/themes/powerline/powerline.base.bash index 08da5f7c..b648e2a0 100644 --- a/themes/powerline/powerline.base.bash +++ b/themes/powerline/powerline.base.bash @@ -99,7 +99,7 @@ function __powerline_scm_prompt { elif [[ "${SCM_HG_CHAR}" == "${SCM_CHAR}" ]]; then scm_prompt+="${SCM_CHAR}${SCM_BRANCH}${SCM_STATE}" fi - echo "${scm_prompt}${scm}|${color}" + echo "$(eval "echo ${scm_prompt}")${scm}|${color}" fi } From 93348838a1573342ea369dc3b3351ed23d9e5786 Mon Sep 17 00:00:00 2001 From: tbhaxor Date: Sun, 10 Mar 2019 08:19:14 +0530 Subject: [PATCH 27/50] bug fix in wpscan args --- completion/available/wpscan.completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/completion/available/wpscan.completion.bash b/completion/available/wpscan.completion.bash index 9f21a0d8..3630183e 100644 --- a/completion/available/wpscan.completion.bash +++ b/completion/available/wpscan.completion.bash @@ -2,7 +2,7 @@ if command -v wpscan > /dev/null; then __wpscan_completion() { - local OPTS=("--help --hh --version --url --ignore-main-redirect --verbose --output --format --detection-mode --scope --headers --user-agent --vhost --random-user-agent --user-agents-list --http-auth --max-threads --throttle --request-timeout --connect-timeout --disable-tlc-checks --proxy --proxy-auth --cookie-string --cookie-jar --cache-ttl --clear-cache --server --cache-dir --update --no-update --wp-content-dir --wp-plugins-dir --wp-version-detection --main-theme-detection --enumrate --exclude-content-based --plugins-list --plugins-detection --plugins-version-all --plugins-version-detection --themes-list --themes-detection --themes-version-all --themes-version-detection --timthumbs-list --timthumbs-detection --config-backups-list --config-backups-detection --db-exports-list --db-exports-detection --medias-detection --users-list --users-detection --passwords --usernames --multicall-max-passwords --password-attack --stealthy") + local OPTS=("--help --hh --version --url --ignore-main-redirect --verbose --output --format --detection-mode --scope --headers --user-agent --vhost --random-user-agent --user-agents-list --http-auth --max-threads --throttle --request-timeout --connect-timeout --disable-tlc-checks --proxy --proxy-auth --cookie-string --cookie-jar --cache-ttl --clear-cache --server --cache-dir --update --no-update --wp-content-dir --wp-plugins-dir --wp-version-detection --main-theme-detection --enumerate --exclude-content-based --plugins-list --plugins-detection --plugins-version-all --plugins-version-detection --themes-list --themes-detection --themes-version-all --themes-version-detection --timthumbs-list --timthumbs-detection --config-backups-list --config-backups-detection --db-exports-list --db-exports-detection --medias-detection --users-list --users-detection --passwords --usernames --multicall-max-passwords --password-attack --stealthy") COMPREPLY=() for _opt_ in ${OPTS[@]}; do if [[ "$_opt_" == "$2"* ]]; then From 2eeaf522c0359e455f4827281cd6d149ba10c399 Mon Sep 17 00:00:00 2001 From: tbhaxor Date: Thu, 14 Mar 2019 01:54:35 +0530 Subject: [PATCH 28/50] added parrot os like terminal prompt --- themes/parrot/parrot.theme.bash | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 themes/parrot/parrot.theme.bash diff --git a/themes/parrot/parrot.theme.bash b/themes/parrot/parrot.theme.bash new file mode 100644 index 00000000..63e1cf33 --- /dev/null +++ b/themes/parrot/parrot.theme.bash @@ -0,0 +1,38 @@ +# +# ~/.bashrc +# + +# git branch parser +function parse_git_branch() { + echo -e "\033[1;34m$(git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1)/')\033[0m" +} + +function parse_git_branch_no_color() { + echo -e "$(git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1)/')" +} + +function promp() { + # If not running interactively, don't do anything + [[ $- != *i* ]] && return + + force_color_prompt=yes + + if [ -n "$force_color_prompt" ]; then + if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then + # We have color support; assume it's compliant with Ecma-48 + # (ISO/IEC-6429). (Lack of such support is extremely rare, and such + # a case would tend to support setf rather than setaf.) + color_prompt=yes + else + color_prompt= + fi + fi + + if [ "$color_prompt" = yes ]; then + PS1="\[\033[0;31m\]\342\224\214\342\224\200\$([[ \$? != 0 ]] && echo \"[\[\033[0;31m\]\342\234\227\[\033[0;37m\]]\342\224\200\")[$(if [[ ${EUID} == 0 ]]; then echo '\[\033[01;31m\]root\[\033[01;33m\]@\[\033[01;96m\]\h'; else echo '\[\033[0;39m\]\u\[\033[01;33m\]@\[\033[01;96m\]\h'; fi)\[\033[0;31m\]]\342\224\200[\[\033[0;32m\]\w\[\033[0;31m\]]\n\[\033[0;31m\]\342\224\224\342\224\200\342\224\200\342\225\274 \[\033[0m\]\[\e[01;33m\]$(parse_git_branch) $\[\e[0m\] " + + else + PS1='┌──[\u@\h]─[\w]\n└──╼ $(parse_git_branch_no_color) $ ' + fi +} + From 7a88b6524ec991aa5b9269ac465ce15fcf5d62c7 Mon Sep 17 00:00:00 2001 From: tbhaxor Date: Thu, 14 Mar 2019 13:04:37 +0530 Subject: [PATCH 29/50] deleted unnecessary line --- themes/parrot/parrot.theme.bash | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/themes/parrot/parrot.theme.bash b/themes/parrot/parrot.theme.bash index 63e1cf33..1aa0ab93 100644 --- a/themes/parrot/parrot.theme.bash +++ b/themes/parrot/parrot.theme.bash @@ -1,6 +1,4 @@ -# -# ~/.bashrc -# + # git branch parser function parse_git_branch() { From 9576b878c803156985853b0a2f2f27228116ae98 Mon Sep 17 00:00:00 2001 From: tbhaxor Date: Thu, 14 Mar 2019 13:11:04 +0530 Subject: [PATCH 30/50] :bug: fix in prompt --- themes/parrot/parrot.theme.bash | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/themes/parrot/parrot.theme.bash b/themes/parrot/parrot.theme.bash index 1aa0ab93..afc8dd53 100644 --- a/themes/parrot/parrot.theme.bash +++ b/themes/parrot/parrot.theme.bash @@ -9,20 +9,20 @@ function parse_git_branch_no_color() { echo -e "$(git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1)/')" } -function promp() { +function prompt() { # If not running interactively, don't do anything [[ $- != *i* ]] && return - force_color_prompt=yes + local force_color_prompt=yes if [ -n "$force_color_prompt" ]; then if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then # We have color support; assume it's compliant with Ecma-48 # (ISO/IEC-6429). (Lack of such support is extremely rare, and such # a case would tend to support setf rather than setaf.) - color_prompt=yes + local color_prompt=yes else - color_prompt= + local color_prompt= fi fi @@ -34,3 +34,4 @@ function promp() { fi } +safe_append_prompt_command prompt From c61d716ee4609538825737a46f1223045fd392d0 Mon Sep 17 00:00:00 2001 From: Renan Date: Thu, 14 Mar 2019 15:21:55 +0100 Subject: [PATCH 31/50] add git patch alias --- aliases/available/git.aliases.bash | 1 + 1 file changed, 1 insertion(+) diff --git a/aliases/available/git.aliases.bash b/aliases/available/git.aliases.bash index 75b7997a..b4a3274b 100644 --- a/aliases/available/git.aliases.bash +++ b/aliases/available/git.aliases.bash @@ -67,6 +67,7 @@ alias gt="git tag" alias gta="git tag -a" alias gtd="git tag -d" alias gtl="git tag -l" +alias gpatch="git format-patch -1" # From http://blogs.atlassian.com/2014/10/advanced-git-aliases/ # Show commits since last pull alias gnew="git log HEAD@{1}..HEAD@{0}" From 9f37a1a0d0a47a6b44544b261ca99d3dd339b755 Mon Sep 17 00:00:00 2001 From: Sangeeth Sudheer Date: Thu, 21 Mar 2019 13:21:09 +0530 Subject: [PATCH 32/50] feat(git.aliases): alias to push current branch w/ tracking --- aliases/available/git.aliases.bash | 1 + 1 file changed, 1 insertion(+) diff --git a/aliases/available/git.aliases.bash b/aliases/available/git.aliases.bash index b4a3274b..29fae023 100644 --- a/aliases/available/git.aliases.bash +++ b/aliases/available/git.aliases.bash @@ -31,6 +31,7 @@ alias gp='git push' alias gpo='git push origin' alias gpu='git push --set-upstream' alias gpuo='git push --set-upstream origin' +alias gpuoc='git push --set-upstream origin $(git symbolic-ref --short HEAD)' alias gpom='git push origin master' alias gr='git remote' alias grv='git remote -v' From 7156011f83ea595f9406bb606ff9f9be68f1b4ed Mon Sep 17 00:00:00 2001 From: Truong Ma Phi Date: Fri, 22 Mar 2019 16:21:29 +0700 Subject: [PATCH 33/50] Update docker completion --- completion/available/docker.completion.bash | 994 +++++++++++++++----- 1 file changed, 766 insertions(+), 228 deletions(-) diff --git a/completion/available/docker.completion.bash b/completion/available/docker.completion.bash index 3a4fbe3d..2b3e805c 100644 --- a/completion/available/docker.completion.bash +++ b/completion/available/docker.completion.bash @@ -1,12 +1,14 @@ #!/usr/bin/env bash -# shellcheck disable=SC2016,SC2119,SC2155 +# shellcheck disable=SC2016,SC2119,SC2155,SC2206,SC2207 # # Shellcheck ignore list: # - SC2016: Expressions don't expand in single quotes, use double quotes for that. # - SC2119: Use foo "$@" if function's $1 should mean script's $1. # - SC2155: Declare and assign separately to avoid masking return values. -# -# You can find more details for each warning at the following page: +# - SC2206: Quote to prevent word splitting, or split robustly with mapfile or read -a. +# - SC2207: Prefer mapfile or read -a to split command output (or quote to avoid splitting). +# +# You can find more details for each warning at the following page: # https://github.com/koalaman/shellcheck/wiki/ # # bash completion file for core docker commands @@ -68,7 +70,7 @@ __docker_previous_extglob_setting=$(shopt -p extglob) shopt -s extglob __docker_q() { - docker ${host:+-H "$host"} ${config:+--config "$config"} 2>/dev/null "$@" + docker ${host:+--host "$host"} ${config:+--config "$config"} ${context:+--context "$context"} 2>/dev/null "$@" } # __docker_configs returns a list of configs. Additional options to @@ -156,6 +158,11 @@ __docker_complete_containers_running() { __docker_complete_containers "$@" --filter status=running } +# shellcheck disable=SC2120 +__docker_complete_containers_stoppable() { + __docker_complete_containers "$@" --filter status=running --filter status=paused +} + # shellcheck disable=SC2120 __docker_complete_containers_stopped() { __docker_complete_containers "$@" --filter status=exited @@ -178,52 +185,103 @@ __docker_complete_container_ids() { COMPREPLY=( $(compgen -W "${containers[*]}" -- "$cur") ) } +# __docker_contexts returns a list of contexts without the special "default" context. +# Completions may be added with `--add`, e.g. `--add default`. +__docker_contexts() { + local add=() + while true ; do + case "$1" in + --add) + add+=("$2") + shift 2 + ;; + *) + break + ;; + esac + done + __docker_q context ls -q + echo "${add[@]}" +} + +__docker_complete_contexts() { + local contexts=( $(__docker_contexts "$@") ) + COMPREPLY=( $(compgen -W "${contexts[*]}" -- "$cur") ) +} + + +# __docker_images returns a list of images. For each image, up to three representations +# can be generated: the repository (e.g. busybox), repository:tag (e.g. busybox:latest) +# and the ID (e.g. sha256:ee22cbbd4ea3dff63c86ba60c7691287c321e93adfc1009604eb1dde7ec88645). +# +# The optional arguments `--repo`, `--tag` and `--id` select the representations that +# may be returned. Whether or not a particular representation is actually returned +# depends on the user's customization through several environment variables: +# - image IDs are only shown if DOCKER_COMPLETION_SHOW_IMAGE_IDS=all|non-intermediate. +# - tags can be excluded by setting DOCKER_COMPLETION_SHOW_TAGS=no. +# - repositories are always shown. +# +# In cases where an exact image specification is needed, `--force-tag` can be used. +# It ignores DOCKER_COMPLETION_SHOW_TAGS and only lists valid repository:tag combinations, +# avoiding repository names that would default to a potentially missing default tag. +# +# Additional arguments to `docker image ls` may be specified in order to filter the list, +# e.g. `__docker_images --filter dangling=true`. +# __docker_images() { - local images_args="" + local repo_format='{{.Repository}}' + local tag_format='{{.Repository}}:{{.Tag}}' + local id_format='{{.ID}}' + local all + local format - case "$DOCKER_COMPLETION_SHOW_IMAGE_IDS" in - all) - images_args="--no-trunc -a" - ;; - non-intermediate) - images_args="--no-trunc" - ;; - esac - - local repo_print_command - if [ "${DOCKER_COMPLETION_SHOW_TAGS:-yes}" = "yes" ]; then - repo_print_command='print $1; print $1":"$2' - else - repo_print_command='print $1' + if [ "$DOCKER_COMPLETION_SHOW_IMAGE_IDS" = "all" ] ; then + all='--all' fi - local awk_script - case "$DOCKER_COMPLETION_SHOW_IMAGE_IDS" in - all|non-intermediate) - awk_script='NR>1 { print $3; if ($1 != "") { '"$repo_print_command"' } }' - ;; - none|*) - awk_script='NR>1 && $1 != "" { '"$repo_print_command"' }' - ;; - esac + while true ; do + case "$1" in + --repo) + format+="$repo_format\n" + shift + ;; + --tag) + if [ "${DOCKER_COMPLETION_SHOW_TAGS:-yes}" = "yes" ]; then + format+="$tag_format\n" + fi + shift + ;; + --id) + if [[ $DOCKER_COMPLETION_SHOW_IMAGE_IDS =~ ^(all|non-intermediate)$ ]] ; then + format+="$id_format\n" + fi + shift + ;; + --force-tag) + # like `--tag` but ignores environment setting + format+="$tag_format\n" + shift + ;; + *) + break + ;; + esac + done - __docker_q images $images_args | awk "$awk_script" | grep -v '$' + __docker_q image ls --no-trunc --format "${format%\\n}" $all "$@" | grep -v '$' } +# __docker_complete_images applies completion of images based on the current value of `$cur` or +# the value of the optional first option `--cur`, if given. +# See __docker_images for customization of the returned items. __docker_complete_images() { - COMPREPLY=( $(compgen -W "$(__docker_images)" -- "$cur") ) - __ltrim_colon_completions "$cur" -} - -__docker_complete_image_repos() { - local repos="$(__docker_q images | awk 'NR>1 && $1 != "" { print $1 }')" - COMPREPLY=( $(compgen -W "$repos" -- "$cur") ) -} - -__docker_complete_image_repos_and_tags() { - local reposAndTags="$(__docker_q images | awk 'NR>1 && $1 != "" { print $1; print $1":"$2 }')" - COMPREPLY=( $(compgen -W "$reposAndTags" -- "$cur") ) - __ltrim_colon_completions "$cur" + local current="$cur" + if [ "$1" = "--cur" ] ; then + current="$2" + shift 2 + fi + COMPREPLY=( $(compgen -W "$(__docker_images "$@")" -- "$current") ) + __ltrim_colon_completions "$current" } # __docker_networks returns a list of all networks. Additional options to @@ -262,10 +320,9 @@ __docker_complete_networks() { COMPREPLY=( $(compgen -W "$(__docker_networks "$@")" -- "$current") ) } -# shellcheck disable=SC2128,SC2178 __docker_complete_containers_in_network() { - local containers=$(__docker_q network inspect -f '{{range $i, $c := .Containers}}{{$i}} {{$c.Name}} {{end}}' "$1") - COMPREPLY=( $(compgen -W "$containers" -- "$cur") ) + local containers=($(__docker_q network inspect -f '{{range $i, $c := .Containers}}{{$i}} {{$c.Name}} {{end}}' "$1")) + COMPREPLY=( $(compgen -W "${containers[*]}" -- "$cur") ) } # __docker_volumes returns a list of all volumes. Additional options to @@ -321,8 +378,7 @@ __docker_plugins_bundled() { for del in "${remove[@]}" ; do plugins=(${plugins[@]/$del/}) done - # shellcheck disable=SC2145 - echo "${plugins[@]} ${add[@]}" + echo "${plugins[@]}" "${add[@]}" } # __docker_complete_plugins_bundled applies completion of plugins based on the current @@ -539,23 +595,64 @@ __docker_append_to_completions() { COMPREPLY=( ${COMPREPLY[@]/%/"$1"} ) } -# __docker_daemon_is_experimental tests whether the currently configured Docker -# daemon runs in experimental mode. If so, the function exits with 0 (true). -# Otherwise, or if the result cannot be determined, the exit value is 1 (false). -__docker_daemon_is_experimental() { - [ "$(__docker_q version -f '{{.Server.Experimental}}')" = "true" ] +# __docker_fetch_info fetches information about the configured Docker server and updates +# several variables with the results. +# The result is cached for the duration of one invocation of bash completion. +__docker_fetch_info() { + if [ -z "$info_fetched" ] ; then + read -r client_experimental server_experimental server_os <<< "$(__docker_q version -f '{{.Client.Experimental}} {{.Server.Experimental}} {{.Server.Os}}')" + info_fetched=true + fi } -# __docker_daemon_os_is tests whether the currently configured Docker daemon runs +# __docker_client_is_experimental tests whether the Docker cli is configured to support +# experimental features. If so, the function exits with 0 (true). +# Otherwise, or if the result cannot be determined, the exit value is 1 (false). +__docker_client_is_experimental() { + __docker_fetch_info + [ "$client_experimental" = "true" ] +} + +# __docker_server_is_experimental tests whether the currently configured Docker +# server runs in experimental mode. If so, the function exits with 0 (true). +# Otherwise, or if the result cannot be determined, the exit value is 1 (false). +__docker_server_is_experimental() { + __docker_fetch_info + [ "$server_experimental" = "true" ] +} + +# __docker_server_os_is tests whether the currently configured Docker server runs # on the operating system passed in as the first argument. -# It does so by querying the daemon for its OS. The result is cached for the duration -# of one invocation of bash completion so that this function can be used to test for -# several different operating systems without additional costs. # Known operating systems: linux, windows. -__docker_daemon_os_is() { +__docker_server_os_is() { local expected_os="$1" - local actual_os=${daemon_os=$(__docker_q version -f '{{.Server.Os}}')} - [ "$actual_os" = "$expected_os" ] + __docker_fetch_info + [ "$server_os" = "$expected_os" ] +} + +# __docker_stack_orchestrator_is tests whether the client is configured to use +# the orchestrator that is passed in as the first argument. +__docker_stack_orchestrator_is() { + case "$1" in + kubernetes) + if [ -z "$stack_orchestrator_is_kubernetes" ] ; then + __docker_q stack ls --help | grep -qe --namespace + stack_orchestrator_is_kubernetes=$? + fi + return $stack_orchestrator_is_kubernetes + ;; + swarm) + if [ -z "$stack_orchestrator_is_swarm" ] ; then + __docker_q stack deploy --help | grep -qe "with-registry-auth" + stack_orchestrator_is_swarm=$? + fi + return $stack_orchestrator_is_swarm + ;; + *) + return 1 + ;; + + esac } # __docker_pos_first_nonflag finds the position of the first word that is neither @@ -816,6 +913,7 @@ __docker_complete_log_drivers() { gelf journald json-file + local logentries none splunk @@ -839,7 +937,8 @@ __docker_complete_log_options() { local gcplogs_options="$common_options1 $common_options2 gcp-log-cmd gcp-meta-id gcp-meta-name gcp-meta-zone gcp-project" local gelf_options="$common_options1 $common_options2 gelf-address gelf-compression-level gelf-compression-type gelf-tcp-max-reconnect gelf-tcp-reconnect-delay tag" local journald_options="$common_options1 $common_options2 tag" - local json_file_options="$common_options1 $common_options2 max-file max-size" + local json_file_options="$common_options1 $common_options2 compress max-file max-size" + local local_options="$common_options1 compress max-file max-size" local logentries_options="$common_options1 $common_options2 line-only logentries-token tag" local splunk_options="$common_options1 $common_options2 splunk-caname splunk-capath splunk-format splunk-gzip splunk-gzip-level splunk-index splunk-insecureskipverify splunk-source splunk-sourcetype splunk-token splunk-url splunk-verify-connection tag" local syslog_options="$common_options1 $common_options2 syslog-address syslog-facility syslog-format syslog-tls-ca-cert syslog-tls-cert syslog-tls-key syslog-tls-skip-verify tag" @@ -868,6 +967,9 @@ __docker_complete_log_options() { json-file) COMPREPLY=( $( compgen -W "$json_file_options" -S = -- "$cur" ) ) ;; + local) + COMPREPLY=( $( compgen -W "$local_options" -S = -- "$cur" ) ) + ;; logentries) COMPREPLY=( $( compgen -W "$logentries_options" -S = -- "$cur" ) ) ;; @@ -897,7 +999,7 @@ __docker_complete_log_driver_options() { __docker_nospace return ;; - fluentd-async-connect) + compress|fluentd-async-connect) COMPREPLY=( $( compgen -W "false true" -- "${cur##*=}" ) ) return ;; @@ -1024,6 +1126,23 @@ __docker_complete_signals() { COMPREPLY=( $( compgen -W "${signals[*]} ${signals[*]#SIG}" -- "$( echo "$cur" | tr '[:lower:]' '[:upper:]')" ) ) } +__docker_complete_stack_orchestrator_options() { + case "$prev" in + --kubeconfig) + _filedir + return 0 + ;; + --namespace) + return 0 + ;; + --orchestrator) + COMPREPLY=( $( compgen -W "all kubernetes swarm" -- "$cur") ) + return 0 + ;; + esac + return 1 +} + __docker_complete_user_group() { if [[ $cur == *:* ]] ; then COMPREPLY=( $(compgen -g -- "${cur#*:}") ) @@ -1046,6 +1165,10 @@ _docker_docker() { _filedir -d return ;; + --context|-c) + __docker_complete_contexts + return + ;; --log-level|-l) __docker_complete_log_levels return @@ -1062,7 +1185,8 @@ _docker_docker() { *) local counter=$( __docker_pos_first_nonflag "$(__docker_to_extglob "$global_options_with_args")" ) if [ "$cword" -eq "$counter" ]; then - __docker_daemon_is_experimental && commands+=(${experimental_commands[*]}) + __docker_client_is_experimental && commands+=(${experimental_client_commands[*]}) + __docker_server_is_experimental && commands+=(${experimental_server_commands[*]}) COMPREPLY=( $( compgen -W "${commands[*]} help" -- "$cur" ) ) fi ;; @@ -1078,6 +1202,41 @@ _docker_build() { } +_docker_builder() { + local subcommands=" + prune + " + __docker_subcommands "$subcommands" && return + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) + ;; + *) + COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) ) + ;; + esac +} + +_docker_builder_prune() { + case "$prev" in + --filter) + COMPREPLY=( $( compgen -S = -W "description id inuse parent private shared type until unused-for" -- "$cur" ) ) + __docker_nospace + return + ;; + --keep-storage) + return + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--all -a --filter --force -f --help --keep-storage" -- "$cur" ) ) + ;; + esac +} + _docker_checkpoint() { local subcommands=" create @@ -1194,14 +1353,18 @@ _docker_config_create() { --label|-l) return ;; + --template-driver) + COMPREPLY=( $( compgen -W "golang" -- "$cur" ) ) + return + ;; esac case "$cur" in -*) - COMPREPLY=( $( compgen -W "--help --label -l" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--help --label -l --template-driver" -- "$cur" ) ) ;; *) - local counter=$(__docker_pos_first_nonflag '--label|-l') + local counter=$(__docker_pos_first_nonflag '--label|-l|--template-driver') if [ "$cword" -eq "$((counter + 1))" ]; then _filedir fi @@ -1354,11 +1517,8 @@ _docker_container_commit() { if [ "$cword" -eq "$counter" ]; then __docker_complete_containers_all return - fi - (( counter++ )) - - if [ "$cword" -eq "$counter" ]; then - __docker_complete_image_repos_and_tags + elif [ "$cword" -eq "$((counter + 1))" ]; then + __docker_complete_images --repo --tag return fi ;; @@ -1368,7 +1528,7 @@ _docker_container_commit() { _docker_container_cp() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "--follow-link -L --help" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--archive -a --follow-link -L --help" -- "$cur" ) ) ;; *) local counter=$(__docker_pos_first_nonflag) @@ -1387,8 +1547,7 @@ _docker_container_cp() { local containers=( ${COMPREPLY[@]} ) COMPREPLY=( $( compgen -W "${files[*]} ${containers[*]}" -- "$cur" ) ) - # shellcheck disable=SC2128 - if [[ "$COMPREPLY" == *: ]]; then + if [[ "${COMPREPLY[*]}" = *: ]]; then __docker_nospace fi return @@ -1529,8 +1688,7 @@ _docker_container_ls() { local key=$(__docker_map_key_of_current_option '--filter|-f') case "$key" in ancestor) - cur="${cur##*=}" - __docker_complete_images + __docker_complete_images --cur "${cur##*=}" --repo --tag --id return ;; before) @@ -1720,6 +1878,7 @@ _docker_container_run_and_create() { --dns --dns-option --dns-search + --domainname --entrypoint --env -e --env-file @@ -1772,14 +1931,14 @@ _docker_container_run_and_create() { --volume -v --workdir -w " - __docker_daemon_os_is windows && options_with_args+=" + __docker_server_os_is windows && options_with_args+=" --cpu-count --cpu-percent --io-maxbandwidth --io-maxiops --isolation " - __docker_daemon_is_experimental && options_with_args+=" + __docker_server_is_experimental && options_with_args+=" --platform " @@ -1887,8 +2046,7 @@ _docker_container_run_and_create() { ;; *) COMPREPLY=( $( compgen -W 'none host private shareable container:' -- "$cur" ) ) - # shellcheck disable=SC2128 - if [ "$COMPREPLY" = "container:" ]; then + if [ "${COMPREPLY[*]}" = "container:" ]; then __docker_nospace fi ;; @@ -1896,7 +2054,7 @@ _docker_container_run_and_create() { return ;; --isolation) - if __docker_daemon_os_is windows ; then + if __docker_server_os_is windows ; then __docker_complete_isolation return fi @@ -1942,8 +2100,7 @@ _docker_container_run_and_create() { ;; *) COMPREPLY=( $( compgen -W 'host container:' -- "$cur" ) ) - # shellcheck disable=SC2128 - if [ "$COMPREPLY" = "container:" ]; then + if [ "${COMPREPLY[*]}" = "container:" ]; then __docker_nospace fi ;; @@ -1998,7 +2155,7 @@ _docker_container_run_and_create() { *) local counter=$( __docker_pos_first_nonflag "$( __docker_to_alternatives "$options_with_args" )" ) if [ "$cword" -eq "$counter" ]; then - __docker_complete_images + __docker_complete_images --repo --tag --id fi ;; esac @@ -2006,15 +2163,14 @@ _docker_container_run_and_create() { _docker_container_start() { __docker_complete_detach_keys && return - # shellcheck disable=SC2078 case "$prev" in --checkpoint) - if [ __docker_daemon_is_experimental ] ; then + if __docker_server_is_experimental ; then return fi ;; --checkpoint-dir) - if [ __docker_daemon_is_experimental ] ; then + if __docker_server_is_experimental ; then _filedir -d return fi @@ -2024,7 +2180,7 @@ _docker_container_start() { case "$cur" in -*) local options="--attach -a --detach-keys --help --interactive -i" - __docker_daemon_is_experimental && options+=" --checkpoint --checkpoint-dir" + __docker_server_is_experimental && options+=" --checkpoint --checkpoint-dir" COMPREPLY=( $( compgen -W "$options" -- "$cur" ) ) ;; *) @@ -2062,7 +2218,7 @@ _docker_container_stop() { COMPREPLY=( $( compgen -W "--help --time -t" -- "$cur" ) ) ;; *) - __docker_complete_containers_running + __docker_complete_containers_stoppable ;; esac } @@ -2110,6 +2266,7 @@ _docker_container_update() { --memory -m --memory-reservation --memory-swap + --pids-limit --restart " @@ -2149,6 +2306,172 @@ _docker_container_wait() { } +_docker_context() { + local subcommands=" + create + export + import + inspect + ls + rm + update + use + " + local aliases=" + list + remove + " + __docker_subcommands "$subcommands $aliases" && return + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) + ;; + *) + COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) ) + ;; + esac +} + +_docker_context_create() { + case "$prev" in + --default-stack-orchestrator) + COMPREPLY=( $( compgen -W "all kubernetes swarm" -- "$cur" ) ) + return + ;; + --description|--docker|--kubernetes) + return + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--default-stack-orchestrator --description --docker --help --kubernetes" -- "$cur" ) ) + ;; + esac +} + +_docker_context_export() { + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--help --kubeconfig" -- "$cur" ) ) + ;; + *) + local counter=$(__docker_pos_first_nonflag) + if [ "$cword" -eq "$counter" ]; then + __docker_complete_contexts + elif [ "$cword" -eq "$((counter + 1))" ]; then + _filedir + fi + ;; + esac +} + +_docker_context_import() { + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) + ;; + *) + local counter=$(__docker_pos_first_nonflag) + if [ "$cword" -eq "$counter" ]; then + : + elif [ "$cword" -eq "$((counter + 1))" ]; then + _filedir + fi + ;; + esac +} + +_docker_context_inspect() { + case "$prev" in + --format|-f) + return + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--format -f --help" -- "$cur" ) ) + ;; + *) + __docker_complete_contexts + ;; + esac +} + +_docker_context_list() { + _docker_context_ls +} + +_docker_context_ls() { + case "$prev" in + --format|-f) + return + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--format -f --help --quiet -q" -- "$cur" ) ) + ;; + esac +} + +_docker_context_remove() { + _docker_context_rm +} + +_docker_context_rm() { + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--force -f --help" -- "$cur" ) ) + ;; + *) + __docker_complete_contexts + ;; + esac +} + +_docker_context_update() { + case "$prev" in + --default-stack-orchestrator) + COMPREPLY=( $( compgen -W "all kubernetes swarm" -- "$cur" ) ) + return + ;; + --description|--docker|--kubernetes) + return + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--default-stack-orchestrator --description --docker --help --kubernetes" -- "$cur" ) ) + ;; + *) + local counter=$(__docker_pos_first_nonflag) + if [ "$cword" -eq "$counter" ]; then + __docker_complete_contexts + fi + ;; + esac +} + +_docker_context_use() { + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) + ;; + *) + local counter=$(__docker_pos_first_nonflag) + if [ "$cword" -eq "$counter" ]; then + __docker_complete_contexts --add default + fi + ;; + esac +} + + _docker_commit() { _docker_container_commit } @@ -2196,6 +2519,7 @@ _docker_daemon() { --cpu-rt-period --cpu-rt-runtime --data-root + --default-address-pool --default-gateway --default-gateway-v6 --default-runtime @@ -2386,13 +2710,75 @@ _docker_daemon() { } _docker_deploy() { - __docker_daemon_is_experimental && _docker_stack_deploy + __docker_server_is_experimental && _docker_stack_deploy } _docker_diff() { _docker_container_diff } + +_docker_engine() { + local subcommands=" + activate + check + update + " + __docker_subcommands "$subcommands" && return + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) + ;; + *) + COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) ) + ;; + esac +} + +_docker_engine_activate() { + case "$prev" in + --containerd|--engine-image|--format|--license|--registry-prefix|--version) + return + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--containerd --display-only --engine-image --format --help --license --quiet --registry-prefix --version" -- "$cur" ) ) + ;; + esac +} + +_docker_engine_check() { + case "$prev" in + --containerd|--engine-image|--format|--registry-prefix) + return + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--containerd --downgrades --engine-image --format --help --pre-releases --quiet -q --registry-prefix --upgrades" -- "$cur" ) ) + ;; + esac +} + +_docker_engine_update() { + case "$prev" in + --containerd|--engine-image|--registry-prefix|--version) + return + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--containerd --engine-image --help --registry-prefix --version" -- "$cur" ) ) + ;; + esac +} + + _docker_events() { _docker_system_events } @@ -2472,7 +2858,7 @@ _docker_image_build() { --target --ulimit " - __docker_daemon_os_is windows && options_with_args+=" + __docker_server_os_is windows && options_with_args+=" --isolation " @@ -2486,7 +2872,7 @@ _docker_image_build() { --quiet -q --rm " - if __docker_daemon_is_experimental ; then + if __docker_server_is_experimental ; then options_with_args+=" --platform " @@ -2513,7 +2899,7 @@ _docker_image_build() { return ;; --cache-from) - __docker_complete_image_repos_and_tags + __docker_complete_images --repo --tag --id return ;; --file|-f|--iidfile) @@ -2521,7 +2907,7 @@ _docker_image_build() { return ;; --isolation) - if __docker_daemon_os_is windows ; then + if __docker_server_os_is windows ; then __docker_complete_isolation return fi @@ -2541,7 +2927,7 @@ _docker_image_build() { return ;; --tag|-t) - __docker_complete_image_repos_and_tags + __docker_complete_images --repo --tag return ;; --target) @@ -2587,9 +2973,9 @@ _docker_image_history() { COMPREPLY=( $( compgen -W "--format --help --human=false -H=false --no-trunc --quiet -q" -- "$cur" ) ) ;; *) - local counter=$(__docker_pos_first_nonflag) + local counter=$(__docker_pos_first_nonflag '--format') if [ "$cword" -eq "$counter" ]; then - __docker_complete_images + __docker_complete_images --force-tag --id fi ;; esac @@ -2601,24 +2987,24 @@ _docker_image_images() { _docker_image_import() { case "$prev" in - --change|-c|--message|-m) + --change|-c|--message|-m|--platform) return ;; esac case "$cur" in -*) - COMPREPLY=( $( compgen -W "--change -c --help --message -m" -- "$cur" ) ) + local options="--change -c --help --message -m" + __docker_server_is_experimental && options+=" --platform" + COMPREPLY=( $( compgen -W "$options" -- "$cur" ) ) ;; *) local counter=$(__docker_pos_first_nonflag '--change|-c|--message|-m') if [ "$cword" -eq "$counter" ]; then + _filedir return - fi - (( counter++ )) - - if [ "$cword" -eq "$counter" ]; then - __docker_complete_image_repos_and_tags + elif [ "$cword" -eq "$((counter + 1))" ]; then + __docker_complete_images --repo --tag return fi ;; @@ -2651,9 +3037,8 @@ _docker_image_list() { _docker_image_ls() { local key=$(__docker_map_key_of_current_option '--filter|-f') case "$key" in - before|since|reference) - cur="${cur##*=}" - __docker_complete_images + before|since) + __docker_complete_images --cur "${cur##*=}" --force-tag --id return ;; dangling) @@ -2663,6 +3048,10 @@ _docker_image_ls() { label) return ;; + reference) + __docker_complete_images --cur "${cur##*=}" --repo --tag + return + ;; esac case "$prev" in @@ -2684,7 +3073,7 @@ _docker_image_ls() { return ;; *) - __docker_complete_image_repos + __docker_complete_images --repo --tag ;; esac } @@ -2714,8 +3103,8 @@ _docker_image_pull() { case "$cur" in -*) - local options="--all-tags -a --disable-content-trust=false --help" - __docker_daemon_is_experimental && options+=" --platform" + local options="--all-tags -a --disable-content-trust=false --help --quiet -q" + __docker_server_is_experimental && options+=" --platform" COMPREPLY=( $( compgen -W "$options" -- "$cur" ) ) ;; @@ -2725,12 +3114,12 @@ _docker_image_pull() { for arg in "${COMP_WORDS[@]}"; do case "$arg" in --all-tags|-a) - __docker_complete_image_repos + __docker_complete_images --repo return ;; esac done - __docker_complete_image_repos_and_tags + __docker_complete_images --repo --tag fi ;; esac @@ -2744,7 +3133,7 @@ _docker_image_push() { *) local counter=$(__docker_pos_first_nonflag) if [ "$cword" -eq "$counter" ]; then - __docker_complete_image_repos_and_tags + __docker_complete_images --repo --tag fi ;; esac @@ -2760,7 +3149,7 @@ _docker_image_rm() { COMPREPLY=( $( compgen -W "--force -f --help --no-prune" -- "$cur" ) ) ;; *) - __docker_complete_images + __docker_complete_images --force-tag --id ;; esac } @@ -2782,7 +3171,7 @@ _docker_image_save() { COMPREPLY=( $( compgen -W "--help --output -o" -- "$cur" ) ) ;; *) - __docker_complete_images + __docker_complete_images --repo --tag --id ;; esac } @@ -2796,13 +3185,10 @@ _docker_image_tag() { local counter=$(__docker_pos_first_nonflag) if [ "$cword" -eq "$counter" ]; then - __docker_complete_image_repos_and_tags + __docker_complete_images --force-tag --id return - fi - (( counter++ )) - - if [ "$cword" -eq "$counter" ]; then - __docker_complete_image_repos_and_tags + elif [ "$cword" -eq "$((counter + 1))" ]; then + __docker_complete_images --repo --tag return fi ;; @@ -2858,7 +3244,7 @@ _docker_inspect() { '') COMPREPLY=( $( compgen -W " $(__docker_containers --all) - $(__docker_images) + $(__docker_images --force-tag --id) $(__docker_networks) $(__docker_nodes) $(__docker_plugins_installed) @@ -2872,7 +3258,7 @@ _docker_inspect() { __docker_complete_containers_all ;; image) - __docker_complete_images + __docker_complete_images --force-tag --id ;; network) __docker_complete_networks @@ -3045,6 +3431,10 @@ _docker_network_inspect() { _docker_network_ls() { local key=$(__docker_map_key_of_current_option '--filter|-f') case "$key" in + dangling) + COMPREPLY=( $( compgen -W "false true" -- "${cur##*=}" ) ) + return + ;; driver) __docker_complete_plugins_bundled --cur "${cur##*=}" --type Network --add macvlan return @@ -3069,7 +3459,7 @@ _docker_network_ls() { case "$prev" in --filter|-f) - COMPREPLY=( $( compgen -S = -W "driver id label name scope type" -- "$cur" ) ) + COMPREPLY=( $( compgen -S = -W "dangling driver id label name scope type" -- "$cur" ) ) __docker_nospace return ;; @@ -3195,7 +3585,7 @@ _docker_service_logs() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "--follow -f --help --no-resolve --no-task-ids --no-trunc --since --tail --timestamps -t" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--details --follow -f --help --no-resolve --no-task-ids --no-trunc --raw --since --tail --timestamps -t" -- "$cur" ) ) ;; *) local counter=$(__docker_pos_first_nonflag '--since|--tail') @@ -3334,8 +3724,6 @@ _docker_service_update_and_create() { local options_with_args=" --endpoint-mode --entrypoint - --env -e - --force --health-cmd --health-interval --health-retries @@ -3343,13 +3731,12 @@ _docker_service_update_and_create() { --health-timeout --hostname --isolation - --label -l --limit-cpu --limit-memory --log-driver --log-opt - --mount --replicas + --replicas-max-per-node --reserve-cpu --reserve-memory --restart-condition @@ -3373,14 +3760,16 @@ _docker_service_update_and_create() { --user -u --workdir -w " - __docker_daemon_os_is windows && options_with_args+=" + __docker_server_os_is windows && options_with_args+=" --credential-spec " local boolean_options=" --detach -d --help + --init --no-healthcheck + --no-resolve-image --read-only --tty -t --with-registry-auth @@ -3396,52 +3785,31 @@ _docker_service_update_and_create() { --dns --dns-option --dns-search + --env -e --env-file --generic-resource --group --host + --label -l --mode + --mount --name --network --placement-pref --publish -p --secret + --sysctl " case "$prev" in - --config) - __docker_complete_configs - return - ;; --env-file) _filedir return ;; - --group) - COMPREPLY=( $(compgen -g -- "$cur") ) - return - ;; - --host) - case "$cur" in - *:) - __docker_complete_resolved_hostname - return - ;; - esac - ;; --mode) COMPREPLY=( $( compgen -W "global replicated" -- "$cur" ) ) return ;; - --placement-pref) - COMPREPLY=( $( compgen -W "spread" -S = -- "$cur" ) ) - __docker_nospace - return - ;; - --secret) - __docker_complete_secrets - return - ;; esac fi if [ "$subcommand" = "update" ] ; then @@ -3459,6 +3827,8 @@ _docker_service_update_and_create() { --dns-rm --dns-search-add --dns-search-rm + --env-add + --env-rm --generic-resource-add --generic-resource-rm --group-add @@ -3466,6 +3836,10 @@ _docker_service_update_and_create() { --host-add --host-rm --image + --label-add + --label-rm + --mount-add + --mount-rm --network-add --network-rm --placement-pref-add @@ -3475,40 +3849,21 @@ _docker_service_update_and_create() { --rollback --secret-add --secret-rm + --sysctl-add + --sysctl-rm + " + + boolean_options="$boolean_options + --force " case "$prev" in - --config-add|--config-rm) - __docker_complete_configs + --env-rm) + COMPREPLY=( $( compgen -e -- "$cur" ) ) return ;; - --group-add|--group-rm) - COMPREPLY=( $(compgen -g -- "$cur") ) - return - ;; - --host-add|--host-rm) - case "$cur" in - *:) - __docker_complete_resolved_hostname - return - ;; - esac - ;; --image) - __docker_complete_image_repos_and_tags - return - ;; - --network-add|--network-rm) - __docker_complete_networks - return - ;; - --placement-pref-add|--placement-pref-rm) - COMPREPLY=( $( compgen -W "spread" -S = -- "$cur" ) ) - __docker_nospace - return - ;; - --secret-add|--secret-rm) - __docker_complete_secrets + __docker_complete_images --repo --tag --id return ;; esac @@ -3524,16 +3879,32 @@ _docker_service_update_and_create() { esac case "$prev" in + --config|--config-add|--config-rm) + __docker_complete_configs + return + ;; --endpoint-mode) COMPREPLY=( $( compgen -W "dnsrr vip" -- "$cur" ) ) return ;; - --env|-e) + --env|-e|--env-add) # we do not append a "=" here because "-e VARNAME" is legal systax, too COMPREPLY=( $( compgen -e -- "$cur" ) ) __docker_nospace return ;; + --group|--group-add|--group-rm) + COMPREPLY=( $(compgen -g -- "$cur") ) + return + ;; + --host|--host-add|--host-rm) + case "$cur" in + *:) + __docker_complete_resolved_hostname + return + ;; + esac + ;; --isolation) __docker_complete_isolation return @@ -3546,10 +3917,15 @@ _docker_service_update_and_create() { __docker_complete_log_options return ;; - --network) + --network|--network-add|--network-rm) __docker_complete_networks return ;; + --placement-pref|--placement-pref-add|--placement-pref-rm) + COMPREPLY=( $( compgen -W "spread" -S = -- "$cur" ) ) + __docker_nospace + return + ;; --restart-condition) COMPREPLY=( $( compgen -W "any none on-failure" -- "$cur" ) ) return @@ -3558,6 +3934,10 @@ _docker_service_update_and_create() { COMPREPLY=( $( compgen -W "continue pause" -- "$cur" ) ) return ;; + --secret|--secret-add|--secret-rm) + __docker_complete_secrets + return + ;; --stop-signal) __docker_complete_signals return @@ -3591,7 +3971,7 @@ _docker_service_update_and_create() { fi else if [ "$cword" -eq "$counter" ]; then - __docker_complete_images + __docker_complete_images --repo --tag --id fi fi ;; @@ -3654,7 +4034,7 @@ _docker_swarm_init() { COMPREPLY=( $( compgen -W "active drain pause" -- "$cur" ) ) return ;; - --cert-expiry|--dispatcher-heartbeat|--external-ca|--max-snapshots|--snapshot-interval|--task-history-limit) + --cert-expiry|--data-path-port|--default-addr-pool|--default-addr-pool-mask-length|--dispatcher-heartbeat|--external-ca|--max-snapshots|--snapshot-interval|--task-history-limit ) return ;; --data-path-addr) @@ -3674,7 +4054,7 @@ _docker_swarm_init() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "--advertise-addr --autolock --availability --cert-expiry --data-path-addr --dispatcher-heartbeat --external-ca --force-new-cluster --help --listen-addr --max-snapshots --snapshot-interval --task-history-limit" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--advertise-addr --autolock --availability --cert-expiry --data-path-addr --data-path-port --default-addr-pool --default-addr-pool-mask-length --dispatcher-heartbeat --external-ca --force-new-cluster --help --listen-addr --max-snapshots --snapshot-interval --task-history-limit " -- "$cur" ) ) ;; esac } @@ -3774,6 +4154,109 @@ _docker_swarm_update() { esac } +_docker_manifest() { + local subcommands=" + annotate + create + inspect + push + " + __docker_subcommands "$subcommands" && return + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) + ;; + *) + COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) ) + ;; + esac +} + +_docker_manifest_annotate() { + case "$prev" in + --arch) + COMPREPLY=( $( compgen -W " + 386 + amd64 + arm + arm64 + mips64 + mips64le + ppc64le + s390x" -- "$cur" ) ) + return + ;; + --os) + COMPREPLY=( $( compgen -W " + darwin + dragonfly + freebsd + linux + netbsd + openbsd + plan9 + solaris + windows" -- "$cur" ) ) + return + ;; + --os-features|--variant) + return + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--arch --help --os --os-features --variant" -- "$cur" ) ) + ;; + *) + local counter=$( __docker_pos_first_nonflag "--arch|--os|--os-features|--variant" ) + if [ "$cword" -eq "$counter" ] || [ "$cword" -eq "$((counter + 1))" ]; then + __docker_complete_images --force-tag --id + fi + ;; + esac +} + +_docker_manifest_create() { + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--amend -a --help --insecure" -- "$cur" ) ) + ;; + *) + __docker_complete_images --force-tag --id + ;; + esac +} + +_docker_manifest_inspect() { + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--help --insecure --verbose -v" -- "$cur" ) ) + ;; + *) + local counter=$( __docker_pos_first_nonflag ) + if [ "$cword" -eq "$counter" ] || [ "$cword" -eq "$((counter + 1))" ]; then + __docker_complete_images --force-tag --id + fi + ;; + esac +} + +_docker_manifest_push() { + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--help --insecure --purge -p" -- "$cur" ) ) + ;; + *) + local counter=$( __docker_pos_first_nonflag ) + if [ "$cword" -eq "$counter" ]; then + __docker_complete_images --force-tag --id + fi + ;; + esac +} + _docker_node() { local subcommands=" demote @@ -4237,14 +4720,18 @@ _docker_secret_create() { --driver|-d|--label|-l) return ;; + --template-driver) + COMPREPLY=( $( compgen -W "golang" -- "$cur" ) ) + return + ;; esac case "$cur" in -*) - COMPREPLY=( $( compgen -W "--driver -d --help --label -l" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--driver -d --help --label -l --template-driver" -- "$cur" ) ) ;; *) - local counter=$(__docker_pos_first_nonflag '--driver|-d|--label|-l') + local counter=$(__docker_pos_first_nonflag '--driver|-d|--label|-l|--template-driver') if [ "$cword" -eq "$((counter + 1))" ]; then _filedir fi @@ -4367,11 +4854,15 @@ _docker_stack() { remove up " + + __docker_complete_stack_orchestrator_options && return __docker_subcommands "$subcommands $aliases" && return case "$cur" in -*) - COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) + local options="--help --orchestrator" + __docker_stack_orchestrator_is kubernetes && options+=" --kubeconfig" + COMPREPLY=( $( compgen -W "$options" -- "$cur" ) ) ;; *) COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) ) @@ -4380,12 +4871,12 @@ _docker_stack() { } _docker_stack_deploy() { + __docker_complete_stack_orchestrator_options && return + case "$prev" in --bundle-file) - if __docker_daemon_is_experimental ; then - _filedir dab - return - fi + _filedir dab + return ;; --compose-file|-c) _filedir yml @@ -4399,12 +4890,14 @@ _docker_stack_deploy() { case "$cur" in -*) - local options="--compose-file -c --help --prune --resolve-image --with-registry-auth" - __docker_daemon_is_experimental && options+=" --bundle-file" + local options="--compose-file -c --help --orchestrator" + __docker_server_is_experimental && __docker_stack_orchestrator_is swarm && options+=" --bundle-file" + __docker_stack_orchestrator_is kubernetes && options+=" --kubeconfig --namespace" + __docker_stack_orchestrator_is swarm && options+=" --prune --resolve-image --with-registry-auth" COMPREPLY=( $( compgen -W "$options" -- "$cur" ) ) ;; *) - local counter=$(__docker_pos_first_nonflag '--bundle-file|--compose-file|-c|--resolve-image') + local counter=$(__docker_pos_first_nonflag '--bundle-file|--compose-file|-c|--kubeconfig|--namespace|--orchestrator|--resolve-image') if [ "$cword" -eq "$counter" ]; then __docker_complete_stacks fi @@ -4421,6 +4914,8 @@ _docker_stack_list() { } _docker_stack_ls() { + __docker_complete_stack_orchestrator_options && return + case "$prev" in --format) return @@ -4429,7 +4924,9 @@ _docker_stack_ls() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "--format --help" -- "$cur" ) ) + local options="--format --help --orchestrator" + __docker_stack_orchestrator_is kubernetes && options+=" --all-namespaces --kubeconfig --namespace" + COMPREPLY=( $( compgen -W "$options" -- "$cur" ) ) ;; esac } @@ -4451,6 +4948,8 @@ _docker_stack_ps() { ;; esac + __docker_complete_stack_orchestrator_options && return + case "$prev" in --filter|-f) COMPREPLY=( $( compgen -S = -W "id name desired-state" -- "$cur" ) ) @@ -4464,10 +4963,12 @@ _docker_stack_ps() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "--filter -f --format --help --no-resolve --no-trunc --quiet -q" -- "$cur" ) ) + local options="--filter -f --format --help --no-resolve --no-trunc --orchestrator --quiet -q" + __docker_stack_orchestrator_is kubernetes && options+=" --all-namespaces --kubeconfig --namespace" + COMPREPLY=( $( compgen -W "$options" -- "$cur" ) ) ;; *) - local counter=$(__docker_pos_first_nonflag '--filter|-f') + local counter=$(__docker_pos_first_nonflag '--all-namespaces|--filter|-f|--format|--kubeconfig|--namespace') if [ "$cword" -eq "$counter" ]; then __docker_complete_stacks fi @@ -4480,9 +4981,13 @@ _docker_stack_remove() { } _docker_stack_rm() { + __docker_complete_stack_orchestrator_options && return + case "$cur" in -*) - COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) + local options="--help --orchestrator" + __docker_stack_orchestrator_is kubernetes && options+=" --kubeconfig --namespace" + COMPREPLY=( $( compgen -W "$options" -- "$cur" ) ) ;; *) __docker_complete_stacks @@ -4506,6 +5011,8 @@ _docker_stack_services() { ;; esac + __docker_complete_stack_orchestrator_options && return + case "$prev" in --filter|-f) COMPREPLY=( $( compgen -S = -W "id label name" -- "$cur" ) ) @@ -4519,10 +5026,12 @@ _docker_stack_services() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "--filter -f --format --help --quiet -q" -- "$cur" ) ) + local options="--filter -f --format --help --orchestrator --quiet -q" + __docker_stack_orchestrator_is kubernetes && options+=" --kubeconfig --namespace" + COMPREPLY=( $( compgen -W "$options" -- "$cur" ) ) ;; *) - local counter=$(__docker_pos_first_nonflag '--filter|-f|--format') + local counter=$(__docker_pos_first_nonflag '--filter|-f|--format|--kubeconfig|--namespace|--orchestrator') if [ "$cword" -eq "$counter" ]; then __docker_complete_stacks fi @@ -4609,6 +5118,7 @@ _docker_system_events() { enable exec_create exec_detach + exec_die exec_start export health_status @@ -4639,16 +5149,19 @@ _docker_system_events() { return ;; image) - cur="${cur##*=}" - __docker_complete_images + __docker_complete_images --cur "${cur##*=}" --repo --tag return ;; network) __docker_complete_networks --cur "${cur##*=}" return ;; + scope) + COMPREPLY=( $( compgen -W "local swarm" -- "${cur##*=}" ) ) + return + ;; type) - COMPREPLY=( $( compgen -W "container daemon image network plugin volume" -- "${cur##*=}" ) ) + COMPREPLY=( $( compgen -W "config container daemon image network plugin secret service volume" -- "${cur##*=}" ) ) return ;; volume) @@ -4659,7 +5172,7 @@ _docker_system_events() { case "$prev" in --filter|-f) - COMPREPLY=( $( compgen -S = -W "container daemon event image label network type volume" -- "$cur" ) ) + COMPREPLY=( $( compgen -S = -W "container daemon event image label network scope type volume" -- "$cur" ) ) __docker_nospace return ;; @@ -4713,9 +5226,9 @@ _docker_tag() { _docker_trust() { local subcommands=" + inspect revoke sign - view " __docker_subcommands "$subcommands" && return @@ -4729,6 +5242,20 @@ _docker_trust() { esac } +_docker_trust_inspect() { + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--help --pretty" -- "$cur" ) ) + ;; + *) + local counter=$(__docker_pos_first_nonflag) + if [ "$cword" -eq "$counter" ]; then + __docker_complete_images --repo --tag + fi + ;; + esac +} + _docker_trust_revoke() { case "$cur" in -*) @@ -4737,7 +5264,7 @@ _docker_trust_revoke() { *) local counter=$(__docker_pos_first_nonflag) if [ "$cword" -eq "$counter" ]; then - __docker_complete_images + __docker_complete_images --repo --tag fi ;; esac @@ -4751,21 +5278,7 @@ _docker_trust_sign() { *) local counter=$(__docker_pos_first_nonflag) if [ "$cword" -eq "$counter" ]; then - __docker_complete_images - fi - ;; - esac -} - -_docker_trust_view() { - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) - ;; - *) - local counter=$(__docker_pos_first_nonflag) - if [ "$cword" -eq "$counter" ]; then - __docker_complete_images + __docker_complete_images --force-tag --id fi ;; esac @@ -4785,6 +5298,8 @@ _docker_top() { } _docker_version() { + __docker_complete_stack_orchestrator_options && return + case "$prev" in --format|-f) return @@ -4793,7 +5308,9 @@ _docker_version() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "--format -f --help" -- "$cur" ) ) + local options="--format -f --help" + __docker_stack_orchestrator_is kubernetes && options+=" --kubeconfig" + COMPREPLY=( $( compgen -W "$options" -- "$cur" ) ) ;; esac } @@ -4936,8 +5453,11 @@ _docker() { shopt -s extglob local management_commands=( + builder config container + context + engine image network node @@ -4947,6 +5467,7 @@ _docker() { stack swarm system + trust volume ) @@ -4996,10 +5517,13 @@ _docker() { wait ) - local experimental_commands=( + local experimental_client_commands=( + manifest + ) + + local experimental_server_commands=( checkpoint deploy - trust ) local commands=(${management_commands[*]} ${top_level_commands[*]}) @@ -5014,6 +5538,7 @@ _docker() { " local global_options_with_args=" --config + --context -c --host -H --log-level -l --tlscacert @@ -5021,7 +5546,12 @@ _docker() { --tlskey " - local host config daemon_os + # variables to cache server info, populated on demand for performance reasons + local info_fetched server_experimental server_os + # variables to cache client info, populated on demand for performance reasons + local client_experimental stack_orchestrator_is_kubernetes stack_orchestrator_is_swarm + + local host config context COMPREPLY=() local cur prev words cword @@ -5031,6 +5561,9 @@ _docker() { local counter=1 while [ "$counter" -lt "$cword" ]; do case "${words[$counter]}" in + docker) + return 0 + ;; # save host so that completion can use custom daemon --host|-H) (( counter++ )) @@ -5041,6 +5574,11 @@ _docker() { (( counter++ )) config="${words[$counter]}" ;; + # save context so that completion can use custom daemon + --context|-c) + (( counter++ )) + context="${words[$counter]}" + ;; $(__docker_to_extglob "$global_options_with_args") ) (( counter++ )) ;; @@ -5076,4 +5614,4 @@ _docker() { eval "$__docker_previous_extglob_setting" unset __docker_previous_extglob_setting -complete -F _docker docker docker.exe dockerd dockerd.exe +complete -F _docker docker docker.exe dockerd dockerd.exe \ No newline at end of file From 4329da109ecf43f5672617309a081a51d4f9061e Mon Sep 17 00:00:00 2001 From: Truong Ma Phi Date: Fri, 22 Mar 2019 16:21:45 +0700 Subject: [PATCH 34/50] Update docker compose collection --- .../available/docker-compose.completion.bash | 224 +++++++++++------- 1 file changed, 134 insertions(+), 90 deletions(-) diff --git a/completion/available/docker-compose.completion.bash b/completion/available/docker-compose.completion.bash index 87161d0a..dedd8e31 100644 --- a/completion/available/docker-compose.completion.bash +++ b/completion/available/docker-compose.completion.bash @@ -16,6 +16,8 @@ # below to your .bashrc after bash completion features are loaded # . ~/.docker-compose-completion.sh +__docker_compose_previous_extglob_setting=$(shopt -p extglob) +shopt -s extglob __docker_compose_q() { docker-compose 2>/dev/null "${top_level_options[@]}" "$@" @@ -48,64 +50,56 @@ __docker_compose_has_option() { return 1 } +# Returns `key` if we are currently completing the value of a map option (`key=value`) +# which matches the extglob passed in as an argument. +# This function is needed for key-specific completions. +__docker_compose_map_key_of_current_option() { + local glob="$1" + + local key glob_pos + if [ "$cur" = "=" ] ; then # key= case + key="$prev" + glob_pos=$((cword - 2)) + elif [[ $cur == *=* ]] ; then # key=value case (OSX) + key=${cur%=*} + glob_pos=$((cword - 1)) + elif [ "$prev" = "=" ] ; then + key=${words[$cword - 2]} # key=value case + glob_pos=$((cword - 3)) + else + return + fi + + [ "${words[$glob_pos]}" = "=" ] && ((glob_pos--)) # --option=key=value syntax + + [[ ${words[$glob_pos]} == @($glob) ]] && echo "$key" +} + # suppress trailing whitespace __docker_compose_nospace() { # compopt is not available in ancient bash versions type compopt &>/dev/null && compopt -o nospace } -# Extracts all service names from the compose file. -___docker_compose_all_services_in_compose_file() { - __docker_compose_q config --services + +# Outputs a list of all defined services, regardless of their running state. +# Arguments for `docker-compose ps` may be passed in order to filter the service list, +# e.g. `status=running`. +__docker_compose_services() { + __docker_compose_q ps --services "$@" } -# All services, even those without an existing container -__docker_compose_services_all() { - COMPREPLY=( $(compgen -W "$(___docker_compose_all_services_in_compose_file)" -- "$cur") ) -} - -# All services that have an entry with the given key in their compose_file section -___docker_compose_services_with_key() { - # flatten sections under "services" to one line, then filter lines containing the key and return section name - __docker_compose_q config \ - | sed -n -e '/^services:/,/^[^ ]/p' \ - | sed -n 's/^ //p' \ - | awk '/^[a-zA-Z0-9]/{printf "\n"};{printf $0;next;}' \ - | awk -F: -v key=": +$1:" '$0 ~ key {print $1}' -} - -# All services that are defined by a Dockerfile reference -__docker_compose_services_from_build() { - COMPREPLY=( $(compgen -W "$(___docker_compose_services_with_key build)" -- "$cur") ) -} - -# All services that are defined by an image -__docker_compose_services_from_image() { - COMPREPLY=( $(compgen -W "$(___docker_compose_services_with_key image)" -- "$cur") ) -} - -# The services for which containers have been created, optionally filtered -# by a boolean expression passed in as argument. -__docker_compose_services_with() { - local containers names - containers="$(__docker_compose_q ps -q)" - names=$(docker 2>/dev/null inspect -f "{{if ${1:-true}}}{{range \$k, \$v := .Config.Labels}}{{if eq \$k \"com.docker.compose.service\"}}{{\$v}}{{end}}{{end}}{{end}}" $containers) - COMPREPLY=( $(compgen -W "$names" -- "$cur") ) -} - -# The services for which at least one paused container exists -__docker_compose_services_paused() { - __docker_compose_services_with '.State.Paused' +# Applies completion of services based on the current value of `$cur`. +# Arguments for `docker-compose ps` may be passed in order to filter the service list, +# see `__docker_compose_services`. +__docker_compose_complete_services() { + COMPREPLY=( $(compgen -W "$(__docker_compose_services "$@")" -- "$cur") ) } # The services for which at least one running container exists -__docker_compose_services_running() { - __docker_compose_services_with '.State.Running' -} - -# The services for which at least one stopped container exists -__docker_compose_services_stopped() { - __docker_compose_services_with 'not .State.Running' +__docker_compose_complete_running_services() { + local names=$(__docker_compose_services --filter status=running) + COMPREPLY=( $(compgen -W "$names" -- "$cur") ) } @@ -116,14 +110,17 @@ _docker_compose_build() { __docker_compose_nospace return ;; + --memory|-m) + return + ;; esac case "$cur" in -*) - COMPREPLY=( $( compgen -W "--build-arg --force-rm --help --memory --no-cache --pull" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--build-arg --compress --force-rm --help --memory -m --no-cache --no-rm --pull --parallel -q --quiet" -- "$cur" ) ) ;; *) - __docker_compose_services_from_build + __docker_compose_complete_services --filter source=build ;; esac } @@ -142,7 +139,18 @@ _docker_compose_bundle() { _docker_compose_config() { - COMPREPLY=( $( compgen -W "--help --quiet -q --resolve-image-digests --services --volumes" -- "$cur" ) ) + case "$prev" in + --hash) + if [[ $cur == \\* ]] ; then + COMPREPLY=( '\*' ) + else + COMPREPLY=( $(compgen -W "$(__docker_compose_services) \\\* " -- "$cur") ) + fi + return + ;; + esac + + COMPREPLY=( $( compgen -W "--hash --help --quiet -q --resolve-image-digests --services --volumes" -- "$cur" ) ) } @@ -152,7 +160,7 @@ _docker_compose_create() { COMPREPLY=( $( compgen -W "--build --force-recreate --help --no-build --no-recreate" -- "$cur" ) ) ;; *) - __docker_compose_services_all + __docker_compose_complete_services ;; esac } @@ -168,18 +176,22 @@ _docker_compose_docker_compose() { _filedir "y?(a)ml" return ;; + --log-level) + COMPREPLY=( $( compgen -W "debug info warning error critical" -- "$cur" ) ) + return + ;; --project-directory) _filedir -d return ;; - $(__docker_compose_to_extglob "$top_level_options_with_args") ) + $(__docker_compose_to_extglob "$daemon_options_with_args") ) return ;; esac case "$cur" in -*) - COMPREPLY=( $( compgen -W "$top_level_boolean_options $top_level_options_with_args --help -h --no-ansi --verbose --version -v" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "$daemon_boolean_options $daemon_options_with_args $top_level_options_with_args --help -h --no-ansi --verbose --version -v" -- "$cur" ) ) ;; *) COMPREPLY=( $( compgen -W "${commands[*]}" -- "$cur" ) ) @@ -194,11 +206,14 @@ _docker_compose_down() { COMPREPLY=( $( compgen -W "all local" -- "$cur" ) ) return ;; + --timeout|-t) + return + ;; esac case "$cur" in -*) - COMPREPLY=( $( compgen -W "--help --rmi --volumes -v --remove-orphans" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--help --rmi --timeout -t --volumes -v --remove-orphans" -- "$cur" ) ) ;; esac } @@ -216,7 +231,7 @@ _docker_compose_events() { COMPREPLY=( $( compgen -W "--help --json" -- "$cur" ) ) ;; *) - __docker_compose_services_all + __docker_compose_complete_services ;; esac } @@ -224,17 +239,17 @@ _docker_compose_events() { _docker_compose_exec() { case "$prev" in - --index|--user|-u) + --index|--user|-u|--workdir|-w) return ;; esac case "$cur" in -*) - COMPREPLY=( $( compgen -W "-d --help --index --privileged -T --user -u" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "-d --detach --help --index --privileged -T --user -u --workdir -w" -- "$cur" ) ) ;; *) - __docker_compose_services_running + __docker_compose_complete_running_services ;; esac } @@ -247,10 +262,10 @@ _docker_compose_help() { _docker_compose_images() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "--help -q" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--help --quiet -q" -- "$cur" ) ) ;; *) - __docker_compose_services_all + __docker_compose_complete_services ;; esac } @@ -268,7 +283,7 @@ _docker_compose_kill() { COMPREPLY=( $( compgen -W "--help -s" -- "$cur" ) ) ;; *) - __docker_compose_services_running + __docker_compose_complete_running_services ;; esac } @@ -286,7 +301,7 @@ _docker_compose_logs() { COMPREPLY=( $( compgen -W "--follow -f --help --no-color --tail --timestamps -t" -- "$cur" ) ) ;; *) - __docker_compose_services_all + __docker_compose_complete_services ;; esac } @@ -298,7 +313,7 @@ _docker_compose_pause() { COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) ;; *) - __docker_compose_services_running + __docker_compose_complete_running_services ;; esac } @@ -320,19 +335,39 @@ _docker_compose_port() { COMPREPLY=( $( compgen -W "--help --index --protocol" -- "$cur" ) ) ;; *) - __docker_compose_services_all + __docker_compose_complete_services ;; esac } _docker_compose_ps() { + local key=$(__docker_compose_map_key_of_current_option '--filter') + case "$key" in + source) + COMPREPLY=( $( compgen -W "build image" -- "${cur##*=}" ) ) + return + ;; + status) + COMPREPLY=( $( compgen -W "paused restarting running stopped" -- "${cur##*=}" ) ) + return + ;; + esac + + case "$prev" in + --filter) + COMPREPLY=( $( compgen -W "source status" -S "=" -- "$cur" ) ) + __docker_compose_nospace + return; + ;; + esac + case "$cur" in -*) - COMPREPLY=( $( compgen -W "--help -q" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--all -a --filter --help --quiet -q --services" -- "$cur" ) ) ;; *) - __docker_compose_services_all + __docker_compose_complete_services ;; esac } @@ -341,10 +376,10 @@ _docker_compose_ps() { _docker_compose_pull() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "--help --ignore-pull-failures --parallel --quiet" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--help --ignore-pull-failures --include-deps --no-parallel --quiet -q" -- "$cur" ) ) ;; *) - __docker_compose_services_from_image + __docker_compose_complete_services --filter source=image ;; esac } @@ -356,7 +391,7 @@ _docker_compose_push() { COMPREPLY=( $( compgen -W "--help --ignore-push-failures" -- "$cur" ) ) ;; *) - __docker_compose_services_all + __docker_compose_complete_services ;; esac } @@ -374,7 +409,7 @@ _docker_compose_restart() { COMPREPLY=( $( compgen -W "--help --timeout -t" -- "$cur" ) ) ;; *) - __docker_compose_services_running + __docker_compose_complete_running_services ;; esac } @@ -387,9 +422,9 @@ _docker_compose_rm() { ;; *) if __docker_compose_has_option "--stop|-s" ; then - __docker_compose_services_all + __docker_compose_complete_services else - __docker_compose_services_stopped + __docker_compose_complete_services --filter status=stopped fi ;; esac @@ -410,10 +445,10 @@ _docker_compose_run() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "-d --entrypoint -e --help --label -l --name --no-deps --publish -p --rm --service-ports -T --user -u --volume -v --workdir -w" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--detach -d --entrypoint -e --help --label -l --name --no-deps --publish -p --rm --service-ports -T --use-aliases --user -u --volume -v --workdir -w" -- "$cur" ) ) ;; *) - __docker_compose_services_all + __docker_compose_complete_services ;; esac } @@ -435,7 +470,7 @@ _docker_compose_scale() { COMPREPLY=( $( compgen -W "--help --timeout -t" -- "$cur" ) ) ;; *) - COMPREPLY=( $(compgen -S "=" -W "$(___docker_compose_all_services_in_compose_file)" -- "$cur") ) + COMPREPLY=( $(compgen -S "=" -W "$(__docker_compose_services)" -- "$cur") ) __docker_compose_nospace ;; esac @@ -448,7 +483,7 @@ _docker_compose_start() { COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) ;; *) - __docker_compose_services_stopped + __docker_compose_complete_services --filter status=stopped ;; esac } @@ -466,7 +501,7 @@ _docker_compose_stop() { COMPREPLY=( $( compgen -W "--help --timeout -t" -- "$cur" ) ) ;; *) - __docker_compose_services_running + __docker_compose_complete_running_services ;; esac } @@ -478,7 +513,7 @@ _docker_compose_top() { COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) ;; *) - __docker_compose_services_running + __docker_compose_complete_running_services ;; esac } @@ -490,7 +525,7 @@ _docker_compose_unpause() { COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) ;; *) - __docker_compose_services_paused + __docker_compose_complete_services --filter status=paused ;; esac } @@ -503,11 +538,11 @@ _docker_compose_up() { return ;; --exit-code-from) - __docker_compose_services_all + __docker_compose_complete_services return ;; --scale) - COMPREPLY=( $(compgen -S "=" -W "$(___docker_compose_all_services_in_compose_file)" -- "$cur") ) + COMPREPLY=( $(compgen -S "=" -W "$(__docker_compose_services)" -- "$cur") ) __docker_compose_nospace return ;; @@ -518,10 +553,10 @@ _docker_compose_up() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "--abort-on-container-exit --build -d --exit-code-from --force-recreate --help --no-build --no-color --no-deps --no-recreate --no-start --remove-orphans --scale --timeout -t" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--abort-on-container-exit --always-recreate-deps --build -d --detach --exit-code-from --force-recreate --help --no-build --no-color --no-deps --no-recreate --no-start --renew-anon-volumes -V --remove-orphans --scale --timeout -t" -- "$cur" ) ) ;; *) - __docker_compose_services_all + __docker_compose_complete_services ;; esac } @@ -571,14 +606,12 @@ _docker_compose() { # Options for the docker daemon that have to be passed to secondary calls to # docker-compose executed by this script. - # Other global otions that are not relevant for secondary calls are defined in - # `_docker_compose_docker_compose`. - local top_level_boolean_options=" + local daemon_boolean_options=" --skip-hostname-check --tls --tlsverify " - local top_level_options_with_args=" + local daemon_options_with_args=" --file -f --host -H --project-directory @@ -588,6 +621,11 @@ _docker_compose() { --tlskey " + # These options are require special treatment when searching the command. + local top_level_options_with_args=" + --log-level + " + COMPREPLY=() local cur prev words cword _get_comp_words_by_ref -n : cur prev words cword @@ -600,15 +638,18 @@ _docker_compose() { while [ $counter -lt $cword ]; do case "${words[$counter]}" in - $(__docker_compose_to_extglob "$top_level_boolean_options") ) + $(__docker_compose_to_extglob "$daemon_boolean_options") ) local opt=${words[counter]} top_level_options+=($opt) ;; - $(__docker_compose_to_extglob "$top_level_options_with_args") ) + $(__docker_compose_to_extglob "$daemon_options_with_args") ) local opt=${words[counter]} local arg=${words[++counter]} top_level_options+=($opt $arg) ;; + $(__docker_compose_to_extglob "$top_level_options_with_args") ) + (( counter++ )) + ;; -*) ;; *) @@ -626,4 +667,7 @@ _docker_compose() { return 0 } -complete -F _docker_compose docker-compose docker-compose.exe +eval "$__docker_compose_previous_extglob_setting" +unset __docker_compose_previous_extglob_setting + +complete -F _docker_compose docker-compose docker-compose.exe \ No newline at end of file From df011d7ab636239145354b24839d49a5ba0cc84d Mon Sep 17 00:00:00 2001 From: Truong Ma Phi Date: Fri, 22 Mar 2019 16:21:55 +0700 Subject: [PATCH 35/50] Update docker machine completion --- .../available/docker-machine.completion.bash | 62 +++++++++++++++++-- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/completion/available/docker-machine.completion.bash b/completion/available/docker-machine.completion.bash index e92bd4cf..8c692ff6 100644 --- a/completion/available/docker-machine.completion.bash +++ b/completion/available/docker-machine.completion.bash @@ -86,6 +86,40 @@ _docker_machine_map_key_of_current_option() { [[ ${words[$glob_pos]} == $glob ]] && echo "$key" } +# Finds the position of the first word that is neither option nor an option's argument. +# If there are options that require arguments, you need to pass a glob describing +# those options, e.g. "--option1|-o|--option2". +# Use this function to restrict completions to exact positions after the options. +_docker_machine_pos_first_nonflag() { + local argument_flags=$1 + + local counter=$((${subcommand_pos:-${command_pos}} + 1)) + while [ "$counter" -le "$cword" ]; do + if [ -n "$argument_flags" ] && eval "case '${words[$counter]}' in $argument_flags) true ;; *) false ;; esac"; then + (( counter++ )) + # eat "=" in case of --option=arg syntax + [ "${words[$counter]}" = "=" ] && (( counter++ )) + else + case "${words[$counter]}" in + -*) + ;; + *) + break + ;; + esac + fi + + # Bash splits words at "=", retaining "=" as a word, examples: + # "--debug=false" => 3 words, "--log-opt syslog-facility=daemon" => 4 words + while [ "${words[$counter + 1]}" = "=" ] ; do + counter=$(( counter + 2)) + done + + (( counter++ )) + done + + echo $counter +} # --- completion functions --------------------------------------------------- _docker_machine_active() { @@ -128,7 +162,7 @@ _docker_machine_create() { _docker_machine_env() { case "${prev}" in --shell) - COMPREPLY=($(compgen -W "cmd fish powershell tcsh" -- "${cur}")) + COMPREPLY=($(compgen -W "cmd emacs fish powershell tcsh" -- "${cur}")) return ;; esac @@ -208,6 +242,21 @@ _docker_machine_ls() { fi } +_docker_machine_mount() { + if [[ "${cur}" == -* ]]; then + COMPREPLY=($(compgen -W "--help --unmount -u" -- "${cur}")) + else + local pos=$(_docker_machine_pos_first_nonflag) + if [ "$cword" -eq "$pos" ]; then + # We can't complete remote filesystems. All we can do here is to complete the machine. + COMPREPLY=($(compgen -W "$(_docker_machine_machines --filter state=Running)" -S: -- "${cur}")) + _docker_machine_nospace + elif [ "$cword" -eq "$((pos + 1))" ]; then + _filedir -d + fi + fi +} + _docker_machine_provision() { if [[ "${cur}" == -* ]]; then COMPREPLY=($(compgen -W "--help" -- "${cur}")) @@ -218,7 +267,7 @@ _docker_machine_provision() { _docker_machine_regenerate_certs() { if [[ "${cur}" == -* ]]; then - COMPREPLY=($(compgen -W "--force -f --help" -- "${cur}")) + COMPREPLY=($(compgen -W "--client-certs --force -f --help" -- "${cur}")) else COMPREPLY=($(compgen -W "$(_docker_machine_machines --filter state=Running)" -- "${cur}")) fi @@ -250,7 +299,7 @@ _docker_machine_ssh() { _docker_machine_scp() { if [[ "${cur}" == -* ]]; then - COMPREPLY=($(compgen -W "--delta -d --help --recursive -r" -- "${cur}")) + COMPREPLY=($(compgen -W "--delta -d --help --quiet -q --recursive -r" -- "${cur}")) else _filedir # It would be really nice to ssh to the machine and ls to complete @@ -329,7 +378,7 @@ _docker_machine_docker_machine() { _docker_machine() { COMPREPLY=() - local commands=(active config create env inspect ip kill ls provision regenerate-certs restart rm ssh scp start status stop upgrade url version help) + local commands=(active config create env inspect ip kill ls mount provision regenerate-certs restart rm ssh scp start status stop upgrade url version help) local flags=(--debug --native-ssh --github-api-token --bugsnag-api-token --help --version) local wants_dir=(--storage-path) @@ -343,7 +392,7 @@ _docker_machine() { local cur prev words cword _get_comp_words_by_ref -n : cur prev words cword local i - local command=docker-machine + local command=docker-machine command_pos=0 for (( i=1; i < ${cword}; ++i)); do local word=${words[i]} @@ -352,6 +401,7 @@ _docker_machine() { (( ++i )) elif [[ " ${commands[*]} " =~ " ${word} " ]]; then command=${word} + command_pos=$i fi done @@ -363,4 +413,4 @@ _docker_machine() { return 0 } -complete -F _docker_machine docker-machine docker-machine.exe +complete -F _docker_machine docker-machine docker-machine.exe \ No newline at end of file From 918ccd25d6b37c17157d749ceebdc13ee9da0e3d Mon Sep 17 00:00:00 2001 From: Brian Phillips Date: Tue, 26 Mar 2019 10:04:19 -0500 Subject: [PATCH 36/50] Add support for helm completion Signed-off-by: Brian Phillips --- completion/available/helm.completion.bash | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 completion/available/helm.completion.bash diff --git a/completion/available/helm.completion.bash b/completion/available/helm.completion.bash new file mode 100644 index 00000000..d0936fab --- /dev/null +++ b/completion/available/helm.completion.bash @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +# helm (Kubernetes Package Manager) completion + +if command -v helm &>/dev/null +then + eval "$(helm completion bash)" +fi From 2cadeaab05e47e6f8e87bc88b3b3b0b2eebf422a Mon Sep 17 00:00:00 2001 From: LanikSJ Date: Thu, 28 Mar 2019 15:12:03 -0700 Subject: [PATCH 37/50] Add Alias for Git Stash --- aliases/available/git.aliases.bash | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aliases/available/git.aliases.bash b/aliases/available/git.aliases.bash index 29fae023..d13c14a4 100644 --- a/aliases/available/git.aliases.bash +++ b/aliases/available/git.aliases.bash @@ -76,6 +76,8 @@ alias gnew="git log HEAD@{1}..HEAD@{0}" alias gcaa="git commit -a --amend -C HEAD" alias ggui="git gui" alias gcsam="git commit -S -am" +alias gst="git stash" +alias gstb="git stash branch" alias gstd="git stash drop" alias gstl="git stash list" alias gh='cd "$(git rev-parse --show-toplevel)"' From fae61b3b98dfeeeb49d8362aad3f02a81b9329c9 Mon Sep 17 00:00:00 2001 From: LanikSJ Date: Tue, 2 Apr 2019 07:06:03 -0700 Subject: [PATCH 38/50] Remove gst Alias --- aliases/available/git.aliases.bash | 1 - 1 file changed, 1 deletion(-) diff --git a/aliases/available/git.aliases.bash b/aliases/available/git.aliases.bash index d13c14a4..7e6eeb24 100644 --- a/aliases/available/git.aliases.bash +++ b/aliases/available/git.aliases.bash @@ -18,7 +18,6 @@ alias gm="git merge" alias gmv='git mv' alias g='git' alias get='git' -alias gst='git status' alias gs='git status' alias gss='git status -s' alias gsu='git submodule update --init --recursive' From b5c9ecaf27551fcd37fcc8c5ed1eefbbec922e29 Mon Sep 17 00:00:00 2001 From: Joao Pereira Date: Thu, 4 Apr 2019 17:51:32 -0400 Subject: [PATCH 39/50] Add CNB pack cli plugin Adds the plugin that enables autocompletion for pack CLI --- plugins/available/pack.plugin.bash | 621 +++++++++++++++++++++++++++++ 1 file changed, 621 insertions(+) create mode 100644 plugins/available/pack.plugin.bash diff --git a/plugins/available/pack.plugin.bash b/plugins/available/pack.plugin.bash new file mode 100644 index 00000000..a63a8e57 --- /dev/null +++ b/plugins/available/pack.plugin.bash @@ -0,0 +1,621 @@ +# bash completion for pack -*- shell-script -*- + +cite about-plugin +about-plugin 'CNB pack cli aliases' + + +__pack_debug() +{ + if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then + echo "$*" >> "${BASH_COMP_DEBUG_FILE}" + fi +} + +# Homebrew on Macs have version 1.3 of bash-completion which doesn't include +# _init_completion. This is a very minimal version of that function. +__pack_init_completion() +{ + COMPREPLY=() + _get_comp_words_by_ref "$@" cur prev words cword +} + +__pack_index_of_word() +{ + local w word=$1 + shift + index=0 + for w in "$@"; do + [[ $w = "$word" ]] && return + index=$((index+1)) + done + index=-1 +} + +__pack_contains_word() +{ + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done + return 1 +} + +__pack_handle_reply() +{ + __pack_debug "${FUNCNAME[0]}" + case $cur in + -*) + if [[ $(type -t compopt) = "builtin" ]]; then + compopt -o nospace + fi + local allflags + if [ ${#must_have_one_flag[@]} -ne 0 ]; then + allflags=("${must_have_one_flag[@]}") + else + allflags=("${flags[*]} ${two_word_flags[*]}") + fi + COMPREPLY=( $(compgen -W "${allflags[*]}" -- "$cur") ) + if [[ $(type -t compopt) = "builtin" ]]; then + [[ "${COMPREPLY[0]}" == *= ]] || compopt +o nospace + fi + + # complete after --flag=abc + if [[ $cur == *=* ]]; then + if [[ $(type -t compopt) = "builtin" ]]; then + compopt +o nospace + fi + + local index flag + flag="${cur%=*}" + __pack_index_of_word "${flag}" "${flags_with_completion[@]}" + COMPREPLY=() + if [[ ${index} -ge 0 ]]; then + PREFIX="" + cur="${cur#*=}" + ${flags_completion[${index}]} + if [ -n "${ZSH_VERSION}" ]; then + # zsh completion needs --flag= prefix + eval "COMPREPLY=( \"\${COMPREPLY[@]/#/${flag}=}\" )" + fi + fi + fi + return 0; + ;; + esac + + # check if we are handling a flag with special work handling + local index + __pack_index_of_word "${prev}" "${flags_with_completion[@]}" + if [[ ${index} -ge 0 ]]; then + ${flags_completion[${index}]} + return + fi + + # we are parsing a flag and don't have a special handler, no completion + if [[ ${cur} != "${words[cword]}" ]]; then + return + fi + + local completions + completions=("${commands[@]}") + if [[ ${#must_have_one_noun[@]} -ne 0 ]]; then + completions=("${must_have_one_noun[@]}") + fi + if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then + completions+=("${must_have_one_flag[@]}") + fi + COMPREPLY=( $(compgen -W "${completions[*]}" -- "$cur") ) + + if [[ ${#COMPREPLY[@]} -eq 0 && ${#noun_aliases[@]} -gt 0 && ${#must_have_one_noun[@]} -ne 0 ]]; then + COMPREPLY=( $(compgen -W "${noun_aliases[*]}" -- "$cur") ) + fi + + if [[ ${#COMPREPLY[@]} -eq 0 ]]; then + declare -F __custom_func >/dev/null && __custom_func + fi + + # available in bash-completion >= 2, not always present on macOS + if declare -F __ltrim_colon_completions >/dev/null; then + __ltrim_colon_completions "$cur" + fi + + # If there is only 1 completion and it is a flag with an = it will be completed + # but we don't want a space after the = + if [[ "${#COMPREPLY[@]}" -eq "1" ]] && [[ $(type -t compopt) = "builtin" ]] && [[ "${COMPREPLY[0]}" == --*= ]]; then + compopt -o nospace + fi +} + +# The arguments should be in the form "ext1|ext2|extn" +__pack_handle_filename_extension_flag() +{ + local ext="$1" + _filedir "@(${ext})" +} + +__pack_handle_subdirs_in_dir_flag() +{ + local dir="$1" + pushd "${dir}" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 +} + +__pack_handle_flag() +{ + __pack_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + + # if a command required a flag, and we found it, unset must_have_one_flag() + local flagname=${words[c]} + local flagvalue + # if the word contained an = + if [[ ${words[c]} == *"="* ]]; then + flagvalue=${flagname#*=} # take in as flagvalue after the = + flagname=${flagname%=*} # strip everything after the = + flagname="${flagname}=" # but put the = back + fi + __pack_debug "${FUNCNAME[0]}: looking for ${flagname}" + if __pack_contains_word "${flagname}" "${must_have_one_flag[@]}"; then + must_have_one_flag=() + fi + + # if you set a flag which only applies to this command, don't show subcommands + if __pack_contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then + commands=() + fi + + # keep flag value with flagname as flaghash + # flaghash variable is an associative array which is only supported in bash > 3. + if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then + if [ -n "${flagvalue}" ] ; then + flaghash[${flagname}]=${flagvalue} + elif [ -n "${words[ $((c+1)) ]}" ] ; then + flaghash[${flagname}]=${words[ $((c+1)) ]} + else + flaghash[${flagname}]="true" # pad "true" for bool flag + fi + fi + + # skip the argument to a two word flag + if __pack_contains_word "${words[c]}" "${two_word_flags[@]}"; then + c=$((c+1)) + # if we are looking for a flags value, don't show commands + if [[ $c -eq $cword ]]; then + commands=() + fi + fi + + c=$((c+1)) + +} + +__pack_handle_noun() +{ + __pack_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + + if __pack_contains_word "${words[c]}" "${must_have_one_noun[@]}"; then + must_have_one_noun=() + elif __pack_contains_word "${words[c]}" "${noun_aliases[@]}"; then + must_have_one_noun=() + fi + + nouns+=("${words[c]}") + c=$((c+1)) +} + +__pack_handle_command() +{ + __pack_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + + local next_command + if [[ -n ${last_command} ]]; then + next_command="_${last_command}_${words[c]//:/__}" + else + if [[ $c -eq 0 ]]; then + next_command="_pack_root_command" + else + next_command="_${words[c]//:/__}" + fi + fi + c=$((c+1)) + __pack_debug "${FUNCNAME[0]}: looking for ${next_command}" + declare -F "$next_command" >/dev/null && $next_command +} + +__pack_handle_word() +{ + if [[ $c -ge $cword ]]; then + __pack_handle_reply + return + fi + __pack_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + if [[ "${words[c]}" == -* ]]; then + __pack_handle_flag + elif __pack_contains_word "${words[c]}" "${commands[@]}"; then + __pack_handle_command + elif [[ $c -eq 0 ]]; then + __pack_handle_command + elif __pack_contains_word "${words[c]}" "${command_aliases[@]}"; then + # aliashash variable is an associative array which is only supported in bash > 3. + if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then + words[c]=${aliashash[${words[c]}]} + __pack_handle_command + else + __pack_handle_noun + fi + else + __pack_handle_noun + fi + __pack_handle_word +} + +_pack_build() +{ + last_command="pack_build" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--builder=") + local_nonpersistent_flags+=("--builder=") + flags+=("--buildpack=") + local_nonpersistent_flags+=("--buildpack=") + flags+=("--clear-cache") + local_nonpersistent_flags+=("--clear-cache") + flags+=("--env=") + two_word_flags+=("-e") + local_nonpersistent_flags+=("--env=") + flags+=("--env-file=") + local_nonpersistent_flags+=("--env-file=") + flags+=("--help") + flags+=("-h") + local_nonpersistent_flags+=("--help") + flags+=("--no-pull") + local_nonpersistent_flags+=("--no-pull") + flags+=("--path=") + two_word_flags+=("-p") + local_nonpersistent_flags+=("--path=") + flags+=("--publish") + local_nonpersistent_flags+=("--publish") + flags+=("--run-image=") + local_nonpersistent_flags+=("--run-image=") + flags+=("--no-color") + flags+=("--quiet") + flags+=("-q") + flags+=("--timestamps") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +_pack_run() +{ + last_command="pack_run" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--builder=") + local_nonpersistent_flags+=("--builder=") + flags+=("--buildpack=") + local_nonpersistent_flags+=("--buildpack=") + flags+=("--clear-cache") + local_nonpersistent_flags+=("--clear-cache") + flags+=("--env=") + two_word_flags+=("-e") + local_nonpersistent_flags+=("--env=") + flags+=("--env-file=") + local_nonpersistent_flags+=("--env-file=") + flags+=("--help") + flags+=("-h") + local_nonpersistent_flags+=("--help") + flags+=("--no-pull") + local_nonpersistent_flags+=("--no-pull") + flags+=("--path=") + two_word_flags+=("-p") + local_nonpersistent_flags+=("--path=") + flags+=("--port=") + local_nonpersistent_flags+=("--port=") + flags+=("--run-image=") + local_nonpersistent_flags+=("--run-image=") + flags+=("--no-color") + flags+=("--quiet") + flags+=("-q") + flags+=("--timestamps") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +_pack_rebase() +{ + last_command="pack_rebase" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--help") + flags+=("-h") + local_nonpersistent_flags+=("--help") + flags+=("--no-pull") + local_nonpersistent_flags+=("--no-pull") + flags+=("--publish") + local_nonpersistent_flags+=("--publish") + flags+=("--run-image=") + local_nonpersistent_flags+=("--run-image=") + flags+=("--no-color") + flags+=("--quiet") + flags+=("-q") + flags+=("--timestamps") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +_pack_create-builder() +{ + last_command="pack_create-builder" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--builder-config=") + two_word_flags+=("-b") + local_nonpersistent_flags+=("--builder-config=") + flags+=("--help") + flags+=("-h") + local_nonpersistent_flags+=("--help") + flags+=("--no-pull") + local_nonpersistent_flags+=("--no-pull") + flags+=("--publish") + local_nonpersistent_flags+=("--publish") + flags+=("--no-color") + flags+=("--quiet") + flags+=("-q") + flags+=("--timestamps") + + must_have_one_flag=() + must_have_one_flag+=("--builder-config=") + must_have_one_flag+=("-b") + must_have_one_noun=() + noun_aliases=() +} + +_pack_set-run-image-mirrors() +{ + last_command="pack_set-run-image-mirrors" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--help") + flags+=("-h") + local_nonpersistent_flags+=("--help") + flags+=("--mirror=") + two_word_flags+=("-m") + local_nonpersistent_flags+=("--mirror=") + flags+=("--no-color") + flags+=("--quiet") + flags+=("-q") + flags+=("--timestamps") + + must_have_one_flag=() + must_have_one_flag+=("--mirror=") + must_have_one_flag+=("-m") + must_have_one_noun=() + noun_aliases=() +} + +_pack_inspect-builder() +{ + last_command="pack_inspect-builder" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--help") + flags+=("-h") + local_nonpersistent_flags+=("--help") + flags+=("--no-color") + flags+=("--quiet") + flags+=("-q") + flags+=("--timestamps") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +_pack_set-default-builder() +{ + last_command="pack_set-default-builder" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--help") + flags+=("-h") + local_nonpersistent_flags+=("--help") + flags+=("--no-color") + flags+=("--quiet") + flags+=("-q") + flags+=("--timestamps") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +_pack_version() +{ + last_command="pack_version" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--help") + flags+=("-h") + local_nonpersistent_flags+=("--help") + flags+=("--no-color") + flags+=("--quiet") + flags+=("-q") + flags+=("--timestamps") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +_pack_completion() +{ + last_command="pack_completion" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--help") + flags+=("-h") + local_nonpersistent_flags+=("--help") + flags+=("--no-color") + flags+=("--quiet") + flags+=("-q") + flags+=("--timestamps") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +_pack_root_command() +{ + last_command="pack" + + command_aliases=() + + commands=() + commands+=("build") + commands+=("run") + commands+=("rebase") + commands+=("create-builder") + commands+=("set-run-image-mirrors") + commands+=("inspect-builder") + commands+=("set-default-builder") + commands+=("version") + commands+=("completion") + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--help") + flags+=("-h") + local_nonpersistent_flags+=("--help") + flags+=("--no-color") + flags+=("--quiet") + flags+=("-q") + flags+=("--timestamps") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +__start_pack() +{ + local cur prev words cword + declare -A flaghash 2>/dev/null || : + declare -A aliashash 2>/dev/null || : + if declare -F _init_completion >/dev/null 2>&1; then + _init_completion -s || return + else + __pack_init_completion -n "=" || return + fi + + local c=0 + local flags=() + local two_word_flags=() + local local_nonpersistent_flags=() + local flags_with_completion=() + local flags_completion=() + local commands=("pack") + local must_have_one_flag=() + local must_have_one_noun=() + local last_command + local nouns=() + + __pack_handle_word +} + +if [[ $(type -t compopt) = "builtin" ]]; then + complete -o default -F __start_pack pack +else + complete -o default -o nospace -F __start_pack pack +fi + +# ex: ts=4 sw=4 et filetype=sh From c8b4ffbac1f9722c39ae544cf1603c3a05ae202b Mon Sep 17 00:00:00 2001 From: trmaphi Date: Fri, 5 Apr 2019 16:38:18 +0700 Subject: [PATCH 40/50] Update bash completion for git v2.21.0 --- completion/available/git.completion.bash | 1953 ++++++++++++---------- 1 file changed, 1116 insertions(+), 837 deletions(-) diff --git a/completion/available/git.completion.bash b/completion/available/git.completion.bash index e3918c87..1e3fdcac 100644 --- a/completion/available/git.completion.bash +++ b/completion/available/git.completion.bash @@ -28,27 +28,57 @@ # completion style. For example '!f() { : git commit ; ... }; f' will # tell the completion to use commit completion. This also works with aliases # of form "!sh -c '...'". For example, "!sh -c ': git commit ; ... '". +# +# Compatible with bash 3.2.57. +# +# You can set the following environment variables to influence the behavior of +# the completion routines: +# +# GIT_COMPLETION_CHECKOUT_NO_GUESS +# +# When set to "1", do not include "DWIM" suggestions in git-checkout +# completion (e.g., completing "foo" when "origin/foo" exists). case "$COMP_WORDBREAKS" in *:*) : great ;; *) COMP_WORDBREAKS="$COMP_WORDBREAKS:" esac +# Discovers the path to the git repository taking any '--git-dir=' and +# '-C ' options into account and stores it in the $__git_repo_path +# variable. +__git_find_repo_path () +{ + if [ -n "$__git_repo_path" ]; then + # we already know where it is + return + fi + + if [ -n "${__git_C_args-}" ]; then + __git_repo_path="$(git "${__git_C_args[@]}" \ + ${__git_dir:+--git-dir="$__git_dir"} \ + rev-parse --absolute-git-dir 2>/dev/null)" + elif [ -n "${__git_dir-}" ]; then + test -d "$__git_dir" && + __git_repo_path="$__git_dir" + elif [ -n "${GIT_DIR-}" ]; then + test -d "${GIT_DIR-}" && + __git_repo_path="$GIT_DIR" + elif [ -d .git ]; then + __git_repo_path=.git + else + __git_repo_path="$(git rev-parse --git-dir 2>/dev/null)" + fi +} + +# Deprecated: use __git_find_repo_path() and $__git_repo_path instead # __gitdir accepts 0 or 1 arguments (i.e., location) # returns location of .git repo __gitdir () { if [ -z "${1-}" ]; then - if [ -n "${__git_dir-}" ]; then - echo "$__git_dir" - elif [ -n "${GIT_DIR-}" ]; then - test -d "${GIT_DIR-}" || return 1 - echo "$GIT_DIR" - elif [ -d .git ]; then - echo .git - else - git rev-parse --git-dir 2>/dev/null - fi + __git_find_repo_path || return 1 + echo "$__git_repo_path" elif [ -d "$1/.git" ]; then echo "$1/.git" else @@ -56,6 +86,78 @@ __gitdir () fi } +# Runs git with all the options given as argument, respecting any +# '--git-dir=' and '-C ' options present on the command line +__git () +{ + git ${__git_C_args:+"${__git_C_args[@]}"} \ + ${__git_dir:+--git-dir="$__git_dir"} "$@" 2>/dev/null +} + +# Removes backslash escaping, single quotes and double quotes from a word, +# stores the result in the variable $dequoted_word. +# 1: The word to dequote. +__git_dequote () +{ + local rest="$1" len ch + + dequoted_word="" + + while test -n "$rest"; do + len=${#dequoted_word} + dequoted_word="$dequoted_word${rest%%[\\\'\"]*}" + rest="${rest:$((${#dequoted_word}-$len))}" + + case "${rest:0:1}" in + \\) + ch="${rest:1:1}" + case "$ch" in + $'\n') + ;; + *) + dequoted_word="$dequoted_word$ch" + ;; + esac + rest="${rest:2}" + ;; + \') + rest="${rest:1}" + len=${#dequoted_word} + dequoted_word="$dequoted_word${rest%%\'*}" + rest="${rest:$((${#dequoted_word}-$len+1))}" + ;; + \") + rest="${rest:1}" + while test -n "$rest" ; do + len=${#dequoted_word} + dequoted_word="$dequoted_word${rest%%[\\\"]*}" + rest="${rest:$((${#dequoted_word}-$len))}" + case "${rest:0:1}" in + \\) + ch="${rest:1:1}" + case "$ch" in + \"|\\|\$|\`) + dequoted_word="$dequoted_word$ch" + ;; + $'\n') + ;; + *) + dequoted_word="$dequoted_word\\$ch" + ;; + esac + rest="${rest:2}" + ;; + \") + rest="${rest:1}" + break + ;; + esac + done + ;; + esac + done +} + # The following function is based on code from: # # bash_completion - programmable completion functions for bash 3.2+ @@ -75,8 +177,7 @@ __gitdir () # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# along with this program; if not, see . # # The latest version of this software can be obtained here: # @@ -185,6 +286,20 @@ _get_comp_words_by_ref () } fi +# Fills the COMPREPLY array with prefiltered words without any additional +# processing. +# Callers must take care of providing only words that match the current word +# to be completed and adding any prefix and/or suffix (trailing space!), if +# necessary. +# 1: List of newline-separated matching completion words, complete with +# prefix and suffix. +__gitcomp_direct () +{ + local IFS=$'\n' + + COMPREPLY=($1) +} + __gitcompappend () { local x i=${#COMPREPLY[@]} @@ -215,9 +330,32 @@ __gitcomp () case "$cur_" in --*=) ;; + --no-*) + local c i=0 IFS=$' \t\n' + for c in $1; do + if [[ $c == "--" ]]; then + continue + fi + c="$c${4-}" + if [[ $c == "$cur_"* ]]; then + case $c in + --*=*|*.) ;; + *) c="$c " ;; + esac + COMPREPLY[i++]="${2-}$c" + fi + done + ;; *) local c i=0 IFS=$' \t\n' for c in $1; do + if [[ $c == "--" ]]; then + c="--no-...${4-}" + if [[ $c == "$cur_"* ]]; then + COMPREPLY[i++]="${2-}$c " + fi + break + fi c="$c${4-}" if [[ $c == "$cur_"* ]]; then case $c in @@ -231,6 +369,47 @@ __gitcomp () esac } +# Clear the variables caching builtins' options when (re-)sourcing +# the completion script. +if [[ -n ${ZSH_VERSION-} ]]; then + unset $(set |sed -ne 's/^\(__gitcomp_builtin_[a-zA-Z0-9_][a-zA-Z0-9_]*\)=.*/\1/p') 2>/dev/null +else + unset $(compgen -v __gitcomp_builtin_) +fi + +# This function is equivalent to +# +# __gitcomp "$(git xxx --git-completion-helper) ..." +# +# except that the output is cached. Accept 1-3 arguments: +# 1: the git command to execute, this is also the cache key +# 2: extra options to be added on top (e.g. negative forms) +# 3: options to be excluded +__gitcomp_builtin () +{ + # spaces must be replaced with underscore for multi-word + # commands, e.g. "git remote add" becomes remote_add. + local cmd="$1" + local incl="$2" + local excl="$3" + + local var=__gitcomp_builtin_"${cmd/-/_}" + local options + eval "options=\$$var" + + if [ -z "$options" ]; then + # leading and trailing spaces are significant to make + # option removal work correctly. + options=" $incl $(__git ${cmd/_/ } --git-completion-helper) " + for i in $excl; do + options="${options/ $i / }" + done + eval "$var=\"$options\"" + fi + + __gitcomp "$options" +} + # Variation of __gitcomp_nl () that appends to the existing list of # completion candidates, COMPREPLY. __gitcomp_nl_append () @@ -254,6 +433,24 @@ __gitcomp_nl () __gitcomp_nl_append "$@" } +# Fills the COMPREPLY array with prefiltered paths without any additional +# processing. +# Callers must take care of providing only paths that match the current path +# to be completed and adding any prefix path components, if necessary. +# 1: List of newline-separated matching paths, complete with all prefix +# path components. +__gitcomp_file_direct () +{ + local IFS=$'\n' + + COMPREPLY=($1) + + # use a hack to enable file mode in bash < 4 + compopt -o filenames +o nospace 2>/dev/null || + compgen -f /non-existing-dir/ >/dev/null || + true +} + # Generates completion reply with compgen from newline-separated possible # completion filenames. # It accepts 1 to 3 arguments: @@ -273,7 +470,8 @@ __gitcomp_file () # use a hack to enable file mode in bash < 4 compopt -o filenames +o nospace 2>/dev/null || - compgen -f /non-existing-dir/ > /dev/null + compgen -f /non-existing-dir/ >/dev/null || + true } # Execute 'git ls-files', unless the --committable option is specified, in @@ -283,11 +481,13 @@ __gitcomp_file () __git_ls_files_helper () { if [ "$2" == "--committable" ]; then - git -C "$1" diff-index --name-only --relative HEAD + __git -C "$1" -c core.quotePath=false diff-index \ + --name-only --relative HEAD -- "${3//\\/\\\\}*" else # NOTE: $2 is not quoted in order to support multiple options - git -C "$1" ls-files --exclude-standard $2 - fi 2>/dev/null + __git -C "$1" -c core.quotePath=false ls-files \ + --exclude-standard $2 -- "${3//\\/\\\\}*" + fi } @@ -297,101 +497,283 @@ __git_ls_files_helper () # If provided, only files within the specified directory are listed. # Sub directories are never recursed. Path must have a trailing # slash. +# 3: List only paths matching this path component (optional). __git_index_files () { - local dir="$(__gitdir)" root="${2-.}" file + local root="$2" match="$3" - if [ -d "$dir" ]; then - __git_ls_files_helper "$root" "$1" | - while read -r file; do - case "$file" in - ?*/*) echo "${file%%/*}" ;; - *) echo "$file" ;; - esac - done | sort | uniq - fi + __git_ls_files_helper "$root" "$1" "$match" | + awk -F / -v pfx="${2//\\/\\\\}" '{ + paths[$1] = 1 + } + END { + for (p in paths) { + if (substr(p, 1, 1) != "\"") { + # No special characters, easy! + print pfx p + continue + } + + # The path is quoted. + p = dequote(p) + if (p == "") + continue + + # Even when a directory name itself does not contain + # any special characters, it will still be quoted if + # any of its (stripped) trailing path components do. + # Because of this we may have seen the same direcory + # both quoted and unquoted. + if (p in paths) + # We have seen the same directory unquoted, + # skip it. + continue + else + print pfx p + } + } + function dequote(p, bs_idx, out, esc, esc_idx, dec) { + # Skip opening double quote. + p = substr(p, 2) + + # Interpret backslash escape sequences. + while ((bs_idx = index(p, "\\")) != 0) { + out = out substr(p, 1, bs_idx - 1) + esc = substr(p, bs_idx + 1, 1) + p = substr(p, bs_idx + 2) + + if ((esc_idx = index("abtvfr\"\\", esc)) != 0) { + # C-style one-character escape sequence. + out = out substr("\a\b\t\v\f\r\"\\", + esc_idx, 1) + } else if (esc == "n") { + # Uh-oh, a newline character. + # We cant reliably put a pathname + # containing a newline into COMPREPLY, + # and the newline would create a mess. + # Skip this path. + return "" + } else { + # Must be a \nnn octal value, then. + dec = esc * 64 + \ + substr(p, 1, 1) * 8 + \ + substr(p, 2, 1) + out = out sprintf("%c", dec) + p = substr(p, 3) + } + } + # Drop closing double quote, if there is one. + # (There isnt any if this is a directory, as it was + # already stripped with the trailing path components.) + if (substr(p, length(p), 1) == "\"") + out = out substr(p, 1, length(p) - 1) + else + out = out p + + return out + }' } +# __git_complete_index_file requires 1 argument: +# 1: the options to pass to ls-file +# +# The exception is --committable, which finds the files appropriate commit. +__git_complete_index_file () +{ + local dequoted_word pfx="" cur_ + + __git_dequote "$cur" + + case "$dequoted_word" in + ?*/*) + pfx="${dequoted_word%/*}/" + cur_="${dequoted_word##*/}" + ;; + *) + cur_="$dequoted_word" + esac + + __gitcomp_file_direct "$(__git_index_files "$1" "$pfx" "$cur_")" +} + +# Lists branches from the local repository. +# 1: A prefix to be added to each listed branch (optional). +# 2: List only branches matching this word (optional; list all branches if +# unset or empty). +# 3: A suffix to be appended to each listed branch (optional). __git_heads () { - local dir="$(__gitdir)" - if [ -d "$dir" ]; then - git --git-dir="$dir" for-each-ref --format='%(refname:short)' \ - refs/heads - return - fi + local pfx="${1-}" cur_="${2-}" sfx="${3-}" + + __git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \ + "refs/heads/$cur_*" "refs/heads/$cur_*/**" } +# Lists tags from the local repository. +# Accepts the same positional parameters as __git_heads() above. __git_tags () { - local dir="$(__gitdir)" - if [ -d "$dir" ]; then - git --git-dir="$dir" for-each-ref --format='%(refname:short)' \ - refs/tags - return - fi + local pfx="${1-}" cur_="${2-}" sfx="${3-}" + + __git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \ + "refs/tags/$cur_*" "refs/tags/$cur_*/**" } -# __git_refs accepts 0, 1 (to pass to __gitdir), or 2 arguments -# presence of 2nd argument means use the guess heuristic employed -# by checkout for tracking branches +# Lists refs from the local (by default) or from a remote repository. +# It accepts 0, 1 or 2 arguments: +# 1: The remote to list refs from (optional; ignored, if set but empty). +# Can be the name of a configured remote, a path, or a URL. +# 2: In addition to local refs, list unique branches from refs/remotes/ for +# 'git checkout's tracking DWIMery (optional; ignored, if set but empty). +# 3: A prefix to be added to each listed ref (optional). +# 4: List only refs matching this word (optional; list all refs if unset or +# empty). +# 5: A suffix to be appended to each listed ref (optional; ignored, if set +# but empty). +# +# Use __git_complete_refs() instead. __git_refs () { - local i hash dir="$(__gitdir "${1-}")" track="${2-}" + local i hash dir track="${2-}" + local list_refs_from=path remote="${1-}" local format refs - if [ -d "$dir" ]; then - case "$cur" in + local pfx="${3-}" cur_="${4-$cur}" sfx="${5-}" + local match="${4-}" + local fer_pfx="${pfx//\%/%%}" # "escape" for-each-ref format specifiers + + __git_find_repo_path + dir="$__git_repo_path" + + if [ -z "$remote" ]; then + if [ -z "$dir" ]; then + return + fi + else + if __git_is_configured_remote "$remote"; then + # configured remote takes precedence over a + # local directory with the same name + list_refs_from=remote + elif [ -d "$remote/.git" ]; then + dir="$remote/.git" + elif [ -d "$remote" ]; then + dir="$remote" + else + list_refs_from=url + fi + fi + + if [ "$list_refs_from" = path ]; then + if [[ "$cur_" == ^* ]]; then + pfx="$pfx^" + fer_pfx="$fer_pfx^" + cur_=${cur_#^} + match=${match#^} + fi + case "$cur_" in refs|refs/*) format="refname" - refs="${cur%/*}" + refs=("$match*" "$match*/**") track="" ;; *) - for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do - if [ -e "$dir/$i" ]; then echo $i; fi + for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD REBASE_HEAD; do + case "$i" in + $match*) + if [ -e "$dir/$i" ]; then + echo "$pfx$i$sfx" + fi + ;; + esac done - format="refname:short" - refs="refs/tags refs/heads refs/remotes" + format="refname:strip=2" + refs=("refs/tags/$match*" "refs/tags/$match*/**" + "refs/heads/$match*" "refs/heads/$match*/**" + "refs/remotes/$match*" "refs/remotes/$match*/**") ;; esac - git --git-dir="$dir" for-each-ref --format="%($format)" \ - $refs + __git_dir="$dir" __git for-each-ref --format="$fer_pfx%($format)$sfx" \ + "${refs[@]}" if [ -n "$track" ]; then # employ the heuristic used by git checkout # Try to find a remote branch that matches the completion word # but only output if the branch name is unique - local ref entry - git --git-dir="$dir" for-each-ref --shell --format="ref=%(refname:short)" \ - "refs/remotes/" | \ - while read -r entry; do - eval "$entry" - ref="${ref#*/}" - if [[ "$ref" == "$cur"* ]]; then - echo "$ref" - fi - done | sort | uniq -u + __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \ + --sort="refname:strip=3" \ + "refs/remotes/*/$match*" "refs/remotes/*/$match*/**" | \ + uniq -u fi return fi - case "$cur" in + case "$cur_" in refs|refs/*) - git ls-remote "$dir" "$cur*" 2>/dev/null | \ + __git ls-remote "$remote" "$match*" | \ while read -r hash i; do case "$i" in *^{}) ;; - *) echo "$i" ;; + *) echo "$pfx$i$sfx" ;; esac done ;; *) - echo "HEAD" - git for-each-ref --format="%(refname:short)" -- \ - "refs/remotes/$dir/" 2>/dev/null | sed -e "s#^$dir/##" + if [ "$list_refs_from" = remote ]; then + case "HEAD" in + $match*) echo "${pfx}HEAD$sfx" ;; + esac + __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \ + "refs/remotes/$remote/$match*" \ + "refs/remotes/$remote/$match*/**" + else + local query_symref + case "HEAD" in + $match*) query_symref="HEAD" ;; + esac + __git ls-remote "$remote" $query_symref \ + "refs/tags/$match*" "refs/heads/$match*" \ + "refs/remotes/$match*" | + while read -r hash i; do + case "$i" in + *^{}) ;; + refs/*) echo "$pfx${i#refs/*/}$sfx" ;; + *) echo "$pfx$i$sfx" ;; # symbolic refs + esac + done + fi ;; esac } +# Completes refs, short and long, local and remote, symbolic and pseudo. +# +# Usage: __git_complete_refs [