From 09f80f74ac3cfb6d6e57da080158fed7a97d49dc Mon Sep 17 00:00:00 2001 From: Pete Ehlke Date: Mon, 13 Jul 2026 13:18:19 -0700 Subject: [PATCH] fix: don't crash when list-colors zstyle isn't dircolors-shaped -z4h-set-list-colors assumed the list-colors style always holds simple GNU dircolors "code=value" pairs. Third-party completion tools (e.g. carapace) can set list-colors themselves using zsh's richer match-highlight syntax (multiple `=`-separated fields per entry), which splits into an odd number of fields here. Building the associative arrays directly from that odd-length list is a fatal error ("bad set of key/value pairs for associative array") that aborts the whole completion widget, not just this function. Validate the split lists have even length before building the associative arrays, and bail out (skip colorization for this value) the same way the function already does when no list-colors style is set at all. --- fn/-z4h-set-list-colors | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/fn/-z4h-set-list-colors b/fn/-z4h-set-list-colors index 68e50233..e31d7f55 100644 --- a/fn/-z4h-set-list-colors +++ b/fn/-z4h-set-list-colors @@ -12,9 +12,21 @@ zstyle -a :completion:$curcontext:default list-colors list_colors || return if [[ ${list_types}${(pj:\0:)list_colors} != $_z4h_last_list_colors ]]; then # This takes ~20ms. Can be easily optimized. + local -a name_colors mode_colors + name_colors=(${(@s:=:)${(@s.:.)list_colors}:#[[:alpha:]][[:alpha:]]=*}) + mode_colors=(${(@Ms:=:)${(@s.:.)list_colors}:#[[:alpha:]][[:alpha:]]=*}) + # The list-colors style can be set by third-party completion tools (not + # just dircolors/LS_COLORS) using zsh's richer match-highlighting syntax + # (multiple `=`-separated fields per entry) rather than the simple + # "code=value" pairs assumed above. That splits into an odd number of + # fields here, which would otherwise crash the `typeset -gA` calls below + # with "bad set of key/value pairs for associative array". Bail out + # (skip colorization for this value) instead of crashing the completion + # widget. + (( $#name_colors % 2 == 0 && $#mode_colors % 2 == 0 )) || return 1 typeset -g _z4h_last_list_colors=${list_types}${(pj:\0:)list_colors} - typeset -gA _z4h_name_colors=(${(@s:=:)${(@s.:.)list_colors}:#[[:alpha:]][[:alpha:]]=*}) - typeset -gA _z4h_mode_colors=(${(@Ms:=:)${(@s.:.)list_colors}:#[[:alpha:]][[:alpha:]]=*}) + typeset -gA _z4h_name_colors=($name_colors) + typeset -gA _z4h_mode_colors=($mode_colors) typeset -gA _z4h_mode_codes=() () { local -i8 a b c d