AppImageLauncher icon indicating copy to clipboard operation
AppImageLauncher copied to clipboard

Request for bash-completion support

Open tresf opened this issue 5 years ago • 26 comments

Enhancement request for the ability to add bash-completion support to the launcher registration.

Not sure how technically feasible this is. The location is ~/.bash_completion file per: https://serverfault.com/a/506707/34498

Complementary discussion with @azubieta on #appimage on irc.freenode.net.

tresf avatar Sep 19 '18 15:09 tresf

@tresf what shall be completed? Please add more details.

TheAssassin avatar Sep 19 '18 18:09 TheAssassin

I'm not yet familiar with AppImageLauncher, I was told that appimaged was deprecated in favor of this.

To that point, this is just adding a command's bash-completion to user-space upon app registration not unlike what appimaged currently does for mimetypes, application icons, etc.

Although this is most common for CLI applications, GUI applications may also choose to offer this as well.

tresf avatar Sep 19 '18 18:09 tresf

The question here will be if there is a way to extend the bash-completion functionalities without the need of being root. Like we do with desktop files, icons, and mimetypes.

azubieta avatar Sep 19 '18 18:09 azubieta

@azubieta good point.

If the current user-space specification does not permit the use of an equivalent of the root bash-completion.d directory, it would require something like a one-time script chunk (e.g. one-liner) to be placed in ~/.bash_completion that points to a wrapper that mimics bash-completion.d behavior and/or structure.

If there are plans in the gnu.org specification to eventually add user-space bash-completion.d, it would not require the adding (and thus eventual removing) of this one-time script chunk or one-liner.

tresf avatar Sep 19 '18 19:09 tresf

@tresf currently we are focused on other topics, but we are open for "pull requests" :smile:

azubieta avatar Sep 20 '18 11:09 azubieta

This is a topic for libappimage's desktop integration code. CC @probonopd

TheAssassin avatar Sep 20 '18 21:09 TheAssassin

This is a topic for libappimage's desktop integration code.

Yes. Question: I have 5 different versions of foo installed. Which one will "win" in terms of bash completion?

probonopd avatar Sep 21 '18 17:09 probonopd

Yes. Question: I have 5 different versions of foo installed. Which one will "win" in terms of bash completion?

Good question. I have a counter-question and it may be too soon to answer but if this ever comes to fruition, is the standard place OK for bundling, e.g. <AppImage>/usr/share/bash-completion/completions ? That way if it's ever implemented, the packages out there in the wild will already have the completion script in the right place.

tresf avatar Sep 21 '18 17:09 tresf

As per the AppDir spec (made by the ROX filer team), it is not defined where stuff goes inside an AppDir.

However, for appimaged I assumed that people will use the $APPDIR/usr as the $PREFIX. So the unwritten rule is that whatever goes to /usr/foo on a "normal" system, goes into $APPDIR/usr/foo in an AppImage.

So if on "normal" systems /usr/share/bash-completion/completions is the correct place then yes, you are right.

probonopd avatar Sep 21 '18 17:09 probonopd

Bash completions for myapp should be:

  • put in <AppImage>/usr/share/bash-completion/completions/myapp
  • installed to ~/.local/share/bash-completion/completions/myapp

However, before the completions can be used, the user must first load the completion script by running:

source ~/.local/share/bash-completion/completions/myapp

The completion will now work, but only until the user closes their shell. To make it permanent you could add that line to ~/.bashrc or similar, but that would only work for myapp. A better way would be to add this to ~/.bashrc instead:

for script in ~/.local/share/bash-completion/completions/*; do
  [[ -r "${script}" ]] && source "${script}"
done

This only has to be done once and it will work for all past and future completion scripts installed in ~/.local/share/bash-completion/completions/.

shoogle avatar May 06 '19 21:05 shoogle

The completions script for myapp will look something like this:

# install this file to '/usr/share/bash-completion/completions/myapp' or similar

function _myapp() {
  # completion logic
  }

complete -F _myapp myapp

Notice that this is just an ordinary bash script. It contains a function called _myapp, and the line complete -F _myapp myapp which associates the _myapp function with the myapp binary.

If the binary was called something other than myapp, such as MyApp.AppImage, then the final line would need to be changed to:

complete -F _myapp MyApp.AppImage

In this case it is not necessary to rename the function or the script file itself. However, if multiple versions of myapp are installed then it becomes necessary to disambiguate everything. This means renaming the completions script and patching its contents as follows:

# install this file to '/usr/share/bash-completion/completions/myapp-1.0' or similar

function _myapp-1.0() {
  # completion logic
  }

complete -F _myapp-1.0 MyApp-1.0.AppImage

shoogle avatar May 06 '19 23:05 shoogle

Yes. Question: I have 5 different versions of foo installed. Which one will "win" in terms of bash completion?

Can't this same question be asked for all AppImage registrations? It seems like no matter what the appimage daemon does, it stands to conflict in areas of multiple registrations. FIFO is probably best philosophy; last to register is priority (unless the daemon has some other priority logic I'm unaware of).

Anyway... @shoogle thanks for chiming in. I feel strongly that the namespace avoidance won't be well received by application developers (they won't want to hard-code any versioning into the completion logic)... but to entertain the concept... Take the following examples which accommodates just-in-time namespace avoidance...

eval "$(sed 's/_myapp/_myapp_a1b2c3d4e5/g' /usr/share/bash-completion/completions/myapp)"
# where "myapp" is the registered executable name*
# where "a1b2c3d4e5" is the appimage's unique hash prefix/suffix

* Note, the above is only for example and does not take into consideration the mounted location.

Test bash completion registration...

complete |grep myapp
# outputs:
# complete -F _myapp_a1b2c3d4e5 myapp

If the daemon could do something similar, it should work. What's unique about completion is it lives in memory only, so just-in-time variable replacement should be of no consequence.

complete -F _myapp-1.0 MyApp-1.0.AppImage

I hadn't thought of the consequences. I guess a final sed pass (or whatever's used) would be needed to swap myapp with the appropriate appimage.

- complete -F _myapp_a1b2c3d4e5 myapp
+ (e.g.) complete -F _myapp_a1b2c3d4e5 <path-to>/myapp_1.0.0.AppImage
+ (e.g.) complete -F _myapp_a1b2c3d4e5 ~/Downloads/myapp_1.0.0.AppImage

Resulting in something like this...

eval "$(sed 's/_myapp/_myapp_a1b2c3d4e5/g' /usr/share/bash-completion/completions/myapp | sed 's/complete -F _myapp_a1b2c3d4e5 myapp/complete -F _myapp_a1b2c3d4e5 \/$HOME\/Downloads\/myapp-1.0.0.AppImage/g')"
# FIXME: Doesn't guard for spaces in appimage path

tresf avatar May 07 '19 14:05 tresf

Yes. Question: I have 5 different versions of foo installed. Which one will "win" in terms of bash completion?

Can't this same question be asked for all AppImage registrations? It seems like no matter what the appimage daemon does, it stands to conflict in areas of multiple registrations.

The question has indeed been asked for other registrations, and in most cases it has even been answered! There are usually ways to avoid conflicts, usually by renaming files, though it does occasionally involve patching upstream code.

Bash completion is an interesting case. Since these completion scripts are just ordinary Bash scripts, they can be made to execute any arbitrary code. You could even have something like this:

# install this file to '/usr/share/bash-completion/completions/appimages' or similar

function _appimage_completion() {
  # 1. work out which appimage is being completed for
  # 2. mount and extract that appimage
  # 3. source its completion script
  # 4. hand over to its completion function
  }

complete -F _appimage_completion MyApp-1.0.AppImage
complete -F _appimage_completion MyApp-2.0.AppImage
complete -F _appimage_completion OtherApp-1.0.AppImage

So in the specific case of Bash completion, there is actually no need to "install" the completion files at all, as they can be loaded dynamically from within the AppImage, thereby avoiding conflicts altogether! Of course this might not be such a great idea as it involves mounting and extracting the AppImage each time the user wants to complete something, but it is at least a possibility.

shoogle avatar May 07 '19 14:05 shoogle

@tresf, it's best not to give a full path to complete. If you do this...

complete -F _myapp /home/user/Applications/MyApp.AppImage

... then completion will only work when the user types the full path to the command.

/home/user/Applications/MyApp.AppImage --he[tab]  # completion works

cd /home/user/Applications
./MyApp.AppImage --he[tab]  # completion does not work

cd /home/user
export PATH="/home/user/Applications:${PATH}"
MyApp.AppImage --he[tab]  # completion does not work

If you do...

complete -F _myapp MyApp.AppImage

... then completion works in all cases. Admittedly this means it will be triggered for any binary called MyApp.AppImage, not just the one in /home/user/Applications, but this is usually a price worth paying.

shoogle avatar May 07 '19 15:05 shoogle

Since Bash completion scripts run actual terminal commands, patching them can get complicated and could even be considered dangerous! Consider this example:

# install this file to '/usr/share/bash-completion/completions/myapp' or similar

function _myapp() {
  # completion logic
  var="$(_helper_function "${args}")"
  # do something with ${var}
  }

function _helper_function() {
  # more completion logic
  }

complete -F _myapp myapp

In this case the _myapp completion function calls a helper function. In the extreme case, each version of myapp will have its own completion script and its own _helper_function, so they all must be disambiguated to prevent conflicts:

# install this file to '/usr/share/bash-completion/completions/myapp-1.0' or similar

function _myapp-1.0() {
  # completion logic
  var="$(_myapp-1.0_helper_function "${args}")"
  # do something with ${var}
  }

function _myapp-1.0_helper_function() {
  # more completion logic
  }

complete -F _myapp-1.0 MyApp-1.0.AppImage

This can quickly get complicated, so it is probably better to either:

  1. Use a dynamic extraction script as I suggested above, or
  2. Rename the file but don't patch it, and accept that completion will only work properly for one version of myapp. The user can install a shim or symlink at ~/bin/myapp that points to ~/Applications/MyApp-1.0.AppImage or whichever version "wins" the completion race.

shoogle avatar May 07 '19 16:05 shoogle

The disambiguous use-case isn't an issue in my tests. Works fine so as long as the functions are properly prefixed with _myapp. Even if it renames something it did not mean to, it's rarely bug-inducing as it would do so universally. I don't see a bug in the example provided.

tresf avatar May 07 '19 18:05 tresf

Oh, I see the bug... _helper_function. Yeah, I would consider that a bug with the completion, not the sed. Internal functions should be namespaced. The same bug could exist outside of the appimage spec, I don't see how it's related other than the fact that appimage potentially increases the frequency of it happening.

tresf avatar May 07 '19 18:05 tresf

Seems like you're making some progress here. Haven't had the time to read it all yet, though.

One thing I am concerned about is, do we really want to automatically install stuff that changes the users' shell behavior? Those scripts might break the shell even, and prevent logging in.

I guess, if we realize this, it must be an opt-in feature, and there should be a preview feature or so.

TheAssassin avatar May 07 '19 21:05 TheAssassin

AppImage is about GUI applications first and foremost. macOS .app bundles don't install command line completion either... not saying I'm strongly against it, but it's really not the focus of AppImage, which is about GUI applications.

probonopd avatar May 07 '19 21:05 probonopd

I wouldn't mind merging a PR into libappimage on this.

TheAssassin avatar May 07 '19 21:05 TheAssassin

@TheAssassin said

do we really want to automatically install stuff that changes the users' shell behavior? Those scripts might break the shell even, and prevent logging in.

Quite right! The safest thing to do is scan for files in <AppImage>/usr/share/bash-completion/completions/ and copy them to ~/.local/share/bash-completion/completions/, adding a unique prefix/suffix to the filename as usual (md5 hash or whatever). As I said before, most distributions do not actually look for completions in that directory by default, so it is safe to leave the completion scripts there where they would do literally nothing. Users who know about them can "opt-in" to using them by adding these lines to their ./.bashrc, ./.bash_profile or ~/.bash_completion:

for script in ~/.local/share/bash-completion/completions/*; do
  [[ -r "${script}" ]] && source "${script}"
done

An opt-in system like this offers a nice preview of what users can expect to enjoy by default once the wider issues with desktop integration have been sorted out, but being opt-in also means that we don't need to worry about conflicts and bugs as much as we would have to if it were opt-out.

@probonopd said

AppImage is about GUI applications first and foremost. macOS .app bundles don't install command line completion either...

No, but then Homebrew Cask arose as a way to install .app bundles from the command line for people who like automation. Their tagline is: “To install, drag this icon…” no more!. Many casks even put a symlink or "shim" in the user's ${PATH} to allow the application to be used easily from the command line without typing the full path to the executable. Admittedly they don't offer manpages or Bash completions for casks yet, but that's not to say that we shouldn't!

shoogle avatar May 08 '19 00:05 shoogle

AppImage is about GUI applications first and foremost. macOS .app bundles don't install command line completion either... not saying I'm strongly against it, but it's really not the focus of AppImage, which is about GUI applications.

Yet the two steps to run an appimage per the official website are still CLI instructions. ;)

tresf avatar May 08 '19 03:05 tresf

Yet the two steps to run an appimage per the official website are still CLI instructions. ;)

Excellent point. Although that's only an issue of the website, not of AppImage: https://discourse.appimage.org/t/how-to-run-an-appimage/80

[The reason the website is in its current state is that it is almost impossible for me to edit (without the fear of breaking everything) since it has been divided into individual sentences for translation with Weblate. I like Weblate but for a webpage it doesn't seem to be the most straightforward tool.]

probonopd avatar May 08 '19 05:05 probonopd

@shoogle what I meant was a "per AppImage" choice, not a "take it or leave it" one. Maybe with a checkbox in the integration dialog.

TheAssassin avatar May 08 '19 17:05 TheAssassin

It arguably is "per AppImage", because completion won't actually happen unless the binary is named like in:

complete -F _script binary

Since most will be named like Binary-X.Y.Z-x86_64.AppImage rather than plain binary, this means you would need to also do this to get completions to work:

ln -s ~/Applications/Binary-X.Y.Z-x86_64.AppImage ~/bin/binary

I left this out to keep it simple. I think it is reasonable to assume that most people who want to use Bash completion with an AppImage also wants it to be in ${PATH}.

However, the completion scripts would still be sourced even though the functions inside them were never actually used. You can make it fully "per AppImage" by sourcing each script individually:

source ~/.local/share/bash-completion/completions/myapp
source ~/.local/share/bash-completion/completions/myotherapp

shoogle avatar May 10 '19 05:05 shoogle

You can make it fully "per AppImage" by sourcing each script individually:

Which is kind of awkward especially when trying to support completions for different versions of the same app (you'd have some random MD5 data in the names then).

Therefore the idea of making it like "easy" to do it per-AppImage, e.g., with a checkmark. Then you can just include anything in that directory instead.

TheAssassin avatar May 11 '19 00:05 TheAssassin