#autoload # compinit_doctor — inspect, audit, diff, and (optionally) rebuild zsh completion state # Usage: compinit_doctor [--status] [--audit] [--rebuild] [--snap FILE] [--diff FILE] [--dump PATH] [--json] [--help] compinit_doctor() { emulate -L zsh setopt extended_glob zmodload -F zsh/datetime 2>/dev/null || true # -------- defaults -------- local dump="${ZSH_COMPDUMP:-${XDG_CACHE_HOME:-$HOME/.cache}/zcompdump}" local action=status json=0 snap_file="" diff_file="" # -------- arg parse -------- while (( $# > 0 )); do case "$1" in --help|-h) action=help;; --status|-s) action=status;; --audit|-a) action=audit;; --rebuild|-r) action=rebuild;; --snap|-S) shift; snap_file="${1:-}"; [[ -z $snap_file ]] && { print -ru2 "compinit_doctor: --snap requires a file"; return 2; }; action=snap;; --diff|-d) shift; diff_file="${1:-}"; [[ -z $diff_file ]] && { print -ru2 "compinit_doctor: --diff requires a file"; return 2; }; action=diff;; --dump=*|-D=*) dump="${1#*=}";; --dump|-D) shift; dump="${1:-$dump}";; --json|-j) json=1;; *) print -ru2 "compinit_doctor: unknown option: $1"; return 2;; esac shift done # -------- helpers -------- __mtime() { [[ -e $1 ]] && stat -c %Y -- "$1" 2>/dev/null || return 1 } __age_human() { local mt now delta mt="$(__mtime "$1")" || { print -r -- "—"; return 0; } now="${EPOCHSECONDS:-$(date +%s)}" delta=$(( now - mt )); (( delta < 0 )) && delta=0 printf "%dd %02dh %02dm" $((delta/86400)) $(((delta%86400)/3600)) $(((delta%3600)/60)) } __exists() { [[ -e $1 ]] && print yes || print no; } __count_comps() { (( ${+_comps} )) && print ${#_comps} || print 0; } __print_status_text() { local dump_mtime dump_age compiled="${dump}.zwc" dump_mtime="$(__mtime "$dump" 2>/dev/null)" || dump_mtime="—" dump_age="$(__age_human "$dump")" print -r -- "compinit status:" print -r -- " dump path : $dump" print -r -- " dump exists : $(__exists "$dump")" print -r -- " dump age : $dump_age" print -r -- " compiled exists : $(__exists "$compiled")" print -r -- " \$fpath entries : ${#fpath}" print -r -- " \$fpath missing : $( local -a miss d for d in $fpath; do [[ -d $d ]] || miss+=("$d"); done print ${#miss} )" print -r -- " completion funcs : $(__count_comps)" print -r -- " compinit present : $((($+functions[compinit] || $+commands[compinit])) && print "yes" || print "no")" } __print_status_json() { local -a missing=(); local d for d in $fpath; do [[ -d $d ]] || missing+=("$d"); done local compinit_present=false if (( $+functions[compinit] || $+commands[compinit] )); then compinit_present=true; fi printf '{"dump":"%s","dump_exists":%s,"dump_age":"%s","compiled_exists":%s,' \ "$dump" "$([[ -e $dump ]] && print true || print false)" "$(__age_human "$dump")" "$([[ -e ${dump}.zwc ]] && print true || print false)" printf '"fpath_count":%d,"fpath_missing_count":%d,"completion_funcs":%d,"compinit_present":%s}\n' \ "${#fpath}" "${#missing}" "$(__count_comps)" "$compinit_present" } __do_audit() { if ! whence -w compaudit >/dev/null 2>&1; then print -ru2 "compinit_doctor: compaudit not available"; return 3 fi local -a insecure insecure=(${(f)"$(compaudit 2>/dev/null)"}) if (( ${#insecure} )); then print -r -- "Insecure completion dirs/files:" printf " %s\n" "${insecure[@]}" print -r -- "\nSuggested fix (review before running):" print -r -- " chmod -R go-w -- ${insecure:q}" else print -r -- "No insecure completion paths found (compaudit)." fi } __do_rebuild() { local t0 t1 dt t0="${EPOCHREALTIME:-$(perl -MTime::HiRes=time -e 'printf \"%.6f\", time()')}" autoload -U compinit compinit -i -d "$dump" || { print -ru2 "compinit_doctor: compinit failed"; return 4; } { zcompile "$dump" 2>/dev/null || true; } & t1="${EPOCHREALTIME:-$(perl -MTime::HiRes=time -e 'printf \"%.6f\", time()')}" dt=$(awk -v s="$t0" -v e="$t1" 'BEGIN{printf "%.3f",(e-s)*1000}') printf 'Rebuilt dump at: %s (compinit %.3fms)\n' "$dump" "$dt" } __snap() { local file="$1" [[ -n $file ]] || { print -ru2 "compinit_doctor: snapshot file required"; return 2; } mkdir -p -- "${file:h}" 2>/dev/null { print "# compinit_doctor snapshot" print "# date: $(date +'%Y-%m-%d %H:%M:%S %z')" printf "%s\n" "${(o)fpath[@]}" } >| "$file" print -r -- "Snapshot written: $file" } __diff() { local file="$1" [[ -r $file ]] || { print -ru2 "compinit_doctor: cannot read snapshot: $file"; return 2; } local -a snap cur added removed snap=(${(f)"$(<"$file")"}) snap=(${snap:#\#*}) cur=(${(o)fpath}) local -Ua _S _C _S=("${snap[@]}"); _C=("${cur[@]}") local s c for s in "${_S[@]}"; do [[ -z ${_C[(r)$s]} ]] && removed+=("$s"); done for c in "${_C[@]}"; do [[ -z ${_S[(r)$c]} ]] && added+=("$c"); done print -r -- "fpath diff vs $file:" print -r -- " added (${#added}):" printf " + %s\n" "${added[@]}" print -r -- " removed(${#removed}):" printf " - %s\n" "${removed[@]}" } # -------- dispatch -------- case "$action" in help) cat <<'EOF' compinit_doctor — inspect, audit, diff, and rebuild zsh completion state --status|-s Show current compinit/zcompdump status (default) --audit |-a Run compaudit and list insecure paths --rebuild|-r Force compinit -i -d and background zcompile --snap |-S FILE Snapshot current $fpath (sorted) to FILE --diff |-d FILE Diff current $fpath against FILE snapshot --dump |-D PATH Override zcompdump path (default: $XDG_CACHE_HOME/zcompdump) --json |-j Emit status as a single JSON object --help |-h Show this help EOF ;; audit) __do_audit;; rebuild) __do_rebuild;; snap) __snap "$snap_file";; diff) __diff "$diff_file";; status) (( json )) && __print_status_json || __print_status_text ;; esac }