-
-
Notifications
You must be signed in to change notification settings - Fork 111
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #12296 from keymanapp/feat/builder-autocomplete
feat: add builder tab-completion script
- Loading branch information
Showing
2 changed files
with
155 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
# As this script must be sourced by shells to be useful, no practical #shebang is possible. | ||
|
||
# Note: must 'source' this file again in the terminal after any edits! | ||
_comp_builder() { | ||
# Does the file actually exist? If not, abort. | ||
local CMD_PATH=`readlink -f "$1"` | ||
if [ -z "${CMD_PATH}" ]; then | ||
exit 0 | ||
fi | ||
|
||
# Detect - is this a keyman repo? If not, exit early; we don't expect repo-external compatibility. | ||
local CMD_DIR="$(dirname "${CMD_PATH}")" | ||
cd "${CMD_DIR}" | ||
# Works correctly even if the directory has no backing repository. | ||
git remote get-url origin | grep -q 'github.com/keymanapp' || exit 0 | ||
|
||
# Some of the configuration differs when ZSH is the OS's primary terminal; this flag | ||
# may be used to tailor this function accordingly. | ||
local IS_ZSH=false | ||
if [ "${SHELL##*/}" = "zsh" ]; then | ||
IS_ZSH=true | ||
fi | ||
|
||
# Warning - zsh uses -A, not -a, for array-read operations! | ||
# This is 'sourced' by the shell profile script, so macOS zsh terminal | ||
# usage must be aware of this and adapt. | ||
local READ_ARRAY="-a" | ||
if [ $IS_ZSH = true ]; then | ||
READ_ARRAY="-A" | ||
fi | ||
|
||
# Is it actually a builder-script? If not, abort. If so, it defines | ||
# a special option used to provide us with completion-target data. | ||
local builder_params=`${CMD_PATH} --builder-completion-describe` || exit 0 | ||
|
||
local BASE_NAME=$(basename "$CMD_PATH") | ||
local BUILDER_ARG_STR="${COMP_LINE##*${BASE_NAME} }" | ||
|
||
local builder_args | ||
# Does not actually preserve an empty token at the end. | ||
read -r $READ_ARRAY builder_args <<< "${BUILDER_ARG_STR}" | ||
|
||
# Determine the current token (given the caret position) and | ||
# all existing, already-completed actions, targets, and options | ||
# for the script before the current token. | ||
local current_token= | ||
# If the caret is adjacent to non-whitespace - thus is editing | ||
# a builder argument... | ||
if [ ! -z "${COMP_WORDS[COMP_CWORD]}" ]; then | ||
# Then note the last token as the token being edited... | ||
current_token="${builder_args[-1]}" | ||
# ... and thus not pre-completed. | ||
unset builder_args[-1] | ||
fi | ||
|
||
# Parse the builder-description for completion target data. | ||
local action_str target_str option_str | ||
IFS=";" read -r action_str target_str option_str <<< "$builder_params" | ||
local actions targets options | ||
IFS=" " read -r $READ_ARRAY actions <<< "$action_str" | ||
IFS=" " read -r $READ_ARRAY targets <<< "$target_str" | ||
IFS=" " read -r $READ_ARRAY options <<< "$option_str" | ||
|
||
local action target | ||
if [[ $current_token =~ : ]]; then | ||
IFS=: read -r action target <<< "$current_token" | ||
target=:$target | ||
else | ||
action="$current_token" | ||
target= | ||
fi | ||
|
||
local all=() | ||
# If there is no $action component, it's a standalone target. | ||
|
||
if [[ -z "$action" ]] && [[ -z "$target" ]]; then | ||
all+="${actions[@]}" | ||
all+=" ${targets[@]}" | ||
all+=" ${options[@]}" | ||
|
||
COMPREPLY=( $(compgen -W "${all[@]}" -- "${current_token}") ) | ||
elif [[ -z "$action" ]]; then | ||
# It's an unpaired target. | ||
all="${targets[@]}" | ||
COMPREPLY=( $(compgen -W "${all[@]}" -- "${current_token}") ) | ||
|
||
if [ $IS_ZSH == false ]; then | ||
# bash doesn't handle completion with colons well; we should remove the | ||
# colon prefix from each entry. ZSH actually doesn't have this issue. | ||
local i | ||
for (( i=0; i<${#COMPREPLY[@]}; i++ )); do | ||
COMPREPLY[$i]="${COMPREPLY[$i]##*:}" | ||
done | ||
fi | ||
elif [[ -z "$target" ]]; then | ||
# It's an untargeted action or an option. | ||
all+="${actions[@]}" | ||
all+=" ${options[@]}" | ||
|
||
COMPREPLY=( $(compgen -W "${all[@]}" -- "${current_token}") ) | ||
else | ||
# Ah, an action-target pair. We have an existing action, so we only need to | ||
# complete the target part. | ||
|
||
# First, build up the list of legal paired tokens. | ||
local actiontargets=() | ||
for e in "${targets[@]}"; do | ||
actiontargets+=("${action}${e}") | ||
done | ||
|
||
# Now, complete from that. | ||
all+="${actiontargets[@]}" | ||
COMPREPLY=( $(compgen -W "${all[@]}" -- "${current_token}") ) | ||
|
||
if [ $IS_ZSH == false ]; then | ||
# bash doesn't handle completion with colons well; we should remove the | ||
# colon prefix from each entry. ZSH actually doesn't have this issue. | ||
local i | ||
for (( i=0; i<${#COMPREPLY[@]}; i++ )); do | ||
COMPREPLY[$i]="${COMPREPLY[$i]##*:}" | ||
done | ||
fi | ||
fi | ||
} | ||
|
||
# When this script is sourced by .bashrc or similar, this provides autocompletion for | ||
# builder scripts named build.sh and test.sh. | ||
complete -o bashdefault -F _comp_builder build.sh | ||
complete -o bashdefault -F _comp_builder test.sh |