Merge pull request #1932 from gaelicWizard/search

Lib/search: lint, cleanup, and small fixes
pull/2075/head
Noah Gorny 2022-01-30 00:09:49 +02:00 committed by GitHub
commit 39e5652ee5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 249 additions and 232 deletions

View File

@ -83,6 +83,7 @@ completion/available/wpscan.completion.bash
lib/helpers.bash lib/helpers.bash
lib/log.bash lib/log.bash
lib/preexec.bash lib/preexec.bash
lib/search.bash
lib/utilities.bash lib/utilities.bash
# plugins # plugins

450
lib/search.bash 100755 → 100644
View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash # shellcheck shell=bash
# #
# Search by Konstantin Gredeskoul «github.com/kigster» # Search by Konstantin Gredeskoul «github.com/kigster»
#——————————————————————————————————————————————————————————————————————————————— #———————————————————————————————————————————————————————————————————————————————
@ -47,49 +47,52 @@
# completions: git # completions: git
# #
_bash-it-search() { function _bash-it-search() {
_about 'searches for given terms amongst bash-it plugins, aliases and completions' _about 'searches for given terms amongst bash-it plugins, aliases and completions'
_param '1: term1' _param '1: term1'
_param '2: [ term2 ]...' _param '2: [ term2 ]...'
_example '$ _bash-it-search @git ruby -rvm rake bundler' _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
local BASH_IT_SEARCH_USE_COLOR="${BASH_IT_SEARCH_USE_COLOR:=true}"
local -a BASH_IT_COMPONENTS=('aliases' 'plugins' 'completions')
local component if [[ $# -eq 0 ]]; then
export BASH_IT_SEARCH_USE_COLOR=true _bash-it-search-help
declare -a BASH_IT_COMPONENTS=(aliases plugins completions) return 0
fi
if [[ -z "$*" ]] ; then local -a args=()
_bash-it-search-help for word in "$@"; do
return 0 case "${word}" in
fi '-h' | '--help')
_bash-it-search-help
return 0
;;
'-r' | '--refresh')
_bash-it-clean-component-cache
;;
'-c' | '--no-color')
BASH_IT_SEARCH_USE_COLOR=false
;;
*)
args+=("${word}")
;;
esac
done
local -a args=() if [[ ${#args} -gt 0 ]]; then
for word in $@; do for component in "${BASH_IT_COMPONENTS[@]}"; do
if [[ ${word} == "--help" || ${word} == "-h" ]]; then _bash-it-search-component "${component}" "${args[@]}"
_bash-it-search-help done
return 0 fi
elif [[ ${word} == "--refresh" || ${word} == "-r" ]]; 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
if [[ ${#args} -gt 0 ]]; then return 0
for component in "${BASH_IT_COMPONENTS[@]}" ; do
_bash-it-search-component "${component}" "${args[@]}"
done
fi
return 0
} }
_bash-it-search-help() { function _bash-it-search-help() {
printf "${echo_normal} printf '%b' "${echo_normal-}
${echo_underline_yellow}USAGE${echo_normal} ${echo_underline_yellow-}USAGE${echo_normal-}
bash-it search [-|@]term1 [-|@]term2 ... \\ bash-it search [-|@]term1 [-|@]term2 ... \\
[ --enable | -e ] \\ [ --enable | -e ] \\
@ -98,9 +101,9 @@ ${echo_underline_yellow}USAGE${echo_normal}
[ --refresh | -r ] \\ [ --refresh | -r ] \\
[ --help | -h ] [ --help | -h ]
${echo_underline_yellow}DESCRIPTION${echo_normal} ${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 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 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). enabled are shown in green (or with a check box if --no-color option is used).
@ -117,42 +120,42 @@ ${echo_underline_yellow}DESCRIPTION${echo_normal}
* To perform an exact match, use character '@' in front of the term, * To perform an exact match, use character '@' in front of the term,
eg. '@git' would only match aliases, plugins and completions named 'git'. eg. '@git' would only match aliases, plugins and completions named 'git'.
${echo_underline_yellow}FLAGS${echo_normal} ${echo_underline_yellow-}FLAGS${echo_normal-}
--enable | -e ${echo_purple}Enable all matching componenents.${echo_normal} --enable | -e ${echo_purple-}Enable all matching componenents.${echo_normal-}
--disable | -d ${echo_purple}Disable all matching componenents.${echo_normal} --disable | -d ${echo_purple-}Disable all matching componenents.${echo_normal-}
--help | -h ${echo_purple}Print this help.${echo_normal} --help | -h ${echo_purple-}Print this help.${echo_normal-}
--refresh | -r ${echo_purple}Force a refresh of the search cache.${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} --no-color | -c ${echo_purple-}Disable color output and use monochrome text.${echo_normal-}
${echo_underline_yellow}EXAMPLES${echo_normal} ${echo_underline_yellow-}EXAMPLES${echo_normal-}
For example, ${echo_bold_green}bash-it search git${echo_normal} would match any alias, completion 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 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 it's description. You should see something like this when you run this
command: command:
${echo_bold_green} bash-it search git${echo_bold_blue} ${echo_bold_green-} bash-it search git${echo_bold_blue-}
${echo_bold_yellow}aliases: ${echo_bold_green}git ${echo_normal}gitsvn ${echo_bold_yellow-}aliases: ${echo_bold_green-}git ${echo_normal-}gitsvn
${echo_bold_yellow}plugins: ${echo_normal}autojump ${echo_bold_green}git ${echo_normal}git-subrepo jgitflow jump ${echo_bold_yellow-}plugins: ${echo_normal-}autojump ${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} ${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: 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_green-} bash-it search git -flow -svn${echo_bold_blue-}
${echo_bold_yellow}aliases: ${echo_normal}git ${echo_bold_yellow-}aliases: ${echo_normal-}git
${echo_bold_yellow}plugins: ${echo_normal}autojump git git-subrepo jump ${echo_bold_yellow-}plugins: ${echo_normal-}autojump git git-subrepo jump
${echo_bold_yellow}completions: ${echo_normal}git${echo_normal} ${echo_bold_yellow-}completions: ${echo_normal-}git${echo_normal-}
Finally, if you prefix a term with '@' symbol, that indicates an exact Finally, if you prefix a term with '@' symbol, that indicates an exact
match. Note, that we also pass the '--enable' flag, which would ensure match. Note, that we also pass the '--enable' flag, which would ensure
that all matches are automatically enabled. The example is below: that all matches are automatically enabled. The example is below:
${echo_bold_green} bash-it search @git --enable${echo_bold_blue} ${echo_bold_green-} bash-it search @git --enable${echo_bold_blue-}
${echo_bold_yellow}aliases: ${echo_normal}git ${echo_bold_yellow-}aliases: ${echo_normal-}git
${echo_bold_yellow}plugins: ${echo_normal}git ${echo_bold_yellow-}plugins: ${echo_normal-}git
${echo_bold_yellow}completions: ${echo_normal}git${echo_normal} ${echo_bold_yellow-}completions: ${echo_normal-}git${echo_normal-}
${echo_underline_yellow}SUMMARY${echo_normal} ${echo_underline_yellow-}SUMMARY${echo_normal-}
Take advantage of the search functionality to discover what Bash-It can do 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 for you. Try searching for partial term matches, mix and match with the
@ -164,197 +167,210 @@ ${echo_underline_yellow}SUMMARY${echo_normal}
" "
} }
_bash-it-is-partial-match() { function _bash-it-is-partial-match() {
local component="$1" local component="${1?${FUNCNAME[0]}: component type must be specified}"
local term="$2" local term="${2:-}"
_bash-it-component-help "${component}" | _bash-it-egrep -i -q -- "${term}" _bash-it-component-help "${component}" | _bash-it-egrep -i -q -- "${term}"
} }
_bash-it-component-term-matches-negation() { function _bash-it-component-term-matches-negation() {
local match="$1"; shift local match="$1"
local negative shift
for negative in "$@"; do local negative
[[ "${match}" =~ "${negative}" ]] && return 0 for negative in "$@"; do
done [[ "${match}" =~ ${negative} ]] && return 0
done
return 1 return 1
} }
_bash-it-search-component() { function _bash-it-search-component() {
local component="$1" _about 'searches for given terms amongst a given component'
shift _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'
_about 'searches for given terms amongst a given component' local component="${1?${FUNCNAME[0]}: component type must be specified}"
_param '1: component type, one of: [ aliases | plugins | completions ]' shift
_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 # if one of the search terms is --enable or --disable, we will apply
# this action to the matches further ` down. # this action to the matches further ` down.
local component_singular action action_func local component_singular action action_func
local -a search_commands=(enable disable) local -a search_commands=('enable' 'disable')
for search_command in "${search_commands[@]}"; do for search_command in "${search_commands[@]}"; do
if $(_bash-it-array-contains-element "--${search_command}" "$@"); then if _bash-it-array-contains-element "--${search_command}" "$@"; then
component_singular=${component} component_singular="${component/es/}" # aliases -> alias
component_singular=${component_singular/es/} # aliases -> alias component_singular="${component_singular/ns/n}" # plugins -> plugin
component_singular=${component_singular/ns/n} # plugins -> plugin
action="${search_command}" action="${search_command}"
action_func="_${action}-${component_singular}" action_func="_${action}-${component_singular}"
break break
fi fi
done done
local -a terms=($@) # passed on the command line local -a terms=("$@") # passed on the command line
unset exact_terms local -a exact_terms=() # terms that should be included only if they match exactly
unset partial_terms local -a partial_terms=() # terms that should be included if they match partially
unset negative_terms local -a negative_terms=() # negated partial terms that should be excluded
local -a exact_terms=() # terms that should be included only if they match exactly local term line
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=()
local -a component_list=( $(_bash-it-component-list "${component}") ) while IFS='' read -r line; do
local term component_list+=("$line")
done < <(_bash-it-component-list "${component}")
for term in "${terms[@]}"; do for term in "${terms[@]}"; do
local search_term="${term:1}" local search_term="${term:1}"
if [[ "${term:0:2}" == "--" ]] ; then if [[ "${term:0:2}" == "--" ]]; then
continue continue
elif [[ "${term:0:1}" == "-" ]] ; then elif [[ "${term:0:1}" == "-" ]]; then
negative_terms=(${negative_terms[@]} "${search_term}") negative_terms+=("${search_term}")
elif [[ "${term:0:1}" == "@" ]] ; then elif [[ "${term:0:1}" == "@" ]]; then
if $(_bash-it-array-contains-element "${search_term}" "${component_list[@]}"); then if _bash-it-array-contains-element "${search_term}" "${component_list[@]:-}"; then
exact_terms=(${exact_terms[@]} "${search_term}") exact_terms+=("${search_term}")
fi fi
else else
partial_terms=(${partial_terms[@]} $(_bash-it-component-list-matching "${component}" "${term}") ) while IFS='' read -r line; do
fi partial_terms+=("$line")
done done < <(_bash-it-component-list-matching "${component}" "${term}")
local -a total_matches=( $(_bash-it-array-dedup ${exact_terms[@]} ${partial_terms[@]}) ) fi
done
unset matches local -a total_matches=()
declare -a matches=() while IFS='' read -r line; do
for match in ${total_matches[@]}; do total_matches+=("$line")
local include_match=true done < <(_bash-it-array-dedup "${exact_terms[@]:-}" "${partial_terms[@]:-}")
if [[ ${#negative_terms[@]} -gt 0 ]]; then
( _bash-it-component-term-matches-negation "${match}" "${negative_terms[@]}" ) && include_match=false local -a matches=()
fi for match in "${total_matches[@]}"; do
( ${include_match} ) && matches=(${matches[@]} "${match}") local -i include_match=1
done if [[ ${#negative_terms[@]} -gt 0 ]]; then
_bash-it-search-result "${component}" "${action}" "${action_func}" "${matches[@]}" _bash-it-component-term-matches-negation "${match}" "${negative_terms[@]:-}" && include_match=0
unset matches final_matches terms fi
((include_match)) && matches+=("${match}")
done
_bash-it-search-result "${component}" "${action:-}" "${action_func:-}" "${matches[@]:-}"
} }
_bash-it-search-result() { function _bash-it-search-result() {
local component="$1"; shift local component="${1?${FUNCNAME[0]}: component type must be specified}"
local action="$1"; shift shift
local action_func="$1"; shift local action="${1:-}"
local -a matches=($@) shift
local action_func="${1:-}"
shift
local color_component color_enable color_disable color_off local color_component color_enable color_disable color_off
local color_sep=':' line
color_sep=':' local -a matches=()
# Discard any empty arguments
while IFS='' read -r line; do
[[ -n "${line}" ]] && matches+=("$line")
done < <(_bash-it-array-dedup "${@}")
( ${BASH_IT_SEARCH_USE_COLOR} ) && { if [[ "${BASH_IT_SEARCH_USE_COLOR}" == "true" ]]; then
color_component='\e[1;34m' color_component='\e[1;34m'
color_enable='\e[1;32m' color_enable='\e[1;32m'
suffix_enable='' suffix_enable=''
suffix_disable='' suffix_disable=''
color_disable='\e[0;0m' color_disable='\e[0;0m'
color_off='\e[0;0m' color_off='\e[0;0m'
} else
color_component=''
suffix_enable=' ✓ '
suffix_disable=' '
color_enable=''
color_disable=''
color_off=''
fi
( ${BASH_IT_SEARCH_USE_COLOR} ) || { local match
color_component='' local -i modified=0
suffix_enable=' ✓ '
suffix_disable=' '
color_enable=''
color_disable=''
color_off=''
}
local match if [[ "${#matches[@]}" -gt 0 ]]; then
local modified=0 printf "${color_component}%13s${color_sep}${color_off} " "${component}"
if [[ "${#matches[@]}" -gt 0 ]] ; then for match in "${matches[@]}"; do
printf "${color_component}%13s${color_sep} ${color_off}" "${component}" local -i enabled=0
_bash-it-component-item-is-enabled "${component}" "${match}" && enabled=1
for match in "${matches[@]}"; do local match_color compatible_action suffix opposite_suffix
local enabled=0
( _bash-it-component-item-is-enabled "${component}" "${match}" ) && enabled=1
local match_color compatible_action suffix opposite_suffix if ((enabled)); then
match_color="${color_enable}"
suffix="${suffix_enable}"
opposite_suffix="${suffix_disable}"
compatible_action="disable"
else
match_color="${color_disable}"
suffix="${suffix_disable}"
opposite_suffix="${suffix_enable}"
compatible_action="enable"
fi
(( ${enabled} )) && { local matched="${match}${suffix}"
match_color=${color_enable} local -i len="${#matched}"
suffix=${suffix_enable}
opposite_suffix=${suffix_disable}
compatible_action="disable"
}
(( ${enabled} )) || { printf '%b' "${match_color}${matched}" # print current state
match_color=${color_disable} if [[ "${action}" == "${compatible_action}" ]]; then
suffix=${suffix_disable} if [[ "${action}" == "enable" && "${BASH_IT_SEARCH_USE_COLOR}" == "true" ]]; then
opposite_suffix=${suffix_enable} _bash-it-flash-term "${len}" "${matched}"
compatible_action="enable" else
} _bash-it-erase-term "${len}" "${matched}"
fi
modified=1
# shellcheck disable=SC2034 # no idea if `$result` is ever used
result=$("${action_func}" "${match}")
local temp="color_${compatible_action}"
match_color="${!temp}"
_bash-it-rewind "${len}"
printf '%b' "${match_color}${match}${opposite_suffix}"
fi
local m="${match}${suffix}" printf '%b' "${color_off} "
local len done
len=${#m}
printf " ${match_color}${match}${suffix}" # print current state ((modified)) && _bash-it-clean-component-cache "${component}"
if [[ "${action}" == "${compatible_action}" ]]; then printf "\n"
if [[ ${action} == "enable" && ${BASH_IT_SEARCH_USE_COLOR} == false ]]; then fi
_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}
_bash-it-rewind ${len}
printf "${match_color}${match}${opposite_suffix}"
fi
printf "${color_off}"
done
[[ ${modified} -gt 0 ]] && _bash-it-clean-component-cache ${component}
printf "\n"
fi
} }
_bash-it-rewind() { function _bash-it-rewind() {
local len="$1" local -i len="${1:-0}"
printf "\033[${len}D" printf '%b' "\033[${len}D"
} }
_bash-it-flash-term() { function _bash-it-flash-term() {
local len="$1" local -i len="${1:-0}" # redundant
local match="$2" local term="${2:-}"
local delay=0.1 # as currently implemented, `$match` has already been printed to screen the first time
local color local delay=0.1
local color
[[ "${#term}" -gt 0 ]] && len="${#term}"
for color in ${text_black} ${echo_bold_blue} ${bold_yellow} ${bold_red} ${echo_bold_green} ; do for color in "${echo_black-}" "${echo_bold_blue-}" "${echo_bold_yellow-}" "${echo_bold_red-}" "${echo_bold_green-}" "${echo_normal-}"; do
sleep ${delay} sleep "${delay}"
_bash-it-rewind "${len}" _bash-it-rewind "${len}"
printf "${color}${match}" printf '%b' "${color}${term}"
done done
} }
_bash-it-erase-term() { function _bash-it-erase-term() {
local len="$1" local -i len="${1:-0}" i
_bash-it-rewind ${len} local delay=0.05
for a in {0..30}; do local term="${2:-}" # calculate length ourselves
[[ ${a} -gt ${len} ]] && break [[ "${#term}" -gt 0 ]] && len="${#term}"
printf "%.*s" $a " "
sleep 0.05 _bash-it-rewind "${len}"
done # white-out the already-printed term by printing blanks
for ((i = 0; i <= len; i++)); do
printf "%.*s" "$i" " "
sleep "${delay}"
done
} }

30
test/lib/search.bats 100644 → 100755
View File

@ -28,42 +28,42 @@ function local_teardown {
@test "search: plugin base" { @test "search: plugin base" {
export BASH_IT_SEARCH_USE_COLOR=false export BASH_IT_SEARCH_USE_COLOR=false
run _bash-it-search-component 'plugins' 'base' run _bash-it-search-component 'plugins' 'base'
assert_line -n 0 ' plugins: base ' assert_line -n 0 ' plugins: base '
} }
@test "search: git" { @test "search: git" {
run _bash-it-search 'git' --no-color run _bash-it-search 'git' --no-color
assert_line -n 0 ' aliases: git gitsvn ' assert_line -n 0 ' aliases: git gitsvn '
assert_line -n 1 -p ' plugins:' assert_line -n 1 -p ' plugins:'
for plugin in "autojump" "git" "gitstatus" "git-subrepo" "jgitflow" "jump" for plugin in "autojump" "git" "gitstatus" "git-subrepo" "jgitflow" "jump"
do do
echo $plugin echo $plugin
assert_line -n 1 -p $plugin assert_line -n 1 -p $plugin
done done
assert_line -n 2 ' completions: git git_flow git_flow_avh github-cli ' assert_line -n 2 ' completions: git git_flow git_flow_avh github-cli '
} }
@test "search: ruby gem bundle rake rails" { @test "search: ruby gem bundle rake rails" {
run _bash-it-search rails ruby gem bundler rake --no-color run _bash-it-search rails ruby gem bundler rake --no-color
assert_line -n 0 ' aliases: bundler rails ' assert_line -n 0 ' aliases: bundler rails '
assert_line -n 1 ' plugins: chruby chruby-auto rails ruby ' assert_line -n 1 ' plugins: chruby chruby-auto rails ruby '
assert_line -n 2 ' completions: bundler gem rake ' assert_line -n 2 ' completions: bundler gem rake '
} }
@test "search: rails ruby gem bundler rake -chruby" { @test "search: rails ruby gem bundler rake -chruby" {
run _bash-it-search rails ruby gem bundler rake -chruby --no-color run _bash-it-search rails ruby gem bundler rake -chruby --no-color
assert_line -n 0 ' aliases: bundler rails ' assert_line -n 0 ' aliases: bundler rails '
assert_line -n 1 ' plugins: rails ruby ' assert_line -n 1 ' plugins: rails ruby '
assert_line -n 2 ' completions: bundler gem rake ' assert_line -n 2 ' completions: bundler gem rake '
} }
@test "search: @git" { @test "search: @git" {
run _bash-it-search '@git' --no-color run _bash-it-search '@git' --no-color
assert_line -n 0 ' aliases: git ' assert_line -n 0 ' aliases: git '
assert_line -n 1 ' plugins: git ' assert_line -n 1 ' plugins: git '
assert_line -n 2 ' completions: git ' assert_line -n 2 ' completions: git '
} }
@test "search: @git --enable / --disable" { @test "search: @git --enable / --disable" {
@ -76,7 +76,7 @@ function local_teardown {
run _bash-it-search '@git' --disable --no-color run _bash-it-search '@git' --disable --no-color
run _bash-it-search '@git' --no-color run _bash-it-search '@git' --no-color
assert_line -n 0 ' aliases: git ' assert_line -n 0 ' aliases: git '
assert_line -n 0 ' aliases: git ' assert_line -n 1 ' plugins: git '
assert_line -n 2 ' completions: git ' assert_line -n 2 ' completions: git '
} }