From b0f23d8e98ee8048561693c3508bd72608e812e6 Mon Sep 17 00:00:00 2001 From: John D Pell Date: Sun, 16 Jan 2022 09:43:23 -0800 Subject: [PATCH 1/6] completion/alias: eliminate use of `eval` --- .../available/alias-completion.plugin.bash | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/plugins/available/alias-completion.plugin.bash b/plugins/available/alias-completion.plugin.bash index eb368d93..0ce2820e 100644 --- a/plugins/available/alias-completion.plugin.bash +++ b/plugins/available/alias-completion.plugin.bash @@ -1,26 +1,18 @@ # shellcheck shell=bash -# Load after the other completions to understand what needs to be completed -# BASH_IT_LOAD_PRIORITY: 365 - -cite about-plugin about-plugin 'Automatic completion of aliases' +# Load after the other completions to understand what needs to be completed +# BASH_IT_LOAD_PRIORITY: 800 + # References: # http://superuser.com/a/437508/119764 # http://stackoverflow.com/a/1793178/1228454 -# This needs to be a plugin so it gets executed after the completions and the aliases have been defined. -# Bash-it loads its components in the order -# 1) Aliases -# 2) Completions -# 3) Plugins -# 4) Custom scripts - # Automatically add completion for all aliases to commands having completion functions -function alias_completion { +function alias_completion() { local namespace="alias_completion" local tmp_file completion_loader alias_name alias_tokens line completions - local alias_arg_words new_completion compl_func compl_wrapper + local alias_arg_words new_completion compl_func compl_wrapper alias_defn # parse function based completion definitions, where capture group 2 => function and 3 => trigger local compl_regex='complete( +[^ ]+)* -F ([^ ]+) ("[^"]+"|[^ ]+)' @@ -28,9 +20,13 @@ function alias_completion { local alias_regex="alias( -- | )([^=]+)='(\"[^\"]+\"|[^ ]+)(( +[^ ]+)*)'" # create array of function completion triggers, keeping multi-word triggers together - eval "completions=($(complete -p | sed -Ene "/$compl_regex/s//'\3'/p"))" + IFS=$'\n' read -d '' -ra completions < <(complete -p) ((${#completions[@]} == 0)) && return 0 + completions=("${completions[@]##complete -* * -}") # strip all but last option plus trigger(s) + completions=("${completions[@]#complete -}") # strip last option and arg, leaving only trigger(s) + completions=("${completions[@]#? * }") # strip last option and arg, leaving only trigger(s) + # create temporary file for wrapper functions and completions tmp_file="$(mktemp -t "${namespace}-${RANDOM}XXXXXX")" || return 1 @@ -40,13 +36,16 @@ function alias_completion { # some aliases do have backslashes that needs to be interpreted # shellcheck disable=SC2162 while read line; do - eval "alias_tokens=($line)" 2> /dev/null || continue # some alias arg patterns cause an eval parse error - # shellcheck disable=SC2154 # see `eval` above - alias_name="${alias_tokens[0]}" alias_cmd="${alias_tokens[1]}" alias_args="${alias_tokens[2]# }" + line="${line#alias }" + alias_name="${line%%=*}" + alias_defn="${line#*=}" # alias definition + alias_cmd="${alias_defn%%[[:space:]]*}" # first word of alias + alias_cmd="${alias_cmd:1}" # lose opening quotation mark + alias_args="${alias_defn#*[[:space:]]}" # everything after first word + alias_args="${alias_args:0:-1}" # lose ending quotation mark # skip aliases to pipes, boolean control structures and other command lists - # (leveraging that eval errs out if $alias_args contains unquoted shell metacharacters) - eval "alias_arg_words=($alias_args)" 2> /dev/null || continue + [[ "${alias_args}" =~ [\|\&\;\)\(\n] ]] && continue # avoid expanding wildcards read -a alias_arg_words <<< "$alias_args" @@ -54,7 +53,7 @@ function alias_completion { if ! _bash-it-array-contains-element "$alias_cmd" "${completions[@]}"; then if [[ -n "$completion_loader" ]]; then # force loading of completions for the aliased command - eval "$completion_loader $alias_cmd" + "${completion_loader:?}" "${alias_cmd}" # 124 means completion loader was successful [[ $? -eq 124 ]] || continue completions+=("$alias_cmd") @@ -97,7 +96,7 @@ function alias_completion { new_completion="${new_completion% *} $alias_name" echo "$new_completion" >> "$tmp_file" fi - done < <(alias -p | sed -Ene "s/$alias_regex/\2 '\3' '\4'/p") + done < <(alias -p) # shellcheck source=/dev/null source "$tmp_file" && command rm -f "$tmp_file" } From d214621d39b4f2079b4bbcad4a516d19d0a3962d Mon Sep 17 00:00:00 2001 From: John D Pell Date: Sun, 16 Jan 2022 10:14:27 -0800 Subject: [PATCH 2/6] completion/alias: `shfmt` && `shellcheck` --- plugins/available/alias-completion.plugin.bash | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/plugins/available/alias-completion.plugin.bash b/plugins/available/alias-completion.plugin.bash index 0ce2820e..a1d1cb89 100644 --- a/plugins/available/alias-completion.plugin.bash +++ b/plugins/available/alias-completion.plugin.bash @@ -3,7 +3,6 @@ about-plugin 'Automatic completion of aliases' # Load after the other completions to understand what needs to be completed # BASH_IT_LOAD_PRIORITY: 800 - # References: # http://superuser.com/a/437508/119764 # http://stackoverflow.com/a/1793178/1228454 @@ -11,21 +10,16 @@ about-plugin 'Automatic completion of aliases' # Automatically add completion for all aliases to commands having completion functions function alias_completion() { local namespace="alias_completion" - local tmp_file completion_loader alias_name alias_tokens line completions + local tmp_file completion_loader alias_name line completions local alias_arg_words new_completion compl_func compl_wrapper alias_defn - # parse function based completion definitions, where capture group 2 => function and 3 => trigger - local compl_regex='complete( +[^ ]+)* -F ([^ ]+) ("[^"]+"|[^ ]+)' - # parse alias definitions, where capture group 1 => trigger, 2 => command, 3 => command arguments - local alias_regex="alias( -- | )([^=]+)='(\"[^\"]+\"|[^ ]+)(( +[^ ]+)*)'" - # create array of function completion triggers, keeping multi-word triggers together IFS=$'\n' read -d '' -ra completions < <(complete -p) ((${#completions[@]} == 0)) && return 0 completions=("${completions[@]##complete -* * -}") # strip all but last option plus trigger(s) - completions=("${completions[@]#complete -}") # strip last option and arg, leaving only trigger(s) - completions=("${completions[@]#? * }") # strip last option and arg, leaving only trigger(s) + completions=("${completions[@]#complete -}") # strip anything missed + completions=("${completions[@]#? * }") # strip last option and arg, leaving only trigger(s) # create temporary file for wrapper functions and completions tmp_file="$(mktemp -t "${namespace}-${RANDOM}XXXXXX")" || return 1 @@ -38,11 +32,11 @@ function alias_completion() { while read line; do line="${line#alias }" alias_name="${line%%=*}" - alias_defn="${line#*=}" # alias definition + alias_defn="${line#*=}" # alias definition alias_cmd="${alias_defn%%[[:space:]]*}" # first word of alias - alias_cmd="${alias_cmd:1}" # lose opening quotation mark + alias_cmd="${alias_cmd:1}" # lose opening quotation mark alias_args="${alias_defn#*[[:space:]]}" # everything after first word - alias_args="${alias_args:0:-1}" # lose ending quotation mark + alias_args="${alias_args%\'}" # lose ending quotation mark # skip aliases to pipes, boolean control structures and other command lists [[ "${alias_args}" =~ [\|\&\;\)\(\n] ]] && continue From 7fcad6ed0d9427e51b94cc721a846666cef8ea82 Mon Sep 17 00:00:00 2001 From: John D Pell Date: Sat, 15 Jan 2022 00:01:25 -0800 Subject: [PATCH 3/6] completion/alias: rename There is no reason for this to be in the `plugins` directory, it just needs to have a load priority sufficiently high that it runs after any aliases are defined. --- .../available/aliases.completion.bash | 0 profiles/default.bash_it | 2 +- .../aliases.completion.bats} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename plugins/available/alias-completion.plugin.bash => completion/available/aliases.completion.bash (100%) rename test/{plugins/alias-completion.plugin.bats => completion/aliases.completion.bats} (100%) diff --git a/plugins/available/alias-completion.plugin.bash b/completion/available/aliases.completion.bash similarity index 100% rename from plugins/available/alias-completion.plugin.bash rename to completion/available/aliases.completion.bash diff --git a/profiles/default.bash_it b/profiles/default.bash_it index 5e4f4631..9a55f6c7 100644 --- a/profiles/default.bash_it +++ b/profiles/default.bash_it @@ -1,10 +1,10 @@ # This is the default profile of Bash-it # plugins -plugins alias-completion plugins base # completion +completion aliases completion bash-it completion system diff --git a/test/plugins/alias-completion.plugin.bats b/test/completion/aliases.completion.bats similarity index 100% rename from test/plugins/alias-completion.plugin.bats rename to test/completion/aliases.completion.bats From b0862899d7cbd8cbfcc9465e09f1aa9b004470e0 Mon Sep 17 00:00:00 2001 From: John D Pell Date: Sun, 16 Jan 2022 10:23:02 -0800 Subject: [PATCH 4/6] completion/alias: fix tests --- test/completion/aliases.completion.bats | 7 ++++--- test/fixtures/bash_it/profiles/test-bad-component.bash_it | 2 +- test/fixtures/bash_it/profiles/test-bad-type.bash_it | 4 ++-- test/install/install.bats | 2 +- test/lib/helpers.bats | 6 +++--- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/test/completion/aliases.completion.bats b/test/completion/aliases.completion.bats index 20d13cf2..813a7bbd 100644 --- a/test/completion/aliases.completion.bats +++ b/test/completion/aliases.completion.bats @@ -3,25 +3,26 @@ load ../test_helper load ../test_helper_libs +# Load something, anything... load ../../completion/available/capistrano.completion @test "alias-completion: See that aliases with double quotes and brackets do not break the plugin" { alias gtest="git log --graph --pretty=format:'%C(bold)%h%Creset%C(magenta)%d%Creset %s %C(yellow)<%an> %C(cyan)(%cr)%Creset' --abbrev-commit --date=relative" - load ../../plugins/available/alias-completion.plugin + run load ../../completion/available/aliases.completion assert_success } @test "alias-completion: See that aliases with single quotes and brackets do not break the plugin" { alias gtest='git log --graph --pretty=format:"%C(bold)%h%Creset%C(magenta)%d%Creset %s %C(yellow)<%an> %C(cyan)(%cr)%Creset" --abbrev-commit --date=relative' - load ../../plugins/available/alias-completion.plugin + run load ../../completion/available/aliases.completion assert_success } @test "alias-completion: See that having aliased rm command does not output unnecessary output" { alias rm='rm -v' - load ../../plugins/available/alias-completion.plugin + run load ../../completion/available/aliases.completion refute_output } diff --git a/test/fixtures/bash_it/profiles/test-bad-component.bash_it b/test/fixtures/bash_it/profiles/test-bad-component.bash_it index 8640265c..068c4b63 100644 --- a/test/fixtures/bash_it/profiles/test-bad-component.bash_it +++ b/test/fixtures/bash_it/profiles/test-bad-component.bash_it @@ -1,8 +1,8 @@ # plugins -plugins alias-completion plugins base # completion +completion aliases completion bash-it completion system diff --git a/test/fixtures/bash_it/profiles/test-bad-type.bash_it b/test/fixtures/bash_it/profiles/test-bad-type.bash_it index ed2d2373..102c52ea 100644 --- a/test/fixtures/bash_it/profiles/test-bad-type.bash_it +++ b/test/fixtures/bash_it/profiles/test-bad-type.bash_it @@ -1,10 +1,10 @@ # plugins -plugins alias-completion plugins base # Bad type -pluugins alias-completion +compleetion aliases # completion +completion aliases completion bash-it completion system diff --git a/test/install/install.bats b/test/install/install.bats index 262bf4f0..b3aee5a7 100644 --- a/test/install/install.bats +++ b/test/install/install.bats @@ -29,7 +29,7 @@ function local_setup { assert_link_exist "$BASH_IT/enabled/150---general.aliases.bash" assert_link_exist "$BASH_IT/enabled/250---base.plugin.bash" - assert_link_exist "$BASH_IT/enabled/365---alias-completion.plugin.bash" + assert_link_exist "$BASH_IT/enabled/800---aliases.completion.bash" assert_link_exist "$BASH_IT/enabled/350---bash-it.completion.bash" assert_link_exist "$BASH_IT/enabled/325---system.completion.bash" } diff --git a/test/lib/helpers.bats b/test/lib/helpers.bats index bd339d04..8c340c58 100755 --- a/test/lib/helpers.bats +++ b/test/lib/helpers.bats @@ -296,7 +296,7 @@ function local_setup { assert_link_exist "$BASH_IT/enabled/150---general.aliases.bash" assert_link_exist "$BASH_IT/enabled/250---base.plugin.bash" - assert_link_exist "$BASH_IT/enabled/365---alias-completion.plugin.bash" + assert_link_exist "$BASH_IT/enabled/800---aliases.completion.bash" assert_link_exist "$BASH_IT/enabled/350---bash-it.completion.bash" assert_link_exist "$BASH_IT/enabled/325---system.completion.bash" } @@ -356,7 +356,7 @@ function local_setup { run _bash-it-profile-load "test" assert_link_not_exist "$BASH_IT/enabled/150---general.aliases.bash" assert_link_not_exist "$BASH_IT/enabled/250---base.plugin.bash" - assert_link_not_exist "$BASH_IT/enabled/365---alias-completion.plugin.bash" + assert_link_not_exist "$BASH_IT/enabled/800---aliases.completion.bash" assert_link_not_exist "$BASH_IT/enabled/350---bash-it.completion.bash" assert_link_not_exist "$BASH_IT/enabled/325---system.completion.bash" } @@ -384,7 +384,7 @@ function local_setup { @test "helpers: profile load corrupted profile file: bad subdirectory" { run _bash-it-profile-load "test-bad-type" - assert_line -n 1 -p "Bad line(#5) in profile, aborting load..." + assert_line -n 1 -p "Bad line(#4) in profile, aborting load..." } @test "helpers: profile rm sanity" { From 880488ec9a0de18714351b7f6284838c9d2185f5 Mon Sep 17 00:00:00 2001 From: John D Pell Date: Wed, 26 Jan 2022 10:14:54 -0800 Subject: [PATCH 5/6] completion/alias: add stub file - put a loader to remove the symlink at `enabled/***---alias-completion.plugin.bash`. --- plugins/available/alias-completion.plugin.bash | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 plugins/available/alias-completion.plugin.bash diff --git a/plugins/available/alias-completion.plugin.bash b/plugins/available/alias-completion.plugin.bash new file mode 100644 index 00000000..84c59a1e --- /dev/null +++ b/plugins/available/alias-completion.plugin.bash @@ -0,0 +1,5 @@ +# shellcheck shell=bash +# stub for renamed file + +_enable-completion aliases && _disable-plugin alias-completion +source "${BASH_IT?}/completion/aliases.completion.bash" From 43df0fe130f00444b836a24f0670a6d5df50ba0e Mon Sep 17 00:00:00 2001 From: John D Pell Date: Thu, 3 Feb 2022 22:46:58 -0800 Subject: [PATCH 6/6] completion/aliases: rename init function Use the callback naming convention for the init function, for later use. --- completion/available/aliases.completion.bash | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/completion/available/aliases.completion.bash b/completion/available/aliases.completion.bash index a1d1cb89..bdcaf917 100644 --- a/completion/available/aliases.completion.bash +++ b/completion/available/aliases.completion.bash @@ -1,6 +1,6 @@ # shellcheck shell=bash about-plugin 'Automatic completion of aliases' -# Load after the other completions to understand what needs to be completed +# Load after all aliases and completions to understand what needs to be completed # BASH_IT_LOAD_PRIORITY: 800 # References: @@ -8,7 +8,7 @@ about-plugin 'Automatic completion of aliases' # http://stackoverflow.com/a/1793178/1228454 # Automatically add completion for all aliases to commands having completion functions -function alias_completion() { +function _bash-it-component-completion-callback-on-init-aliases() { local namespace="alias_completion" local tmp_file completion_loader alias_name line completions local alias_arg_words new_completion compl_func compl_wrapper alias_defn @@ -95,4 +95,4 @@ function alias_completion() { source "$tmp_file" && command rm -f "$tmp_file" } -alias_completion +_bash-it-component-completion-callback-on-init-aliases