Skip to content

refactor(plugin): adopt Zsh Plugin Standard 2 (#119) - #122

Merged
ss-o merged 8 commits into
mainfrom
feature-119
Sep 2, 2026
Merged

refactor(plugin): adopt Zsh Plugin Standard 2 (#119)#122
ss-o merged 8 commits into
mainfrom
feature-119

Conversation

@ss-o

@ss-o ss-o commented Sep 2, 2026

Copy link
Copy Markdown
Member

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

Style context Style Type Default Effect
:zsh-eza:config user-params Shell words Unset Replaces the complete default argument list.
:zsh-eza:config extra-params Shell words Unset Appends arguments after the selected defaults.
:zsh-eza:config autocd Boolean no Set to yes to list the new directory after cd.

A style that is set wins even when its value is empty or false. zstyle -t
returns 1 for a style set to a false value and 2 when unset, so testing
truthiness rather than presence made an explicit autocd no fall through.

Changes

  • Namespace hygiene. functions/.zsh-eza becomes the autoload function
    functions/_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.
  • Shared registry removal. No Plugins[ZSH_EZA] mutation. Nothing else in
    the organization read it.
  • Unload contract. zsh-eza_plugin_unload is declared before the load
    attempt, restores fpath, removes the chpwd hook 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.
  • Legacy surface removed. See the breaking-change note below.
  • Tooling. zsh-lint.json moves to schema v2 with project identifier
    zsh-eza. The Zsh Lint pin moves to 33bce80 (v1.2.0); the previous ref was
    an unreleased commit not reachable from zsh-lint main, which ADR-0020
    point 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_params and AUTOCD no longer configure the
plugin. eza_params, ZSH_EZA_ALIAS_NAMES, ZSH_EZA_SAVED_ALIASES and
ZSH_EZA_FPATH are no longer defined. zsh-eza-auto-list is gone; the hook
function is _zsh_eza_auto_list. Use the :zsh-eza:config styles documented
in 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_unload using the literal
plugin name, so zsh-eza_plugin_unload is the name zi unload z-shell/zsh-eza actually dispatches. zsh-lint derives the shell-visible
prefix by replacing hyphens with underscores and therefore emits an advisory
plugin/function-namespace hint for that name, which is suppressed here with
a reason.

Naming it zsh_eza_plugin_unload to satisfy the analyzer makes unload a
silent no-op, and under zi light there is no per-plugin tracking to fall
back on. Tracked in z-shell/zi#482, which offers both resolutions; the
suppression comes out once one lands.

Verification

Run against the exact zsh-lint commit this branch pins:

  • zsh -n and zcompile: clean on every Zsh file.
  • zsh-lint v1.2.0 through the workflow's own step: 4 targets, 0 diagnostics.
  • ZUnit: 25 / 25.

New runtime coverage for the contract claims: ownership-aware restore, unload
after a load that returned early, fpath restored after a failed load, and
the style-presence cases.

- 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)
ss-o added 7 commits September 2, 2026 04:21
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.
@ss-o
ss-o merged commit f67e458 into main Sep 2, 2026
7 checks passed
@ss-o
ss-o deleted the feature-119 branch September 2, 2026 12:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor(plugin): adopt Zsh Plugin Standard 2

1 participant