diff --git a/CHANGELOG.md b/CHANGELOG.md index 39e9e030..36deb159 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### 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)) +- Support bash completions of file parameters when file names contain spaces or special characters ([#379](https://github.com/ajalt/clikt/pull/379)) ## 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 3a0adb5f..07fb7f93 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 @@ -18,7 +18,7 @@ internal object BashCompletionGenerator { .flatMap { arg -> (1..arg.nvalues).map { "'${arg.name}'" } } .joinToString(" ") val varargName = command._arguments.find { it.nvalues < 0 }?.name.orEmpty() - val paramsWithCandidates = + val paramsWithCandidates: List> = (options.map { o -> o.first.maxByOrNull { it.length }!! to o.second } + arguments) if (options.isEmpty() && subcommands.isEmpty() && arguments.isEmpty()) return "" @@ -50,15 +50,26 @@ internal object BashCompletionGenerator { append( """ - |__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 - |} - | + |__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 + |} + | + |__complete_files() { + | # Generate filename completions + | local word="${'$'}1" + | local IFS=${'$'}'\n' + | + | # quote each completion to support spaces and special characters + | COMPREPLY=(${'$'}(compgen -o filenames -f -- "${'$'}word" | while read -r line; do + | printf "%q\n" "${'$'}line" + | done)) + |} + | """.trimMargin() ) } @@ -233,7 +244,7 @@ internal object BashCompletionGenerator { } CompletionCandidates.Path -> { - append(" COMPREPLY=(\$(compgen -o default -- \"\${word}\"))\n") + append(" __complete_files \"\${word}\"\n") } CompletionCandidates.Hostname -> { 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 354f67bd..c04d931d 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 @@ -17,6 +17,17 @@ class BashCompletionTest : CompletionTestBase("bash") { | fi |} | + |__complete_files() { + | # Generate filename completions + | local word="${'$'}1" + | local IFS=${'$'}'\n' + | + | # quote each completion to support spaces and special characters + | COMPREPLY=(${'$'}(compgen -o filenames -f -- "${'$'}word" | while read -r line; do + | printf "%q\n" "${'$'}line" + | done)) + |} + | |__c_complete___o() { | COMPREPLY=(${'$'}(compgen -W "${'$'}(echo foo bar)" -- "${'$'}{COMP_WORDS[${'$'}COMP_CWORD]}")) |} @@ -100,11 +111,22 @@ class BashCompletionTest : CompletionTestBase("bash") { | # 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 + | if [[ "${'$'}{COMP_WORDS[${'$'}i]}" == '=' ]]; then | (( i = i + 1 )) | fi |} | + |__complete_files() { + | # Generate filename completions + | local word="${'$'}1" + | local IFS=${'$'}'\n' + | + | # quote each completion to support spaces and special characters + | COMPREPLY=(${'$'}(compgen -o filenames -f -- "${'$'}word" | while read -r line; do + | printf "%q\n" "${'$'}line" + | done)) + |} + | |_c() { | local i=1 | local in_param='' @@ -381,6 +403,17 @@ class BashCompletionTest : CompletionTestBase("bash") { | fi |} | + |__complete_files() { + | # Generate filename completions + | local word="${'$'}1" + | local IFS=${'$'}'\n' + | + | # quote each completion to support spaces and special characters + | COMPREPLY=(${'$'}(compgen -o filenames -f -- "${'$'}word" | while read -r line; do + | printf "%q\n" "${'$'}line" + | done)) + |} + | |_c() { | local i=1 | local in_param='' @@ -455,6 +488,17 @@ class BashCompletionTest : CompletionTestBase("bash") { | fi |} | + |__complete_files() { + | # Generate filename completions + | local word="${'$'}1" + | local IFS=${'$'}'\n' + | + | # quote each completion to support spaces and special characters + | COMPREPLY=(${'$'}(compgen -o filenames -f -- "${'$'}word" | while read -r line; do + | printf "%q\n" "${'$'}line" + | done)) + |} + | |_c() { | local i=1 | local in_param='' @@ -525,7 +569,7 @@ class BashCompletionTest : CompletionTestBase("bash") { | "--none") | ;; | "--path") - | COMPREPLY=(${'$'}(compgen -o default -- "${'$'}{word}")) + | __complete_files "${'$'}{word}" | ;; | "--host") | COMPREPLY=(${'$'}(compgen -A hostname -- "${'$'}{word}")) @@ -564,6 +608,17 @@ class BashCompletionTest : CompletionTestBase("bash") { | fi |} | + |__complete_files() { + | # Generate filename completions + | local word="${'$'}1" + | local IFS=${'$'}'\n' + | + | # quote each completion to support spaces and special characters + | COMPREPLY=(${'$'}(compgen -o filenames -f -- "${'$'}word" | while read -r line; do + | printf "%q\n" "${'$'}line" + | done)) + |} + | |_c() { | local i=1 | local in_param=''