From 8f89f88cfdda42afbf5e1f7a3f2d510caff7354d Mon Sep 17 00:00:00 2001 From: vladdoster Date: Thu, 18 Jun 2026 19:02:56 -0500 Subject: [PATCH] feat(zsh): allow configuring settings like HOME_DIR/BIN_DIR via zstyle Resolve ZINIT[...] settings from `zstyle ':zinit:config' ` 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 --- .github/workflows/tests.yaml | 1 + README.md | 16 +++++++++ tests/config-zstyle.zunit | 68 ++++++++++++++++++++++++++++++++++++ zinit.zsh | 28 +++++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 tests/config-zstyle.zunit diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index afacfed3..de7eb603 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -33,6 +33,7 @@ jobs: - annexes - commands - compile + - config-zstyle - gh-r - ices - plugins diff --git a/README.md b/README.md index 7fa85bd0..3d02cb61 100644 --- a/README.md +++ b/README.md @@ -1150,6 +1150,22 @@ ### Customizing Paths 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 Use `create` subcommand with user name `_local` (the default) to create plugin's skeleton in `$ZINIT[PLUGINS_DIR]`. It diff --git a/tests/config-zstyle.zunit b/tests/config-zstyle.zunit new file mode 100644 index 00000000..c8538cd2 --- /dev/null +++ b/tests/config-zstyle.zunit @@ -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' +} diff --git a/zinit.zsh b/zinit.zsh index 20d08752..c6b5dfce 100644 --- a/zinit.zsh +++ b/zinit.zsh @@ -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. #