cscanlin
cscanlin

Reputation: 193

Bash complete function - Separating completion parts with character other than space

I've written a bash completion script to essentially do file/directory completion, but using . as the separator instead of /. However, it's not behaving as I expect it to.

Before I dive further, does anyone know of any options for this, or something that's already been written that can do this? The motivation for this is to enable completion when calling python with the -m flag. It seems crazy that this doesn't exist yet, but I was unable to find anything relevant.

My issue is that bash doesn't recognize . as a separator for completion options, and won't show the next options until I add an additional space to the end of the current command.

Here's a few concrete examples, given this directory structure.

/module
    /script1.py
    /script2.py

For instance, when I use the ls command, it works like this

$ ls mo<TAB>
$ ls module/<TAB><TAB>
   script1.py    script2.py

However, with my function, it's working like this:

$ python -m mod<TAB>
$ python -m module.<TAB><TAB>
   module.

So instead of showing the next entries, it just shows the finished string again. However, if I add a space, it then works, but I don't want it to include the space:

$ python -m mod<TAB>
$ python -m module. <TAB><TAB>  # (note the space here after the dot)
   script1    script2           # (Note, I'm intentionally removing the file extension here).

I'd like the completion to act just like the bottom example, except not be forced to include the space to go to the next set of options

I've got about 50 tabs open and I've tried a bunch of recommendations, but nothing seems to be able to solve this how I'd like. There are a few other caveats here that would take a lot of time to go through, so I'm happy to expand on any other points if I've skipped something important. I've attached my code below, any help would be greatly appreciated. Thanks!

#!/bin/bash

_python_target() {
    local cur opts cur_path
    # Retrieving the current typed argument
    cur="${COMP_WORDS[COMP_CWORD]}"

    # Preparing an array to store available list for completions
    # COMREPLY will be checked to suggest the list
    COMPREPLY=()

    # Here, we'll only handle the case of "-m"
    # Hence, the classic autocompletion is disabled
    # (ie COMREPLY stays an empty array)
    if [[ "${COMP_WORDS[1]}" != "-m" ]]
    then
        return 0
    fi

    # add each path component to the current path to check for additional files
    cur_path=""
    for word in ${COMP_WORDS[@]:2:COMP_CWORD-2}; do
        path_component=$(echo ${word} | sed 's/\./\//g')
        cur_path="${cur_path}${path_component}"
    done
    cur_path="./${cur_path}"

    if [[ ! -f "$cur_path" && ! -d "$cur_path" ]]; then
        return 0
    fi

    # this is not very pretty, but it works. Open to comments on this too
    file_opts="$(find ${cur_path} -name "*.py" -type f -maxdepth 1 -print0 | xargs -0 basename -a | sed 's/\.[^.]*$//')"
    dir_opts="$(find ${cur_path} ! -path ${cur_path} -type d -maxdepth 1 -print0 | xargs -0 basename -a | xargs -I {} echo {}.)"

    opts="${file_opts} ${dir_opts}"

    # We store the whole list by invoking "compgen" and filling
    # COMREPLY with its output content.
    COMPREPLY=($(compgen -W "$opts" -- "$cur"))
    [[ $COMPREPLY == *\. ]] && compopt -o nospace

}

complete -F _python_target python

Upvotes: 0

Views: 271

Answers (1)

pynexj
pynexj

Reputation: 20688

Here's a draft example:

_python_target()
{
    local cmd=$1 cur=$2 pre=$3

    if [[ $pre != -m ]]; then
        return
    fi

    local cur_slash=${cur//./\/}
    local i arr arr2

    arr=( $( compgen -f "$cur_slash" ) )
    arr2=()
    for i in "${arr[@]}"; do
        if [[ -d $i ]]; then
            arr2+=( "$i/" )
        elif [[ $i == *.py ]]; then
            arr2+=( "${i%.py}" )
        fi
    done
    arr2=( "${arr2[@]//\//.}" )

    COMPREPLY=( $( compgen -W "${arr2[*]}" -- "$cur" ) )
}

complete -o nospace -F _python_target python

Try with the python-2.7.18 source code directory:

enter image description here

Upvotes: 1

Related Questions