feat(zsh): allow configuring settings like HOME_DIR/BIN_DIR via zstyle

Resolve ZINIT[...] settings from `zstyle ':zinit:config' <attr>` before
the BIN_DIR/HOME_DIR/derived-dir logic runs. The attribute name is the
hash field lowercased with `_` replaced by `-` (HOME_DIR -> home-dir).

An explicitly-set ZINIT[KEY] always wins over zstyle, which wins over
the built-in default; fully backward-compatible.

Signed-off-by: Vladislav Doster <mvdoster@gmail.com>
This commit is contained in:
vladdoster 2026-06-18 19:02:56 -05:00 committed by Vladislav Doster
parent d2d4237715
commit 8f89f88cfd
4 changed files with 113 additions and 0 deletions

View file

@ -33,6 +33,7 @@ jobs:
- annexes
- commands
- compile
- config-zstyle
- gh-r
- ices
- plugins

View file

@ -1150,6 +1150,22 @@ ### Customizing Paths<a name="customizing-paths"></a>
There is also `$ZPFX`, set by default to `~/.local/share/zinit/polaris` a directory where software with `Makefile`,
etc. can be pointed to, by e.g. `atclone'./configure --prefix=$ZPFX'`.
#### Configuring via `zstyle`
As an alternative to pre-setting the `$ZINIT` hash, the same settings can be configured with `zstyle` under the
`:zinit:config` context. The attribute name is the hash field lowercased with `_` replaced by `-` (e.g.
`ZINIT[HOME_DIR]``zstyle ':zinit:config' home-dir`). This works for every field in the table above, as well as
`services-dir`, `module-dir`, `polaris-dir` and `zpfx`.
Precedence is: an explicitly-set `ZINIT[KEY]` wins over a `zstyle`, which in turn wins over the built-in default. So the
`zstyle` only fills fields you did not set in the hash, keeping the setup fully backward-compatible.
```zsh
zstyle ':zinit:config' home-dir ~/.zinit
zstyle ':zinit:config' bin-dir ~/.zinit/zinit.git
zstyle ':zinit:config' mute-warnings 1
```
### Non-GitHub (Local) Plugins<a name="non-github-local-plugins"></a>
Use `create` subcommand with user name `_local` (the default) to create plugin's skeleton in `$ZINIT[PLUGINS_DIR]`. It

68
tests/config-zstyle.zunit Normal file
View file

@ -0,0 +1,68 @@
#!/usr/bin/env zunit
# vim:ft=zsh:sw=4:sts=4:et:foldmarker={,}:foldmethod=marker
#
# zdharma-continuum/zinit/tests/config-zstyle.zunit
# Copyright (c) 2016-2021 Sebastian Gniazdowski
# Copyright (c) 2021-2024 zdharma-continuum
# Homepage: https://github.com/zdharma-continuum/zinit
# License: MIT License
#
# Tests for configuring ZINIT[...] settings via `zstyle ':zinit:config' ...'.
#
# NOTE: the test bootstrap pre-sets every ZINIT[KEY], which would mask the
# zstyle path. Each test therefore exercises the feature in a CLEAN
# `zsh -fc` subshell that sets a zstyle, sources the patched zinit.zsh
# (located at ${ZINIT[BIN_DIR]}/zinit.zsh inside the test env) and prints the
# resulting hash field.
#
# The subshell is captured with `$(...)` rather than zunit's `run`: `run`
# re-`eval`s its arguments in the *outer* process, where the bootstrap has
# already populated ZINIT[HOME_DIR] et al., which would clobber the inner
# `${ZINIT[...]}` reads we want to observe. Single-quoting the inner script
# defers those reads to the subshell; only ${ZINIT[BIN_DIR]} is expanded
# (concatenated in) by the outer shell to locate zinit.zsh.
#
@test 'zstyle fills an unset key' {
output=$(zsh -fc 'zmodload zsh/zutil; typeset -gAH ZINIT
ZINIT[BIN_DIR]='"${ZINIT[BIN_DIR]}"'
zstyle ":zinit:config" home-dir /tmp/zi-fromstyle
source '"${ZINIT[BIN_DIR]}"'/zinit.zsh >/dev/null 2>&1
print -r -- ${ZINIT[HOME_DIR]}')
state=$?
assert $state equals 0
assert $output same_as '/tmp/zi-fromstyle'
}
@test 'explicit ZINIT[KEY] beats zstyle' {
output=$(zsh -fc 'zmodload zsh/zutil; typeset -gAH ZINIT
ZINIT[BIN_DIR]='"${ZINIT[BIN_DIR]}"'
ZINIT[HOME_DIR]=/tmp/zi-explicit
zstyle ":zinit:config" home-dir /tmp/zi-fromstyle
source '"${ZINIT[BIN_DIR]}"'/zinit.zsh >/dev/null 2>&1
print -r -- ${ZINIT[HOME_DIR]}')
state=$?
assert $state equals 0
assert $output same_as '/tmp/zi-explicit'
}
@test 'derived dir follows zstyle-provided home-dir' {
output=$(zsh -fc 'zmodload zsh/zutil; typeset -gAH ZINIT
ZINIT[BIN_DIR]='"${ZINIT[BIN_DIR]}"'
zstyle ":zinit:config" home-dir /tmp/zi-fromstyle
source '"${ZINIT[BIN_DIR]}"'/zinit.zsh >/dev/null 2>&1
print -r -- ${ZINIT[PLUGINS_DIR]}')
state=$?
assert $state equals 0
assert $output same_as '/tmp/zi-fromstyle/plugins'
}
@test 'no zstyle, no array still resolves a default home-dir' {
output=$(zsh -fc 'zmodload zsh/zutil; typeset -gAH ZINIT
ZINIT[BIN_DIR]='"${ZINIT[BIN_DIR]}"'
source '"${ZINIT[BIN_DIR]}"'/zinit.zsh >/dev/null 2>&1
[[ -n ${ZINIT[HOME_DIR]} ]] && print OK')
state=$?
assert $state equals 0
assert $output same_as 'OK'
}

View file

@ -22,6 +22,34 @@ typeset -gAH ZPLGM
ZINIT=( "${(kv)ZPLGM[@]}" "${(kv)ZINIT[@]}" )
unset ZPLGM
#
# Allow configuring ZINIT[...] settings via zstyle, e.g.:
# zstyle ':zinit:config' home-dir ~/.zinit
# zstyle ':zinit:config' bin-dir ~/.zinit/zinit.git
# An explicitly-set ZINIT[KEY] always wins over the matching zstyle.
#
if zmodload -F zsh/zutil +b:zstyle 2>/dev/null || (( ${+builtins[zstyle]} )); then
() {
local _attr _key
local -A _map=(
bin-dir BIN_DIR home-dir HOME_DIR
plugins-dir PLUGINS_DIR completions-dir COMPLETIONS_DIR
snippets-dir SNIPPETS_DIR services-dir SERVICES_DIR
module-dir MODULE_DIR polaris-dir POLARIS_DIR
zpfx ZPFX man-dir MAN_DIR
list-command LIST_COMMAND zcompdump-path ZCOMPDUMP_PATH
compinit-opts COMPINIT_OPTS mute-warnings MUTE_WARNINGS
no-aliases NO_ALIASES packages-branch PACKAGES_BRANCH
packages-repo PACKAGES_REPO
optimize-out-disk-accesses OPTIMIZE_OUT_DISK_ACCESSES
)
for _attr _key in "${(kv)_map[@]}"; do
[[ -n ${ZINIT[$_key]} ]] && continue
zstyle -s ':zinit:config' "$_attr" "ZINIT[$_key]"
done
}
fi
#
# Common needed values.
#