Reputation: 2090
I have a folder ~/builds
that contains the following sub-directories:
> tree ~/builds
~/builds
├── projectA
│ ├── build_2020_04_10_ok
│ ├── build_2020_04_11_ko
│ ├── build_2020_04_12_ok
│ └── ...
└── projectB
├── build_2020_04_10_ok
├── build_2020_04_11_ok
├── build_2020_04_12_ok
└── ...
Currently, when I write a command that take one of the sub-folder as an argument and I use the auto-completion, bash
lists all the candidates:
> mycmd ~/builds/projectA/[TAB]
> mycmd ~/builds/projectA/build_2020_04_1[TAB][TAB]
build_2020_04_10_ok build_2020_04_11_ko build_2020_04_12_ok ...
That is not the behavior I want.
What I want is more like a Windows-like auto-completion. I know I can use it by modifying my .bashrc
that way:
bind '"\C-g": menu-complete'
Now, here is what happens when I press Ctrl+g
:
> mycmd ~/builds/projectA/[Ctrl+g]
> mycmd ~/builds/projectA/build_2020_04_10_ok[Ctrl+g]
> mycmd ~/builds/projectA/build_2020_04_11_ko[Ctrl+g]
> mycmd ~/builds/projectA/build_2020_04_12_ok
It is almost what I want. I would like to change in which order the sub-folders appear (the newest at first, then older to older). Also, I would like to discard all the ko
builds. In other words, I want the order defined by this command:
ls -dr build*ok
build_2020_04_12_ok/ build_2020_04_10_ok/
Therefore, I would like to define a completion specification for menu-complete
to perform the behavior I want only on the sub-directories of ~/builds
. I saw it is possible to do it for complete
and I found no information to do it on menu-complete
.
Is it possible?
Upvotes: 1
Views: 237
Reputation: 48864
As @chepner said, menu-complete
delegates to the same completions that complete
does (see man readline
). So initially I didn't think it would be possible to configure completions differently based on whether it's going through complete
or menu-complete
. But I checked to see if any different values are set depending on which hook is invoked and something stood out:
# Get the current set of environment variables
# the sed expression strips functions to just print variables
# There's probably a better way to do this...
bash-5.0$ get_env() {
set | sed -n '/^[^=]*$/q;p' | sort
}
# Define a function to use as a completion that simply prints the changes
# in the environment between the normal shell and from within a completion
bash-5.0$ diff_env() {
echo # extra echo's for readability
sdiff -s /tmp/env.txt <(get_env)
}
# Register it as a completion (the command doesn't actually need to exist)
bash-5.0$ complete -F diff_env foo
bash-5.0$ bind '"\C-g": menu-complete'
# Persist the initial environment
bash-5.0$ get_env > /tmp/env.txt
bash-5.0$ foo [TAB]
BASH_LINENO=([0]="12") | BASH_LINENO=([0]="1" [1]="13")
BASH_SOURCE=([0]="main") | BASH_SOURCE=([0]="main" [1]="main")
> COMP_CWORD=1
> COMP_KEY=9
> COMP_LINE='foo '
> COMP_POINT=4
> COMP_TYPE=9
> COMP_WORDS=([0]="foo" [1]="")
> _=echo
FUNCNAME=([0]="get_env") | FUNCNAME=([0]="get_env" [1]="diff_env")
_=get_env <
^C
bash-5.0$ foo [Ctrl+g]
BASH_LINENO=([0]="12") | BASH_LINENO=([0]="1" [1]="14")
BASH_SOURCE=([0]="main") | BASH_SOURCE=([0]="main" [1]="main")
> COMP_CWORD=1
> COMP_KEY=9
> COMP_LINE='foo '
> COMP_POINT=4
> COMP_TYPE=37
> COMP_WORDS=([0]="foo" [1]="")
> _=echo
FUNCNAME=([0]="get_env") | FUNCNAME=([0]="get_env" [1]="diff_env")
_=get_env <
^C
Spot it? COMP_TYPE
is different! man bash
explains that it's set to an integer depending on the completion behavior; the description is a bit opaque but the value is the ASCII-code-point (9 for tab, 37 for %
) for the given type of completion.
So we can define a completion function that checks this value in order to change its behavior:
bash-5.0$ clever_complete() {
case "$COMP_TYPE" in
9) COMPREPLY=("tab!") ;;
37) COMPREPLY=("ctrl-g!") ;;
# should probably behave the same as [TAB], but I split it out to demonstrate
*) COMPREPLY=("IDK... $COMP_TYPE") ;;
esac
}
bash-5.0$ complete -F clever_complete bar
bash-5.0$ bar [TAB]tab! ^C
bash-5.0$ bar [Ctrl+g]ctrl-g! ^C
Hopefully that's enough to go on :) if you need help actually writing the complete
function with the behavior you described I'd suggest starting a separate question with the current implementation (use complete -p [command]
to see the current completion, and type [function-name]
to see the completion's implementation), it should be straightforward to adjust the existing behavior from there.
Upvotes: 5