Files
minimal/bin/compinit_doctor
T
2026-03-23 23:50:58 +00:00

159 lines
6.1 KiB
Plaintext
Executable File

#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 <dump> 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
}