Improving bash completion developer experience

Currently if a developer works on a snap called foo that ships an app called bar, and they want that app to have tab completion in bash, they need to ship a completion snippet for the foo.bar app. This works²⋅³⁰⁺ no matter what aliases are in play, but does force the developer to tweak their completion snippet — most non-snap apps aren’t called things like foo.bar.

The completion snippet will have a line tying the command to whatever completion method is needed, something that starts with the command complete and ends in the command being completed, for example

complete -W 'won too tri for' bar

they’d need to change it to

complete -W 'won too tri for' foo.bar

or, if they want to support both snap and non-snap installs with the same snippet,

complete -W 'won too tri for' bar foo.bar

or even

if [ "$SNAP" ]; then
    complete -W 'won too tri for' foo.bar
else
    complete -W 'won too tri for' bar
fi

I thought that the above was a reasonable compromise between things being magic and things working the way people expect (and people I talked with about this, in the abstract, agreed). However, I might’ve been wrong.

How do we make it better? Should we?

While we could parse the completion snippet to rewrite it to what the in-snap completer needs, doing so in the general case means essentially parsing bash scripts and tweaking the resulting ast. Even bash doesn’t parse bash scripts properly, so I don’t think doing it the magic way is sane.

One thing the developer could do is ship the regular snippet (that talks about bar), but point the completer key to a wrapper snippet that sourced the original and then added the appropriate complete line. This requires no work on snappy’s part, and is relatively straightforward (and separate):

source the-real-completer
complete ... foo.bar

A mixed approach might be to add an additional key to the yaml (on IRC, lundmar suggested completer-alias). Then the current rewriting of the commandline into the snippet happens, but instead of towards foo.bar, towards whatever that key points (defaulting to foo.bar). Alternatively snapcraft could use that key to auto-create a snippet wrapper as above.

WTYT?

1 Like

I agree that care should be taken to not force the developers to add any snap specific details to their software distributions in order to create a snap. Such details should be strictly confined to the snapcraft meta data (.yaml and related files). Also, I think it is important to try create a solution for this problem with minimum meta information.

I’m saying this as a developer/maintainer of various open source software and I want to keep my software distributions agnostic and not impose any snap or other packaging specific details upon them, for the sake of easy maintenance and no-clutter policies.

That being said, giving this issue some more thought, I suggest introducing a new key named ‘completer-function’ like so:

apps:
  lxi:
    command: bin/lxi
    completer: share/bash-completion/completions/lxi
    completer-function: '_lxi'
    plugs: [home, network, avahi-observe]
    aliases: [lxi]

From the meta info above, snapcraft can then generate a separate completion script named e.g. lxi.snap containing the single line:

complete -o default -F _lxi lxi-tools.lxi lxi

The lxi.snap file can be installed next to the original completion script like so (or elsewhere):

/snap/lxi-tools/current/share/bash-completion/completions/lxi
/snap/lxi-tools/current/share/bash-completion/completions/lxi.snap

And then finish by taking care that the lxi.snap file gets sourced after sourcing of the original file (correct order required to append correctly).

Similarly, when users change the alias manually using e.g. the “snap alias lxi-tools.lxi lxi2” command then that operation becomes a simple matter of updating and sourcing the lxi.snap file accordingly:

complete -o default -F _lxi lxi-tools.lxi lxi2

Also, it would be possible to take the solution to the next step by assuming that the default value of the completer-function is ‘_<app_name>’ which is the normal but not guaranteed conventional name for most completer functions. This way there is a chance the solution would work even when the user does not define a completer-function key.

I think this could make for an elegant solution as seen from the point of view of the end users.