akinomyoga / ble.sh

Bash Line Editor―a line editor written in pure Bash with syntax highlighting, auto suggestions, vim modes, etc. for Bash interactive sessions.
BSD 3-Clause "New" or "Revised" License
2.33k stars 77 forks source link

How to write completion functions to work with or without ble.sh? #407

Closed bkerin closed 4 months ago

bkerin commented 4 months ago

I've been doing a little work on completion in git and have recently tried to add dir completion for contexts where a dir is required. I've got this function:

__git_complete_dir ()
{
    local cur_="$cur"

    while test $# != 0; do
        case "$1" in
        --cur=*)    cur_="${1##--cur=}" ;;
        *)      return 1 ;;
        esac
        shift
    done

        # This rev-parse invocation amounts to a pwd which respects -C options
    local context_dir=$(__git rev-parse --show-toplevel --show-prefix 2>/dev/null | paste -s -d '/' 2>/dev/null)
        # FIXME: context dir needs the printf "%q" or ${...@Q} treatment also?  I don't think so but be sure
    [ -d "$context_dir" ] || return 1

        local IFS=$'\n'
        local unescaped_candidates=($(cd "$context_dir" 2>/dev/null && compgen -d -S / -- "$cur_"))
        for ii in "${!unescaped_candidates[@]}"; do
          # This works without ble.sh:
          #COMPREPLY[$ii]=$(printf "%q" "${unescaped_candidates[$ii]}")
          # This works with ble.sh:
          COMPREPLY[$ii]="${unescaped_candidates[$ii]}"
        done
}

The trouble is the ble.sh seems to end up quoting the already-quoted stuff in COMPREPLY a second time, but without quoting completion doesn't work without ble.sh.

It's seems possible to avoid this problem somehow, since file cd etc. on files with weird names work with or without ble.sh. But for e.g.:

git am --directory=/some/dir/with"and in it

I don't know how to get it right for both.

akinomyoga commented 4 months ago

How do you specify the completion function to complete? Have you tried specifying -o noquote? For example,

complete -o noquote -F __git_complete_dir [...some other options]

Or, if the above function is going to be called from another function, and if you don't have a control on the complete setting of the caller, you can run the command compopt -o noquote inside the completion function.

bkerin commented 4 months ago

compopt -o noquote does indeed make it work both with and without ble.sh, thanks.

I have to admit I don't understand for sure how. I guess ble.sh is sensitive to the noquote setting and behaves accordingly in it's own quoting?

akinomyoga commented 4 months ago

I have to admit I don't understand for sure how. I guess ble.sh is sensitive to the noquote setting and behaves accordingly in it's own quoting?

Yes, there is admittedly an intentional behavior difference with Bash/Readline.

Bash/Readline determines whether to quote the results in COMPREPLY by the condition that the generated word matches a filename in the present directory or not. I think there were even more complicated conditions. There are many potential problems with that design: when the generated text accidentally matches a filename in the current directory, it mistakenly quotes the generated words. Or if a generated filename is suffixed by a different string or is located in a different place, it is not quoted.

Because of this behavior, most existing completion settings are broken, though a real problem doesn't arise unless the generated completions contain special characters that need to be quoted. Only a small number of completion settings does the proper quoting. I didn't like those potential problems of the existing settings, so decided to make ble.sh always quote the generated completions by default, and add exceptions for known completions that properly quote the result.

I think the problem of the current design of Bash/Readline is recognized by also the maintainer of Bash. Bash 5.3 in the devel state now has another completion option -o fullquote for the opposite effect to -o noquote. In short, ble.sh behaves as if -o fullquote by default.

bkerin commented 4 months ago

Ok. This explains why the completion code for git is doing it's own thing with sed etc. and also why relying on native escape from bash didn't work, thanks.