3 changed files with 55 additions and 132 deletions
@ -1,127 +0,0 @@ |
|||||
## Recursive completion — call container's own bash completion |
|
||||
|
|
||||
The idea: instead of reimplementing completion, **delegate to bash inside the container** and get back its own completion results. |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## The approach |
|
||||
|
|
||||
```bash |
|
||||
_incus_exec_delegate() { |
|
||||
local cur="${COMP_WORDS[COMP_CWORD]}" |
|
||||
local container="${COMP_WORDS[2]}" |
|
||||
|
|
||||
# find '--' position |
|
||||
local dashdash_pos=0 |
|
||||
local i |
|
||||
for (( i=0; i < ${#COMP_WORDS[@]}; i++ )); do |
|
||||
[[ "${COMP_WORDS[$i]}" == "--" ]] && dashdash_pos=$i |
|
||||
done |
|
||||
|
|
||||
# complete container names |
|
||||
if (( COMP_CWORD == 2 )); then |
|
||||
COMPREPLY=($(compgen -W \ |
|
||||
"$(incus list --format csv --columns n 2>/dev/null)" \ |
|
||||
-- "$cur")) |
|
||||
return |
|
||||
fi |
|
||||
|
|
||||
# before '--' |
|
||||
if (( dashdash_pos == 0 )); then |
|
||||
COMPREPLY=($(compgen -W "-- --env --user --cwd" -- "$cur")) |
|
||||
return |
|
||||
fi |
|
||||
|
|
||||
# after '--' — delegate full completion to container's bash |
|
||||
local inner_words=("${COMP_WORDS[@]:$((dashdash_pos + 1))}") |
|
||||
local inner_cword=$(( COMP_CWORD - dashdash_pos - 1 )) |
|
||||
local inner_cmd="${inner_words[0]}" |
|
||||
local inner_line="${inner_words[*]}" |
|
||||
|
|
||||
# ask container's bash to run completion |
|
||||
COMPREPLY=($(incus exec "$container" -- bash -c " |
|
||||
# load bash completion |
|
||||
source /usr/share/bash-completion/bash_completion 2>/dev/null |
|
||||
source /etc/bash_completion 2>/dev/null |
|
||||
|
|
||||
# rebuild COMP_ variables inside container |
|
||||
COMP_WORDS=(${inner_words[*]@Q}) |
|
||||
COMP_CWORD=$inner_cword |
|
||||
COMP_LINE=${inner_line@Q} |
|
||||
COMP_POINT=\${#COMP_LINE} |
|
||||
|
|
||||
# find and call the completion function for the command |
|
||||
local compfunc |
|
||||
compfunc=\$(complete -p ${inner_cmd@Q} 2>/dev/null \ |
|
||||
| grep -oP '(?<=-F )\S+') |
|
||||
|
|
||||
if [[ -n \"\$compfunc\" ]]; then |
|
||||
\$compfunc ${inner_cmd@Q} ${cur@Q} ${inner_words[$((inner_cword - 1))]@Q} |
|
||||
printf '%s\n' \"\${COMPREPLY[@]}\" |
|
||||
else |
|
||||
# fallback to file completion |
|
||||
compgen -f -- ${cur@Q} |
|
||||
fi |
|
||||
" 2>/dev/null)) |
|
||||
} |
|
||||
|
|
||||
complete -F _incus_exec_delegate incus |
|
||||
``` |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## How it works |
|
||||
|
|
||||
``` |
|
||||
host$ incus exec mycontainer -- git che<TAB> |
|
||||
↑ |
|
||||
inner_cmd="git" |
|
||||
↓ |
|
||||
incus exec mycontainer -- bash -c ' |
|
||||
source /usr/share/bash-completion/bash_completion |
|
||||
COMP_WORDS=(git che) |
|
||||
COMP_CWORD=1 |
|
||||
compfunc=$(complete -p git | grep -oP "(?<=-F )\S+") |
|
||||
$compfunc ... # calls __git_wrap__git_main |
|
||||
printf "%s\n" "${COMPREPLY[@]}" |
|
||||
' |
|
||||
↓ |
|
||||
COMPREPLY=(checkout cherry cherry-pick) |
|
||||
``` |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## Caveats |
|
||||
|
|
||||
```bash |
|
||||
# completion inside container requires bash-completion installed |
|
||||
incus exec mycontainer -- apt install -y bash-completion |
|
||||
|
|
||||
# verify completion available for a command |
|
||||
incus exec mycontainer -- bash -c "complete -p git" |
|
||||
``` |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## Fallback chain |
|
||||
|
|
||||
``` |
|
||||
container has completion function for cmd? |
|
||||
↓ yes → delegate to it |
|
||||
↓ no → compgen -c (commands) |
|
||||
↓ |
|
||||
compgen -f (files) |
|
||||
``` |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## Quick reference |
|
||||
|
|
||||
| Step | What happens | |
|
||||
|------|-------------| |
|
||||
| rebuild `COMP_WORDS` | strip `incus exec CTR --` prefix | |
|
||||
| `complete -p CMD` | find completion function in container | |
|
||||
| `$compfunc` | call it with correct args | |
|
||||
| fallback | `compgen -f` for file completion | |
|
||||
|
|
||||
The key insight is **rebuilding `COMP_WORDS` and `COMP_CWORD`** with the `incus exec CTR --` prefix stripped — the container's bash then sees the command line exactly as if it were running locally. |
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue