# nc-completion.bash — dynamic bash tab-completion for the Cloverleaf-Larry # short commands. Source this file (the installer adds it to your shell rc): # # source /path/to/cloverleaf-larry/bin/nc-completion.bash # # THREE LEVELS, all enumerated LIVE off the NetConfig site tree under $HCIROOT # (never a static list — it reflects exactly what the tools see right now): # # 1. COMMAND NAMES — `tb` → tbn tbp tbh tbpr ; `nc-` → nc-find … # 2. SITE NAMES — `tbn ` → adt ancout codamx epic … # 3. THREAD NAMES — `paths `, `route_test --source-thread `, # `where ` → IB_ADT_epic MUX_ADT_ancout … # # Sites/threads are read through the SAME parser the tools use (lib/nc-parse.sh # via bin/_nc_common.sh), so completion can never drift from reality. Results # are cached per (HCIROOT, mtime-of-tree) for the life of the shell so repeated # TABs are instant; the cache invalidates automatically when a NetConfig under # $HCIROOT changes. # Locate _nc_common.sh relative to THIS completion file (works for both the repo # layout and an installed copy). Sourced once at load. if [ -z "${_NC_COMPLETION_COMMON:-}" ]; then _ncc_self="${BASH_SOURCE[0]}" _NC_COMPLETION_COMMON="$(cd "$(dirname "$_ncc_self")" 2>/dev/null && pwd)/_nc_common.sh" unset _ncc_self fi # --- cache key = HCIROOT + newest NetConfig mtime (cheap staleness check) ---- _nc_comp_cachekey() { local root="${HCIROOT:-}" [ -n "$root" ] && [ -d "$root" ] || { printf 'none'; return; } local newest newest="$(find "$root" -maxdepth 2 -name NetConfig -printf '%T@\n' 2>/dev/null | sort -rn | head -1)" printf '%s@%s' "$root" "${newest:-0}" } # --- live site list (cached) ------------------------------------------------- _NC_COMP_SITES_KEY=""; _NC_COMP_SITES="" _nc_comp_sites() { local key; key="$(_nc_comp_cachekey)" if [ "$key" != "$_NC_COMP_SITES_KEY" ]; then [ -f "$_NC_COMPLETION_COMMON" ] && . "$_NC_COMPLETION_COMMON" _NC_COMP_SITES="$(_nc_sites 2>/dev/null | tr '\n' ' ')" _NC_COMP_SITES_KEY="$key" fi printf '%s' "$_NC_COMP_SITES" } # --- live thread list (cached) ----------------------------------------------- _NC_COMP_THREADS_KEY=""; _NC_COMP_THREADS="" _nc_comp_threads() { local key; key="$(_nc_comp_cachekey)" if [ "$key" != "$_NC_COMP_THREADS_KEY" ]; then [ -f "$_NC_COMPLETION_COMMON" ] && . "$_NC_COMPLETION_COMMON" _NC_COMP_THREADS="$(_nc_threads 2>/dev/null | tr '\n' ' ')" _NC_COMP_THREADS_KEY="$key" fi printf '%s' "$_NC_COMP_THREADS" } # --- generic completer: offer sites + threads (union) for a name argument ---- # Most short commands take "a site OR a thread"; offering both satisfies every # documented level (tbn→sites, paths/where→threads) without over-fitting. _nc_complete_names() { local cur="${COMP_WORDS[COMP_CWORD]}" local pool="$(_nc_comp_sites) $(_nc_comp_threads)" COMPREPLY=( $(compgen -W "$pool" -- "$cur") ) } # --- threads-only completer (route_test --source-thread, paths, where) ------- _nc_complete_threads_only() { local cur="${COMP_WORDS[COMP_CWORD]}" COMPREPLY=( $(compgen -W "$(_nc_comp_threads)" -- "$cur") ) } # --- sites-only completer ---------------------------------------------------- _nc_complete_sites_only() { local cur="${COMP_WORDS[COMP_CWORD]}" COMPREPLY=( $(compgen -W "$(_nc_comp_sites)" -- "$cur") ) } # --- per-command completers --------------------------------------------------- # tbn: thread-name search, but a site name is the natural first thing operators # type (tbn adt) — so offer the union (sites + threads). Same for tbh/tbpr. _nc_complete_tbn() { _nc_complete_names; } _nc_complete_tbh() { _nc_complete_names; } _nc_complete_tbpr() { # process-name search → no live process index; fall back to threads+sites. _nc_complete_names } # tbp: v1 "thread by port". The task wants this to surface thread names on TAB, # so we complete against threads (the operator can still type a bare port). _nc_complete_tbp() { _nc_complete_threads_only; } # where: positional . _nc_complete_where() { _nc_complete_threads_only; } # paths: first arg , optional 2nd arg , then flags. _nc_complete_paths() { local cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" case "$cur" in -*) COMPREPLY=( $(compgen -W "--up --down --site-only --all --site --format --hciroot" -- "$cur") ); return ;; esac case "$prev" in --site) _nc_complete_sites_only; return ;; --format) COMPREPLY=( $(compgen -W "v1 table tsv jsonl nodes" -- "$cur") ); return ;; esac # 1st positional → thread; 2nd positional → site. Count prior non-flag words. local i nonflag=0 for ((i=1; i , or --source-thread [--file ]. _nc_complete_route_test() { local cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" case "$prev" in --source-thread|--thread) _nc_complete_threads_only; return ;; --file) COMPREPLY=( $(compgen -f -- "$cur") ); return ;; esac case "$cur" in -*) COMPREPLY=( $(compgen -W "--source-thread --file --dry-run" -- "$cur") ); return ;; esac # bare first positional → thread; subsequent → file path. local i nonflag=0 for ((i=1; i