From 10dd1e032b22c0196dfcd609ca19c590ef8b7104 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Fri, 8 Jul 2022 19:42:16 -0400 Subject: [PATCH 1/8] Add completions for bash and fish. Update create-artifact script to include completions in artifact. --- .github/bin/create-artifact | 10 ++-- completions/configlet.bash | 104 ++++++++++++++++++++++++++++++++++++ completions/configlet.fish | 35 ++++++++++++ 3 files changed, 142 insertions(+), 7 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..661f867f 100755 --- a/.github/bin/create-artifact +++ b/.github/bin/create-artifact @@ -8,15 +8,11 @@ binary_name='configlet' case "${OS}" in windows) artifact_file="${artifacts_dir}/${binary_name}-${OS}-${ARCH}.zip" - 7z a "${artifact_file}" "${binary_name}.exe" + 7z a "${artifact_file}" "${binary_name}.exe" completions/ ;; - linux) + linux | mac) artifact_file="${artifacts_dir}/${binary_name}-${OS}-${ARCH}.tgz" - tar -cvzf "${artifact_file}" "${binary_name}" - ;; - mac) - artifact_file="${artifacts_dir}/${binary_name}-${OS}-${ARCH}.tgz" - tar -cvzf "${artifact_file}" "${binary_name}" + tar -cvzf "${artifact_file}" "${binary_name}" completions/ ;; esac diff --git a/completions/configlet.bash b/completions/configlet.bash new file mode 100644 index 00000000..c47d7b68 --- /dev/null +++ b/completions/configlet.bash @@ -0,0 +1,104 @@ +# 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 takes 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" + ;; + *) + _configlet_complete_options_ "-e --exercise -o --offline -u --update -y --yes --docs --metadata --tests $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..f33f4e5f --- /dev/null +++ b/completions/configlet.fish @@ -0,0 +1,35 @@ +# 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 proc-spec 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 proc-spec 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 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)" From 12ff843f01a08242303c78c8d43470248b842ca2 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Tue, 12 Jul 2022 10:17:36 -0400 Subject: [PATCH 2/8] missing --filepaths option for sync --- completions/configlet.bash | 12 +++++++++++- completions/configlet.fish | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/completions/configlet.bash b/completions/configlet.bash index c47d7b68..f7458f6b 100644 --- a/completions/configlet.bash +++ b/completions/configlet.bash @@ -81,7 +81,17 @@ _configlet_complete_sync_() { _configlet_complete_slugs_ "practice" ;; *) - _configlet_complete_options_ "-e --exercise -o --offline -u --update -y --yes --docs --metadata --tests $global_opts" + local options=( + -e --exercise + -o --offline + -u --update + -y --yes + --docs + --filepaths + --metadata + --tests + ) + _configlet_complete_options_ "${options[*]} $global_opts" ;; esac } diff --git a/completions/configlet.fish b/completions/configlet.fish index f33f4e5f..d89a9ef4 100644 --- a/completions/configlet.fish +++ b/completions/configlet.fish @@ -29,6 +29,7 @@ complete -c configlet -n "__fish_seen_subcommand_from sync" -s o -l offline -f - 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" \ From 2d6d7d8a46228e3d98a2033f220ae807bd676cb1 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Thu, 4 Aug 2022 10:20:01 +0200 Subject: [PATCH 3/8] create-artifact: exclude `completions` dir from release asset Instead, it looks like we'll bake the completions into the configlet binary via a new subcommand [1]. [1] https://github.com/exercism/configlet/issues/615#issuecomment-1188435956 --- .github/bin/create-artifact | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/bin/create-artifact b/.github/bin/create-artifact index 661f867f..71e28cac 100755 --- a/.github/bin/create-artifact +++ b/.github/bin/create-artifact @@ -8,11 +8,11 @@ binary_name='configlet' case "${OS}" in windows) artifact_file="${artifacts_dir}/${binary_name}-${OS}-${ARCH}.zip" - 7z a "${artifact_file}" "${binary_name}.exe" completions/ + 7z a "${artifact_file}" "${binary_name}.exe" ;; linux | mac) artifact_file="${artifacts_dir}/${binary_name}-${OS}-${ARCH}.tgz" - tar -cvzf "${artifact_file}" "${binary_name}" completions/ + tar -cvzf "${artifact_file}" "${binary_name}" ;; esac From 7646c60ff5fd730b3d7dccf3700615d0da6b74bb Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Thu, 4 Aug 2022 10:20:02 +0200 Subject: [PATCH 4/8] configlet.fish: 'proc-spec' -> 'prob-spec' --- completions/configlet.fish | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/completions/configlet.fish b/completions/configlet.fish index d89a9ef4..f10d2c75 100644 --- a/completions/configlet.fish +++ b/completions/configlet.fish @@ -10,7 +10,7 @@ complete -c configlet -n "__fish_use_subcommand" -a generate -f -d "Generate con # 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 proc-spec cache" +complete -c configlet -n "__fish_seen_subcommand_from info" -s o -l offline -f -d "Do not update prob-spec cache" # uuid subcommand complete -c configlet -n "__fish_use_subcommand" -a uuid -f -d "Output a UUID" @@ -25,7 +25,7 @@ complete -c configlet -n "__fish_seen_subcommand_from fmt" -s e -l exercise -d " # 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 proc-spec cache" +complete -c configlet -n "__fish_seen_subcommand_from sync" -s o -l offline -f -d "Do not update prob-spec 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" From 73ee88aecf0b5a649d1af86d6f5b9324ab0abf6e Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Thu, 4 Aug 2022 10:20:03 +0200 Subject: [PATCH 5/8] configlet.fish: 'prob-spec' -> 'prob-specs' We tend to use "prob-specs" rather than "prob-spec" to abbreviate "problem-specifications". Furthermore, configlet used to have a --prob-specs-dir option (not --prob-spec-dir), and configlet still prints an advisory message when a user writes --prob-specs-dir. $ git grep --heading --break --ignore-case 'prob.*spec[^i]' src/cli.nim 394: if keyNormalized in ["p", "probspecsdir"]: 396: The --prob-specs-dir option was removed in configlet 4.0.0-beta.1 (April 2022). src/info/info.nim 3:import ".."/[cli, sync/probspecs, types_track_config] 58: ProbSpecsExercises = object 63:proc init(T: typedesc[ProbSpecsExercises], probSpecsDir: ProbSpecsDir): T = 65: for kind, path in walkDir(probSpecsDir / "exercises"): 75:proc unimplementedProbSpecsExercises(practiceExercises: seq[PracticeExercise], 77: probSpecsDir: ProbSpecsDir): string = 79: probSpecsExercises = ProbSpecsExercises.init(probSpecsDir) 83: uWith = probSpecsExercises.withCanonicalData - practiceExerciseSlugs - foregone 84: uWithout = probSpecsExercises.withoutCanonicalData - practiceExerciseSlugs - foregone 133: let probSpecsDir = ProbSpecsDir.init(conf) 135: echo unimplementedProbSpecsExercises(t.exercises.practice, t.exercises.foregone, 136: probSpecsDir) src/sync/exercises.nim 4:import "."/[probspecs, tracks] 5:export tracks.`$`, probspecs.pretty 11: json*: ProbSpecsTestCase 38: probSpecsTestCases: ProbSpecsTestCases): T = 40: for testCase in probSpecsTestCases: 49:proc new(T: typedesc[ExerciseTestCase], testCase: ProbSpecsTestCase): T = 56:proc getReimplementations(testCases: ProbSpecsTestCases): Table[string, string] = 65:proc init(T: typedesc[ExerciseTestCases], testCases: ProbSpecsTestCases): T = 84:iterator findExercises*(conf: Conf, probSpecsDir: ProbSpecsDir): Exercise {.inline.} = 88: let testCases = getCanonicalTests(probSpecsDir, practiceExercise.slug.string) src/sync/probspecs.nim 5: ProbSpecsDir* {.requiresInit.} = distinct string 7: ProbSpecsExerciseDir {.requiresInit.} = distinct string 9: ProbSpecsTestCase* = distinct JsonNode 11: ProbSpecsTestCases* = seq[ProbSpecsTestCase] 13:proc `$`(dir: ProbSpecsDir): string {.borrow.} 14:proc dirExists(dir: ProbSpecsDir): bool {.borrow.} 15:proc createDir(dir: ProbSpecsDir) {.borrow.} 16:proc parentDir(path: ProbSpecsDir): string {.borrow.} 17:proc `/`*(head: ProbSpecsDir, tail: string): string {.borrow.} 18:proc `/`(head: ProbSpecsExerciseDir, tail: string): string {.borrow.} 19:proc lastPathPart(path: ProbSpecsExerciseDir): string {.borrow.} 20:proc `[]`(testCase: ProbSpecsTestCase, name: string): JsonNode {.borrow.} 21:proc hasKey(testCase: ProbSpecsTestCase, key: string): bool {.borrow.} 22:proc pretty*(testCase: ProbSpecsTestCase, indent = 2): string {.borrow.} 24:func canonicalDataFile(probSpecsExerciseDir: ProbSpecsExerciseDir): string = 25: probSpecsExerciseDir / "canonical-data.json" 27:func slug(probSpecsExerciseDir: ProbSpecsExerciseDir): string = 28: lastPathPart(probSpecsExerciseDir) 30:proc uuid*(testCase: ProbSpecsTestCase): string = 33:proc description*(testCase: ProbSpecsTestCase): string = 36:func isReimplementation*(testCase: ProbSpecsTestCase): bool = 39:proc reimplements*(testCase: ProbSpecsTestCase): string = 42:proc init(T: typedesc[ProbSpecsTestCases], node: JsonNode, prefix = ""): T = 50: result.add ProbSpecsTestCase(node) 58: result.add ProbSpecsTestCases.init(childNode, prefix) 69:proc parseProbSpecsTestCases(probSpecsExerciseDir: ProbSpecsExerciseDir): ProbSpecsTestCases = 72: let canonicalJsonPath = canonicalDataFile(probSpecsExerciseDir) 74: if slug(probSpecsExerciseDir) == "grains": 78: result = ProbSpecsTestCases.init(j) 80:proc getCanonicalTests*(probSpecsDir: ProbSpecsDir, 81: slug: string): ProbSpecsTestCases = 83: ## `probSpecsDir`. 84: let probSpecsExerciseDir = joinPath(probSpecsDir.string, "exercises", 85: slug).ProbSpecsExerciseDir 86: if fileExists(probSpecsExerciseDir.canonicalDataFile()): 87: result = parseProbSpecsTestCases(probSpecsExerciseDir) 89:proc getNameOfRemote*(probSpecsDir: ProbSpecsDir; 91: ## Returns the name of the remote in `probSpecsDir` that points to `location` 97: &"problem-specifications directory: '{probSpecsDir}'" 98: let remotes = gitCheck(0, ["-C", probSpecsDir.string, "remote", "-v"], msg) 105: &"the cached problem-specifications directory: '{probSpecsDir}'") 111:proc validate(probSpecsDir: ProbSpecsDir, conf: Conf) = 112: ## Raises an error if the given `probSpecsDir` is not a valid 116: logDetailed(&"Using cached 'problem-specifications' dir: {probSpecsDir}") 118: withDir probSpecsDir.string: 124: &"git repository: '{probSpecsDir}'") 128: &"has an unexpected initial commit: '{probSpecsDir}'") 133: &"'{probSpecsDir}'") 139: # prob-specs repo. 144: &"branch: '{probSpecsDir}'") 147: # prob-specs state that hasn't been merged to upstream `main`. 150: &"problem-specifications directory: '{probSpecsDir}'") 157: let remoteName = getNameOfRemote(probSpecsDir, upstreamHost, upstreamLocation) 163: &"problem-specifications directory: '{probSpecsDir}'") 167: &"problem-specifications directory: '{probSpecsDir}'") 169:proc init*(T: typedesc[ProbSpecsDir], conf: Conf): T = src/sync/sync.nim 3:import "."/[exercises, probspecs, sync_common, sync_docs, sync_filepaths, 86: let probSpecsDir = 88: ProbSpecsDir("this_will_not_be_used") 90: ProbSpecsDir.init(conf) 92: let psExercisesDir = probSpecsDir / "exercises" 119: let exercises = toSeq findExercises(conf, probSpecsDir) src/sync/sync_docs.nim 19: ProbSpecsSourceKind = enum 25: pssk: ProbSpecsSourceKind): string = 45: pssk: ProbSpecsSourceKind; 69:func toPath(pssk: ProbSpecsSourceKind): string = src/sync/sync_tests.nim 3:import "."/[exercises, probspecs] tests/all_tests.nim 7: test_probspecs, tests/test_binary.nim 2:import "."/[exec, helpers, lint/validators, sync/probspecs] 838: # Don't leave cached prob-specs dir in detached HEAD state. 842: test "can pull changes into cached prob-specs": 854: let probSpecsDir = ProbSpecsDir(psDir) # Don't use `init` (it performs extra setup). 855: let remoteName = getNameOfRemote(probSpecsDir, upstreamHost, upstreamLocation) 868: &"problem-specifications directory: '{probSpecsDir}'") tests/test_probspecs.nim 1:# This module contains tests for `src/probspecs.nim` 3:import "."/[cli, exec, sync/probspecs] 5:proc getProbSpecsExercises(probSpecsDir: ProbSpecsDir): Table[string, 6: seq[ProbSpecsTestCase]] = 8: ## each exercise in `probSpecsDir`. 9: let pattern = joinPath(probSpecsDir.string, "exercises", "*") 12: result[slug] = getCanonicalTests(probSpecsDir, slug) 22: let probSpecsDir = ProbSpecsDir.init(conf) 23: let probSpecsExercises = getProbSpecsExercises(probSpecsDir) 27: probSpecsExercises.len >= 116 30: let exercise = probSpecsExercises["accumulate"] 36: let firstTestCase = probSpecsExercises["accumulate"][0].JsonNode 52: let exercise = probSpecsExercises["acronym"] 58: let firstTestCase = probSpecsExercises["acronym"][0].JsonNode tests/test_sync.nim 508: # Don't leave cached prob-specs dir in detached HEAD state. --- completions/configlet.fish | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/completions/configlet.fish b/completions/configlet.fish index f10d2c75..1ac6b501 100644 --- a/completions/configlet.fish +++ b/completions/configlet.fish @@ -10,7 +10,7 @@ complete -c configlet -n "__fish_use_subcommand" -a generate -f -d "Generate con # 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-spec cache" +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" @@ -25,7 +25,7 @@ complete -c configlet -n "__fish_seen_subcommand_from fmt" -s e -l exercise -d " # 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-spec cache" +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" From bbf7b59c48f2c4ce744c2b08e72db8fb5e0c7f20 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Thu, 4 Aug 2022 10:20:04 +0200 Subject: [PATCH 6/8] configlet.fish: 'meta/config.json' -> '.meta/config.json' Be consistent with how it's written for --filepaths on a later line: complete -c configlet -n "__fish_seen_subcommand_from sync" -l filepaths -f -d 'Populate .meta/config.json "files" entry' The same inconsistency was in the `configlet --help` message. Blame ee7. That'll be fixed later. --- completions/configlet.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/completions/configlet.fish b/completions/configlet.fish index 1ac6b501..7524cf74 100644 --- a/completions/configlet.fish +++ b/completions/configlet.fish @@ -17,7 +17,7 @@ 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_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" \ From 0ffe10dc2e7ce23315c3fe61f2d7429e99cd16ea Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Thu, 4 Aug 2022 10:20:05 +0200 Subject: [PATCH 7/8] configlet.bash: fix grammar in comment --- completions/configlet.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/completions/configlet.bash b/completions/configlet.bash index f7458f6b..3c03e2a4 100644 --- a/completions/configlet.bash +++ b/completions/configlet.bash @@ -14,7 +14,7 @@ _configlet_completion_() { local cur=${COMP_WORDS[COMP_CWORD]} local prev=${COMP_WORDS[COMP_CWORD - 1]} - # Check for global options that takes a value + # Check for global options that take a value if _configlet_complete_global_option_; then return fi From 75e077d187b6a0e4b86a58b4e11293f237b70a2e Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Thu, 4 Aug 2022 15:58:01 +0200 Subject: [PATCH 8/8] completions: use 2 spaces for indentation Let's match the `fetch-configlet` script, which already exists on many machines, and indents with 2 spaces. We also use 2 spaces for the scripts everywhere else in this repo (that is, the scripts in `bin/` and `.github/bin/`). We could add something like `shfmt --diff -i 2 -ci -sr -kp` to CI in the future. $ shfmt --help usage: shfmt [flags] [path ...] shfmt formats shell programs. If the only argument is a dash ('-') or no arguments are given, standard input will be used. If a given path is a directory, all shell scripts found under that directory will be used. --version show version and exit -l, --list list files whose formatting differs from shfmt's -w, --write write result to file instead of stdout -d, --diff error with a diff when the formatting differs -s, --simplify simplify the code -mn, --minify minify the code to reduce its size (implies -s) Parser options: -ln, --language-dialect str bash/posix/mksh/bats, default "auto" -p, --posix shorthand for -ln=posix --filename str provide a name for the standard input file Printer options: -i, --indent uint 0 for tabs (default), >0 for number of spaces -bn, --binary-next-line binary ops like && and | may start a line -ci, --case-indent switch cases will be indented -sr, --space-redirects redirect operators will be followed by a space -kp, --keep-padding keep column alignment paddings -fn, --func-next-line function opening braces are placed on a separate line Utilities: -f, --find recursively find all shell files and print the paths --tojson print syntax tree to stdout as a typed JSON For more information, see 'man shfmt' and https://github.com/mvdan/sh. --- completions/configlet.bash | 140 ++++++++++++++++++------------------- completions/configlet.fish | 4 +- 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/completions/configlet.bash b/completions/configlet.bash index 3c03e2a4..158cae7a 100644 --- a/completions/configlet.bash +++ b/completions/configlet.bash @@ -10,105 +10,105 @@ complete -F _configlet_completion_ configlet # 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]} + 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 + # 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 + 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_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 + 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_options_ "$global_opts" } _configlet_complete_generate_() { - _configlet_complete_options_ "$global_opts" + _configlet_complete_options_ "$global_opts" } _configlet_complete_info_() { - _configlet_complete_options_ "-o --offline $global_opts" + _configlet_complete_options_ "-o --offline $global_opts" } _configlet_complete_uuid_() { - _configlet_complete_options_ "-n --num $global_opts" + _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 + 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 + 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 - ) + 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") + local choices=$1 + mapfile -t COMPREPLY < <(compgen -o nosort -W "$choices" -- "$cur") } diff --git a/completions/configlet.fish b/completions/configlet.fish index 7524cf74..68006537 100644 --- a/completions/configlet.fish +++ b/completions/configlet.fish @@ -21,7 +21,7 @@ complete -c configlet -n "__fish_use_subcommand" -a fmt -f -d "Format the exerci 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)" + -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" @@ -33,4 +33,4 @@ complete -c configlet -n "__fish_seen_subcommand_from sync" -l filepaths -f 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)" + -x -a "(find ./exercises/practice -maxdepth 1 -mindepth 1 -type d -printf '%P\n' | sort)"