refactor(plugin): adopt Zsh Plugin Standard 2 (#119) - #122
Merged
Conversation
- Remove deprecated Plugins hash registry mutations - Move internal parameters to _zsh_eza_* namespace - Rename autoload entrypoint to functions/_zsh_eza_init and standardize zsh_eza_plugin_unload - Add declarative zstyle ':zsh-eza:config' contexts with legacy fallback - Upgrade zsh-lint.json to schema v2 with 0 diagnostics - Expand ZUnit test suite with zstyle and lifecycle assertions (22/22 pass)
Zi resolves the unload hook as ${plugin}_plugin_unload using the literal
plugin name (lib/zsh/autoload.zsh), so renaming the function to
zsh_eza_plugin_unload made `zi unload z-shell/zsh-eza` a silent no-op and
removed a working capability. Under `zi light` nothing was undone at all.
Restore zsh-eza_plugin_unload as the single unload entrypoint and suppress
the advisory plugin/function-namespace hint with the reason. The hint is
severity Hint and does not gate CI; a second underscore-named wrapper would
only add a plugin/unload-function diagnostic for a name nothing dispatches.
Also drop the duplicate unload test, which asserted exactly what the
existing "unload removes aliases" test already covers.
ADR-0020 point 5 requires cleanup to restore a prior value only while the installed value is unchanged, and to preserve newer user state. The previous loop unaliased unconditionally and then wrote the pre-load value back, so a user who redefined `ls` after loading lost their definition on unload. The guard that was there only checked that some alias existed. Record what each load installs in _zsh_eza_installed_aliases and compare against it before undoing anything. An alias the user changed or removed after load is left as they left it.
…meter The style reads tested truthiness, not presence. `zstyle -t` returns 1 for a style set to a false value and 2 when unset, so both fell through to the legacy branch: an explicit `zstyle ':zsh-eza:config' autocd no` was overridden by `AUTOCD=1` and the chpwd hook was registered anyway. The same shape made an explicit empty `user-params` fall back to `eza_user_params`. Use `zstyle -s` as the presence test and match the value separately, so a style that is set wins even when its value is empty or false. The legacy parameter is consulted only when no style is set at all.
Two separate breaks in the Zsh Lint job: The pinned ref 1e8b0a6 is an unreleased commit that is not reachable from zsh-lint main, and it only understands configuration schema v1, so the v2 `project.identifier` in zsh-lint.json was rejected with `unknown field "identifier"`. ADR-0020 point 9 requires an exact released commit. Move the pin to 33bce80, tagged v1.2.0, which is the release that introduced schema v2. `tests` was added to ZSH_LINT_PATHS as a directory, so `find` handed tests/zsh-eza.zunit to the linter. ZUnit files are not Zsh source and fail to parse: "`}` can only be used to close a block". Name the two Zsh helpers explicitly instead; the tests source root in zsh-lint.json still applies to them. Verified by running the workflow's own step against a v1.2.0 build: 4 targets, 0 diagnostics.
ADR-0020 point 8 says a refactored plugin does not retain legacy aliases, duplicate configuration variables, or alternate declaration systems merely for compatibility. The migration kept all of it: eza_params, ZSH_EZA_ALIAS_NAMES, ZSH_EZA_SAVED_ALIASES, ZSH_EZA_FPATH, the eza_user_params, eza_extra_params and AUTOCD entry points, and a zsh-eza-auto-list wrapper that nothing in the hook path called. The aliases still expanded the legacy eza_params, so the private _zsh_eza_params array was never the operative one and the namespace goal was not actually reached. Keep one declaration system. The aliases expand _zsh_eza_params, the only configuration surface is the :zsh-eza:config zstyle context, and unload owns one set of private parameters. Dropping the legacy fallbacks also removes the precedence branch entirely, so `zstyle -t` is once again the correct autocd test. BREAKING CHANGE: eza_user_params, eza_extra_params and AUTOCD no longer configure the plugin, and eza_params, ZSH_EZA_ALIAS_NAMES, ZSH_EZA_SAVED_ALIASES and ZSH_EZA_FPATH are no longer defined. Use the :zsh-eza:config styles documented in docs/README.md.
A quoted key expansion of an unset associative array yields one empty word rather than none, so after an early return (dumb terminal, or eza missing) the restore loop ran once with an empty alias name and failed with "bad math expression: empty string". ADR-0020 point 5 requires unload to tolerate partial initialization. Expand the keys unquoted so an unset hash produces no iterations. The keys are plugin-owned literals with no whitespace. Cover it by unloading at the end of the existing dumb-terminal test, which already builds the partial-load state.
The entrypoint added the functions directory to fpath, then returned early when _zsh_eza_init failed, before the unload function was defined. A shell where eza is missing was left with a modified fpath and no way to undo it, and `zi unload` found nothing to call. ADR-0020 point 5 requires the unload function to tolerate partial initialization. Define it before the load attempt. It already guards every resource it touches, so it is correct against a load that installed nothing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adopts the canonical contract in Zsh Plugin Standard 2 (ADR-0020) and
aligns with the Zsh Scripting Standard (ADR-0015).
Closes #119.
Configuration is now the zstyle context only
:zsh-eza:configuser-params:zsh-eza:configextra-params:zsh-eza:configautocdnoyesto list the new directory aftercd.A style that is set wins even when its value is empty or false.
zstyle -treturns 1 for a style set to a false value and 2 when unset, so testing
truthiness rather than presence made an explicit
autocd nofall through.Changes
functions/.zsh-ezabecomes the autoload functionfunctions/_zsh_eza_init. Private state is_zsh_eza_params,_zsh_eza_saved_aliases,_zsh_eza_installed_aliases,_zsh_eza_fpath,_zsh_eza_plugin_dir. The aliases expand_zsh_eza_params.Plugins[ZSH_EZA]mutation. Nothing else inthe organization read it.
zsh-eza_plugin_unloadis declared before the loadattempt, restores
fpath, removes thechpwdhook and plugin functions,and restores aliases ownership-aware: an alias the user changed or removed
after load is left as they left it. It tolerates a load that returned early.
zsh-lint.jsonmoves to schema v2 with project identifierzsh-eza. The Zsh Lint pin moves to33bce80(v1.2.0); the previous ref wasan unreleased commit not reachable from
zsh-lintmain, which ADR-0020point 9 disallows, and it predates schema v2. ZUnit files are no longer fed
to the linter, since they are not Zsh source and do not parse.
Breaking changes
eza_user_params,eza_extra_paramsandAUTOCDno longer configure theplugin.
eza_params,ZSH_EZA_ALIAS_NAMES,ZSH_EZA_SAVED_ALIASESandZSH_EZA_FPATHare no longer defined.zsh-eza-auto-listis gone; the hookfunction is
_zsh_eza_auto_list. Use the:zsh-eza:configstyles documentedin
docs/README.md.ADR-0020 point 8 requires this: a refactored plugin does not retain legacy
aliases, duplicate configuration variables, or alternate declaration systems
for compatibility.
On the unload function name
Zi resolves the unload hook as
${plugin}_plugin_unloadusing the literalplugin name, so
zsh-eza_plugin_unloadis the namezi unload z-shell/zsh-ezaactually dispatches.zsh-lintderives the shell-visibleprefix by replacing hyphens with underscores and therefore emits an advisory
plugin/function-namespacehint for that name, which is suppressed here witha reason.
Naming it
zsh_eza_plugin_unloadto satisfy the analyzer makes unload asilent no-op, and under
zi lightthere is no per-plugin tracking to fallback on. Tracked in z-shell/zi#482, which offers both resolutions; the
suppression comes out once one lands.
Verification
Run against the exact
zsh-lintcommit this branch pins:zsh -nandzcompile: clean on every Zsh file.zsh-lintv1.2.0 through the workflow's own step: 4 targets, 0 diagnostics.New runtime coverage for the contract claims: ownership-aware restore, unload
after a load that returned early,
fpathrestored after a failed load, andthe style-presence cases.