From 690e9e2e7cfafbea7e7602602ee0eafa4ba51c61 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Thu, 4 Aug 2022 10:35:01 -0400 Subject: [PATCH] completions: add completions for bash and fish (#629) For later: - Add completions for zsh and PowerShell. - Make `fetch-configlet` detect the user's shell and explain how to install completions. - Make `configlet completion -s ` print the corresponding completion script to stdout. This commit also refactors `create-artifact` slightly. Closes: #615 Co-authored-by: ee7 <45465154+ee7@users.noreply.github.com> --- .github/bin/create-artifact | 6 +- completions/configlet.bash | 114 ++++++++++++++++++++++++++++++++++++ completions/configlet.fish | 36 ++++++++++++ 3 files changed, 151 insertions(+), 5 deletions(-) create mode 100644 completions/configlet.bash create mode 100644 completions/configlet.fish diff --git a/.github/bin/create-artifact b/.github/bin/create-artifact index 70eade1a..71e28cac 100755 --- a/.github/bin/create-artifact +++ b/.github/bin/create-artifact @@ -10,11 +10,7 @@ case "${OS}" in artifact_file="${artifacts_dir}/${binary_name}-${OS}-${ARCH}.zip" 7z a "${artifact_file}" "${binary_name}.exe" ;; - linux) - artifact_file="${artifacts_dir}/${binary_name}-${OS}-${ARCH}.tgz" - tar -cvzf "${artifact_file}" "${binary_name}" - ;; - mac) + linux | mac) artifact_file="${artifacts_dir}/${binary_name}-${OS}-${ARCH}.tgz" tar -cvzf "${artifact_file}" "${binary_name}" ;; diff --git a/completions/configlet.bash b/completions/configlet.bash new file mode 100644 index 00000000..158cae7a --- /dev/null +++ b/completions/configlet.bash @@ -0,0 +1,114 @@ +# completions for the configlet command, bash flavour + +# remove any prior completions +complete -r bin/configlet configlet 2>/dev/null +# and install this one +complete -F _configlet_completion_ configlet + +# @(pattern1|pattern2|...) is bash extended pattern matching meaning +# "one of pattern1 or pattern2 or ..." +# ref: https://www.gnu.org/software/bash/manual/bash.html#Pattern-Matching + +_configlet_completion_() { + local global_opts='-h --help --version -t --track-dir -v --verbosity' + local cur=${COMP_WORDS[COMP_CWORD]} + local prev=${COMP_WORDS[COMP_CWORD - 1]} + + # Check for global options that take a value + if _configlet_complete_global_option_; then + return + fi + + local i + for ((i = 1; i < COMP_CWORD; i++)); do + if [[ ${COMP_WORDS[i]} == @(lint|generate|info|uuid|fmt|sync) ]]; then + "_configlet_complete_${COMP_WORDS[i]}_" + return + fi + done + + _configlet_complete_options_ "fmt generate info lint sync uuid $global_opts" +} + +_configlet_complete_global_option_() { + case $prev in + '-v' | '--verbosity') + _configlet_complete_options_ "quiet normal detailed" + return 0 + ;; + '-t' | '--track-dir') + # Complete a directory based on what the user's typed so far + mapfile -t COMPREPLY < <(compgen -A directory -- "$cur") + return 0 + ;; + esac + return 1 +} + +_configlet_complete_lint_() { + _configlet_complete_options_ "$global_opts" +} + +_configlet_complete_generate_() { + _configlet_complete_options_ "$global_opts" +} + +_configlet_complete_info_() { + _configlet_complete_options_ "-o --offline $global_opts" +} + +_configlet_complete_uuid_() { + _configlet_complete_options_ "-n --num $global_opts" +} + +_configlet_complete_fmt_() { + case $prev in + '-e' | '--exercise') + _configlet_complete_slugs_ "practice" "concept" + ;; + *) + _configlet_complete_options_ "-e --exercise -u --update -y --yes $global_opts" + ;; + esac +} + +_configlet_complete_sync_() { + case $prev in + '--tests') + _configlet_complete_options_ "choose include exclude" + ;; + '-e' | '--exercise') + _configlet_complete_slugs_ "practice" + ;; + *) + local options=( + -e --exercise + -o --offline + -u --update + -y --yes + --docs + --filepaths + --metadata + --tests + ) + _configlet_complete_options_ "${options[*]} $global_opts" + ;; + esac +} + +# Note that configlet expects to be called from the track's root dir. +_configlet_complete_slugs_() { + local subdir + mapfile -t COMPREPLY < <( + for subdir in "$@"; do + if [[ -d "./exercises/$subdir" ]]; then + ( cd "./exercises/$subdir" && compgen -A directory -- "$cur" ) + fi + done + ) +} + +_configlet_complete_options_() { + local choices=$1 + mapfile -t COMPREPLY < <(compgen -o nosort -W "$choices" -- "$cur") +} diff --git a/completions/configlet.fish b/completions/configlet.fish new file mode 100644 index 00000000..68006537 --- /dev/null +++ b/completions/configlet.fish @@ -0,0 +1,36 @@ +# global options +complete -c configlet -s h -l help -f -d "Show help" +complete -c configlet -l version -f -d "Show version info" +complete -c configlet -s t -l track-dir -d "Select a track directory" +complete -c configlet -s v -l verbosity -x -a "quiet normal detailed" -d "Verbose level" + +# subcommands with no options +complete -c configlet -n "__fish_use_subcommand" -a lint -f -d "Check the track configuration for correctness" +complete -c configlet -n "__fish_use_subcommand" -a generate -f -d "Generate concept exercise introductions" + +# info subcommand +complete -c configlet -n "__fish_use_subcommand" -a info -f -d "Track info" +complete -c configlet -n "__fish_seen_subcommand_from info" -s o -l offline -f -d "Do not update prob-specs cache" + +# uuid subcommand +complete -c configlet -n "__fish_use_subcommand" -a uuid -f -d "Output a UUID" +complete -c configlet -n "__fish_seen_subcommand_from uuid" -s n -l num -f -d "How many UUIDs" + +# fmt subcommand +complete -c configlet -n "__fish_use_subcommand" -a fmt -f -d "Format the exercise '.meta/config.json' files" +complete -c configlet -n "__fish_seen_subcommand_from fmt" -s u -l update -d "Write changes" +complete -c configlet -n "__fish_seen_subcommand_from fmt" -s y -l yes -d "Auto-confirm update" +complete -c configlet -n "__fish_seen_subcommand_from fmt" -s e -l exercise -d "exercise slug" \ + -x -a "(find ./exercises/{concept,practice} -maxdepth 1 -mindepth 1 -type d -printf '%P\n' | sort)" + +# sync subcommand +complete -c configlet -n "__fish_use_subcommand" -a sync -d "Check or update Practice Exercise docs, metadata, and tests" +complete -c configlet -n "__fish_seen_subcommand_from sync" -s o -l offline -f -d "Do not update prob-specs cache" +complete -c configlet -n "__fish_seen_subcommand_from sync" -s u -l update -d "Write changes" +complete -c configlet -n "__fish_seen_subcommand_from sync" -s y -l yes -d "Auto-confirm update" +complete -c configlet -n "__fish_seen_subcommand_from sync" -l docs -d "Sync docs only" +complete -c configlet -n "__fish_seen_subcommand_from sync" -l filepaths -f -d 'Populate .meta/config.json "files" entry' +complete -c configlet -n "__fish_seen_subcommand_from sync" -l metadata -d "Sync metadata only" +complete -c configlet -n "__fish_seen_subcommand_from sync" -l tests -d "For auto-confirming" -x -a "choose include exclude" +complete -c configlet -n "__fish_seen_subcommand_from sync" -s e -l exercise -d "exercise slug" \ + -x -a "(find ./exercises/practice -maxdepth 1 -mindepth 1 -type d -printf '%P\n' | sort)"