Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clap_complete bash: filenames with spaces are not completed properly #5313

Closed
2 tasks done
alexheretic opened this issue Jan 16, 2024 · 5 comments · Fixed by #5336
Closed
2 tasks done

clap_complete bash: filenames with spaces are not completed properly #5313

alexheretic opened this issue Jan 16, 2024 · 5 comments · Fixed by #5336
Labels
A-completion Area: completion generator C-bug Category: Updating dependencies E-help-wanted Call for participation: Help is requested to fix this issue. E-medium Call for participation: Experience needed to fix: Medium / intermediate

Comments

@alexheretic
Copy link

Please complete the following tasks

Rust Version

1.75.0

Clap Version

clap_complete: 4.4.7

Minimal reproducible code

Use clap_complete bash completions for an arg that could receive a filename.

Steps to reproduce the bug with the above code

Create directory with files, one of which has spaces in the name.

$ ls
'foo bar.txt'   qux.txt

Try to complete fo the unique start of one filename.

$ ab-av1 encode -i fo<TAB>

Actual Behaviour

Cannot complete.

$ ab-av1 encode -i fo
foo      bar.txt

Expected Behaviour

Should be able to complete handling the spaces in the file. E.g. as ls completions do: ls fo<TAB> -> ls foo\ bar.txt.

So I would expect:
$ ab-av1 encode -i fo<TAB> -> $ ab-av1 encode -i foo\ bar.txt

Additional Context

Downstream: alexheretic/ab-av1#180

Generated bash completion for ab-av1
_ab-av1() {
    local i cur prev opts cmd
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    cmd=""
    opts=""

    for i in ${COMP_WORDS[@]}
    do
        case "${cmd},${i}" in
            ",$1")
                cmd="ab__av1"
                ;;
            ab__av1,auto-encode)
                cmd="ab__av1__auto__encode"
                ;;
            ab__av1,crf-search)
                cmd="ab__av1__crf__search"
                ;;
            ab__av1,encode)
                cmd="ab__av1__encode"
                ;;
            ab__av1,help)
                cmd="ab__av1__help"
                ;;
            ab__av1,print-completions)
                cmd="ab__av1__print__completions"
                ;;
            ab__av1,sample-encode)
                cmd="ab__av1__sample__encode"
                ;;
            ab__av1,vmaf)
                cmd="ab__av1__vmaf"
                ;;
            ab__av1__help,auto-encode)
                cmd="ab__av1__help__auto__encode"
                ;;
            ab__av1__help,crf-search)
                cmd="ab__av1__help__crf__search"
                ;;
            ab__av1__help,encode)
                cmd="ab__av1__help__encode"
                ;;
            ab__av1__help,help)
                cmd="ab__av1__help__help"
                ;;
            ab__av1__help,print-completions)
                cmd="ab__av1__help__print__completions"
                ;;
            ab__av1__help,sample-encode)
                cmd="ab__av1__help__sample__encode"
                ;;
            ab__av1__help,vmaf)
                cmd="ab__av1__help__vmaf"
                ;;
            *)
                ;;
        esac
    done

    case "${cmd}" in
        ab__av1)
            opts="-h -V --help --version sample-encode vmaf encode crf-search auto-encode print-completions help"
            if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                return 0
            fi
            case "${prev}" in
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
            return 0
            ;;
        ab__av1__auto__encode)
            opts="-e -i -o -h --encoder --input --vfilter --pix-format --preset --keyint --scd --svt --enc --enc-input --min-vmaf --max-encoded-percent --min-crf --max-crf --thorough --crf-increment --cache --samples --sample-every --min-samples --temp-dir --vmaf --vmaf-scale --output --acodec --downmix-to-stereo --video-only --help"
            if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                return 0
            fi
            case "${prev}" in
                --encoder)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                -e)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --input)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                -i)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --vfilter)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --pix-format)
                    COMPREPLY=($(compgen -W "yuv420p yuv420p10le yuv444p10le" -- "${cur}"))
                    return 0
                    ;;
                --preset)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --keyint)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --scd)
                    COMPREPLY=($(compgen -W "true false" -- "${cur}"))
                    return 0
                    ;;
                --svt)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --enc)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --enc-input)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --min-vmaf)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --max-encoded-percent)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --min-crf)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --max-crf)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --crf-increment)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --cache)
                    COMPREPLY=($(compgen -W "true false" -- "${cur}"))
                    return 0
                    ;;
                --samples)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --sample-every)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --min-samples)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --temp-dir)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --vmaf)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --vmaf-scale)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --output)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                -o)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --acodec)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
            return 0
            ;;
        ab__av1__crf__search)
            opts="-e -i -h --encoder --input --vfilter --pix-format --preset --keyint --scd --svt --enc --enc-input --min-vmaf --max-encoded-percent --min-crf --max-crf --thorough --crf-increment --cache --samples --sample-every --min-samples --temp-dir --vmaf --vmaf-scale --help"
            if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                return 0
            fi
            case "${prev}" in
                --encoder)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                -e)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --input)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                -i)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --vfilter)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --pix-format)
                    COMPREPLY=($(compgen -W "yuv420p yuv420p10le yuv444p10le" -- "${cur}"))
                    return 0
                    ;;
                --preset)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --keyint)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --scd)
                    COMPREPLY=($(compgen -W "true false" -- "${cur}"))
                    return 0
                    ;;
                --svt)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --enc)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --enc-input)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --min-vmaf)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --max-encoded-percent)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --min-crf)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --max-crf)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --crf-increment)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --cache)
                    COMPREPLY=($(compgen -W "true false" -- "${cur}"))
                    return 0
                    ;;
                --samples)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --sample-every)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --min-samples)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --temp-dir)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --vmaf)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --vmaf-scale)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
            return 0
            ;;
        ab__av1__encode)
            opts="-e -i -o -h --encoder --input --vfilter --pix-format --preset --keyint --scd --svt --enc --enc-input --crf --output --acodec --downmix-to-stereo --video-only --help"
            if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                return 0
            fi
            case "${prev}" in
                --encoder)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                -e)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --input)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                -i)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --vfilter)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --pix-format)
                    COMPREPLY=($(compgen -W "yuv420p yuv420p10le yuv444p10le" -- "${cur}"))
                    return 0
                    ;;
                --preset)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --keyint)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --scd)
                    COMPREPLY=($(compgen -W "true false" -- "${cur}"))
                    return 0
                    ;;
                --svt)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --enc)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --enc-input)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --crf)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --output)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                -o)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --acodec)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
            return 0
            ;;
        ab__av1__help)
            opts="sample-encode vmaf encode crf-search auto-encode print-completions help"
            if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                return 0
            fi
            case "${prev}" in
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
            return 0
            ;;
        ab__av1__help__auto__encode)
            opts=""
            if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                return 0
            fi
            case "${prev}" in
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
            return 0
            ;;
        ab__av1__help__crf__search)
            opts=""
            if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                return 0
            fi
            case "${prev}" in
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
            return 0
            ;;
        ab__av1__help__encode)
            opts=""
            if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                return 0
            fi
            case "${prev}" in
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
            return 0
            ;;
        ab__av1__help__help)
            opts=""
            if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                return 0
            fi
            case "${prev}" in
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
            return 0
            ;;
        ab__av1__help__print__completions)
            opts=""
            if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                return 0
            fi
            case "${prev}" in
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
            return 0
            ;;
        ab__av1__help__sample__encode)
            opts=""
            if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                return 0
            fi
            case "${prev}" in
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
            return 0
            ;;
        ab__av1__help__vmaf)
            opts=""
            if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                return 0
            fi
            case "${prev}" in
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
            return 0
            ;;
        ab__av1__print__completions)
            opts="-h --help bash elvish fish powershell zsh"
            if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                return 0
            fi
            case "${prev}" in
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
            return 0
            ;;
        ab__av1__sample__encode)
            opts="-e -i -h --encoder --input --vfilter --pix-format --preset --keyint --scd --svt --enc --enc-input --crf --samples --sample-every --min-samples --temp-dir --keep --cache --stdout-format --vmaf --vmaf-scale --help"
            if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                return 0
            fi
            case "${prev}" in
                --encoder)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                -e)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --input)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                -i)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --vfilter)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --pix-format)
                    COMPREPLY=($(compgen -W "yuv420p yuv420p10le yuv444p10le" -- "${cur}"))
                    return 0
                    ;;
                --preset)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --keyint)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --scd)
                    COMPREPLY=($(compgen -W "true false" -- "${cur}"))
                    return 0
                    ;;
                --svt)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --enc)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --enc-input)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --crf)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --samples)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --sample-every)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --min-samples)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --temp-dir)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --cache)
                    COMPREPLY=($(compgen -W "true false" -- "${cur}"))
                    return 0
                    ;;
                --stdout-format)
                    COMPREPLY=($(compgen -W "human json" -- "${cur}"))
                    return 0
                    ;;
                --vmaf)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --vmaf-scale)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
            return 0
            ;;
        ab__av1__vmaf)
            opts="-h --reference --reference-vfilter --distorted --vmaf --vmaf-scale --help"
            if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                return 0
            fi
            case "${prev}" in
                --reference)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --reference-vfilter)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --distorted)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --vmaf)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                --vmaf-scale)
                    COMPREPLY=($(compgen -f "${cur}"))
                    return 0
                    ;;
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
            return 0
            ;;
    esac
}

if [[ "${BASH_VERSINFO[0]}" -eq 4 && "${BASH_VERSINFO[1]}" -ge 4 || "${BASH_VERSINFO[0]}" -gt 4 ]]; then
    complete -F _ab-av1 -o nosort -o bashdefault -o default ab-av1
else
    complete -F _ab-av1 -o bashdefault -o default ab-av1
fi

Debug Output

No response

@alexheretic alexheretic added the C-bug Category: Updating dependencies label Jan 16, 2024
@epage epage added E-medium Call for participation: Experience needed to fix: Medium / intermediate A-completion Area: completion generator E-help-wanted Call for participation: Help is requested to fix this issue. labels Jan 18, 2024
@epage
Copy link
Member

epage commented Jan 18, 2024

I wonder if #5240 will fix it.

@sudotac
Copy link
Contributor

sudotac commented Jan 20, 2024

Maybe we should drop -f option from compgen -f.

@alexheretic

Could you try the following patch to the generated completion script, and see if it works as expected?

@@ -323,10 +323,12 @@
                     return 0
                     ;;
                 --input)
-                    COMPREPLY=($(compgen -f "${cur}"))
+                    COMPREPLY=($(compgen "${cur}"))
+                    compopt -o filenames
                     return 0
                     ;;
                 -i)
-                    COMPREPLY=($(compgen -f "${cur}"))
+                    COMPREPLY=($(compgen "${cur}"))
+                    compopt -o filenames
                     return 0
                     ;;
                 --vfilter)

If you just try #5240, please apply the following:

@@ -323,10 +323,12 @@
                     ;;
                 --input)
                     COMPREPLY=($(compgen -f "${cur}"))
+                    compopt -o filenames
                     return 0
                     ;;
                 -i)
                     COMPREPLY=($(compgen -f "${cur}"))
+                    compopt -o filenames
                     return 0
                     ;;
                 --vfilter)

Edit: Fixed context line, and append the example of #5240.

@alexheretic
Copy link
Author

@sudotac that change doesn't seem to make any difference

@sudotac
Copy link
Contributor

sudotac commented Jan 20, 2024

Sorry, I misunderstood the usage. Actually -o filenames should be used with compgen -f.
However, even if we do so, it doesn't seem to work anyway.

@sudotac
Copy link
Contributor

sudotac commented Jan 20, 2024

FWIW if you can install bash-completion package (mostly Debian-based systems), it is possible to use _filedir for better filename completions.
(it is not a portable solution though)

@@ -322,11 +322,11 @@
                     return 0
                     ;;
                 --input)
-                    COMPREPLY=($(compgen -f "${cur}"))
+                    _filedir
                     return 0
                     ;;
                 -i)
-                    COMPREPLY=($(compgen -f "${cur}"))
+                    _filedir
                     return 0
                     ;;
                 --vfilter)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-completion Area: completion generator C-bug Category: Updating dependencies E-help-wanted Call for participation: Help is requested to fix this issue. E-medium Call for participation: Experience needed to fix: Medium / intermediate
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants