Add spaces to the end of some Bash autocomplete options, but not to others?
Asked Answered
L

6

19

I'm trying to create a Bash completion script for a Java program. A typical invocation of that program might look like this:

$ javaProgram -Dproperty=foo option1 option2

Part of my script will be to suggest the various Java properties that are available for this program (i.e., when the user types -D, the script would suggest, say, property=, allowing the user to then type the value).

I'd like the completion to not insert a space after the equals sign. However, when the user is typing the various options for the program (option1 and option2 in the above example), I'd like the script to do the completion, and insert a space when it is completed.

I'm new to Bash completion scripting, but I'm aware of the nospace option for the complete shell builtin. It doesn't seem to work for the compgen builtin, though, which seems like what I want. I've tried using nospace, and then explicitly including spaces at the end of the appropriate options, but they don't seem to be making it through.

Does anyone know how to get spaces at the end of some options, but no spaces at the end of others?

Lullaby answered 26/2, 2010 at 3:4 Comment(1)
Minimal answer hereDamek
M
7

You can do it by removing spaces with the corresponding option, then add them back manually for the options where you want them, all using the simple -W ("wordlist") completion:

complete -o nospace -W 'Dproperty= "option1 " "option2 "' javaProgram

Autocompletion will now insert a space after option1 and option2, but none after Dproperty=.

Mcmahon answered 29/9, 2016 at 19:32 Comment(0)
A
6

The feature in in scp's bash completion mentioned by C4H5As that retains the added space is not the ampersand, that is just sed syntax for "replace with match".

You have to change the IFS so compgen does not split the input on spaces but on newlines or tabs:

local IFS=$'\t\n'
COMPREPLY=( $(compgen -W "$options" -- $cur) )
Anomalous answered 29/8, 2012 at 12:41 Comment(0)
D
3

You can actually alter the complete options using compopt. This worked for me:

_javaProgram(){
  # The completion will not add a space by default. We will alter
  # this behavior by calling `compopt +o nospace` to neutralize this.

  # In completion, '$2' is the current word, and '$3' is the previous
  case "$3" in
    -D) 
        # No space by default.
        COMPREPLY=( $(compgen -W "property=" -- "$2") )
    ;;
    *)  
        # The `case` default:
        COMPREPLY=( $(compgen -W "your default word list" -- "$2") )
        
        # append a space by calling:
        compopt +o nospace
    ;;
  esac
}

complete -F _javaProgram -o nospace javaProgram

However, I only needed space after short options. To contrast long and short options, you can check if there is a = in the COMPREPLY suggestions.

_javaProgram(){
  # The completion will not add a space by default. We will alter
  # this behavior by calling `compopt +o nospace` to neutralize this.

  # In completion, '$2' is the current word, and '$3' is the previous
  case "$3" in
    -D) 
        # Do not worry about appending spaces here ... .
        COMPREPLY=( $(compgen -W "property=" -- "$2") )
    ;;
    *)  
        # Do not worry about appending spaces here ... .
        COMPREPLY=( $(compgen -W "your default word list" -- "$2") )
    ;;
  esac

if [[ ! "${COMPREPLY[@]}" =~ "=" ]]; then
  # Add space if there is not a '=' in suggestions
  compopt +o nospace
fi
}

complete -F _javaProgram -o nospace javaProgram
Damek answered 11/2, 2021 at 8:21 Comment(0)
U
1

It would be helpful to see the code you have so far.

However, look at the completion code for scp in /etc/bash_completion.d/ssh if you have that. It puts spaces after local filenames but not after hostnames.

Ule answered 26/2, 2010 at 6:42 Comment(0)
G
1

Dennis is right on the money, in the function you reference with complete -F _your_func, you want something like this (taken from /etc/bash_completion):

    # escape spaces; remove executables, aliases, pipes and sockets;
    # add space at end of file names
    COMPREPLY=( $( ssh -o 'Batchmode yes' $userhost \
               command ls -aF1d "$path*" 2>/dev/null | \
               sed -e "s/[][(){}<>\",:;^&!$&=?\`|\\ ']/\\\\\\\\\\\\&/g" \
               -e 's/[*@|=]$//g' -e 's/[^\/]$/& /g' ) )
    return 0

This particular example uses the -o nospace option but adds the space manually. It's the last -e ('s/[^\/]$/& /g') that adds the space. In this case it's adding only if the last parameter doesn't end in a slash (which would be a directory and have files to auto-complete).

The key difference seems to be adding an ampersand before the space. Try that and see if it works.

Geordie answered 26/2, 2010 at 8:20 Comment(0)
O
1

As the poster mentioned, it is not possible to pass the -o nospace option to compgen. One possibility is defining two functions where one inserts a call to compopt -o nospace before calling compgen (the other function would not).

Example:

__my_complete_no_space() {
    local IFS=$' \t\n' cur_="${3-$cur}"
    [[ $cur_ == *:* ]] && cur_="${cur#*:}"
    compopt -o nospace
    COMPREPLY=( $(compgen -P "${2-}" -W "$1" -S "${4-}" -- "$cur_") )
}

This is useful when the addition of space (or not) depends on the context of what needs to be completed.

Oilla answered 21/12, 2020 at 14:3 Comment(1)
It is possible to pass options to complete using compopt. Take a look at this answer.Damek

© 2022 - 2024 — McMap. All rights reserved.