93 lines
3.3 KiB
Bash
93 lines
3.3 KiB
Bash
#!/usr/bin/env bash
|
|
# Tab-completion for the `odysseus` umbrella + every `odysseus-*` CLI.
|
|
#
|
|
# Source from your shell rc:
|
|
# source /path/to/odysseus-ui/scripts/_completion/odysseus.bash
|
|
#
|
|
# Or wire it once per machine:
|
|
# sudo install -m 644 odysseus.bash /etc/bash_completion.d/odysseus
|
|
#
|
|
# What it does:
|
|
# - On the first word after `odysseus`, complete with the list of
|
|
# subcommands (`mail`, `calendar`, ...).
|
|
# - On subsequent words, complete with the subcommand's first-token
|
|
# subcommands (`list`, `show`, ...) which we cache by parsing the
|
|
# tool's own --help output. Updates lazily; refresh by running
|
|
# `_odysseus_refresh_cache`.
|
|
# - Same completion works for the individual `odysseus-foo` scripts.
|
|
|
|
_odysseus_scripts_dir() {
|
|
# Resolve the scripts/ dir from the script that sources us. We assume
|
|
# the user sourced the file directly out of scripts/_completion/.
|
|
local self="${BASH_SOURCE[0]}"
|
|
while [ -L "$self" ]; do self=$(readlink "$self"); done
|
|
cd "$(dirname "$self")/.." && pwd
|
|
}
|
|
|
|
declare -A _ODYSSEUS_SUBS_CACHE=()
|
|
|
|
_odysseus_refresh_cache() {
|
|
local dir="$(_odysseus_scripts_dir)"
|
|
_ODYSSEUS_SUBS_CACHE=()
|
|
# Prefer the project venv's Python so deps (bcrypt, sqlalchemy, ...)
|
|
# resolve. Falls back to system `python3` for container installs.
|
|
local py="$dir/../venv/bin/python"
|
|
[ -x "$py" ] || py="$(command -v python3)"
|
|
local f
|
|
for f in "$dir"/odysseus-*; do
|
|
[ -x "$f" ] || continue
|
|
case "$f" in *.bak|*.pyc|*.pre-*) continue ;; esac
|
|
local name="$(basename "$f")"
|
|
local sub="${name#odysseus-}"
|
|
local help_out
|
|
help_out=$("$py" "$f" --help 2>/dev/null) || continue
|
|
local commands
|
|
commands=$(echo "$help_out" | grep -oE '\{[a-z0-9_,-]+\}' | head -1 \
|
|
| tr -d '{}' | tr ',' ' ')
|
|
_ODYSSEUS_SUBS_CACHE[$sub]="$commands"
|
|
done
|
|
}
|
|
|
|
_odysseus_complete() {
|
|
[ ${#_ODYSSEUS_SUBS_CACHE[@]} -eq 0 ] && _odysseus_refresh_cache
|
|
|
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
|
local cmd="${COMP_WORDS[0]}"
|
|
|
|
# `odysseus <tab>` → list every subcommand
|
|
if [ "$cmd" = "odysseus" ]; then
|
|
if [ "$COMP_CWORD" -eq 1 ]; then
|
|
local subs="${!_ODYSSEUS_SUBS_CACHE[@]} help"
|
|
COMPREPLY=($(compgen -W "$subs" -- "$cur"))
|
|
return 0
|
|
fi
|
|
# `odysseus foo <tab>` — complete with foo's own subcommands
|
|
local sub="${COMP_WORDS[1]}"
|
|
# `odysseus help <tab>` lists every subcommand
|
|
if [ "$sub" = "help" ] && [ "$COMP_CWORD" -eq 2 ]; then
|
|
COMPREPLY=($(compgen -W "${!_ODYSSEUS_SUBS_CACHE[*]}" -- "$cur"))
|
|
return 0
|
|
fi
|
|
if [ "$COMP_CWORD" -eq 2 ]; then
|
|
COMPREPLY=($(compgen -W "${_ODYSSEUS_SUBS_CACHE[$sub]}" -- "$cur"))
|
|
return 0
|
|
fi
|
|
return 0
|
|
fi
|
|
|
|
# Direct `odysseus-foo <tab>` (no umbrella)
|
|
local sub="${cmd#odysseus-}"
|
|
if [ "$COMP_CWORD" -eq 1 ]; then
|
|
COMPREPLY=($(compgen -W "${_ODYSSEUS_SUBS_CACHE[$sub]}" -- "$cur"))
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
# Register the completion for every odysseus-* script + the umbrella.
|
|
complete -F _odysseus_complete odysseus
|
|
for f in "$(_odysseus_scripts_dir)"/odysseus-*; do
|
|
[ -x "$f" ] || continue
|
|
case "$f" in *.bak|*.pyc|*.pre-*) continue ;; esac
|
|
complete -F _odysseus_complete "$(basename "$f")"
|
|
done
|