diff --git a/CHANGELOG.md b/CHANGELOG.md index f38eb280..39e9e030 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased ### Fixed - Fixed `@argfiles` not being expanded when specified after a subcommand ([#570](https://github.com/ajalt/clikt/pull/570)) +- Fixed syntax error in generated bash completions when an argument name contained spaces ([#563](https://github.com/ajalt/clikt/pull/563)) ## 5.0.2 ### Changed diff --git a/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/completion/BashCompletionGenerator.kt b/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/completion/BashCompletionGenerator.kt index db5d2d30..3a0adb5f 100644 --- a/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/completion/BashCompletionGenerator.kt +++ b/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/completion/BashCompletionGenerator.kt @@ -224,7 +224,7 @@ internal object BashCompletionGenerator { for ((name, completion) in paramsWithCandidates) { append( """ - | $name) + | "$name") | """.trimMargin() ) diff --git a/test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/BashCompletionTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/BashCompletionTest.kt index 93550a38..354f67bd 100644 --- a/test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/BashCompletionTest.kt +++ b/test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/BashCompletionTest.kt @@ -74,12 +74,12 @@ class BashCompletionTest : CompletionTestBase("bash") { | [[ -z "${'$'}{in_param}" ]] && in_param=${'$'}{vararg_name} | | case "${'$'}{in_param}" in - | --o) + | "--o") | COMPREPLY=(${'$'}(compgen -F __c_complete___o 2>/dev/null)) | ;; - | --help) + | "--help") | ;; - | A) + | "A") | COMPREPLY=(${'$'}(compgen -F __c_complete_A 2>/dev/null)) | ;; | esac @@ -155,7 +155,7 @@ class BashCompletionTest : CompletionTestBase("bash") { | [[ -z "${"$"}{in_param}" ]] && in_param=${"$"}{vararg_name} | | case "${"$"}{in_param}" in - | --help) + | "--help") | ;; | *) | COMPREPLY=(${"$"}(compgen -W 'sub sub-command' -- "${"$"}{word}")) @@ -205,7 +205,7 @@ class BashCompletionTest : CompletionTestBase("bash") { | [[ -z "${"$"}{in_param}" ]] && in_param=${"$"}{vararg_name} | | case "${"$"}{in_param}" in - | --help) + | "--help") | ;; | esac |} @@ -260,7 +260,7 @@ class BashCompletionTest : CompletionTestBase("bash") { | [[ -z "${"$"}{in_param}" ]] && in_param=${"$"}{vararg_name} | | case "${"$"}{in_param}" in - | --help) + | "--help") | ;; | *) | COMPREPLY=(${"$"}(compgen -W 'sub-sub long-sub-command' -- "${"$"}{word}")) @@ -310,7 +310,7 @@ class BashCompletionTest : CompletionTestBase("bash") { | [[ -z "${"$"}{in_param}" ]] && in_param=${"$"}{vararg_name} | | case "${"$"}{in_param}" in - | --help) + | "--help") | ;; | esac |} @@ -357,7 +357,7 @@ class BashCompletionTest : CompletionTestBase("bash") { | [[ -z "${"$"}{in_param}" ]] && in_param=${"$"}{vararg_name} | | case "${"$"}{in_param}" in - | --help) + | "--help") | ;; | esac |} @@ -428,9 +428,9 @@ class BashCompletionTest : CompletionTestBase("bash") { | [[ -z "${'$'}{in_param}" ]] && in_param=${'$'}{vararg_name} | | case "${'$'}{in_param}" in - | --no-flag) + | "--no-flag") | ;; - | --help) + | "--help") | ;; | esac |} @@ -522,24 +522,24 @@ class BashCompletionTest : CompletionTestBase("bash") { | [[ -z "${'$'}{in_param}" ]] && in_param=${'$'}{vararg_name} | | case "${'$'}{in_param}" in - | --none) + | "--none") | ;; - | --path) + | "--path") | COMPREPLY=(${'$'}(compgen -o default -- "${'$'}{word}")) | ;; - | --host) + | "--host") | COMPREPLY=(${'$'}(compgen -A hostname -- "${'$'}{word}")) | ;; - | --user) + | "--user") | COMPREPLY=(${'$'}(compgen -A user -- "${'$'}{word}")) | ;; - | --fixed) + | "--fixed") | COMPREPLY=(${'$'}(compgen -W 'foo bar' -- "${'$'}{word}")) | ;; - | ARGUSER) + | "ARGUSER") | COMPREPLY=(${'$'}(compgen -A user -- "${'$'}{word}")) | ;; - | ARGFIXED) + | "ARGFIXED") | COMPREPLY=(${'$'}(compgen -W 'baz qux' -- "${'$'}{word}")) | ;; | esac @@ -548,4 +548,72 @@ class BashCompletionTest : CompletionTestBase("bash") { |complete -F _c c """ } + + override fun `arg names with spaces expected`(): String { + return """ + |#!/usr/bin/env bash + |# Command completion for c + |# Generated by Clikt + | + |__skip_opt_eq() { + | # this takes advantage of the fact that bash functions can write to local + | # variables in their callers + | (( i = i + 1 )) + | if [[ "${'$'}{COMP_WORDS[${'$'}i]}" == '=' ]]; then + | (( i = i + 1 )) + | fi + |} + | + |_c() { + | local i=1 + | local in_param='' + | local fixed_arg_names=('foo bar') + | local vararg_name='' + | local can_parse_options=1 + | + | while [[ ${'$'}{i} -lt ${'$'}COMP_CWORD ]]; do + | if [[ ${'$'}{can_parse_options} -eq 1 ]]; then + | case "${'$'}{COMP_WORDS[${'$'}i]}" in + | --) + | can_parse_options=0 + | (( i = i + 1 )); + | continue + | ;; + | -h|--help) + | __skip_opt_eq + | in_param='' + | continue + | ;; + | esac + | fi + | case "${'$'}{COMP_WORDS[${'$'}i]}" in + | *) + | (( i = i + 1 )) + | # drop the head of the array + | fixed_arg_names=("${'$'}{fixed_arg_names[@]:1}") + | ;; + | esac + | done + | local word="${'$'}{COMP_WORDS[${'$'}COMP_CWORD]}" + | if [[ "${'$'}{word}" =~ ^[-] ]]; then + | COMPREPLY=(${'$'}(compgen -W '-h --help' -- "${'$'}{word}")) + | return + | fi + | + | # We're either at an option's value, or the first remaining fixed size + | # arg, or the vararg if there are no fixed args left + | [[ -z "${'$'}{in_param}" ]] && in_param=${'$'}{fixed_arg_names[0]} + | [[ -z "${'$'}{in_param}" ]] && in_param=${'$'}{vararg_name} + | + | case "${'$'}{in_param}" in + | "--help") + | ;; + | "foo bar") + | ;; + | esac + |} + | + |complete -F _c c + """ + } } diff --git a/test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/CompletionTestBase.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/CompletionTestBase.kt index 7dfb433a..34ff3657 100644 --- a/test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/CompletionTestBase.kt +++ b/test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/CompletionTestBase.kt @@ -119,6 +119,23 @@ abstract class CompletionTestBase(private val shell: String) { ) } + @JsName("arg_names_with_spaces_expected") + abstract fun `arg names with spaces expected`(): String + + @[Test JsName("arg_names_with_spaces")] + fun `arg names with spaces`() { + class C : TestCommand(autoCompleteEnvvar = "TEST_COMPLETE") { + val a by argument(help = "help", name = "foo bar") + } + + doTest( + `arg names with spaces expected`(), + C().completionOption(hidden = true) + ) + + } + + @[Test JsName("completion_command")] fun `completion command`() { val message = shouldThrow { diff --git a/test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/FishCompletionTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/FishCompletionTest.kt index 17966e5a..4031e269 100644 --- a/test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/FishCompletionTest.kt +++ b/test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/FishCompletionTest.kt @@ -91,4 +91,18 @@ class FishCompletionTest : CompletionTestBase("fish") { | """ } + + override fun `arg names with spaces expected`(): String { + return """ + |# Command completion for c + |# Generated by Clikt + | + |## Options for c + |complete -c c -s h -l help -d 'Show this message and exit' + | + |## Arguments for c + |complete -c c -d 'help' + | + """ + } }