Executing a second completion when pressing tab twice when tab is bound to menu-complete in bash

StackOverflow https://stackoverflow.com/questions/23684115

  •  23-07-2023
  •  | 
  •  

The Setup

The title might be a little obtuse, but I couldn't think of a better way to describe it. I have a bash function that will navigate up an arbitrary number of directories, so this: up 2 would navigate cd up the directory structure 2 levels.

If you do up 2<TAB>, then it will autocomplete and print out the full path.

Imagine this file structure:

└── demos/
    ├── audio/
    │   ├── assets/
    │   └── index.html
    └── video/
        ├── assets/
        └── index.html

My current directory is /demos/video/assets. If I press up 2, then it will cd me into /demos, and if I press up 2<TAB> it will autocomplete to up /demos.

The completion also has one other aspect, which is, if I type: up 2/au<TAB>, it will autocomplete to up /demos/au<CURSOR>

The Problem

However, because I have in my bash_profile: bind 'TAB:menu-complete' then after it autocompletes to up /demos/au<CURSOR>, then it's as if the tab key is trapped, and does no further completion. The only way I can get it to further complete is if I press another key (either pressing another letter of the directory name, or pressing an arrow key once, then pressing tab, etc).

Is there any way I can tell the bash completion to respond to a second tab press or to somehow not trap the tab key after the completion function has returned?

If I don't have tab bound to menu-complete, then the second tab doesn't get swallowed, however, I'd rather not disable menu-complete since it's useful in all other completions.

The code

And just so you can see what I'm doing, here's my completion code (I'm pasting only the interesting parts, but you can see the entire function here: https://gist.github.com/natecavanaugh/10104c3408bc2bc8d733):

function _complete_up {
    COMPREPLY=()

    local curToken=${COMP_WORDS[COMP_CWORD]}

    curToken=${curToken//'\'}

    if [[ $curToken =~ ^[0-9]+/? ]]; then

        local strpath=$( printf "%${curToken%%/*}s" )
        local upDirSpec=${strpath// /../}

        local trailingpath="${curToken#*/}"

        if [[ $trailingpath =~ ^[0-9]+$ ]]; then
            trailingpath=""
        fi

        local dir=$(cd "$upDirSpec"; echo -n "$PWD/")

        if [[ "$dir" == '//' ]]; then dir='/'; fi

        if [[ -n $trailingpath ]]; then
            if [[ "${trailingpath:0:1}" != "/" && "${dir: -1}" != "/" ]]; then
                trailingpath="/$trailingpath"
            fi

            dir="${dir}${trailingpath}"
        fi

        COMPREPLY=("$dir")
    fi

    return 0
}

complete -o plusdirs -o nospace -o filenames -F _complete_up up

Thanks for any help you guys can provide :)

有帮助吗?

解决方案

menu-complete causes the tab key to cycle through the alternatives, but your complete function only provides one alternative. So there's nothing to cycle through.

As far as I know, there is no way to conditionally rebind keys based on typed context; readline as a library would allow such callbacks but bash doesn't take advantage of that feature, and I think I'm not alone in thinking that the bash/readline interface is already excessively complicated ("BUGS: It's too big and too slow", as man bash has said for many years, not specifically with respect to readline).

Have you tried explicitly adding the default completions by changing the last line of the outer if in your completion function to something like:

COMPREPLY=("$dir" ${dir}*)

(Obviously, there are lots of corner cases which make the above insufficiently robust, starting with the case where ${dir}* expands to $dir followed by a * because it doesn't match any filename.)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top