# Z-Shell Wiki full corpus > Locale: en. Generated from the canonical Z-Shell Wiki build. The linked human-facing page remains the editorial source of truth. --- title: 👥 Community description: Community documentation, guides, and resources for Zsh enthusiasts and Z-Shell contributors. canonical_url: https://wiki.zshell.dev/community markdown_url: https://wiki.zshell.dev/community/index.md locale: en source_path: community/index.mdx --- # 👥 Community The Z-Shell community is home to contributors, plugin authors, and Zsh enthusiasts. Find guides, a plugin gallery, community tools, and everything you need to get involved. ![Z-Shell community: Contributing, ZUnit, Gallery, ZSH Guide](https://wiki.zshell.dev/img/svg/community-banner.svg) Get involved Join the [Z-Shell organization](https://github.com/z-shell), help [translate the wiki](https://translate.zshell.dev/), or follow active work in [Z-shell Delivery](https://github.com/orgs/z-shell/projects/28). ## Community Sections [![](https://wiki.zshell.dev/img/svg/community/contributing-zi.svg) ### Contributing Contribute to Zi, create Zsh plugins, improve the docs, or help with project management. Start contributing →](https://wiki.zshell.dev/community/contributing/index.md)[![](https://wiki.zshell.dev/img/svg/cards/syntax-highlighting.svg) ### Zsh Guide Practical guidance for Zsh options, completion, key bindings, and startup profiling. Configure Zsh →](https://wiki.zshell.dev/community/zsh-guide)[![](https://wiki.zshell.dev/img/svg/cards/syntax-highlighting.svg) ### Zsh Scripting Handbook Native Zsh idioms and worked techniques: array forms, expansion, pattern matching, and substitutions. Read the handbook →](https://wiki.zshell.dev/community/zsh_handbook/index.md)[![](https://wiki.zshell.dev/img/svg/cards/history-search.svg) ### ZUnit A unit testing framework for Zsh: write @test blocks, run assertions, and integrate with CI. Explore ZUnit →](https://wiki.zshell.dev/community/zunit/index.md)[![](https://wiki.zshell.dev/img/svg/cards/syntax-highlighting.svg) ### Zsh Lint A standalone semantic analyzer for Zsh scripts with greppable static-analysis diagnostics. Explore Zsh Lint →](https://wiki.zshell.dev/community/zsh_lint/index.md)[![](https://wiki.zshell.dev/img/svg/cards/meta-plugins.svg) ### Plugin Gallery Community-curated collections of plugins, completions, snippets, services, and themes. Browse the gallery →](https://wiki.zshell.dev/community/category/-gallery-of-invocations)[![](https://wiki.zshell.dev/img/svg/community/contributing-plugin-standard.svg) ### Zsh Plugin Standard The plugin interoperability contract: what makes a directory a plugin and how managers load it. Read the standard →](https://wiki.zshell.dev/community/zsh_plugin_standard/index.md) --- --- title: 🤝 Contributing description: Everything you need to contribute to the Z-Shell ecosystem — zi, Zsh plugins, Zi annexes, and the wiki itself. canonical_url: https://wiki.zshell.dev/community/contributing markdown_url: https://wiki.zshell.dev/community/contributing/index.md locale: en source_path: community/00_contributing/index.mdx --- # 🤝 Contributing Contributions are what make the Z-Shell ecosystem thrive. Whether you want to fix a bug in zi, publish a Zsh plugin, or improve this documentation — you are in the right place. ![Contributing workflow: fork → write → PR → merge](https://wiki.zshell.dev/img/svg/community/contributing-banner.svg) Code of Conduct All contributors are expected to follow our [Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). Please read it before participating. ## Contribution Paths Choose the path that matches what you want to do: [![](https://wiki.zshell.dev/img/svg/community/contributing-zi.svg) ### Contribute to Zi Fix bugs, add features, improve performance, or improve the zi source code itself. Contributing to Zi →](https://wiki.zshell.dev/community/contributing/contributing_to_zi/index.md)[![](https://wiki.zshell.dev/img/svg/community/contributing-plugin-standard.svg) ### Create a Zsh Plugin Write your own Zsh plugin following the community standard and best practices. Zsh Plugin Standard →](https://wiki.zshell.dev/community/zsh_plugin_standard/index.md)[![](https://wiki.zshell.dev/img/svg/community/contributing-docs.svg) ### Improve the Docs Add pages, fix typos, improve guides, or help translate the wiki. Contributing to the Wiki →](https://wiki.zshell.dev/community/contributing/contributing_docs/index.md) ## Get Involved
ActionLink
👥 Join the teamOpen a membership request
🌐 Translatetranslate.zshell.dev
📋 Project trackerZ-shell Delivery (Project 28)
💬 DiscussGitHub Discussions
## General Guidelines Before starting any significant work — open an [issue](https://github.com/z-shell/zi/issues/new/choose) to discuss the change first. This avoids wasted effort and helps the maintainers coordinate. All repositories in the z-shell organization share a common branch model, commit convention, and project tracker. See [**Getting Started**](https://wiki.zshell.dev/community/contributing/getting_started/index.md) for the details that apply everywhere. --- --- title: 📝 Contributing to the Wiki description: How to contribute to the Z-Shell wiki — writing MDX, localization via Crowdin, and local development workflow. canonical_url: https://wiki.zshell.dev/community/contributing/contributing_docs markdown_url: https://wiki.zshell.dev/community/contributing/contributing_docs/index.md locale: en source_path: community/00_contributing/04_contributing_docs.mdx --- # 📝 Contributing to the Wiki ![](https://wiki.zshell.dev/img/svg/community/contributing-docs.svg) The wiki is a [Docusaurus 3](https://docusaurus.io/) site. All source files are in the [`z-shell/wiki`](https://github.com/z-shell/wiki) repository. English is the source language; translations are managed via [Crowdin](https://translate.zshell.dev/). ## Prerequisites - [Node.js](https://nodejs.org/) 24 LTS - [pnpm](https://pnpm.io/) 11 ## Local Development ```sh git clone https://github.com//wiki.git cd wiki pnpm install # install dependencies pnpm start # dev server at http://localhost:3000 ``` Other useful commands:
CommandPurpose
pnpm buildFull production build
pnpm build:enEnglish-only build (faster for docs-only checks)
pnpm serveServe the production build locally
pnpm clearClear Docusaurus cache (use when the site behaves oddly)
pnpm lintRun Trunk linters and formatting checks
pnpm lint:fixAuto-fix linting issues
pnpm write-heading-idsRegenerate heading IDs across all docs
## Content Roots The wiki has three independent docs roots:
DirectoryURL PrefixPurpose
docs//docsCore documentation, getting started, guides
community//communityCommunity guides, Zsh plugin standard, tools
ecosystem//ecosystemAnnexes, packages, plugins
## File Naming Conventions - **Numeric prefixes** control sidebar order: `01_first.mdx`, `02_second.mdx` - **Directories** need a `_category_.json` file (see below) - **All English source files only** — never edit files under `i18n/` ### Frontmatter Every `.mdx` file requires these fields: ```mdx --- id: unique_page_id title: "Page Title" sidebar_position: 1 image: /img/png/theme/z/320x320.png description: One-sentence description for SEO. keywords: - relevant - keywords --- ``` ### `_category_.json` Required for every new directory: ```json { "label": "📁 Section Name", "position": 1, "link": { "type": "generated-index" } } ``` ## MDX Authoring Patterns ### Choose the simplest useful presentation Start with ordinary Markdown and add a richer element only when it improves comprehension or scanning.
NeedUse
Short comparison or reference matrixGFM table with compact cells
Steps that must happen in orderNumbered list
Work readers can completeGFM task list (- [ ])
Advice, context, risk, or dangerDocusaurus admonition
Optional or advanced explanation<details> with a one-line <summary>
Equivalent operating-system or tool instructions<Tabs> and <TabItem>
Landing-page navigation<CardGrid> and <Card>
Do not use tables for long prose or sequential procedures, tabs for sequential steps, task boxes as decorative bullets, or raw HTML for layout. Blockquotes are for attributed quotations; use admonitions for callouts. ### Admonitions Use Docusaurus admonitions for callouts instead of blockquotes: ```mdx :::tip Helpful hint. ::: :::info Neutral information. ::: :::warning Something to be cautious about. ::: :::danger Critical warning. ::: ``` ### Global MDX Components These components are available in every `.mdx` file without importing:
ComponentUsage
<Highlight color="...">text</Highlight>Colored text spans
<Emoji symbol="🎉" label="party"/>Accessible emoji
<GhRepoBadge repo="z-shell/zi"/>GitHub repo badge
<ShellCodeCopy>command</ShellCodeCopy>Copyable shell command
### Code Blocks Use language identifiers and `showLineNumbers` when helpful: ````mdx ```zsh showLineNumbers zi light z-shell/F-Sy-H ``` ```` Add `title="path/to/file"` when a filename matters. Highlight only the lines being discussed, and reserve `showLineNumbers` for longer examples referenced by line number. ### Links Prefer relative links between pages: ```mdx See [Getting Started](../getting_started) for more details. ``` Use absolute URLs only for external resources. ## Branch and PR Workflow 1. Branch from `main`: `git checkout -b feature-123 main` 2. Make changes to English source files only 3. Run `pnpm build:en` to catch broken links and MDX errors 4. Open a PR targeting `main` Never edit `i18n/` files directly Translation files under `i18n/` are managed exclusively through [Crowdin](https://translate.zshell.dev/). Manual edits will be overwritten on the next sync. ## Localization The wiki supports multiple languages via Crowdin. To contribute a translation: 1. Join the project at [translate.zshell.dev](https://translate.zshell.dev/) 2. Translate strings in the Crowdin editor 3. Approved translations are synced back to the repo automatically Do **not** fork the repo and edit `i18n/` files manually. ## Style Guidelines - Use sentence case for headings (e.g., `## Code of conduct`, not `## Code Of Conduct`) - Keep paragraphs short — aim for ≤ 5 sentences per paragraph - Prefer tables for structured comparisons - Use admonitions sparingly — only when the callout level genuinely matches the content - Formatting is enforced by Prettier (120-char print width, double quotes, 2-space indent) ## See Also - [Getting Started](https://wiki.zshell.dev/community/contributing/getting_started/index.md) — org-wide branch and commit conventions - [Project Management](https://wiki.zshell.dev/community/contributing/project_management/index.md) — how work is tracked --- --- title: 🔧 Contributing to Zi description: Guidelines for contributing to the zi plugin manager — branch model, commits, PR workflow, and what not to add. canonical_url: https://wiki.zshell.dev/community/contributing/contributing_to_zi markdown_url: https://wiki.zshell.dev/community/contributing/contributing_to_zi/index.md locale: en source_path: community/00_contributing/02_contributing_to_zi.mdx --- # 🔧 Contributing to Zi ![](https://wiki.zshell.dev/img/svg/community/contributing-zi.svg) Thank you for contributing to zi! This page covers the guidelines specific to the [`z-shell/zi`](https://github.com/z-shell/zi) repository. For the org-wide basics (branch model, commit format, PR checklist) see [Getting Started](https://wiki.zshell.dev/community/contributing/getting_started/index.md). ## Repository Overview Zi is a Zsh plugin manager written entirely in Zsh. The source lives in `lib/zsh/` with the main entry point at `lib/zsh/zi.zsh`. The installer lives in [`z-shell/src`](https://github.com/z-shell/src). ```text zi/ lib/zsh/ ← core plugin manager source docs/ ← repository-level docs (CONTRIBUTING.md, man page) tests/ ← ZUnit test suite .github/ workflows/ ← CI workflows (syntax check, ZUnit, CodeQL) ``` ## Setting Up Locally ```sh git clone https://github.com//zi.git cd zi git remote add upstream https://github.com/z-shell/zi.git git checkout -b feature-123 next ``` No build step is required — zi is pure Zsh. To test changes interactively, source the modified files in a Zsh session: ```zsh zsh source lib/zsh/zi.zsh ``` ## Running Tests Zi uses [ZUnit](https://github.com/z-shell/zunit) for tests: ```zunit # Run all tests zunit # Run a single test file zunit tests/install.zunit ``` Tests live in `tests/`. Add or update tests when your change affects behavior. ## Zi-Specific Commit Scopes In addition to the [standard commit types](https://wiki.zshell.dev/community/contributing/getting_started/index.md#commit-message-format), prefer these scopes when relevant:
ScopeCovers
corelib/zsh/zi.zsh and lib/zsh/zi-*.zsh
installinstaller scripts in src
ciGitHub Actions workflows
docsrepository-level documentation
annexannex API surface changes
Examples: ```text fix(core): resolve fpath duplication on plugin reload feat(annex): expose atdelete hook to external annexes docs(ci): document workflow trigger conditions ``` ## What Not to Add to Zi Off-limits - `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.cursorrules`, or any AI-specific config files - Secrets, credentials, or tokens of any kind - Files unrelated to the plugin manager itself ## Opening Issues Before starting significant work, [open an issue](https://github.com/z-shell/zi/issues/new/choose) to discuss the change. Use the appropriate issue template:
TemplateWhen to use
Bug reportReproducible misbehavior
Feature requestNew capability
Annex proposalProposing a new annex
## Stable Branch Promotion Zi is the organization's named persistent-integration exception: 1. Ordinary work merges into `next`. 2. A maintainer opens a promotion pull request from `next` to stable `main`. 3. Promotion uses **Create a merge commit**, never squash or rebase, so Git ancestry is preserved and no routine back-merge is needed. 4. A critical hotfix may merge directly into `main`; maintainers merge it forward into `next` before the next promotion. `main` is a directly consumable stable ref, not a tag-driven package publication boundary. Maintainers handle promotion. As a contributor, target `next` unless you are working on an explicitly coordinated critical hotfix. ## See Also - [Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) - [Getting Started](https://wiki.zshell.dev/community/contributing/getting_started/index.md) — org-wide branch and commit conventions - [Project Management](https://wiki.zshell.dev/community/contributing/project_management/index.md) — how work is tracked across the org --- --- title: 🚀 Getting Started description: General contributor setup — branch model, commit conventions, and pull request workflow for all Z-Shell repositories. canonical_url: https://wiki.zshell.dev/community/contributing/getting_started markdown_url: https://wiki.zshell.dev/community/contributing/getting_started/index.md locale: en source_path: community/00_contributing/01_getting_started.mdx --- # 🚀 Getting Started ![](https://wiki.zshell.dev/img/svg/community/contributing-getting-started.svg) This guide covers the conventions shared across the Z-Shell organization — commit format, pull request workflow, and review expectations. Where a convention differs per repository, such as the branch model below, the guide says so and links the authoritative source. Repo-specific additions (zi, wiki, etc.) are documented in their own sub-pages. ## Prerequisites - [Git](https://git-scm.com/) ≥ 2.30 - [Zsh](https://www.zsh.org/) ≥ 5.8 - A [GitHub](https://github.com/) account ## Fork and Clone ```sh # 1. Fork the repo on GitHub, then clone your fork git clone https://github.com//.git cd # 2. Add the upstream remote git remote add upstream https://github.com/z-shell/.git ``` ## Branch Model The organization default is trunk-based development on `main`. Branch from the current `main` and open your pull request into `main`. `zi` is the sole named exception. Its ordinary contributions branch from and target `next`; a maintainer promotes `next` to stable `main` with a merge commit. Do not infer a repository's model merely from whether an old `next` reference exists. Check the repository instructions Opening a pull request against the wrong base is a common first-time contribution mistake. Read the owning repository's `AGENTS.md`. Unless it is `z-shell/zi`, use `main`. The authoritative per-repository table lives in [ADR 0019: Trunk-on-Main Default](https://github.com/z-shell/.github/blob/main/decisions/0019-trunk-on-main-default.md). At the time of writing:
Integration branchRepositories
trunk on mainall repositories except the named exception below
next to stable mainzi
Consult the ADR rather than this table if the two ever disagree; the ADR is the source of truth, and changing a repository's model requires amending it. ### Branch naming Use the issue-linked organization names: ```text feature- planned features, documentation, refactors, and maintenance bug- non-critical bug fixes hotfix- urgent fixes branched from the stable or deployment ref ``` **Rules:** 1. **Branch from current `main`** and open the pull request into `main`. 2. For `zi` ordinary work only, branch from and target `next`. 3. For a critical `zi` hotfix, branch from and target `main`; maintainers then merge the hotfix forward into `next` before the next promotion. 4. Keep the branch short-lived and linked to its owning issue. ## Commit Message Format All commits must follow [Conventional Commits](https://www.conventionalcommits.org/): ```text type(scope): short description Optional body — explain what and why, not how. Wrap at 72 characters. Optional footer(s): Fixes #123 BREAKING CHANGE: description of what breaks ``` ### Allowed Types
TypePurpose
featNew feature
fixBug fix
perfPerformance improvement
refactorCode restructure, no behavior change
docsDocumentation only
testTest additions or corrections
ciCI/CD pipeline changes
choreRoutine maintenance
revertRevert a previous commit
### Rules - Subject line: imperative mood, ≤ 72 characters, no trailing period - Breaking changes: use `!` suffix (`feat!:`) **and** add a `BREAKING CHANGE:` footer - `Co-authored-by` trailers must credit a **real human** — including yourself as the pull request author. **Never credit a bot, AI agent, or automation as a co-author.** `z-shell/.github` and `z-shell/zi` enforce this in CI; the other repositories rely on the author until their own check is live To tidy commits before opening a PR: ```sh # For zi ordinary work only, replace main with next git rebase -i $(git merge-base HEAD main) ``` ## What Not to Add - `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.cursorrules`, or any AI-specific config files - Secrets, credentials, or API tokens of any kind - Binary files unrelated to the project ## Pull Request Checklist Before opening a PR: - Branched from current `main` (or `next` for ordinary `zi` work) - Pull request targets `main` (or `next` for ordinary `zi` work) - Commits follow Conventional Commits - No secrets or AI config files added - Relevant docs or tests updated - All CI checks pass ## Keeping Your Fork Up to Date ```sh git fetch upstream # For zi ordinary work only, replace main with next git rebase upstream/main ``` ## Discussion First Before starting significant work, [open an issue](https://github.com/z-shell/zi/issues/new/choose) to discuss the change. This avoids duplicated effort and ensures alignment with the project roadmap. --- --- title: 📋 Project Management description: How GitHub issues and Project 28 track work across the organization — workflow, labels, and triage. canonical_url: https://wiki.zshell.dev/community/contributing/project_management markdown_url: https://wiki.zshell.dev/community/contributing/project_management/index.md locale: en source_path: community/00_contributing/05_project_management.mdx --- # 📋 Project Management ![](https://wiki.zshell.dev/img/svg/community/contributing-project-management.svg) ## Project Tracker GitHub issues and pull requests are the authoritative record for all work in the Z-Shell organization. Open issues are tracked across repositories in **Z-shell Delivery**, GitHub Project 28: [ Open Z-shell Delivery → ](https://github.com/orgs/z-shell/projects/28) The project board is the cross-repository execution view: status, priority, impact, effort, and dependencies. It does not replace the owning repository's issue thread, which remains where discussion, implementation, and review happen. The board separates workstreams into views so unrelated work does not compete for attention:
ViewContents
Human deliveryOrdinary bugs, features, maintenance, and documentation
AutomationBot dashboards and recurring automation records
Dependency maintenanceRoutine dependency updates and dashboards
SecuritySecurity work requiring maintainer attention
AdministrativeOrganization and repository governance
### What Happens After You File A maintainer triages the issue: classifying it, setting the board fields, and linking it to related work. You do not need to set any of this yourself. Related issues are connected using GitHub's native parent and sub-issue relationships; blockers use native issue dependencies, so if your issue is waiting on something else that link is visible on the issue itself. Pull requests appear on the board once they are linked to a tracked issue or are ready for review. Closed and merged items move to `Done`. The full maintainer procedure lives in the [triage runbook](https://github.com/z-shell/.github/blob/main/runbooks/triage.md). ### Capturing Deferred Work Do not leave postponed work only in local diffs, review notes, or a chat thread. Create or update an issue in the repository that owns the work so it stays visible. When splitting or deferring work: 1. Create **one issue per logical task** so each can be prioritized and completed independently. 2. Include enough context for someone else to resume later: the observed problem, why it matters, relevant files, and known constraints. 3. Use the canonical labels described below instead of repo-local tracking conventions. Do not create standalone project-only items for a deliverable that belongs in a repository issue. ### Priority Maintainers set priority during triage; contributors do not need to. If your issue is urgent, say why in the description rather than setting a field — the justification is what drives the decision. The bands and their meanings are defined in the [triage runbook](https://github.com/z-shell/.github/blob/main/runbooks/triage.md) in `z-shell/.github`. ### Where to File Always open issues and pull requests in the repository that owns the work. That thread is the authoritative record. Maintainers additionally mirror a small amount of cross-repository, strategic, release-blocking, or security-sensitive work into an internal planning tool. That mirror is a convenience view for maintainers and is **not** a source of truth — nothing there replaces the GitHub issue, and contributors never need to consult it. * * * ## Labels All repositories in the `z-shell` organization share one canonical label set. A maintainer applies labels during triage — you do not need to set them yourself, though knowing what they mean helps you read an issue's state. Canonical source The authoritative list, including colors and descriptions, is [`lib/labels.yml`](https://github.com/z-shell/.github/blob/main/lib/labels.yml) in `z-shell/.github`. That file is the source of truth; this page summarizes it rather than restating it, so the two cannot drift apart. Namespaced labels are written **without a space** after the colon — `type:bug`, not `type: bug`. Spaced variants are legacy and are being retired. ### What the namespaces mean
NamespaceAnswersExamples
type:What kind of work is this?type:bug, type:feature, type:docs, type:question, type:maintenance
area:Which part of the ecosystem does it touch?area:zi, area:plugin, area:annex, area:package, area:docs, area:ci
status:What is blocking progress?status:triage, status:blocked
priority:How urgently is it needed?priority:high
Standalone modifiers carry no namespace: `regression`, `security`, `performance`, `breaking-change`, `needs-info`, `duplicate`, `invalid`, and `wontfix`. ### Labels worth watching as a contributor
LabelMeaning
good first issueWell-scoped starter task — a good place to begin
help wantedMaintainers would welcome outside help
needs-infoWaiting on more detail before work can continue
status:blockedCannot proceed until an external dependency or decision changes
### Label Rollout Creating and syncing labels across repositories is a maintainer operation. It is documented in the [`labels.md` runbook](https://github.com/z-shell/.github/blob/main/runbooks/labels.md) in `z-shell/.github`, together with the migration path for legacy names. --- --- title: 🏆 Contributors description: Project contributors canonical_url: https://wiki.zshell.dev/community/contributors markdown_url: https://wiki.zshell.dev/community/contributors/index.md locale: en source_path: community/99_contributors.mdx --- # 🏆 Contributors ![Zsh Logo](https://wiki.zshell.dev/img/zsh/artwork/png/color_horizontal_icon.png)![Zsh Logo](https://wiki.zshell.dev/img/zsh/artwork/png/white_horizontal_icon.png) Contributions make the open-source community an amazing place to learn, inspire, and create. Any contributions you make will benefit everybody else and are greatly appreciated, our target support each other, and the projects we love 💞. > To participate or support the project consider [joining](https://github.com/z-shell/.github/issues/new?assignees=&labels=%F0%9F%91%A5+member&template=05_membership.yml&title=team%3A+) 📝, [translating](https://translate.zshell.dev/) 🌐, and sharing 📢. ## General

Salvydas Lukosius

onokatio

Omelet

Sai

William Cooper

Farzat07
## Content

Sebastian

Callista Chang

signed-log

0xMRTT
## Translations

Colerar

Dongsen

nakayama900

awarewen

Kazuma Miebori

syrinka
## Participation

Benoit de Chezelles

Caleb Cushing

Kritiqual
--- --- title: 🔺 Quick Introduction description: The Gallery of Invocations canonical_url: https://wiki.zshell.dev/community/gallery/collection markdown_url: https://wiki.zshell.dev/community/gallery/collection/index.md locale: en source_path: community/05_gallery/collection/01_collection.mdx --- # 🔺 Quick Introduction info - Recommended to familiarize with [standard syntax](https://wiki.zshell.dev/docs/guides/syntax/standard/index.md). - Additional installation methods: [meta-plugins](https://wiki.zshell.dev/ecosystem/annexes/meta-plugins/index.md), [packages](https://wiki.zshell.dev/ecosystem/packages/synopsis/index.md). - Some installations may require additional functionally, it can be done by installing required [annexes](https://wiki.zshell.dev/ecosystem/annexes/overview/index.md): ```zi zi light-mode for z-shell/z-a-meta-plugins @annexes ``` note Is an excellent tool you like not included in the gallery? Use the "Edit this page" at the bottom or links below for additional support. - [Issue report](https://github.com/z-shell/zi/issues/new/choose) - [Suggestions](https://github.com/orgs/z-shell/discussions/new) Any contributions you make will benefit everybody else and are greatly appreciated. ## Prefixed header tags For the [search](https://wiki.zshell.dev/search?q=GH-R), prefixed tags help as a minimal description of the visible results.
TagType
GH-RGitHub release
COMPCompletion
ZSService
SCScript
BBuild
RARust Annex
THPThemes/Prompts
--- --- title: 🔺 Completions description: The Collection of Completions canonical_url: https://wiki.zshell.dev/community/gallery/collection/completions markdown_url: https://wiki.zshell.dev/community/gallery/collection/completions/index.md locale: en source_path: community/05_gallery/collection/02_completions.mdx --- # 🔺 Completions caution It is highly recommended to check the repository contents before installing. This may help to install it correctly and prevent potential security risks. Do not hesitate to [report any issues](https://github.com/z-shell/zi/issues/new/choose) you encounter. info Related: 1. [Completions managment](https://wiki.zshell.dev/docs/guides/commands/index.md#completions-management) 2. [Calling compinit without turbo mode](https://wiki.zshell.dev/docs/guides/commands/index.md#calling-compinit-without-turbo-mode) 3. [Calling compinit with turbo mode](https://wiki.zshell.dev/docs/guides/commands/index.md#calling-compinit-with-turbo-mode) 4. [Ice modifiers/completions](https://wiki.zshell.dev/docs/guides/syntax/ice-modifiers/index.md#completions) 5. [Ice: src'…', pick'…', multisrc'…'](https://wiki.zshell.dev/docs/guides/syntax/standard/index.md#src-pick-multisrc) tip Create your own syntax e.g: > - The ver'main' - allows selecting a specific version or branch. > - It's optional and can be removed if not required. ```zi z_lucid() { zi ice lucid ver'main' "$@" } zi0a() { z_lucid wait'0a' "$@" } zi_completion() { zi0a as'completion' blockf "$@" } ``` tip Then load as: ```zi zi_completion has'…' zi snippet … zi_completion has'…' pick'…' from'…' zi light … zi_completion has'…' zi snippet … ``` ### COMP: [alacritty/alacritty](https://github.com/alacritty/alacritty/blob/master/extra/completions/_alacritty) ```zi zi ice lucid wait as'completion' blockf has'alacritty' zi snippet https://github.com/alacritty/alacritty/blob/master/extra/completions/_alacritty ``` ### COMP: [Aloxaf/fzf-tab](https://github.com/Aloxaf/fzf-tab) ```zi zi ice lucid wait has'fzf' zi light Aloxaf/fzf-tab ``` ### COMP: [beetbox/beets](https://github.com/beetbox/beets/blob/master/extra/_beet) ```zi zi ice lucid wait as'completion' blockf has'beet' zi snippet https://github.com/beetbox/beets/blob/master/extra/_beet ``` ### COMP: [bugaevc/wl-clipboard](https://github.com/bugaevc/wl-clipboard/tree/master/completions/zsh/) ```zi zi ice lucid wait as'completion' blockf has'wl-copy' zi snippet https://github.com/bugaevc/wl-clipboard/blob/master/completions/zsh/_wl-copy zi ice lucid wait as'completion' blockf has'wl-paste' zi snippet https://github.com/bugaevc/wl-clipboard/blob/master/completions/zsh/_wl-paste ``` ### COMP: [BurntSushi/ripgrep/rg](https://github.com/BurntSushi/ripgrep/blob/master/crates/core/flags/complete/rg.zsh) ```zi zi ice lucid wait as'completion' blockf has'rg' mv'rg.zsh -> _rg' zi snippet https://github.com/BurntSushi/ripgrep/blob/master/crates/core/flags/complete/rg.zsh ``` ### COMP: [dbrgn/tealdeer](https://github.com/dbrgn/tealdeer/blob/main/completion/zsh_tealdeer) ```zi zi ice lucid wait as'completion' blockf has'tldr' mv'zsh_tealdeer -> _tldr' zi snippet https://github.com/dbrgn/tealdeer/blob/main/completion/zsh_tealdeer ``` ### COMP: [docker/cli](https://github.com/docker/cli) ```zi zi ice as"completion" zi snippet https://github.com/docker/cli/blob/master/contrib/completion/zsh/_docker ``` ### COMP: [flatpak/flatpak](https://github.com/flatpak/flatpak/blob/master/completion/_flatpak) ```zi zi ice lucid wait as'completion' blockf has'flatpak' zi light https://github.com/flatpak/flatpak/blob/master/completion/_flatpak ``` ### COMP: [git/git](https://github.com/git/git/blob/master/contrib/completion/git-completion.zsh) ```zi zi ice lucid wait as'completion' blockf mv'git-completion.zsh -> _git' zi snippet https://github.com/git/git/blob/master/contrib/completion/git-completion.zsh ``` ### COMP: [greymd/tmux-xpanes](https://github.com/greymd/tmux-xpanes) ```zi zi ice lucid wait as'completion' blockf has'tmux' pick'completion/zsh' zi light greymd/tmux-xpanes ``` ### COMP: [jarun/Buku](https://github.com/jarun/Buku/blob/master/auto-completion/zsh/_buku) ```zi zi ice lucid wait as'completion' blockf has'buku' zi snippet https://github.com/jarun/Buku/blob/master/auto-completion/zsh/_buku ``` ### COMP: [mpv-player/mpv](https://github.com/mpv-player/mpv/blob/master/etc/_mpv.zsh) ```zi zi ice lucid wait as'completion' blockf has'mpv' zi snippet https://github.com/mpv-player/mpv/blob/master/etc/_mpv.zsh ``` ### COMP: [ohmyzsh/rust](https://github.com/ohmyzsh/ohmyzsh/blob/master/plugins/rust/_rustc) ```zi zi ice lucid wait as'completion' blockf has'rustc' zi snippet https://github.com/ohmyzsh/ohmyzsh/blob/master/plugins/rust/_rustc ``` ### COMP: [oven-sh/bun](https://github.com/oven-sh/bun/blob/main/completions/bun.zsh) ```zi zi ice lucid wait as'completion' blockf has'bun' zi snippet https://github.com/oven-sh/bun/blob/main/completions/bun.zsh ``` ### COMP: [rust-lang/cargo](https://github.com/rust-lang/cargo/blob/master/etc/_cargo) ```zi zi ice lucid wait as'completion' blockf has'cargo' zi snippet https://github.com/rust-lang/cargo/blob/master/etc/_cargo ``` ### COMP: [srijanshetty/zsh-pandoc-completion](https://github.com/srijanshetty/zsh-pandoc-completion) ```zi zi ice lucid wait as'completion' blockf has'pandoc' zi light srijanshetty/zsh-pandoc-completion ``` ### COMP: [TheLocehiliosan/yadm](https://github.com/yadm-dev/yadm/blob/master/completion/zsh/_yadm) ```zi zi ice lucid wait as'completion' blockf has'yadm' zi snippet https://github.com/yadm-dev/yadm/blob/master/completion/zsh/_yadm ``` ### COMP: [x-motemen/ghq](https://github.com/x-motemen/ghq/blob/master/misc/zsh/_ghq) ```zi zi ice lucid wait as'completion' blockf has'ghq' zi snippet https://github.com/x-motemen/ghq/blob/master/misc/zsh/_ghq ``` ### COMP: [zchee/zsh-completions](https://github.com/zchee/zsh-completions) ```zi zi ice lucid wait as'completion' blockf zi light zchee/zsh-completions ``` ### [ajeetdsouza/zoxide](https://github.com/ajeetdsouza/zoxide/blob/main/contrib/completions/_zoxide) ```zi zi ice wait lucid as'completion' blockf has'zoxide' zi snippet https://github.com/ajeetdsouza/zoxide/blob/main/contrib/completions/_zoxide ``` ### COMP: [zsh-users/zsh-completions](https://github.com/zsh-users/zsh-completions) ```zi zi ice lucid wait as'completion' zi light zsh-users/zsh-completions ``` ### COMP: Local ```zi zi ice lucid wait as'completion' blockf has'pip' zi snippet "$SHELL_COMMON/zsh/completions/_pip" zi ice lucid wait as'completion' blockf has'poetry' zi snippet "$SHELL_COMMON/zsh/completions/_poetry" zi ice lucid wait has'thefuck' zi snippet "$SHELL_COMMON/zsh/thefuck/thefuck.sh" zi ice lucid wait zi snippet "$XDG_CONFIG_HOME/less/less_termcap.sh" zi ice lucid wait pick'aliases.sh' \ multisrc'functions_ghq.sh pash.sh functions.sh aliases_private.sh' zi light "$SHELL_COMMON" zi ice lucid wait has'broot' zi snippet "$XDG_CONFIG_HOME/broot/launcher/bash/br" ``` --- --- title: 🔺 Plugins description: The Collection of Plugins canonical_url: https://wiki.zshell.dev/community/gallery/collection/plugins markdown_url: https://wiki.zshell.dev/community/gallery/collection/plugins/index.md locale: en source_path: community/05_gallery/collection/06_plugins.mdx --- # 🔺 Plugins info Related: 1. [Usage: Turbo and Lucid](https://wiki.zshell.dev/docs/getting_started/overview/index.md#turbo-mode--lucid) 2. [Syntax: The `for` syntax](https://wiki.zshell.dev/docs/guides/syntax/for/index.md) 3. [Guides: Standard Syntax](https://wiki.zshell.dev/docs/guides/syntax/standard/index.md) 4. [Guides: Ice Modifiers](https://wiki.zshell.dev/docs/guides/syntax/ice-modifiers/index.md) 5. [Compiling programs](https://wiki.zshell.dev/docs/guides/syntax/standard/index.md#compiling-programs) 6. [Customizing paths](https://wiki.zshell.dev/docs/guides/customization/index.md#customizing-paths) 7. [The Ice modifiers by `bin-gem-node` annex](https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md) tip Load in turbo mode and adjust loading order by appending e.g: ```zi zi ice wait'0a' lucid … zi light … zi ice wait'0b' lucid … zi light … zi ice wait'0c' lucid … zi light … ``` Create your own syntax style: - The ver'main' - allows to select specific commit, version or branch. - It's optional and can be removed if not required. ```zi z_lucid() { zi ice lucid ver'main' "$@" } zi0a() { z_lucid wait'0a' "$@" } zi0b() { z_lucid wait'0b' "$@" } zi0c() { z_lucid wait'0c' "$@" } ``` Load: ```zi zi0a zi light … zi0b zi light … zi0c zi light … ``` ## Without [for](https://wiki.zshell.dev/docs/guides/syntax/for/index.md) syntax ### SC: [trapd00r/LS\_COLORS](https://github.com/trapd00r/LS_COLORS) ```zi zi ice wait lucid reset \ atclone"[[ -z \${commands[dircolors]} ]] && local P=g \${P}sed -i '/DIR/c\DIR 38;5;63;1' LS_COLORS \${P}dircolors -b LS_COLORS >! clrs.zsh" \ atpull'%atclone' pick"clrs.zsh" nocompile'!' \ atload'zstyle ":completion:*:default" list-colors "${(s.:.)LS_COLORS}";' zi light trapd00r/LS_COLORS ``` ### SC: [paoloantinori/hhighlighter](https://github.com/paoloantinori/hhighlighter) ```zi zi ice wait lucid pick"h.sh" zi light paoloantinori/hhighlighter ``` ### SC: [wfxr/forgit](https://github.com/wfxr/forgit) ```zi zi ice wait lucid zi load wfxr/forgit ``` ### SC: [urbainvaes/fzf-marks](https://github.com/urbainvaes/fzf-marks) ```zi zi ice wait lucid zi load urbainvaes/fzf-marks ``` ### SC: [hlissner/zsh-autopair](https://github.com/hlissner/zsh-autopair) ```zi zi ice wait lucid pick'autopair.zsh' zi load hlissner/zsh-autopair ``` ### SC: [voronkovich/gitignore.plugin.zsh](https://github.com/voronkovich/gitignore.plugin.zsh) ```zi zi ice wait lucid zi load voronkovich/gitignore.plugin.zsh ``` ### SC: [xPMo/zsh-toggle-command-prefix](https://github.com/xPMo/zsh-toggle-command-prefix) ```zi zi ice wait lucid zi light xPMo/zsh-toggle-command-prefix ``` ### SC: [leonjza/history-here](https://github.com/leonjza/history-here) ```zi zi ice wait lucid zi light leonjza/history-here ``` ### SC: [hkbakke/bash-insulter](https://github.com/hkbakke/bash-insulter) ```zi zi ice wait lucid pick'src/bash.command-not-found' zi light hkbakke/bash-insulter ``` ### SC: [leophys/zsh-plugin-fzf-finder](https://github.com/leophys/zsh-plugin-fzf-finder) ```zi zi ice wait lucid has'fzf' pick'fzf-finder.plugin.zsh' zi light leophys/zsh-plugin-fzf-finder ``` ### SC: [autosuggestions](https://github.com/zsh-users/zsh-autosuggestions), [fast-syntax-highlighting](https://github.com/z-shell/F-Sy-H) ```zi zi ice wait lucid atinit"ZI[COMPINIT_OPTS]=-C; zicompinit; zicdreplay" zi light z-shell/F-Sy-H zi ice wait lucid atload"!_zsh_autosuggest_start" zi load zsh-users/zsh-autosuggestions ``` ### SC: [z-shell/zsh-github-issues](https://github.com/z-shell/zsh-github-issues) ```zi zi ice lucid id-as"GitHub-notify" \ on-update-of"~/.cache/zsh-github-issues/new_titles.log" \ notify"New issue: $NOTIFY_MESSAGE" zi light z-shell/zsh-github-issues ``` ### SC: [z-shell/declare-zsh](https://github.com/z-shell/declare-zsh) ```zi zi ice wait lucid zi load z-shell/declare-zsh ``` ### SC: [z-shell/zsh-navigation-tools](https://github.com/z-shell/zsh-navigation-tools) ```zi zi ice wait lucid zi load z-shell/zsh-navigation-tools ``` ### SC: [z-shell/H-S-MW](https://github.com/z-shell/H-S-MW) ```zi zstyle ":history-search-multi-word" page-size "11" zi ice wait lucid zi load z-shell/H-S-MW ``` ### SC: [z-shell/zui](https://github.com/z-shell/zui), [z-shell/zi-crasis](https://github.com/z-shell/zi-crasis) ```zi zi ice wait lucid zi load z-shell/zui zi ice wait"[[ -n ${ZLAST_COMMANDS[(r)cra*]} ]]" lucid zi load z-shell/zi-crasis ``` ### SC: [z-shell/zredis](https://github.com/z-shell/zredis) ```zi zstyle ":plugin:zredis" configure_opts "--without-tcsetpgrp" zstyle ":plugin:zredis" cflags "-Wall -O2 -g -Wno-unused-but-set-variable" zi ice wait lucid atload"ztie -d db/redis -a 127.0.0.1:4815/5 -zSL main rdhash" zi load z-shell/zredis ``` ## With [for](https://wiki.zshell.dev/docs/guides/syntax/for/index.md) syntax ### SC: [z-shell/zsh-eza](https://github.com/z-shell/zsh-eza) ```zi zi wait lucid for \ has'eza' atinit'AUTOCD=1' \ z-shell/zsh-eza ``` ### SC: [z-shell/zsh-zoxide](https://github.com/z-shell/zsh-zoxide) ```zi zi has'zoxide' light-mode for \ z-shell/zsh-zoxide ``` ### GH-R: [pemistahl/grex](https://github.com/pemistahl/grex) ```zi zi wait lucid for as"command" from"gh-r" sbin"grex" \ pemistahl/grex ``` ### GH-R: [ahmetb/kubectx](https://github.com/ahmetb/kubectx) ```zi zi wait lucid for as"command" from"gh-r" \ bpick"kubectx;kubens" sbin"kubectx;kubens" \ ahmetb/kubectx ``` ### B: [stedolan/jq](https://github.com/stedolan/jq) ```zi zi wait lucid for if"(( ! ${+commands[jq]} ))" as"null" \ atclone"autoreconf -fi && ./configure --with-oniguruma=builtin && make \ && ln -sfv $PWD/jq.1 $ZI[MAN_DIR]/man1" sbin"jq" \ stedolan/jq ``` ### GH-R: [github/git-sizer](https://github.com/github/git-sizer) ```zi zi wait lucid for \ as"command" from"gh-r" sbin"git-sizer" \ @github/git-sizer ``` --- --- title: 🔺 Programs description: The Collection of Programs canonical_url: https://wiki.zshell.dev/community/gallery/collection/programs markdown_url: https://wiki.zshell.dev/community/gallery/collection/programs/index.md locale: en source_path: community/05_gallery/collection/03_programs.mdx --- # 🔺 Programs caution It is highly recommended to check the repository contents before installing. This may help to install it correctly and prevent potential security risks. Do not hesitate to [report any issues](https://github.com/z-shell/zi/issues/new/choose) you encounter. Related 1. [Overview: as'program'](https://wiki.zshell.dev/docs/getting_started/overview/index.md#about-asprogram) 2. [Turbo and lucid](https://wiki.zshell.dev/docs/getting_started/overview/index.md#turbo-mode--lucid) 3. [The "for" syntax](https://wiki.zshell.dev/docs/guides/syntax/for/index.md) 4. [The "make" syntax](https://wiki.zshell.dev/docs/guides/syntax/standard/index.md#utilizing-make) 5. [Standard syntax](https://wiki.zshell.dev/docs/guides/syntax/standard/index.md) 6. [Ice modifiers](https://wiki.zshell.dev/docs/guides/syntax/ice-modifiers/index.md) 7. [Compiling programs](https://wiki.zshell.dev/docs/guides/syntax/standard/index.md#compiling-programs) 8. [Customizing paths](https://wiki.zshell.dev/docs/guides/customization/index.md#customizing-paths) 9. [Ice modifiers: "bin-gem-node" annex](https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md) Secure with a specific version The `ver'…'` - allows to select a specific version, branch, or commit hash, also known as [permalink](https://docs.github.com/en/repositories/working-with-files/using-files/getting-permanent-links-to-files). > It is recommended but not required (HEAD branch auto-selected). Example: ```zi zi wait lucid for \ ver'88f3dae4f5391db589257ea069ab8fe4717c22c6' \ z-shell/F-Sy-H ``` Style your syntax Command wrap example for cleaner or preferred syntax. ```zi z_lucid() { zi ice lucid ver'master' "$@" } zi0a() { z_lucid wait'0a' "$@" } zi_program() { zi0a as'program' "$@" } ``` Then load as: ```zi zi_program has'…' zi light … zi_program has'…' pick'…' from'…' zi light … zi_program has'…' zi light … ``` ## Without [for](https://wiki.zshell.dev/docs/guides/syntax/for/index.md) syntax ### GH-R: [asciinema/agg](https://github.com/asciinema/agg) `agg` is a command-line tool for generating animated `GIF` files from asciicast v2 files produced by [asciinema terminal recorder](https://github.com/asciinema/asciinema). - Default - Annex ```zi zi ice has'asciinema' as'program' from'gh-r' \ mv'agg* -> agg' pick'agg' zi light asciinema/agg ``` Install using the [bin-gem-node](https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md) annex. ```zi zi ice has'asciinema' as'program' from'gh-r' sbin'agg* -> agg' zi light asciinema/agg ``` ### GH-R: [dandavison/delta](https://github.com/dandavison/delta) ```zi zi ice wait lucid as'program' from'gh-r' sbin'**/delta -> delta' zi light dandavison/delta ``` ### GH-R: [denisidoro/navi](https://github.com/denisidoro/navi) ```zi zi ice lucid wait as'program' from"gh-r" has'fzf' zi light denisidoro/navi ``` ### GH-R: [junegunn/fzf](https://github.com/junegunn/fzf) ```zi zi ice from'gh-r' as'program' zi light junegunn/fzf ``` ### GH-R: [sharkdp/fd](https://github.com/sharkdp/fd) ```zi zi ice from'gh-r' as'program' mv'fd* fd' sbin'**/fd(.exe|) -> fd' zi light @sharkdp/fd ``` ### GH-R: [sharkdp/bat](https://github.com/sharkdp/bat) ```zi zi ice from'gh-r' as'program' mv'bat* bat' sbin'**/bat(.exe|) -> bat' zi light @sharkdp/bat ``` ### GH-R: [sharkdp/hexyl](https://github.com/sharkdp/hexyl) ```zi zi ice from'gh-r' as'program' mv'hexyl* hexyl' sbin'**/hexyl(.exe|) -> hexyl' zi light @sharkdp/hexyl ``` ### GH-R: [sharkdp/hyperfine](https://github.com/sharkdp/hyperfine) ```zi zi ice from'gh-r' as'program' mv"hyperfine* hyperfine" sbin"**/hyperfine(.exe|) -> hyperfine" zi light @sharkdp/hyperfine ``` ### GH-R: [sharkdp/vivid](https://github.com/sharkdp/vivid) ```zi zi ice from'gh-r' as'program' mv'vivid* vivid' sbin'**/vivid(.exe|) -> vivid' zi light @sharkdp/vivid ``` ### GH-R: [ogham/exa](https://github.com/ogham/exa) ```zi zi ice from'gh-r' as'program' sbin'**/exa -> exa' atclone'cp -vf completions/exa.zsh _exa' zi light ogham/exa ``` ### GH-R: [docker/compose](https://github.com/docker/compose) ```zi zi ice from"gh-r" as'program' mv'docker* -> docker-compose' zi light docker/compose ``` ### GH-R: [neovim/neovim](https://github.com/neovim/neovim) ```zi zi ice as'program' from'gh-r' \ bpick'nvim-linux64.tar.gz' sbin'**/bin/nvim -> nvim' zi light neovim/neovim ``` ### GH-R: [direnv/direnv](https://github.com/direnv/direnv) ```zi zi ice as'program' from'gh-r' mv'direnv* -> direnv' zi light direnv/direnv ``` ### GH-R: [mvdan/sh](https://github.com/mvdan/sh) ```zi zi ice as'program' from'gh-r' mv'shfmt* -> shfmt' zi light mvdan/sh ``` ### GH-R: [b4b4r07/gotcha](https://github.com/babarot/gotcha) ```zi zi ice as'program' from'gh-r' mv'gotcha_* -> gotcha' zi light b4b4r07/gotcha ``` ### GH-R: [ajeetdsouza/zoxide](https://github.com/ajeetdsouza/zoxide) ```zi zi ice as'program' from'gh-r' pick'zoxide' \ atclone'ln -s completions/_zoxide -> _zoxide; cp man/man1/*.1 $ZI[MAN_DIR]/man1; ./zoxide init zsh --cmd x > init.zsh' \ atpull'%atclone' src'init.zsh' nocompile'!' zi light ajeetdsouza/zoxide ``` ### SC: [z-shell/revolver](https://github.com/z-shell/src/blob/d38202ee20efeec42900ce26f747a3024028845f/public/zsh/snippets/revolver) ```zi zi ice wait lucid as'program' pick'revolver' zi light z-shell/revolver ``` ### SC: [z-shell/zunit](https://github.com/z-shell/zunit) ```zi zi ice wait lucid as'program' pick'zunit' atclone'./build.zsh' atpull'%atclone' zi load z-shell/zunit ``` ### SC: [Osse/git-scripts/git-unique](https://github.com/Osse/git-scripts/blob/master/git-unique) ```zi zi ice as'program' id-as'git-unique' pick'git-unique' zi snippet https://github.com/Osse/git-scripts/blob/master/git-unique ``` ### SC: [mfaerevaag/wd](https://github.com/mfaerevaag/wd) ```zi zi ice wait lucid as'program' cp'wd.sh -> wd' \ mv'_wd.sh -> _wd' atpull'!git reset --hard' pick'wd' zi light mfaerevaag/wd ``` ### SC: [z-shell/zsh-diff-so-fancy](https://github.com/z-shell/zsh-diff-so-fancy) ```zi zi ice wait lucid as'program' pick'bin/git-dsf' zi load z-shell/zsh-diff-so-fancy ``` ### SC: [obihann/archey-osx](https://github.com/obihann/archey-osx) ```zi zi ice wait lucid as'program' pick'bin/archey' zi light obihann/archey-osx ``` ### SC: [eth-p/bat-extras](https://github.com/eth-p/bat-extras) ```zi zi ice lucid wait as'program' has'bat' pick'src/*' zi light eth-p/bat-extras ``` ### SC: [paulirish/git-open](https://github.com/paulirish/git-open) ```zi zi ice lucid wait as'program' has'git' \ atclone"cp git-open.1.md $ZI[MAN_DIR]/man1/git-open.1" atpull'%atclone' zi light paulirish/git-open ``` ### SC: [LuRsT/hr](https://github.com/LuRsT/hr) ```zi zi ice lucid wait as'program' atclone"cp hr.1 $ZI[MAN_DIR]/man1" atpull'%atclone' zi light LuRsT/hr ``` ### SC: [Seirdy/stpv](https://github.com/Seirdy/stpv) ```zi zi ice lucid wait as'program' has'fzf' pick'fzfp' zi light Seirdy/stpv ``` ```zi zi ice lucid wait as'program' has'ueberzug' pick'stpvimg' zi light Seirdy/stpv ``` ```zi zi ice lucid wait as'program' pick'stpv' zi light Seirdy/stpv ``` ### SC: [exiftool/exiftool](https://github.com/exiftool/exiftool) ```zi zi ice lucid wait as'program' has'perl' has'convert' pick'exiftool' zi light exiftool/exiftool ``` ### SC: [smxi/inxi](https://github.com/smxi/inxi) ```zi zi ice if'[ -z "$SSH_CONNECTION" ]' lucid wait \ as'program' has'perl' pick'inxi' zi light smxi/inxi ``` ### SC: [dylanaraps/pash](https://github.com/dylanaraps/pash) ```zi zi ice lucid wait as'program' has'gpg' zi light dylanaraps/pash ``` ### SC: [hackerb9/lsix](https://github.com/hackerb9/lsix) ```zi zi ice lucid wait as'program' has'mogrify' zi light hackerb9/lsix ``` ### SC: [denilsonsa/prettyping](https://github.com/denilsonsa/prettyping) ```zi zi ice lucid wait as'program' pick'prettyping' has'ping' zi light denilsonsa/prettyping ``` ### SC: [greymd/tmux-xpanes](https://github.com/greymd/tmux-xpanes) ```zi zi ice lucid wait as'program' has'tmux' pick'bin/xpanes' zi light greymd/tmux-xpanes ``` ### SC: [DanielG/dxld-mullvad/am-i-mullvad.sh](https://github.com/DanielG/dxld-mullvad/blob/master/am-i-mullvad.sh) ```zi zi ice lucid wait as'program' has'jq' zi snippet 'https://github.com/DanielG/dxld-mullvad/blob/master/am-i-mullvad.sh' ``` ### B: [abishekvashok/cmatrix](https://github.com/abishekvashok/cmatrix) Standard syntax ```zi zi ice lucid as'program' atclone"autoreconf -i; ./configure --prefix=$ZPFX" \ atpull'%atclone' make"install" pick"$ZPFX/bin/cmatrix" zi light abishekvashok/cmatrix ``` The "for" syntax ```zi zi for as'program' atclone"autoreconf -i; ./configure --prefix=$ZPFX" \ atpull'%atclone' make"all install" pick"$ZPFX/bin/cmatrix" \ abishekvashok/cmatrix ``` ### B: [tj/git-extras](https://github.com/tj/git-extras) ```zi zi ice wait lucid as'program' pick'$ZPFX/bin/git-*' make'PREFIX=$ZPFX' nocompile zi light tj/git-extras ``` ### B: [k4rthik/git-cal](https://github.com/k4rthik/git-cal) ```zi zi ice wait lucid as'program' atclone'perl Makefile.PL PREFIX=$ZPFX' \ atpull'%atclone' make'install' pick'$ZPFX/bin/git-cal' zi light k4rthik/git-cal ``` ### B: [aaronNG/reddio](https://gitlab.com/aaronNG/reddio) ```zi zi ice lucid wait as'program' has'jq' pick'reddio' from'gitlab' zi light aaronNG/reddio ``` ### B: [TheLocehiliosan/yadm](https://github.com/yadm-dev/yadm) ```zi zi ice lucid wait as'program' has'git' pick'yadm' atclone"cp yadm.1 $ZI[MAN_DIR]/man1" atpull'%atclone' zi light TheLocehiliosan/yadm ``` ### B: [sdushantha/farge](https://github.com/sdushantha/farge) ```zi zi ice if'[[ -n "$WAYLAND_DISPLAY" ]]' lucid wait as'program' pick'farge' zi light 'sdushantha/farge' ``` ### B: [dylanaraps/neofetch](https://github.com/dylanaraps/neofetch) ```zi zi ice lucid wait as'program' pick'neofetch' \ atclone"cp neofetch.1 $ZI[MAN_DIR]/man1" atpull'%atclone' zi light dylanaraps/neofetch ``` ### B: [vim/vim](https://github.com/vim/vim) ```zi zi ice as'program' atclone'rm -f src/auto/config.cache; ./configure' \ atpull'%atclone' make pick'src/vim' zi light vim/vim ``` ### B: [direnv/direnv](https://github.com/direnv/direnv) ```zi zi ice as'program' make'!' atclone'./direnv hook zsh > zhook.zsh' \ atpull'%atclone' src'zhook.zsh' zi light direnv/direnv ``` ### B: [mptre/yank](https://github.com/mptre/yank) ```zi zi ice as'program' pick'yank' make zi light mptre/yank ``` ### B: [pyenv/pyenv](https://github.com/pyenv/pyenv) ```zi zi ice atclone'PYENV_ROOT="$PWD" ./libexec/pyenv init - > zpyenv.zsh' \ atinit'export PYENV_ROOT="$PWD"' atpull"%atclone" \ as'program' pick'bin/pyenv' src"zpyenv.zsh" nocompile'!' zi light pyenv/pyenv ``` ### B: [sdkman/sdkman-cli](https://github.com/sdkman/sdkman-cli) ```zi zi ice as'program' pick'$ZPFX/sdkman/bin/sdk' id-as'sdkman' run-atpull nocompile \ atclone'curl -s "https://get.sdkman.io?rcupdate=false" -o scr.sh; SDKMAN_DIR=$ZPFX/sdkman bash scr.sh' \ atpull'SDKMAN_DIR=$ZPFX/sdkman sdk selfupdate' \ atinit'export SDKMAN_DIR=$ZPFX/sdkman; source $ZPFX/sdkman/bin/sdkman-init.sh' zi light z-shell/0 ``` ### B: [asciinema/asciinema](https://github.com/asciinema/asciinema) ```zi zi ice as"program" wait lucid atinit"export PYTHONPATH=$ZPFX/lib/python3.10/site-packages/" \ atclone"PYTHONPATH=$ZPFX/lib/python3.10/site-packages/ python3 setup.py --quiet install --prefix $ZPFX" \ atpull"%atclone" test"0" pick"$ZPFX/bin/asciinema" zi load asciinema/asciinema ``` ### RA: Rust and [Peltoche/lsd](https://github.com/lsd-rs/lsd) ```zi zi ice rustup cargo'!lsd' id-as'lsd' as'program' nocompile zi load z-shell/0 ``` ### RA: Rust and [ogham/exa](https://github.com/ogham/exa) The `ls` shim exposing the \`exa\`\` binary ```zi zi ice rustup cargo'!exa -> ls' id-as'exa' as'program' nocompile zi load z-shell/0 ``` Shim with standard error redirected to `/dev/null` ```zi zi ice rustup cargo'!E:exa' id-as'exa' as'program' nocompile zi load z-shell/0 ``` ### RA: Rust and [ogham/exa](https://github.com/ogham/exa), [Peltoche/lsd](https://github.com/lsd-rs/lsd) ```zi zi ice rustup cargo'exa;lsd' nocompile zi load z-shell/0 ``` Expose binaries by altering $PATH: ```zi zi ice rustup cargo'exa;lsd' as'program' pick"bin/(exa|lsd)" nocompile zi load z-shell/0 ``` ### RA: Rust compiler environment Install rust and make it available globally in the system: ```zi zi ice id-as"rust" wait"0" lucid rustup as"program" pick"bin/rustc" \ atload="export nocompile CARGO_HOME=\$PWD RUSTUP_HOME=\$PWD/rustup" zi load z-shell/0 ``` ## With [for](https://wiki.zshell.dev/docs/guides/syntax/for/index.md) syntax ### GH-R: [argoproj/argo-cd](https://github.com/argoproj/argo-cd) ```zi zi light-mode for \ as'completions' atclone'./argocd* completion zsh > _argocd' \ atpull'%atclone' from'gh-r' if'[[ "$(uname -m)" == x86_64 ]]' \ sbin'argocd* -> argocd' \ argoproj/argo-cd ``` ### GH-R: [junegunn/fzf](https://github.com/junegunn/fzf) + extras ```zi zi for atclone'mkdir -p $ZPFX/{bin,man/man1}' atpull'%atclone' from'gh-r' dl' https://raw.githubusercontent.com/junegunn/fzf/master/shell/completion.zsh -> _fzf_completion; https://raw.githubusercontent.com/junegunn/fzf/master/shell/key-bindings.zsh -> key-bindings.zsh; https://raw.githubusercontent.com/junegunn/fzf/master/man/man1/fzf-tmux.1 -> $ZI[MAN_DIR]/man1/fzf-tmux.1; https://raw.githubusercontent.com/junegunn/fzf/master/man/man1/fzf.1 -> $ZI[MAN_DIR]/man1/fzf.1' \ id-as'junegunn/fzf' nocompile pick'/dev/null' sbin'fzf' src'key-bindings.zsh' \ junegunn/fzf ``` ### GH-R: [junegunn/fzf](https://github.com/junegunn/fzf), [sharkdp/fd](https://github.com/sharkdp/fd), [sharkdp/bat](https://github.com/sharkdp/bat), [ogham/exa](https://github.com/ogham/exa) ```zi zi from"gh-r" as"null" for \ sbin"fzf" junegunn/fzf \ sbin"**/fd" @sharkdp/fd \ sbin"**/bat" @sharkdp/bat \ sbin"**/exa -> exa" atclone"cp -vf completions/exa.zsh _exa" ogham/exa ``` ### SC: [z-shell/revolver](https://github.com/z-shell/src/blob/d38202ee20efeec42900ce26f747a3024028845f/public/zsh/snippets/revolver), [z-shell/zunit](https://github.com/z-shell/zunit) ```zi zi wait lucid for as'program' \ atclone'ln -sfv revolver.zsh-completion _revolver' \ atpull'%atclone' pick'revolver' \ @z-shell/revolver \ as'completion' atclone'./build.zsh; ln -sfv zunit.zsh-completion _zunit' \ atpull'%atclone' sbin'zunit' \ @z-shell/zunit ``` ### SC: [tj/n](https://github.com/tj/n) ```zi zi light-mode for as'program' atinit'export N_PREFIX="$PWD/n"; \ [[ :$PATH: == *":$N_PREFIX/bin:"* ]] || PATH+=":$N_PREFIX/bin"' pick"bin/n" \ tj/n ``` ### GH-R: [pnpm/pnpm](https://github.com/pnpm/pnpm) tip - Manage [pnpm env](https://pnpm.io/cli/env) - Install required [pnpm release](https://github.com/pnpm/pnpm/releases) - See available releases in the repository. - Use `bpick` to match the most relevant part of the release name as shown in the example below. ```zi zi light-mode for id-as'pnpm' from'gh-r' bpick'*-linux-x64' as'program' \ atinit'export PNPM_HOME=$ZPFX/bin; [[ -z $NODE_PATH ]] && \ export NODE_PATH=$PWD' sbin'pnpm* -> pnpm' nocompile \ pnpm/pnpm ``` ### GH-R: [yarnpkg/yarn](https://github.com/yarnpkg/yarn) ```zi zi light-mode for from'gh-r' as'program' \ atinit'export PATH="$HOME/.yarn/bin:$PATH"' mv'yarn* -> yarn' \ pick"yarn/bin/yarn" bpick'*.tar.gz' \ yarnpkg/yarn ``` ### B: [jarun/nnn](https://github.com/jarun/nnn) ```zi zi light-mode for pick'misc/quitcd/quitcd.zsh' as'program' nocompile \ sbin make \ jarun/nnn ``` ### SC: [homebrew/brew](https://github.com/homebrew/brew) ```zi zi for as'null' depth'3' nocompletions sbin'bin/brew' \ atclone'+zi-message "{auto}Installing brew …"; ./bin/brew update --preinstall; \ ln -sf $PWD/completions/zsh/_brew $ZI[COMPLETIONS_DIR]; \ rm -f brew.zsh; ./bin/brew shellenv --dummy-arg > brew.zsh; \ zcompile brew.zsh;' \ atpull'%atclone' src'brew.zsh' \ @homebrew/brew ``` ### GH-R: [dbrgn/tealdeer](https://github.com/dbrgn/tealdeer) ```zi zi wait lucid as'program' from'gh-r' for \ mv'tealdeer* -> tealdeer' \ sbin'**/tealdeer -> tldr' \ pick'$ZPFX/bin/tldr' \ @dbrgn/tealdeer ``` ### GH-R: [koalaman/shellcheck](https://github.com/koalaman/shellcheck) ```zi zi wait lucid as'program' from'gh-r' for \ mv'shellcheck* -> shellcheck' \ sbin'**/shellcheck -> shellcheck' \ @koalaman/shellcheck ``` ### GH-R: [hadolint/hadolint](https://github.com/hadolint/hadolint) ```zi zi wait lucid as'program' from'gh-r' for \ mv'hadolint* -> hadolint' \ sbin'hadolint -> hadolint' \ pick'$ZPFX/bin/hadolint' \ @hadolint/hadolint ``` ### RA: Rust compiler environment + completions ```zi zi id-as"rust" wait=1 as=null sbin="bin/*" lucid rustup nocompile \ atload="[[ ! -f ${ZI[COMPLETIONS_DIR]}/_cargo ]] && zi creinstall -q rust; \ export CARGO_HOME=\$PWD; export RUSTUP_HOME=\$PWD/rustup" for \ z-shell/0 ``` ### B: [ytdl-org/youtube-dl](https://github.com/ytdl-org/youtube-dl) ```zi zi for as'program' nocompile'!' depth'1' \ has'python' pick'$ZPFX/bin/youtube-dl*' make'!PREFIX=$ZPFX install' \ atclone'ln -sfv youtube-dl.zsh _youtube-dl' atpull'%atclone' \ ytdl-org/youtube-dl ``` --- --- title: 🔺 Services description: The Collection of Services canonical_url: https://wiki.zshell.dev/community/gallery/collection/services markdown_url: https://wiki.zshell.dev/community/gallery/collection/services/index.md locale: en source_path: community/05_gallery/collection/05_services.mdx --- # 🔺 Services ### ZS: [z-shell/redis](https://github.com/z-shell/redis) ```zi zi ice wait lucid service"redis" zi light z-shell/redis ``` ### ZS: [z-shell/zsh-github-issues](https://github.com/z-shell/zsh-github-issues) ```zi GIT_SLEEP_TIME=700 GIT_PROJECTS=z-shell/zsh-github-issues:z-shell/zi zi ice wait lucid service"GIT" pick"zsh-github-issues.service.zsh" zi light z-shell/zsh-github-issues ``` --- --- title: 🔺 Snippets description: The Collection of Snippets canonical_url: https://wiki.zshell.dev/community/gallery/collection/snippets markdown_url: https://wiki.zshell.dev/community/gallery/collection/snippets/index.md locale: en source_path: community/05_gallery/collection/04_snippets.mdx --- # 🔺 Snippets Related: 1. [Ice: multisrc'…'](https://wiki.zshell.dev/docs/guides/syntax/standard/index.md#the-multisrc-ice) ### SC: [OMZ::lib/git.zsh](https://github.com/ohmyzsh/ohmyzsh/blob/master/lib/git.zsh) ```zi zi snippet OMZL::git.zsh ``` ```zi zi ice multisrc'git.zsh \ functions.zsh history.zsh grep.zsh' zi snippet OMZ::lib/completion.zsh ``` ### SC: [OMZ::plugin/macos](https://github.com/ohmyzsh/ohmyzsh/blob/master/plugins/macos/macos.plugin.zsh) ```zi zi snippet OMZP::macos ``` --- --- title: 🔺 Themes description: The Collection of Themes canonical_url: https://wiki.zshell.dev/community/gallery/collection/themes markdown_url: https://wiki.zshell.dev/community/gallery/collection/themes/index.md locale: en source_path: community/05_gallery/collection/07_themes.mdx --- # 🔺 Themes Related 1. [Multiple prompts](https://wiki.zshell.dev/docs/guides/customization/index.md#multiple-prompts) 2. [Automatic load/unload based on condition](https://wiki.zshell.dev/docs/getting_started/overview/index.md#automatic-condition-based---load--unload) 3. [Ice `atclone`, `atpull`, `atinit`, `atload`](https://wiki.zshell.dev/docs/guides/syntax/standard/index.md#atclone-atpull-atinit-atload) tip Zsh tweak - map colors to the nearest color in the available palette. ```zsh [[ $COLORTERM = *(24bit|truecolor)* ]] || zmodload zsh/nearcolor ``` ### THP: [romkatv/powerlevel10k](https://github.com/romkatv/powerlevel10k) tip Include at the top of `.zshrc` for powerlevel10k theme ~/.zshrc ```zsh if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" fi ``` Install fonts for powerlevel10k. It has to match your system settings as this is an example of how flexible Zi is and can install anything in a preferred way. The following snippet placed in the `.zshrc` file will: - check if conditions are met to proceed with the install - download, extract, clean not required files, and move fonts to the required directory. - update font cache. When running: `zi update` will: - if an update is available, will update the fonts. - repeat the install process to update fonts. ```zi zi ice if"[[ -d ${HOME}/.fonts/ttf ]] && [[ $OSTYPE = linux* ]]" \ id-as"meslo" from"gh-r" bpick"Meslo.zip" extract nocompile depth"1" \ atclone="rm -f *Windows*; mv -vf *.ttf ${HOME}/.fonts/ttf/; fc-cache -v -f" atpull"%atclone" zi light ryanoasis/nerd-fonts ``` Load prompt if the terminal has at least 256 colors. ```zi zi ice if"[ "${TERM##*-}" = '256color' ] || [ "${terminfo[colors]:?}" -gt 255 ]" depth=1 zi light romkatv/powerlevel10k ``` Oneliner: ```zi zi ice depth=1; zi light romkatv/powerlevel10k ``` [meta-plugins](https://wiki.zshell.dev/ecosystem/annexes/meta-plugins/index.md) with configuration wizard disabled by default: - Run manually: `p10k configure` (The file `~/.p10k.zsh` auto sourced if exists). ```zi zi light-mode for @romkatv ``` After finishing the configuration wizard last question: - "Apply changes to ~/.zshrc?" choose no - unless you know what you're doing. ```zi zi ice depth'1' atload"[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh" nocd zi light romkatv/powerlevel10k ``` ### THP: [ohmyzsh/robbyrussell](https://github.com/ohmyzsh/ohmyzsh/blob/master/themes/robbyrussell.zsh-theme) ```zi zi wait'!' lucid for OMZL::prompt_info_functions.zsh \ OMZT::robbyrussell ``` ### THP: [z-shell/zprompts](https://github.com/z-shell/zprompts) ```zi zi lucid for \ atload"!promptinit; typeset -g PSSHORT=0; \ prompt sprint3 yellow red green blue" nocd \ z-shell/zprompts ``` ### THP: [halfo/lambda-mod-zsh-theme](https://github.com/halfo/lambda-mod-zsh-theme) ```zi zi lucid for nocd \ halfo/lambda-mod-zsh-theme ``` ### THP: [geometry-zsh/geometry](https://github.com/geometry-zsh/geometry) ```zi zi lucid for atload"!geometry::prompt" \ atinit"GEOMETRY_COLOR_DIR=63 GEOMETRY_PATH_COLOR=63" nocd \ geometry-zsh/geometry ``` ### THP: [sindresorhus/pure](https://github.com/sindresorhus/pure) ```zi zi lucid for pick"/dev/null" multisrc"{async,pure}.zsh" \ atload"!prompt_pure_precmd" nocd \ sindresorhus/pure ``` Install as meta-plugin: ```zi zi light-mode for @sindresorhus/pure ``` Personalised: ```zi zi light-mode for compile'(pure|async).zsh' pick'async.zsh' src'pure.zsh' atload" \ PURE_GIT_UP_ARROW='↑'; PURE_GIT_DOWN_ARROW='↓'; PURE_PROMPT_SYMBOL='ᐳ'; PURE_PROMPT_VICMD_SYMBOL='ᐸ'; \ zstyle ':prompt:pure:prompt:success' color 'green' \ zstyle ':prompt:pure:git:action' color 'yellow'; \ zstyle ':prompt:pure:git:branch' color 'blue'; \ zstyle ':prompt:pure:git:dirty' color 'red'; \ zstyle ':prompt:pure:path' color 'cyan'" \ sindresorhus/pure ``` ### THP: [agkozak/agkozak-zsh-prompt](https://github.com/agkozak/agkozak-zsh-prompt) ```zi zi lucid nocd atinit"AGKOZAK_COLORS_PROMPT_CHAR='magenta' AGKOZAK_MULTILINE=0 \ AGKOZAK_PROMPT_CHAR=( ❯ ❯ ❮ ) AGKOZAK_USER_HOST_DISPLAY=0" for \ agkozak/agkozak-zsh-prompt ``` Install as meta-plugin: ```zi zi for @agkozak/agkozak-zsh-prompt ``` ### THP: [chauncey-garrett/zsh-prompt-garrett](https://github.com/chauncey-garrett/zsh-prompt-garrett) ```zi zi ice atload"fpath+=( \$PWD );" zi light chauncey-garrett/zsh-prompt-garrett ``` ### THP: [starship/starship](https://github.com/starship/starship) ```zi zi ice as"command" from"gh-r" \ atclone"./starship init zsh > init.zsh; ./starship completions zsh > _starship" \ atpull"%atclone" src"init.zsh" zi light starship/starship ``` ### THP: [robobenklein/zinc](https://github.com/robobenklein/zinc) ```zi zi ice wait'!' lucid nocompletions \ compile"{zinc_functions/*,segments/*,zinc.zsh}" \ atload'!prompt_zinc_setup; prompt_zinc_precmd' zi load robobenklein/zinc ``` ZINC git info is already async, but if you want it even faster with [gitstatus](https://github.com/romkatv/gitstatus) in turbo mode: ```zi zi ice wait'1' atload'zinc_optional_dependency_loaded' zi load romkatv/gitstatus ``` --- --- title: 🔤 Zsh Native Scripting Handbook description: A handbook covering native Zsh scripting techniques and best practices. canonical_url: https://wiki.zshell.dev/community/zsh_handbook markdown_url: https://wiki.zshell.dev/community/zsh_handbook/index.md locale: en source_path: community/02_zsh_native_scripting_handbook.mdx --- # 🔤 Zsh Native Scripting Handbook ## Information ### @ is about keeping array form How do access all array elements in a shell? The standard answer: `use @ subscript`, i.e. `${array[@]}`. However, this is the Bash & Ksh way (and with the option `KSH_ARRAYS`, Zsh also works this way, i.e. needs `@` to access the whole array). Z shell **is different**: it is a `$array` that refers to all elements anyway. There is no need for the `@` subscript. So what use has `@` in the Zsh-world? It is: "`keep array form`" or "`do not join`". When is it activated? When the user quotes the array, i.e. invokes `"$array"`, he induces *joining* of all array elements (into a single string). `@` is to have elements still quoted (so empty elements are preserved), but not joined. Two forms are available, `"$array[@]"` and `"${(@)array}"`. The first form has an additional effect – when an option `KSH_ARRAYS` is set, it indeed induces referencing to the whole array instead of a first element only. It should then use braces, i.e. `${array[@]}`, `"${array[@]}"` (`KSH_ARRAYS` requirement). In practice, if you'll use `@` as a subscript – `[@]`, not as a flag – `${(@)...}`, then you'll make the code `KSH_ARRAYS`\-compatible. ### extended\_glob Glob-flags `#b` and `#m` require `setopt extended_glob`. Patterns utilizing `~` and `^` also require it. Extended-glob is one of the main features of Zsh. ## Constructs ### Reading a file ```zsh typeset -a lines lines=( "${(@f)"$( ### Reading from stdin This topic is governed by the same principles as the previous paragraph (`Reading a file`), with the single difference that instead of the substitution `"$( ### Skipping dirname basename `dirname` and `basename` can be skipped by: ```zsh local dirname="${PWD:h}" local basename="${PWD:t}" ``` Read more: [zsh: 14 Expansion](https://zsh.sourceforge.net/Doc/Release/Expansion.html#Modifiers). ### Resolve Symlinks Symbolic links can be turned into an absolute path with: ```zsh local absolute_path="${PWD:A}" ``` ### Skipping grep ```zsh typeset -a lines; lines=( "${(@f)"$( #### Multi-line matching like with grep Suppose you have a Git repository and want to check if it contains files not under version control. You could do this in Bash style like follows: ```zsh local git_status="$(git status --porcelain)" if [[ -n "$(echo "$git_status" | \grep \^\?\?)" ]]; then echo found fi ``` Those are 3 forks: for `git status`, for `echo`, and for `grep`. This can be solved by the `:#` substitution and the `(M)` flag described above in this section (just check if the number of matched lines is greater than 0). However, there's a more direct approach: ```zsh local git_status="$(git status --porcelain)" nl=$'\n' if [[ "$git_status" = *((#s)|$nl)\?\?* ]]; then echo found fi ``` This requires `extended_glob`. The `(#s)` means: "start of the string". So `((#s)|$nl)` means "start of the string OR preceded by a new-line". If the `extended_glob` option cannot be used for some reason, this can be achieved also without it, but essentially it means that the alternative (i.e. `|`) of two versions of the pattern will have to be matched: ```zsh setopt local_options no_extended_glob local git_status="$(git status --porcelain)" nl=$'\n' if [[ "$git_status" = (\?\?*|*$nl\?\?*) ]]; then echo found fi ``` In general, multi-line matching falls into the following idiom (`extended glob` version): ```zsh local needle="??" required_preceding='[[:space:]]#' [[ "$(git status --porcelain)" = *((#s)|$nl)${~required_preceding}${needle}* ]] && echo found ``` It does a single fork (called `git` status). The `${~variable}` means (the `~` init): "the variable is holding a pattern, interpret it". All in all, instead of regular expressions we were using patterns (globs) (see [this section](#using-built-in-regular-expressions-engine)). ### Pattern matching in AND-fashion ```zsh [[ "abc xyz efg" = *abc*~^*efg* ]] && print Match found ``` The `~` is a negation -- `match \*abc* but not ...`. Then, `^` is also a negation. The effect is: `\*ABC* but not those that don't have \*efg*` which equals to: `\*ABC* but those that have also \*efg*`. This is a regular pattern and it can be used with `:#` above to search arrays, or with the `R`\-subscript flag to search hashes (`${hsh[\(R)\*pattern*]}`), etc. The inventor of those patterns is Mikael Magnusson. ### Skipping tr ```zsh typeset -A map; map=( a 1 b 2 ); text=( "ab" "ba" ) text=( ${text[@]//(#m)?/${map[$MATCH]}} ) print $text → 12 21 ``` `#m` flag enables the `$MATCH` parameter. At each `//` substitution, `$map` is queried for character-replacement. You can substitute a text variable too, just skip `[@]` and parentheses in the assignment. ### Ternary expressions with `+,-,:+,:-` substitutions ```zsh HELP="yes"; print ${${HELP:+help enabled}:-help disabled} → help enabled HELP=""; print ${${HELP:+help enabled}:-help disabled} → help disabled ``` Ternary expression is known from the `C` language but exists also in Zsh, but directly only in a math context, i.e. `\(( a = a > 0 ? b : c ))`. The flexibility of Zsh allows such expressions also in a normal context. Above is an example. `:+` is "if not empty, substitute …" `:-` is "if empty, substitute …". You can save a great number of lines of code with those substitutions, it's normally at least 4-lines `if` condition or lengthy `&&`/`||` use. ### Ternary expressions with `:#` substitution ```zsh var=abc; print ${${${(M)var:#abc}:+is abc}:-not abc} → is abc var=abcd; print ${${${(M)var:#abc}:+is abc}:-not abc} → not abc ``` A one-line "if var = x, then …, else …". Again, can spare a great amount of boring code that makes a 10-line function a 20-line one. ### Using built-in regular expressions engine ```zsh [[ "aabbb" = (#b)(a##)*(b(#c2,2)) ]] && print ${match[1]}-${match[2]} → aa-bb ``` `\##` is: "1 or more". `(#c2,2)` is: "exactly 2". A few other constructs: `#` is "0 or more", `?` is "any character", `(a|b|)` is "a or b or empty match". `#b` enables the `$match` parameters. There's also `#m` but it has one parameter `$MATCH` for whole matched text, not for any parenthesis. Zsh patterns are a custom regular expressions engine. They are slightly faster than the `zsh/regex` module (used for the `=~` operator) and don't have that dependency (regex module can be not present, e.g. in the default static build of Zsh). Also, they can be used in substitutions, for example in the `//` substitution. ### Skipping uniq ```zsh typeset -aU array; array=( a a b ); print $array → a b typeset -a array; array=( a a b ); print ${(u)array} → a b ``` Enable the `-U` flag for the array so that it guards elements to be unique, or use the `u`\-flag to make unique elements of an array. ### Skipping awk ```zsh typeset -a list; list=( "a,b,c,1,e" "p,q,r,2,t" ); print "${list[@]/(#b)([^,]##,)(#c3,3)([^,]##)*/${match[2]}}" → 1 2 ``` The pattern specifies 3 blocks of `[^,]##,` so 3 "not-comma multiple times, then comma", then the single block of "not-comma multiple times" in second parentheses -- and then replaces this with second parentheses. The result is the 4th column extracted from multiple lines of text, something `awk` is often used for. Another method is the use of the `s`\-flag. For a single line of text: ```zsh text="a,b,c,1,e"; print ${${(s:,:)text}[4]} → 1 ``` Thanks to in-substitution code-execution capabilities it's possible to use the `s`\-flag to apply it to multiple lines: ```zsh typeset -a list; list=( "a,b,c,1,e" "p,q,r,2,t" ); print "${list[@]/(#m)*/${${(s:,:)MATCH}[4]}}" → 1 2 ``` There is a problem with the `(s::)` flag that can be solved if Zsh is version `5.4` or higher: if there will be single input column, e.g. `list=( "column1" "a,b")` instead of two or more columns (i.e. `list=( "column1,column2" "a,b" )`), then `(s::)` will return **string** instead of 1-element **array**. So the index `[4]` in the above snippet will index a string, and show its 4th letter. Starting with Zsh 5.4, thanks to a patch by Bart Schaefer (`40640: the (A) parameter flag forces array result even if...`), it is possible to force **array**\-kind of result even for a single column, by adding `(A)` flag, i.e.: ```zsh typeset -a list; list=( "a,b,c,1,e" "p,q,r,2,t" "column1" ); print "${list[@]/(#m)*/${${(As:,:)MATCH}[4]}}" → 1 2 print "${list[@]/(#m)*/${${(s:,:)MATCH}[4]}}" → 1 2 u ``` Side-note: `(A)` flag is often used together with the `::=` assignment-substitution and `(P)` flag, to assign arrays and hashes by name. ### Searching arrays ```zsh typeset -a array; array=( a b " c1" d ); print ${array[(r)[[:space:]][[:alpha:]]*]} → c1 ``` `\[[:space:]]` contains unicode spaces. This is often used in conditional expression like `[[ -z ${array[(r)...]} ]]`. Note that [Skipping grep](#skipping-grep) that uses `:#` substitution can also be used to search arrays. ### Code execution in `//` substitution ```zsh append() { gathered+=( $array[$1] ); } functions -M append 1 1 append typeset -a array; array=( "Value 1" "Other data" "Value 2" ) typeset -a gathered; integer idx=0 : ${array[@]/(#b)(Value ([[:digit:]]##)|*)/$(( ${#match[2]} > 0 ? append(++idx) : ++idx ))} print $gathered → Value 1 Value 2 ``` Use of the `#b` glob flag enables math-code execution (and not only) in `/` and `//` substitutions. Implementation is very fast. ### Serializing data ```zsh typeset -A hsh deserialized; hsh=( key value ) serialized="${(j: :)${(qkv@)hsh}}" deserialized=( "${(Q@)${(z@)serialized}}" ) print ${(kv)deserialized} → key value ``` `j`\-flag means join -- by spaces, in this case. Flags `kv` mean keys and values, interleaving. Important `q`\-flag means: quote. So what is obtained is each key and value quoted, and put into a string separated by spaces. `z`\-flag means: split as if Zsh parser would split. So quoting (with backslashes, double quoting, and others) is recognized. Obtained is array `( "key" "value")` which is then de-quoted with `Q`\-flag. This yields original data, assigned to the hash `deserialized`. Use this to e.g. implement an array of hashes. Note: to be compatible with `setopt ksharrays`, use `[@]` instead of `(@)`, e.g.: `...( "${(Q)${(z)serialized[@]}[@]}" )` #### Tip: serializing with Bash ```zsh array=( key1 key2 ) printf -v serialized "%q " "${array[@]}" eval "deserialized=($serialized)" ``` This method works also with Zsh. The drawback is the use of `eval`, however, no problem may occur unless someone compromises the variable's value, but as always, `eval` should be avoided if possible. ## Real-world examples ### Testing for Git subcommand Following code checks, if there is a `git` subcommand `$mysub`: ```zsh if git help -a | grep "^ [a-z]" | tr ' ' '\n' | grep -x $mysub > /dev/null > /dev/null; then ``` Those are `4` forks. The code can be replaced according to this guide: ```zsh local -a lines_list lines_list=( ${(f)"$(git help -a)"} ) lines_list=( ${(M)${(s: :)${(M)lines_list:# [a-z]*}}:#$mysub} ) if (( ${#lines_list} > 0 )); then … fi ``` The result is just `1` fork. ### Counting unquoted-only apostrophes A project was needing this to do some Zle line-continuation tricks (when you put a backslash-\\ at the end of the line and press enters – it is the line continuation that occurs at that moment). The required functionality is: in the given string, count the number of apostrophes, but *only the unquoted ones*. This means that only apostrophes with null or an even number of preceding backslashes should be accepted into the count: ```zsh buf="word'continue\'after\\\'afterSecnd\\''afterPair" integer count=0 : ${buf//(#b)((#s)|[^\\])([\\][\\])#(\'\'#)/$(( count += ${#match[3]} ))} echo $count → 3 ``` The answer (i.e. the output) to the above presentation and example is: `3` (there are `3` unquoted apostrophes in total in the string kept in the variable `$buf`). Below follows a variation of the above snippet that doesn't use math-code execution: ```zsh buf="word'continue\'after\\\'afterSecnd\\''afterPair" buf="${(S)buf//(#b)*((#s)|[^\\])([\\][\\])#(\'\'#)*/${match[3]}}"; buf=${buf%%[^\']##} integer count=${#buf} echo $count → 3 ``` This is possible thanks to `(S)` flag – non-greedy matching, `([\\][\\])#` trick – it matches only unquoted following `(\'\'##)` characters (which are the apostrophes) and a general strategy to replace `anything-apostrope(s)` (unquoted ones) with `the-apostrope(s)` (and then count them with `${#buf}`). ## Tips and Tricks ### Parsing INI file With Zshell `extended_glob` parsing an `ini` file is an easy task. It will not result in a nested-arrays data structure (Zsh doesn't support nested hashes), but the hash keys are intuitive such as `$DB_CONF[db1__host]`. The code should be placed in a file named `read-ini-file`, in `$fpath`, and `autoload read-ini-file` should be invoked. ```zsh # $1 - path to the ini file to parse # $2 - the name of the output hash # $3 - prefix for keys in the hash # # Writes to given hash under keys built-in following way: ${3}
_field. # Values are values from the ini file. Example invocation: # # read-ini-file ./database1-setup.ini DB_CONF db1_ # read-ini-file ./database2-setup.ini DB_CONF db2_ # setopt local_options extended_glob local __ini_file="$1" __out_hash="$2" __key_prefix="$3" local IFS='' __line __cur_section="void" __access_string local -a match mbegin mend [[ ! -r "$__ini_file" ]] && { builtin print -r "read-ini-file: an ini file is unreadable ($__ini_file)"; return 1; } while read -r -t 1 __line; do if [[ "$__line" = [[:blank:]]#\;* ]]; then continue # Match "[Section]" line elif [[ "$__line" = (#b)[[:blank:]]#\[([^\]]##)\][[:blank:]]# ]]; then __cur_section="${match[1]}" # Match "string = string" line elif [[ "$__line" = (#b)[[:blank:]]#([^[:blank:]=]##)[[:blank:]]#[=][[:blank:]]#(*) ]]; then match[2]="${match[2]%"${match[2]##*[! $'\t']}"}" # severe trick - remove trailing whitespace __access_string="${__out_hash}[${__key_prefix}<$__cur_section>_${match[1]}]" : "${(P)__access_string::=${match[2]}}" fi done < "$__ini_file" return 0 ``` --- --- title: 🔍 Zsh Lint description: Start using Zsh Lint, understand which files it checks, and find focused guides. canonical_url: https://wiki.zshell.dev/community/zsh_lint markdown_url: https://wiki.zshell.dev/community/zsh_lint/index.md locale: en source_path: community/04_zsh_lint/index.mdx --- # 🔍 Zsh Lint `zsh-lint` is a standalone semantic analyzer for Zsh. Give it one or more source-file paths and it reports parser errors, correctness problems, and advisory findings in a greppable format. ## Try it in five minutes Install the current published release with an exact version: ```sh go install github.com/z-shell/zsh-lint/cmd/zsh-lint@v1.1.0 ``` Then check a file: ```sh zsh-lint path/to/script.zsh ``` If Go's binary directory is not already on your `PATH`, run the installed binary as `"$(go env GOPATH)/bin/zsh-lint"`. :::important File recognition The published CLI does not inspect a shebang, require a `.zsh` extension, scan directories, or discover files. Every explicit path is treated as Zsh source. An extensionless autoloaded function works when you name it directly, while a Bash file is still parsed as Zsh if you pass its path. ::: For parser-only corpus evaluation without lint rules, use the advanced [`zsh-lint-survey`](https://wiki.zshell.dev/community/zsh_lint/zsh-lint-parser-survey/index.md) command. ## Choose a guide [![](https://wiki.zshell.dev/img/svg/cards/packages.svg) ### Getting started Install a pinned release, lint a small script, and understand the result. Run the first check →](https://wiki.zshell.dev/community/zsh_lint/zsh-lint-getting-started/index.md)[![](https://wiki.zshell.dev/img/svg/cards/history-search.svg) ### Choosing files Learn how explicit paths, shebangs, extensions, directories, and mixed shell repositories behave. Select inputs →](https://wiki.zshell.dev/community/zsh_lint/zsh-lint-choosing-files/index.md)[![](https://wiki.zshell.dev/img/svg/cards/annexes.svg) ### Project configuration Try the unreleased configuration support for plugins and multi-profile projects. Preview main →](https://wiki.zshell.dev/community/zsh_lint/zsh-lint-project-configuration/index.md)[![](https://wiki.zshell.dev/img/svg/cards/flexible.svg) ### Rules and suppressions Read diagnostics and document intentional exceptions without hiding other rules. Handle findings →](https://wiki.zshell.dev/community/zsh_lint/zsh-lint-rules-and-suppressions/index.md)[![](https://wiki.zshell.dev/img/svg/cards/ecosystem.svg) ### CI and JSON Add a reproducible GitHub Actions check and consume structured output. Automate checks →](https://wiki.zshell.dev/community/zsh_lint/zsh-lint-ci-and-json/index.md)[![](https://wiki.zshell.dev/img/svg/cards/zi-console.svg) ### Troubleshooting Resolve install, input, parser, exit-code, and configuration surprises. Fix common problems →](https://wiki.zshell.dev/community/zsh_lint/zsh-lint-troubleshooting/index.md)[![](https://wiki.zshell.dev/img/svg/cards/speed.svg) ### Parser survey Use the advanced parser-coverage command without running lint rules. Survey a corpus →](https://wiki.zshell.dev/community/zsh_lint/zsh-lint-parser-survey/index.md)[![](https://wiki.zshell.dev/img/svg/cards/syntax-highlighting.svg) ### Published rule reference See the readable rule set generated from the latest published release. Browse rules →](https://wiki.zshell.dev/community/zsh_lint/zsh-lint-rule-reference/index.md) ## Published and unreleased behavior The stable guides describe v1.1.0. The project-configuration guide is clearly marked as an unreleased preview of `main`. Automatic file discovery and shebang-based classification are not implemented; that design is tracked in [zsh-lint issue 183](https://github.com/z-shell/zsh-lint/issues/183). Source code and development status live in [z-shell/zsh-lint](https://github.com/z-shell/zsh-lint). --- --- title: Choosing files description: Understand how Zsh Lint selects inputs and safely build an explicit file list. canonical_url: https://wiki.zshell.dev/community/zsh_lint/zsh-lint-choosing-files markdown_url: https://wiki.zshell.dev/community/zsh_lint/zsh-lint-choosing-files/index.md locale: en source_path: community/04_zsh_lint/02_choosing_files.mdx --- # Choosing files ## The rule is simple In the published v1.1.0 CLI, every positional argument is a file to parse as Zsh. The path is the selection signal.
Input signalUsed to recognize Zsh?What happens
Explicit file pathYesThe file is opened and parsed as Zsh.
#!/usr/bin/env zsh shebangNoIt is ordinary source text to the parser.
.zsh or .plugin.zsh extensionNoExtensions have no special meaning.
Extensionless function nameNo automatic detectionIt works when passed explicitly.
Directory pathNoOpening the directory fails; there is no recursive scan.
Standard inputNoThe CLI requires one or more paths.
This means both of these are accepted as inputs: ```sh zsh-lint script.zsh zsh-lint functions/example-run ``` It also means `zsh-lint deploy.sh` does not decide whether `deploy.sh` is Zsh, Bash, or POSIX shell. If you pass it, the parser applies Zsh semantics. ## Prefer a reviewed input list For a small project, list the files directly in CI. For a larger project, build a list from repository conventions and review what it selects: ```sh git ls-files -- '*.zsh' '*.plugin.zsh' git ls-files -z -- '*.zsh' '*.plugin.zsh' | xargs -0 -r zsh-lint ``` The extension patterns do not include extensionless autoloaded functions. Add known function directories explicitly, for example: ```sh find functions completions -type f -print0 | xargs -0 -r zsh-lint ``` Do not blindly lint every `*.sh` file in a mixed-shell repository. First classify whether each file is native Zsh, Bash, or portable `sh`. ## What is planned Automatic discovery and classification, including possible shebang and extension policies, require an explicit design because real Zsh projects also contain extensionless functions, startup files, fixtures, and generated code. That work is tracked in [zsh-lint issue 183](https://github.com/z-shell/zsh-lint/issues/183). --- --- title: CI and JSON output description: Run a pinned Zsh Lint release in GitHub Actions and consume JSON diagnostics. canonical_url: https://wiki.zshell.dev/community/zsh_lint/zsh-lint-ci-and-json markdown_url: https://wiki.zshell.dev/community/zsh_lint/zsh-lint-ci-and-json/index.md locale: en source_path: community/04_zsh_lint/05_ci_and_json.mdx --- # CI and JSON output ## A reproducible GitHub Actions job This example pins the actions and the linter release. Replace the file list with the reviewed Zsh inputs in your repository. ```yaml name: Zsh checks on: pull_request: push: branches: [main] permissions: contents: read jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: "1.25" - name: Install Zsh Lint run: go install github.com/z-shell/zsh-lint/cmd/zsh-lint@v1.1.0 - name: Check native syntax run: zsh -f -n -- scripts/build.zsh example.plugin.zsh - name: Run semantic analysis run: zsh-lint scripts/build.zsh example.plugin.zsh ``` The native `zsh -n` check remains useful because official Zsh semantics are authoritative when a supplemental parser has a coverage gap. ## JSON output Pass `--format=json` before the paths: ```sh zsh-lint --format=json scripts/build.zsh example.plugin.zsh > zsh-lint.json ``` The output is one versioned JSON object with sorted `diagnostics` and a `summary`: ```json { "version": 1, "diagnostics": [], "summary": { "files": 2, "diagnostics": 0, "errors": 0, "warnings": 0, "infos": 0, "hints": 0 } } ``` Parser failures appear as `parse/error` diagnostics. The exit code does not change in JSON mode: 0 means no Warning or Error, 1 means a Warning, Error, parser failure, or unreadable input, and 2 means a usage or output error. Machine consumers should reject a JSON `version` they do not understand. --- --- title: Getting started description: Install Zsh Lint v1.1.0 and run a first semantic check. canonical_url: https://wiki.zshell.dev/community/zsh_lint/zsh-lint-getting-started markdown_url: https://wiki.zshell.dev/community/zsh_lint/zsh-lint-getting-started/index.md locale: en source_path: community/04_zsh_lint/01_getting_started.mdx --- # Getting started ## What you need - Go 1.25 or a compatible version accepted by the module - A Zsh source file to inspect Zsh itself is strongly recommended for a native syntax check, but the linter binary is written in Go and does not source or execute the file it analyzes. ## Install a reproducible version ```sh go install github.com/z-shell/zsh-lint/cmd/zsh-lint@v1.1.0 ``` Confirm that your shell can find it: ```sh zsh-lint 2>&1 | head -n 1 ``` Seeing the usage line confirms the binary runs. It exits with status 2 because no input path was supplied. If the command is not found, use: ```sh "$(go env GOPATH)/bin/zsh-lint" 2>&1 | head -n 1 ``` ## Make a small input Save this as `hello.zsh`: ```zsh #!/usr/bin/env zsh emulate -R zsh print -r -- "${1:-hello}" ``` Check native Zsh syntax, then run semantic analysis: ```sh zsh -f -n -- hello.zsh zsh-lint hello.zsh ``` No output and exit status 0 means the file parsed and no warning or error was reported. A finding looks like this: ```text hello.zsh:4:13: [quoting/unquoted-var] Variable expansion should be double-quoted ``` The bracketed value is the rule ID. Use it to find rule details or write a narrow suppression. ## Check more than one file ```sh zsh-lint bin/build.zsh functions/example-run example.plugin.zsh ``` Each argument must be a readable file. Zsh Lint does not recurse through a directory or read source from standard input in v1.1.0. Next, read [Choosing files](https://wiki.zshell.dev/community/zsh_lint/zsh-lint-choosing-files/index.md) before applying the command to a mixed-language repository. --- --- title: Parser survey description: Survey parser coverage across an explicit Zsh corpus without running lint rules. canonical_url: https://wiki.zshell.dev/community/zsh_lint/zsh-lint-parser-survey markdown_url: https://wiki.zshell.dev/community/zsh_lint/zsh-lint-parser-survey/index.md locale: en source_path: community/04_zsh_lint/07_parser_survey.mdx --- # Parser survey `zsh-lint-survey` is an advanced companion command for parser evaluation. It answers whether the current parser accepts each explicit input. It does not run lint rules and should not replace `zsh-lint` in ordinary CI. ## Install and run ```sh go install github.com/z-shell/zsh-lint/cmd/zsh-lint-survey@v1.1.0 zsh-lint-survey scripts/build.zsh functions/example-run ``` The command prints parser failures in a greppable form and ends with a summary. Like the linter, it does not discover files or classify dialects. Supply a reviewed Zsh-only list. ## When to use it - Evaluate parser coverage before adopting the linter across a large corpus. - Reduce a native-Zsh construct that the supplemental parser rejects. - Track whether a known parser gap remains reproducible after an update. Always compare a suspected gap with native Zsh: ```sh zsh -f -n -- path/to/source zsh-lint-survey path/to/source ``` If the first command succeeds and the survey fails, native Zsh validity takes priority. Report the reduced example rather than changing valid code solely for the survey. --- --- title: Project configuration preview description: Try the unreleased project and source-profile configuration available on main. canonical_url: https://wiki.zshell.dev/community/zsh_lint/zsh-lint-project-configuration markdown_url: https://wiki.zshell.dev/community/zsh_lint/zsh-lint-project-configuration/index.md locale: en source_path: community/04_zsh_lint/03_project_configuration.mdx --- # Project configuration preview :::warning Unreleased feature The published v1.1.0 binary does not recognize `--config`. This page documents behavior currently on the `main` branch so early testers can provide feedback. Do not add it to release-pinned CI until a release includes the option. ::: Project configuration gives rules explicit context that one syntax tree cannot provide, such as whether a source is an executable, sourced plugin entrypoint, autoloaded function, completion, or test fixture. Version 1 does not discover a configuration file automatically, so pass `--config` on every invocation. ## Try the standalone example Build the current source checkout: ```sh git clone https://github.com/z-shell/zsh-lint.git cd zsh-lint go build -o ./zsh-lint ./cmd/zsh-lint ./zsh-lint --config examples/standalone/zsh-lint.json \ examples/standalone/script.zsh ``` The source checkout's `examples/standalone` and `examples/plugin` directories contain the validated examples used on this page. Their delivery is tracked in [zsh-lint issue 182](https://github.com/z-shell/zsh-lint/issues/182). ## Minimal configuration ```json { "version": 1, "project": { "kind": "application", "minimum_zsh": "5.8", "function_namespaces": [] }, "sources": [{"root": "script.zsh", "profile": "standalone-executable"}] } ``` Paths are relative to the directory containing the configuration. Every input must stay under that directory and match one source root. ## Source profiles
ProfileUse it for
standalone-executableA script launched as its own process.
startup-fileFiles such as .zshrc or .zprofile.
sourced-libraryPlugin entrypoints and libraries loaded into an existing shell.
autoload-functionOne function body loaded through Zsh autoload.
test-fixtureTest inputs that should not inherit production lifecycle assumptions.
The optional `completion` role is valid only with `autoload-function`. ## Plugin example ```sh ./zsh-lint --config examples/plugin/zsh-lint.json \ examples/plugin/example.plugin.zsh \ examples/plugin/functions/example-run \ examples/plugin/completions/_example \ examples/plugin/tests/example-fixture.zsh ``` Configured analysis treats the complete explicit input list as one project. Files you omit are outside cross-file validation. For the strict schema and failure contract, see the [source-adjacent project configuration contract](https://github.com/z-shell/zsh-lint/blob/main/docs/project/project-configuration.md). --- --- title: Published rule reference description: Read the Zsh Lint rules generated from the latest published release. canonical_url: https://wiki.zshell.dev/community/zsh_lint/zsh-lint-rule-reference markdown_url: https://wiki.zshell.dev/community/zsh_lint/zsh-lint-rule-reference/index.md locale: en source_path: community/04_zsh_lint/08_rule_reference.mdx --- # Published rule reference This page is generated from the documented rules in the latest published Zsh Lint release. Start with [Rules and suppressions](https://wiki.zshell.dev/community/zsh_lint/zsh-lint-rules-and-suppressions/index.md) if you are new to diagnostics and intentional exceptions. Do not hand-edit the marked region. The source repository's release-aligned documentation workflow replaces it automatically. :::info Published rule set This reference was generated from the published `v1.1.0` release at commit [`fde7795feaa65a222acf42459a26eb0f9271523d`](https://github.com/z-shell/zsh-lint/commit/fde7795feaa65a222acf42459a26eb0f9271523d). It matches the rules users receive from `go install github.com/z-shell/zsh-lint/cmd/zsh-lint@v1.1.0`. ::: ## Rules - [`compat/special-param-shadow`](#rule-compatspecial-param-shadow): Shadowing special shell parameters - [`plugin/fpath-hygiene`](#rule-pluginfpath-hygiene): fpath manipulation hygiene in plugin entrypoints - [`plugin/function-scoped-options`](#rule-pluginfunction-scoped-options): Function files should scope shell options - [`plugin/unload-function`](#rule-pluginunload-function): Unload function convention and hygiene - [`plugin/zero-handling`](#rule-pluginzero-handling): Zero-handling idiom in plugin entrypoint - [`quoting/unquoted-var`](#rule-quotingunquoted-var): Unquoted variable expansion - [`security/eval`](#rule-securityeval): Use of eval - [`style/backquotes`](#rule-stylebackquotes): Prefer dollar-parenthesis command substitution - [`style/function-decl`](#rule-stylefunction-decl): Function declaration style - [`style/prefer-double-brackets`](#rule-styleprefer-double-brackets): Prefer \[\[ \]\] over \[ \] or test ## Rule: `compat/special-param-shadow` **Name:** Shadowing special shell parameters **Summary:** Reports `local`, `typeset`, `declare`, or `readonly` declarations of a curated set of shell-set parameters (for example `ZSH_VERSION`, `OSTYPE`, and `pipestatus`); explicit non-local `-g` declarations (including `readonly -g`), the `export` builtin, and `typeset`\-family query/display/function modes (standalone `+`, `-p`/`+p`, `+m`, and `-f`/`+f`) are excluded. Pattern declarations with `-m` are reported only when an effective `+g` makes matching non-local parameters local. The rule also stays silent when dynamic option words or the ambient `GLOBAL_EXPORT` option make declaration scope uncertain. Reads are never reported. Why: The Zsh manual's zshparam "Parameters Set By The Shell" section documents these as shell-provided state. In a function, `readonly` is `typeset -r` and creates a local binding unless `-g` explicitly selects non-local behavior. Version probes such as `is-at-least $ZSH_VERSION` are pervasive in plugin code, so a `local ZSH_VERSION=...` in a caller silently feeds the override to all nested code for the lifetime of the scope. At top level, the same declaration form clobbers the shell-managed outer binding instead of creating a temporary local. Unlike ordinary shadowing, the reader has no declaration of the original to look up -- the shell set it. See [https://zsh.sourceforge.io/Doc/Release/Parameters.html#Parameters-Set-By-The-Shell](https://zsh.sourceforge.io/Doc/Release/Parameters.html#Parameters-Set-By-The-Shell). **Bad:** ```zsh compile_zsh() { local ZSH_VERSION="$1" } ``` **Good:** ```zsh compile_zsh() { local target_zsh_version="$1" } ``` **Severity:** Warning. The pattern is functional but misleading and can change what nested code observes; deliberate compatibility shims are realistic, so it is suppressible rather than an error. False positives: Deliberate compatibility shims or test harnesses that fake `ZSH_VERSION` or `OSTYPE` for downstream code are the rule's target behavior made intentional; suppress them with a reason. The rule flags only a curated allowlist of read-mostly shell-set parameters and stays silent for reads and the conventionally mutated `path`, `fpath`, `PATH`, `REPLY`, and `match`. The shell-special `status` and `pipestatus` cases remain included because `local -h status=...` and `local -h pipestatus=(...)` can replace their special behavior with ordinary local bindings. **Suppression:** Use `# zsh-lint disable=compat/special-param-shadow -- ` on the finding line or immediately before the next non-comment, non-blank source line. Corpus evidence: Issue #64 records `zd/docker/utils.zsh:78`: `local ZSH_VERSION="$1"` inside a helper that takes a target version as its first argument. Deferred issue #72 tracks bare assignments in function scope and `integer`/`float` declarations; both are out of scope for this rule version. ## Rule: `plugin/fpath-hygiene` **Name:** fpath manipulation hygiene in plugin entrypoints **Summary:** Detects destructive assignments to `fpath` that overwrite existing autoload paths, additions of non-function directories (`bin/`, `tests/`), and hardcoded user paths. Why: Sourced Zsh plugins must not destructively replace `fpath` (which removes standard system and user function directories), add hardcoded user machine paths, or add non-function directories (`bin/`, `tests/`) that can trigger completion security audit warnings (`compaudit`) or namespace collisions. Plugins should append or prepend relative `functions/` or `completions/` directories. See [https://wiki.zshell.dev/community/zsh\_plugin\_standard#completions-and-compinit-ownership](https://wiki.zshell.dev/community/zsh_plugin_standard/index.md#completions-and-compinit-ownership). **Bad:** ```zsh #### Destructively overwrites existing autoload paths fpath=( "${0:h}/functions" ) #### Adds binary directory to fpath fpath+=( "${0:h}/bin" ) ``` **Good:** ```zsh fpath+=( "${0:h}/functions" "${0:h}/completions" ) #### Prepending while preserving existing paths fpath=( "${0:h}/functions" $fpath ) ``` **Severity:** Warning for destructive `fpath` overwrites or additions of `bin`/`tests` directories to `fpath`. False positives: Hermetic test runners or standalone shell bootstrap scripts that deliberately isolate `fpath`. Suppress with a reason. **Suppression:** Use `# zsh-lint disable=plugin/fpath-hygiene -- ` on the finding line or immediately before the next non-comment, non-blank source line. Corpus evidence: `z-a-meta-plugins/z-a-meta-plugins.plugin.zsh:12` uses compliant `fpath+=( "${0:h}/functions" )`. ## Rule: `plugin/function-scoped-options` **Name:** Function files should scope shell options **Summary:** Reports executable files beneath a `functions` directory when neither `builtin emulate -L zsh` nor `setopt local_options` appears before the first non-guard top-level statement. Why: Zsh functions inherit the caller's option state. The Zsh manual documents `LOCAL_OPTIONS` as restoring options on function return, while `emulate -L` both selects Zsh emulation and makes option changes local. Without either form, options such as `SH_WORD_SPLIT`, `KSH_ARRAYS`, or `NO_EXTENDED_GLOB` can silently change function behavior. See [https://zsh.sourceforge.io/Doc/Release/Options.html#index-LOCAL\_005fOPTIONS](https://zsh.sourceforge.io/Doc/Release/Options.html#index-LOCAL_005fOPTIONS) and [https://zsh.sourceforge.io/Doc/Release/Shell-Builtin-Commands.html#index-emulate](https://zsh.sourceforge.io/Doc/Release/Shell-Builtin-Commands.html#index-emulate). **Bad:** ```zsh local -a matches matches=( $~pattern ) ``` **Good:** ```zsh builtin emulate -L zsh local -a matches matches=( $~pattern ) ``` **Severity:** Hint. Missing option scoping enables latent caller-dependent bugs, but some helper functions intentionally inspect or mutate caller option state. False positives: Functions that intentionally share caller option state and trivial single-builtin functions remain findings by design; suppress them with a reason. Files outside a complete `functions` path segment are out of scope. A contiguous prefix of recognized `condition || return` or single-return `if` guards is accepted before scoping. **Suppression:** Use `# zsh-lint disable=plugin/function-scoped-options -- ` on the finding line or immediately before the next non-comment, non-blank source line. Corpus evidence: Issue #63 reported missing scoping across z-a-meta-plugins and zsh-fancy-completions function files. The analyzer currently flags `zsh-fancy-completions/functions/.completion-prediction` and `zsh-fancy-completions/functions/.force_rehash`. The originally cited `z-a-meta-plugins/functions/.za-meta-plugins-meta-cmd-help-handler` is a fully commented-out stub and is correctly silent. Compliant `setopt local_options` and `builtin emulate -L zsh` examples exist in the same repositories. ## Rule: `plugin/unload-function` **Name:** Unload function convention and hygiene **Summary:** Checks that plugins registering persistent shell hooks or widgets define a namespaced unload function (`*_plugin_unload`), and that defined unload functions cleanly unfunction themselves upon completion. Why: The Zsh Plugin Standard specifies that plugins with persistent side effects (hooks via `add-zsh-hook`, line-editor widgets via `add-zle-hook-widget` or `zle -N`) should provide a namespaced `*_plugin_unload` function so plugin managers and users can cleanly unload the plugin without restarting the shell. The unload function must explicitly remove only its own resources and unfunction itself upon completion. See [https://wiki.zshell.dev/community/zsh\_plugin\_standard#lifecycle-and-resource-ownership](https://wiki.zshell.dev/community/zsh_plugin_standard/index.md#lifecycle-and-resource-ownership). **Bad:** ```zsh #### Plugin installs hook but provides no unload function add-zsh-hook precmd _my_precmd ``` **Good:** ```zsh add-zsh-hook precmd _my_precmd my_plugin_unload() { emulate -L zsh autoload -Uz add-zsh-hook add-zsh-hook -d precmd _my_precmd unfunction _my_precmd my_plugin_unload } ``` **Severity:** Hint. Missing unload functions or self-unfunction in unload definitions are lifecycle recommendations. Indiscriminate function wiping is a Warning. False positives: Plugins intended only for static, once-per-session loading without unload support. Suppress with a reason. **Suppression:** Use `# zsh-lint disable=plugin/unload-function -- ` on the finding line or immediately before the next non-comment, non-blank source line. Corpus evidence: `zsh-fancy-completions/lib/state.zsh:72` implements `zsh-fancy-completions_plugin_unload` with full resource restoration and self-unfunction. ## Rule: `plugin/zero-handling` **Name:** Zero-handling idiom in plugin entrypoint **Summary:** Reports direct uses of `$0` at the top level of plugin scripts before `$0` has been initialized using prompt expansions (`${(%):-%N}` or `${(%):-%x}`). Why: When a Zsh plugin is sourced, positional parameter `$0` evaluates to the name of the shell (`zsh` or `-zsh`) rather than the path of the sourced script, unless `FUNCTION_ARGZERO` is active. Plugin entrypoints must initialize `$0` using prompt expansion `${(%):-%N}` or `${(%):-%x}` (optionally with `$ZERO` fallback) before using `$0` to derive directories or autoload paths. See [https://wiki.zshell.dev/community/zsh\_plugin\_standard#zero-handling](https://wiki.zshell.dev/community/zsh_plugin_standard/index.md#zero-handling). **Bad:** ```zsh fpath+=( "${0:h}/functions" ) ``` **Good:** ```zsh 0="${ZERO:-${${0:#$ZSH_ARGZERO}:-${(%):-%N}}}" 0="${${(M)0:#/*}:-$PWD/$0}" fpath+=( "${0:h}/functions" ) ``` **Severity:** Warning. Deriving paths from uninitialized `$0` in a sourced plugin leads to incorrect directory paths or runtime loading failures. False positives: Scripts intended solely for direct execution (not sourcing) or functions where `$0` refers to the function name. Suppress with a reason. **Suppression:** Use `# zsh-lint disable=plugin/zero-handling -- ` on the finding line or immediately before the next non-comment, non-blank source line. Corpus evidence: `z-a-meta-plugins/z-a-meta-plugins.plugin.zsh:7` uses the compliant `0="${ZERO:-${${0:#$ZSH_ARGZERO}:-${(%):-%N}}}"` idiom before referencing `${0:h}` on line 12. ## Rule: `quoting/unquoted-var` **Name:** Unquoted variable expansion **Summary:** Reports parameter expansions in command names or arguments that are not enclosed in double quotes. Why: The Zsh manual's Parameter Expansion section explains that unquoted parameters are not split on whitespace by default, unlike in sh, but null words are still elided; enabling `SH_WORD_SPLIT` also makes unquoted values subject to field splitting. Double quotes preserve an empty scalar as an argument and keep the expansion single-word under either option state. See [https://zsh.sourceforge.io/Doc/Release/Expansion.html#Parameter-Expansion](https://zsh.sourceforge.io/Doc/Release/Expansion.html#Parameter-Expansion). **Bad:** ```zsh print -r -- $value ``` **Good:** ```zsh print -r -- "$value" ``` **Severity:** Warning. Losing an empty argument or inheriting `SH_WORD_SPLIT` can change command behavior, while intentional elision remains realistic. False positives: Code may intentionally omit an empty argument, deliberately rely on `SH_WORD_SPLIT`, or expand a value guaranteed to be non-empty. Those cases should use a reasoned suppression rather than weakening unrelated diagnostics. **Suppression:** Use `# zsh-lint disable=quoting/unquoted-var -- ` on the finding line or immediately before the next non-comment, non-blank source line. Corpus evidence: Sourced scripts and plugin entrypoints frequently use quoted parameters for scalar configuration and path derivation. ## Rule: `security/eval` **Name:** Use of eval **Summary:** Reports commands whose literal command name is `eval`. Why: The Zsh manual documents that `eval` reads its arguments as shell input and executes the resulting commands in the current shell process. Dynamic or untrusted text can therefore become shell syntax rather than inert data. See [https://zsh.sourceforge.io/Doc/Release/Shell-Builtin-Commands.html#index-eval](https://zsh.sourceforge.io/Doc/Release/Shell-Builtin-Commands.html#index-eval). **Bad:** ```zsh eval "print -r -- $user_input" ``` **Good:** ```zsh print -r -- "$user_input" ``` **Severity:** Info. Re-evaluating dynamic input is risky, but deliberate uses such as trusted code generation and compatibility shims are common enough that the pattern is not automatically a bug. False positives: Static, maintainer-controlled command strings and deliberate shell-language adapters may require `eval`. Keep the finding visible unless the trust boundary is documented next to the call. **Suppression:** Use `# zsh-lint disable=security/eval -- ` on the finding line or immediately before the next non-comment, non-blank source line. A reason is strongly recommended for this security-category rule. Corpus evidence: The June 12, 2026 LangZsh clean-baseline run produced zero findings from this rule across the 11 parseable corpus files. This grandfathered rule therefore has no positive corpus citation yet. ## Rule: `style/backquotes` **Name:** Prefer dollar-parenthesis command substitution **Summary:** Reports the grave-accent form of command substitution in favor of `$()`. Why: The Zsh manual's Command Substitution section documents both `$()` and grave accents as supported forms. This rule prefers `$()` as the clearer, readily nestable modern idiom; it does not claim grave accents are invalid Zsh syntax. See [https://zsh.sourceforge.io/Doc/Release/Expansion.html#Command-Substitution](https://zsh.sourceforge.io/Doc/Release/Expansion.html#Command-Substitution). **Bad:** ```zsh current=`pwd` ``` **Good:** ```zsh current=$(pwd) ``` **Severity:** Hint. The forms are semantically supported; this is an idiom and readability preference rather than a correctness diagnostic. False positives: A project may intentionally preserve historical style or mirror code shared with an environment where that spelling is required. **Suppression:** Use `# zsh-lint disable=style/backquotes -- ` on the finding line or immediately before the next non-comment, non-blank source line. Corpus evidence: The June 12, 2026 LangZsh clean-baseline run produced zero findings from this rule across the 11 parseable corpus files. This grandfathered rule therefore has no positive corpus citation yet. ## Rule: `style/function-decl` **Name:** Function declaration style **Summary:** Reports declarations written as `function name()` instead of choosing either `name()` or `function name`. Why: The Zsh manual's Complex Commands grammar documents the sh-compatible `word ()` form and the Zsh `function word` form as alternatives. Combining both spellings is accepted but redundant, so choosing one form communicates the intended style more clearly. See [https://zsh.sourceforge.io/Doc/Release/Shell-Grammar.html#Complex-Commands](https://zsh.sourceforge.io/Doc/Release/Shell-Grammar.html#Complex-Commands). **Bad:** ```zsh function render() { print ok; } ``` **Good:** ```zsh render() { print ok; } ``` **Severity:** Hint. The mixed declaration is valid Zsh and the suggested change is stylistic. False positives: Generated code or a project-wide convention may intentionally use the mixed spelling even though Zsh does not require it. **Suppression:** Use `# zsh-lint disable=style/function-decl -- ` on the finding line or immediately before the next non-comment, non-blank source line. Corpus evidence: The June 12, 2026 LangZsh clean-baseline run produced zero findings from this rule across the 11 parseable corpus files. This grandfathered rule therefore has no positive corpus citation yet. ## Rule: `style/prefer-double-brackets` **Name:** Prefer \[\[ \]\] over \[ \] or test **Summary:** Reports literal `[` and `test` command names in favor of Zsh's `[[ ... ]]` compound command. Why: The Zsh manual's Conditional Expressions section documents that expansions inside `[[ ... ]]` are constrained to one word and do not perform filename generation. With `[` or `test`, normal command-line globbing can produce multiple words and confuse the test command's syntax. See [https://zsh.sourceforge.io/Doc/Release/Conditional-Expressions.html](https://zsh.sourceforge.io/Doc/Release/Conditional-Expressions.html). **Bad:** ```zsh if [ "$name" = *.zsh ]; then print match; fi ``` **Good:** ```zsh if [[ $name = *.zsh ]]; then print match; fi ``` **Severity:** Hint. `[` and `test` remain valid commands; the rule recommends the safer and more expressive Zsh-native conditional syntax. False positives: Scripts intentionally kept portable across POSIX shells, or code that explicitly needs `test` command semantics, should retain the portable form and document the choice. **Suppression:** Use `# zsh-lint disable=style/prefer-double-brackets -- ` on the finding line or immediately before the next non-comment, non-blank source line. Corpus evidence: The June 12, 2026 LangZsh clean-baseline run produced zero findings from this rule across the 11 parseable corpus files. This grandfathered rule therefore has no positive corpus citation yet. --- --- title: Rules and suppressions description: Read Zsh Lint diagnostics and suppress one intentional finding safely. canonical_url: https://wiki.zshell.dev/community/zsh_lint/zsh-lint-rules-and-suppressions markdown_url: https://wiki.zshell.dev/community/zsh_lint/zsh-lint-rules-and-suppressions/index.md locale: en source_path: community/04_zsh_lint/04_rules_and_suppressions.mdx --- # Rules and suppressions ## Read a diagnostic Human-readable output uses this shape: ```text path:line:column: [rule-id] message ``` For example: ```text lib/render.zsh:8:10: [quoting/unquoted-var] Variable expansion should be double-quoted ``` The rule ID is stable machine-facing identity. Browse the [published rule reference](https://wiki.zshell.dev/community/zsh_lint/zsh-lint-rule-reference/index.md) for each rule's purpose, examples, severity, and suppression form. Warnings and errors make the command exit 1. Info and Hint findings are still reported but do not make the command fail. ## Fix first, suppress only when intentional A suppression names the exact rule and applies to one source line: ```zsh print $words # zsh-lint disable=quoting/unquoted-var -- intentional word splitting ``` You can also put it on the preceding comment line. It then applies to the next non-comment, non-blank source line: ```zsh # zsh-lint disable=quoting/unquoted-var -- intentional word splitting print $words ``` Multiple rule IDs are comma-separated without spaces: ```zsh # zsh-lint disable=rule/one,rule/two -- compatibility boundary command_to_review ``` There is no blanket, block-wide, or file-wide disable in the published contract. A malformed directive reports `meta/malformed-suppression`. A rule ID that suppresses nothing reports `meta/unused-suppression`, which helps keep exceptions from becoming stale. Use a short reason even when it is optional. The reason lets the next reviewer decide whether the exception is still valid. --- --- title: Troubleshooting description: Resolve common Zsh Lint installation, input, parser, and exit-code problems. canonical_url: https://wiki.zshell.dev/community/zsh_lint/zsh-lint-troubleshooting markdown_url: https://wiki.zshell.dev/community/zsh_lint/zsh-lint-troubleshooting/index.md locale: en source_path: community/04_zsh_lint/06_troubleshooting.mdx --- # Troubleshooting ## `zsh-lint: command not found` Go normally installs binaries under `$(go env GOPATH)/bin`. Add that directory to `PATH`, or invoke the binary directly: ```sh "$(go env GOPATH)/bin/zsh-lint" path/to/script.zsh ``` ## Passing a directory fails The published CLI opens each argument as one file. It does not recurse through directories. Build an explicit file list as described in [Choosing files](https://wiki.zshell.dev/community/zsh_lint/zsh-lint-choosing-files/index.md). ## A Bash or `sh` file reports strange parse errors Zsh Lint does not classify dialects from the shebang or extension. Every path you pass is parsed as Zsh. Remove non-Zsh files from the input list. ## Valid Zsh reports a parser error First check the same file with the supported native Zsh version: ```sh zsh -f -n -- path/to/script.zsh ``` If native Zsh accepts it, collect a minimal example and report a parser gap in [z-shell/zsh-lint](https://github.com/z-shell/zsh-lint/issues). Do not rewrite valid native Zsh only to satisfy a supplemental parser. ## `--config` is rejected The option is not available in v1.1.0. It currently exists only on `main`; see the [project configuration preview](https://wiki.zshell.dev/community/zsh_lint/zsh-lint-project-configuration/index.md). ## Exit status 1 with no obvious error Warnings also return status 1. Read the human diagnostics or save JSON and inspect the summary: ```sh zsh-lint --format=json path/to/script.zsh | jq '.summary' ``` Because the linter itself exits 1 for findings, append `|| status=$?` only when you are deliberately inspecting output. Do not hide the failure in CI. ## An intentional pattern keeps failing Read the rule, verify that the code is intentional, then add a line-scoped suppression with the exact rule ID and a reason. See [Rules and suppressions](https://wiki.zshell.dev/community/zsh_lint/zsh-lint-rules-and-suppressions/index.md). --- --- title: ℹ️ Zsh Plugin Standard description: Standards and best practices for creating Zsh plugins. canonical_url: https://wiki.zshell.dev/community/zsh_plugin_standard markdown_url: https://wiki.zshell.dev/community/zsh_plugin_standard/index.md locale: en source_path: community/03_zsh_plugin_standard.mdx --- # ℹ️ Zsh Plugin Standard This page defines what a Zsh plugin is, how it is structured, and how plugins and plugin managers interoperate. Following the portable core lets people source your plugin directly or load it through different frameworks and plugin managers without learning a repository-specific architecture first. Status of this document This is ecosystem guidance, not an official Zsh language specification. Zsh guarantees are identified and linked to the official manual. Everything else is a recommendation for interoperability. Where each rule lives This page owns the **plugin interoperability contract**: what makes a directory a plugin, and how a manager and a plugin talk to each other. It does not own general Zsh authoring rules. For language semantics, options, quoting, expansion, and safety rules, follow the canonical [Zsh Scripting Standard](https://github.com/z-shell/.github/blob/main/.github/instructions/zsh-scripting.instructions.md). For idioms and worked techniques, see the [Zsh Native Scripting Handbook](https://wiki.zshell.dev/community/zsh_handbook/index.md). Official Zsh documentation remains authoritative for shell semantics. ## Conformance language This page publishes version 2 of the portable contract. The terms in this page have these meanings: - **Zsh guarantee** describes behavior documented by the current Zsh manual. - **MUST**, **MUST NOT**, **SHOULD**, and **MAY** have their usual normative meanings. A documented reason is required when a plugin does not follow a **SHOULD** rule. - **Portable core** describes manager-independent requirements for a plugin. It is the architecture, loading, namespace, configuration, lifecycle, and testing contract below. - **Loader interface** describes an optional facility that a plugin manager may expose. It is not part of portable conformance. - **Optional profile** describes conventions implemented by named managers. A plugin can use a profile, but must not silently require it. A portable plugin does not require a manager, a manager-owned global parameter, or a manifest. There is no plugin manifest standard defined by this page. Version 2 is a clean contract, not a compatibility mode. Older conventions can be documented as history, but they are not alternative ways to claim current portable conformance. Each optional loader interface carries a **STATUS** block recording which managers implement it, with pinned source citations. ## What is a Zsh plugin? Zsh plugins were first defined in practice by Oh My Zsh. They provide a way to package together files that extend or configure the shell's functionality in a particular way. At a simple level, a plugin is a directory with one documented sourceable Zsh entrypoint. It may also contain private sourced libraries, autoload functions, completions, user-invoked programs, and documentation. Each directory has the execution semantics defined by the portable layout below. A plugin can be sourced directly without a manager. A framework or manager may discover the entrypoint and arrange `fpath`, but the portable plugin must not require manager state merely to load or unload. ## Optional loader interfaces The following numbered interfaces document facilities implemented by existing plugin managers. They are not portable requirements and a plugin does not need to implement or inspect any of them. A plugin that deliberately opts into one must feature-test that exact facility, retain its direct-source behavior, and test the named manager profile independently. These interfaces are retained as manager interoperability reference, not as a legacy conformance mode. ### 1\. Standardized source-path handoff Portable plugins use the [canonical scoped loader](#entrypoints-and-repository-layout) defined in the portable core. Its final argument is evaluated at the sourced file's call site, before the anonymous loader function begins: Manager-compatible source-path handoff ```zsh "${ZERO:-${${0:#$ZSH_ARGZERO}:-${(%):-%N}}}" ``` The expression will: 1. Be backward compatible with normal `$0` setting and usage. 2. Use `ZERO` if it is not empty. 2.1. The plugin manager is then easily able to alter the effective `$0` before loading a plugin. 2.2. This allows, for example, `eval "$( #### Use in standalone scripts The same call-site expression works in a standalone script, one run through a `#!` line, to find the directory the script file resides in. A standalone process does not need the anonymous loader merely to protect its caller, but it must still store the result in a purpose-specific parameter rather than repurposing `0`. Verified on Zsh 5.9.2 when invoked by absolute path, from an unrelated working directory, by relative path, and through a symlink. Two differences apply outside a plugin manager: - `ZERO` is inert, because nothing sets it. The `%N` branch does the work. - `:a` does not resolve symlinks, so a script reached through a symlink resolves to the directory holding the **link**, not the target. Use `:A` when you want the target's directory. `%N` must remain at the sourced-file call site Inside a function, `%N` expands to the **function name**. Moving the fallback into the loader body would therefore resolve against the current directory instead of failing: ```shell-session $ zsh -f -c 'f() { print -r -- ${(%):-%N} }; f' f $ zsh -f -c 'myplugin() { print -r -- ${${(%):-%N}:a:h} }; myplugin' /current/working/directory # not the plugin directory ``` The canonical loader avoids this trap by evaluating the handoff expression after the closing brace, while Zsh is still executing the sourced file. Change from earlier revisions Earlier revisions assigned the resolved path to `0`, then made it absolute with either `$PWD` or `:a`. That mutation could replace caller state and fails when `posix_argzero` makes `0` read-only: ```shell-session $ zsh -f -c 'setopt posix_argzero; typeset -p 0' typeset 0=zsh $ zsh -f -c 'setopt posix_argzero; 0=x' zsh:1: read-only variable: 0 ``` The scoped loader supersedes those forms while retaining the `ZERO` manager handoff. Do not infer the repository directory from the caller's current directory. #### STATUS: \[ zero-handling \] - **Zi:** implemented. Declares and exports `ZERO` in its [bootstrap](https://github.com/z-shell/zi/blob/1ab7a2f152243e3bb27839a70f9d17aaa570cf3f/zi.zsh#L7-L49). - **Zinit:** implemented independently. Derives `ZINIT[ZERO]` in its [bootstrap](https://github.com/zdharma-continuum/zinit/blob/fb12fbc83af3c6fbba94bc98a429cb93e89b546e/zinit.zsh#L13-L189). - **zcomet:** implemented. Its loader [supplies `ZERO` before sourcing](https://github.com/agkozak/zcomet/blob/3dfe6f837479c6731e74400a233739500ec6d648/zcomet.zsh#L201-L205). - **zgenom:** implemented. Its loader [supplies `ZERO`](https://github.com/jandamm/zgenom/blob/d99d5dc4b27695612de2b7054bb583dd19a7235d/functions/zgenom-load#L8-L32). ### 2\. Functions directory Current-standard plugins have their main directory added to `$fpath`. A cleaner approach is proposed: plugins use a subdirectory called `functions` to store their completions and autoload functions. The plugin manager should add that directory to `$fpath`. Where a manager lacks support, the plugin can resolve it through the [activity indicator](#activity-indicator): Self-fpath through the activity indicator ```zsh if [[ ${zsh_loaded_plugins[-1]} != */kalc && -z ${fpath[(r)${0:h}/functions]} ]]; then fpath+=( "${0:h}/functions" ) fi ``` or through the [`PMSPEC` parameter](#global-parameter-with-capabilities): Self-fpath through PMSPEC ```zsh if [[ $PMSPEC != *f* ]]; then fpath+=( "${0:h}/functions" ) fi ``` Either snippet, added to the `plugin.zsh` file, adds the directory to `$fpath` while preserving compatibility with managers that already handle it. The existence of the `functions` subdirectory cancels the normal adding of the main plugin directory to `$fpath`. Store each native completion in a file named `_command`. See [Completions and `compinit` ownership](#completions-and-compinit-ownership) for who is responsible for running `compinit`. Note on a widespread typo Many published plugins link to `#funtions-directory`, missing the first `c`. That fragment is kept alive on this page so those links resolve, but the correct anchor is `#functions-directory`. #### STATUS: \[ functions-directory \] - **Zi:** implemented. Advertises `f` in `PMSPEC`. - **zcomet:** implemented. Advertises `f` in [its `PMSPEC`](https://github.com/agkozak/zcomet/blob/3dfe6f837479c6731e74400a233739500ec6d648/zcomet.zsh#L20-L28). - **zgenom:** implemented. Sets `PMSPEC` in [`options.zsh`](https://github.com/jandamm/zgenom/blob/d99d5dc4b27695612de2b7054bb583dd19a7235d/options.zsh#L1-L43). - **Zinit:** implemented. Advertises `f` in its [bootstrap](https://github.com/zdharma-continuum/zinit/blob/fb12fbc83af3c6fbba94bc98a429cb93e89b546e/zinit.zsh#L13-L189). - **Adoption:** the `*f*` guard is the most widely used capability check in the z-shell organization. ### 3\. Binaries directory Plugins sometimes provide a runnable script or program, either for internal use or for the end user. For the latter, the plugin should use a `bin/` subdirectory inside its main directory. For internal use, call the runnable through the `$0` value obtained in [requirement 1](#zero-handling). The runnable should be placed in that directory with the `+x` access right set. The plugin manager should: 1. Before sourcing the plugin's script, test whether the `bin/` directory exists within the plugin directory. 2. If it does, add the directory to `$PATH`. 3. Alternatively, instead of extending `$PATH`, create a **shim**, that is a forwarder script, or a symbolic link inside a common directory that is already on `$PATH`, to limit how far `$PATH` grows. 4. Optionally ensure `+x` access rights on the directory contents. The `$PMSPEC` code letter for the feature is `b`. It allows the plugin to handle the `$PATH` extension itself when the manager does not: Self-PATH through PMSPEC ```zsh if [[ $PMSPEC != *b* ]]; then path+=( "${0:h}/bin" ) fi ``` This is a guarded extension, not an unconditional one The guard is the point. The plugin yields to any manager that advertises `b`, and only acts when nothing else will. Do not extend `path` unconditionally, and do not add `$ZPFX/bin` from a plugin; see [requirement 8](#global-parameter-with-prefix). #### STATUS: \[ binaries-directory \] - **zcomet:** implemented. Advertises `b` in [its `PMSPEC`](https://github.com/agkozak/zcomet/blob/3dfe6f837479c6731e74400a233739500ec6d648/zcomet.zsh#L20-L28). - **zgenom:** implemented conditionally. Sets the letter according to its automatic `bin` setting in [`options.zsh`](https://github.com/jandamm/zgenom/blob/d99d5dc4b27695612de2b7054bb583dd19a7235d/options.zsh#L1-L43). - **Zi, Zinit:** do not advertise `b`, so the guarded plugin-side extension is the operative path under both. - **Adoption:** four plugin entrypoints in the z-shell organization use the `*b*` guard. ### 4\. Unload function If a plugin is named `kalc`, and is available through the `any-user/kalc` plugin ID, then it can provide a function `kalc_plugin_unload` that a plugin manager calls to undo the effects of loading that plugin. A plugin manager can implement its own tracking of the changes a plugin makes, so this is optional in general. However, to properly unload something like a prompt, dedicated tracking, which is easy for the plugin author to write, gives better and more predictable results. Uncommon effects of loading a plugin can only be undone by a dedicated function. An interesting compromise is available: withdraw only the special effects through the plugin-provided function and leave the rest to the plugin manager. Maintaining a function that withdraws **all** side effects can be a daunting task requiring constant attention during development. The unload function should also delete itself. Use `unfunction kalc_plugin_unload` rather than `unfunction $0`, for compatibility with the `*_argzero` options. A namespaced unload function ```zsh kalc_plugin_unload() { emulate -L zsh autoload -Uz add-zsh-hook add-zsh-hook -d precmd _kalc_precmd unfunction _kalc_precmd kalc_plugin_unload unset KALC_STATE } ``` Keep cleanup idempotent, list each owned resource explicitly, and tolerate partial initialization. See [Lifecycle and resource ownership](#lifecycle-and-resource-ownership) for the full inventory of what counts as an owned resource. #### STATUS: \[ unload-function \] - **Zi:** implemented. Calls a plugin-named `*_plugin_unload` function in [`lib/zsh/autoload.zsh`](https://github.com/z-shell/zi/blob/1ab7a2f152243e3bb27839a70f9d17aaa570cf3f/lib/zsh/autoload.zsh#L708-L765). - **Zinit:** implemented independently. Invokes a plugin-named unload function in [`zinit-autoload.zsh`](https://github.com/zdharma-continuum/zinit/blob/fb12fbc83af3c6fbba94bc98a429cb93e89b546e/zinit-autoload.zsh#L2453-L2503). - **zcomet:** implemented, with reservations. Its [`zcomet_unload` function](https://github.com/agkozak/zcomet/blob/3dfe6f837479c6731e74400a233739500ec6d648/functions/zcomet_unload#L3-L34) conditionally invokes `*_plugin_unload`, but the source itself describes the routine as needing work. Do not infer broader lifecycle guarantees from it. - **In the wild:** [romkatv/powerlevel10k is using](https://github.com/romkatv/powerlevel10k/blob/f17081ca/internal/p10k.zsh#L5390) the function to shut down its background [gitstatus](https://github.com/romkatv/gitstatus) daemon; [agkozak/agkozak-zsh-prompt is using](https://github.com/agkozak/agkozak-zsh-prompt/blob/ed228952d68fea6d5cad3beee869167f76c59606/agkozak-zsh-prompt.plugin.zsh#L992-L1039), [agkozak/zsh-z is using](https://github.com/agkozak/zsh-z/blob/16fba5e9d5c4b650358d65e07609dda4947f97e8/zsh-z.plugin.zsh#L680-L698), and [agkozak/zhooks is using](https://github.com/agkozak/zhooks/blob/628e1e3b8373bf31c26cb154f71c16ebe9d13b51/zhooks.plugin.zsh#L75-L82) it to unload completely. ### 5\. `@zsh-plugin-run-on-unload` call The plugin manager can provide a function `@zsh-plugin-run-on-unload` with the following call syntax: Register unload snippets ```zsh @zsh-plugin-run-on-unload "{code-snippet-1}" "{code-snippet-2}" … ``` The function registers pieces of code to be run by the plugin manager **on the unloading of the plugin**. Execution is done by the `eval` builtin, in the same order the snippets are passed. The code runs in the plugin's directory, in the current shell. The mechanism provides a second way, alongside the [unload function](#unload-function), for a plugin to participate in unloading. This is an eval-based interface Because the manager evaluates these strings, never pass untrusted or externally-derived data into the call. Prefer a named, idempotent unload function as the primary mechanism and treat this as manager-profile compatibility rather than portable lifecycle practice. #### STATUS: \[ run-on-unload-call \] - **Zi:** implemented. Stores manager callback strings; see [Zi's callback source](https://github.com/z-shell/zi/blob/1ab7a2f152243e3bb27839a70f9d17aaa570cf3f/zi.zsh#L1121-L1133). Advertises `U` in `PMSPEC`. - **Zinit:** implemented separately; see [Zinit's callback source](https://github.com/zdharma-continuum/zinit/blob/fb12fbc83af3c6fbba94bc98a429cb93e89b546e/zinit.zsh#L1287-L1299). Advertises `U`. - **Shared name, separate implementations.** Support in both projects is shared convention, not evidence that they are the same manager. Test each profile you opt into. ### 6\. `@zsh-plugin-run-on-update` call The plugin manager can provide a function `@zsh-plugin-run-on-update` with the following call syntax: Register update snippets ```zsh @zsh-plugin-run-on-update "{code-snippet-1}" "{code-snippet-2}" … ``` The function registers pieces of code to be run by the plugin manager on an update of the plugin. Execution is done by the `eval` builtin in the order the snippets are passed. The code runs in the plugin's directory, possibly in a subshell, **after downloading any new commits** to the repository. The same warning about evaluated strings applies. A portable plugin should also expose an explicit update or migration command, and must not assume that any manager evaluates update callbacks. Keep persisted formats versioned and migrate them atomically. #### STATUS: \[ run-on-update-call \] - **Zi:** implemented; see [Zi's callback source](https://github.com/z-shell/zi/blob/1ab7a2f152243e3bb27839a70f9d17aaa570cf3f/zi.zsh#L1121-L1133). Advertises `p` in `PMSPEC`. - **Zinit:** implemented separately; see [Zinit's callback source](https://github.com/zdharma-continuum/zinit/blob/fb12fbc83af3c6fbba94bc98a429cb93e89b546e/zinit.zsh#L1287-L1299). Advertises `p`. ### 7\. Plugin manager activity indicator Plugin managers should set the `$zsh_loaded_plugins` array to contain all previously loaded plugins and the plugin currently being loaded, as the last element. This allows any plugin to: 1. Check which plugins are already loaded. 2. Check whether it is being loaded by a plugin manager, rather than simply sourced. The first item allows a plugin to issue a notice about missing dependencies, or to satisfy them from resources it ships. For example, the `pure` prompt bundles a `zsh-async` dependency library that is normally a separate project. Consequently the prompt could decide to source its private copy, having also a reliable `$0` from [requirement 1](#zero-handling). Note that `pure` does not normally do this. The second item allows a plugin to set up `$fpath` itself, knowing the plugin manager will not: Self-fpath when no manager is present ```zsh if [[ ${zsh_loaded_plugins[-1]} != */kalc && -z ${fpath[(r)${0:h}]} ]]; then fpath+=( "${0:h}" ) fi ``` This lets a user reliably source the plugin without a plugin manager. The code wraps parameters in braces, as in `${fpath…}`, for compatibility with the `KSH_ARRAYS` option, and quotes `${0:h}` for compatibility with the `SH_WORD_SPLIT` option. A plugin may also accept a manager-supplied `ZERO` as its execution path. This does not authorize evaluating the plugin text or overwriting `0`. The contents and timing of `zsh_loaded_plugins` belong to the manager, so do not use it to decide whether the plugin must initialize its own portable behavior. #### STATUS: \[ activity-indicator \] - **Zi:** implemented. Declares `zsh_loaded_plugins` in its [bootstrap](https://github.com/z-shell/zi/blob/1ab7a2f152243e3bb27839a70f9d17aaa570cf3f/zi.zsh#L7-L49) and records entries in its [loader](https://github.com/z-shell/zi/blob/1ab7a2f152243e3bb27839a70f9d17aaa570cf3f/lib/zsh/autoload.zsh#L1170-L1181). Advertises `i`. - **Zinit:** implemented independently in its [bootstrap](https://github.com/zdharma-continuum/zinit/blob/fb12fbc83af3c6fbba94bc98a429cb93e89b546e/zinit.zsh#L13-L189). Advertises `i`. - **zcomet:** implemented. Its loader [appends to `zsh_loaded_plugins`](https://github.com/agkozak/zcomet/blob/3dfe6f837479c6731e74400a233739500ec6d648/zcomet.zsh#L201-L205). Advertises `i`. - **zgenom:** implemented. Initializes the array in [`options.zsh`](https://github.com/jandamm/zgenom/blob/d99d5dc4b27695612de2b7054bb583dd19a7235d/options.zsh#L1-L43) and appends the repository ID in [`functions/zgenom-load`](https://github.com/jandamm/zgenom/blob/d99d5dc4b27695612de2b7054bb583dd19a7235d/functions/zgenom-load#L8-L32). ### 8\. Global parameter with PREFIX for make, configure, and similar Plugin managers may export the parameter `$ZPFX`, holding a path to a directory dedicated to user-land software, giving `$ZPFX/bin`, `$ZPFX/lib`, `$ZPFX/share`, and so on. The suggested directory name is `polaris`. Zi uses that name and places the directory at `${ZI[HOME_DIR]}/polaris` by default. Users can then configure hooks to invoke, for example, `make PREFIX=$ZPFX install` on clone and update, to install software such as [tj/git-extras](https://github.com/tj/git-extras). This is the developing role of Zsh plugin managers as package managers, where `.zshrc` has a role similar to a Chef or Puppet configuration and allows the user to **declare** system state and reproduce it across accounts and machines. Facts related to `$ZPFX`: 1. `export ZPFX="$HOME/polaris"`, or `${ZI[HOME_DIR]}/polaris` 2. `make PREFIX=$ZPFX install` 3. `./configure --prefix=$ZPFX` 4. `cmake -DCMAKE_INSTALL_PREFIX=$ZPFX .` 5. `zi ice make"PREFIX=$ZPFX install"` 6. `zi … hook-build:"make PREFIX=$ZPFX install"` `$ZPFX` is an optional manager-provided installation prefix. It is not an XDG variable and not a portable requirement. Use it only during an explicit installation or build action: Use an explicitly supplied installation prefix ```zsh if [[ -n ${ZPFX-} ]]; then command make install "PREFIX=$ZPFX" fi ``` Do not run this during normal plugin loading, and do not add `$ZPFX/bin` to `PATH` from a plugin. Leave that policy to the user or the manager. #### STATUS: \[ global-parameter-with-prefix \] - **Zi:** implemented. Exports `ZPFX` in its [bootstrap](https://github.com/z-shell/zi/blob/1ab7a2f152243e3bb27839a70f9d17aaa570cf3f/zi.zsh#L7-L49). Advertises `P`. - **Zinit:** implemented independently. Exports `ZPFX` in its [bootstrap](https://github.com/zdharma-continuum/zinit/blob/fb12fbc83af3c6fbba94bc98a429cb93e89b546e/zinit.zsh#L13-L189). Advertises `P`. - **zgenom:** implemented. Defines `ZPFX` in [`options.zsh`](https://github.com/jandamm/zgenom/blob/d99d5dc4b27695612de2b7054bb583dd19a7235d/options.zsh#L1-L43). Advertises `P`. - **zcomet:** advertises `P` in [its `PMSPEC`](https://github.com/agkozak/zcomet/blob/3dfe6f837479c6731e74400a233739500ec6d648/zcomet.zsh#L20-L28). ### 9\. Global parameter holding the plugin manager's capabilities Each numbered requirement above constitutes a capability of the plugin manager. It makes sense for those capabilities to be discoverable. The global parameter `PMSPEC`, from *plugin-manager specification*, holds Latin letters, each telling the plugin that the manager supports a given feature:
LetterCapabilityRequirement
0provides the ZERO parameter1
fsupports the functions/ subdirectory2
bsupports the bin/ subdirectory3
ucalls the unload function4
Uprovides @zsh-plugin-run-on-unload5
pprovides @zsh-plugin-run-on-update6
iprovides the zsh_loaded_plugins indicator7
Pprovides the ZPFX global parameter8
sprovides PMSPEC itself, so it is always presentthis requirement
The plugin verifies support by testing for the letter: Feature-test a manager capability ```zsh if [[ $PMSPEC != *b* ]]; then path+=( "${0:h}/bin" ) fi ``` Corrections to earlier revisions of this table Earlier revisions of this page published a table that could not be applied as written. Three defects are corrected above: 1. The letter `b` was defined twice, once for the `bin/` subdirectory and once for the `ZPFX` parameter. `b` means `bin/`. Four plugin entrypoints in the z-shell organization rely on that meaning. 2. The letter `P` appeared in the "fully compliant" string but was never defined. `P` means `ZPFX`, which is how implementations use it. 3. The closing example guarded a `bin/` `PATH` extension on `*P*` instead of `*b*`. Published implementations set different strings, so a plugin must not treat any letter list as a universal capability registry. Feature-test the exact letter you need, and provide a manager-independent path. Observed values:
ManagerPMSPEC
Zi0fuUpiPsX
Zinit0uUpiPsf
zcomet0fbuiPs
Zi's `X` has no definition in any published source and no consumer anywhere in the z-shell organization. Treat it as an unallocated Zi extension observed in the wild; this page does not assign it a meaning. There is no conformance target This page defines the **meaning** of each letter. It does not define a required set, and it does not rank managers by how many letters they advertise. Earlier revisions stated that a fully compliant manager should advertise `0fuUpiPs`. That target is withdrawn. No published manager advertises it, no two of the managers above agree, and a plugin gains nothing from comparing a string against a fixed list. A manager is not non-conforming because it omits a letter; it advertises what it implements. Correspondingly, a plugin must feature-test the exact letter it needs and keep a manager-independent path, rather than testing for a set: Test one capability, not a set ```zsh if [[ $PMSPEC != *f* ]]; then fpath+=( "${0:h}/functions" ) fi ``` #### STATUS: \[ global-parameter-with-capabilities \] - **Zi:** implemented. Sets `PMSPEC=0fuUpiPsX` in its [bootstrap](https://github.com/z-shell/zi/blob/1ab7a2f152243e3bb27839a70f9d17aaa570cf3f/zi.zsh#L7-L49). - **Zinit:** implemented independently. Sets `PMSPEC=0uUpiPsf` in its [bootstrap](https://github.com/zdharma-continuum/zinit/blob/fb12fbc83af3c6fbba94bc98a429cb93e89b546e/zinit.zsh#L13-L189). - **zcomet:** implemented. Sets `PMSPEC=0fbuiPs` in [`zcomet.zsh`](https://github.com/agkozak/zcomet/blob/3dfe6f837479c6731e74400a233739500ec6d648/zcomet.zsh#L20-L28). - **zgenom:** implemented conditionally, according to its automatic `bin` setting, in [`options.zsh`](https://github.com/jandamm/zgenom/blob/d99d5dc4b27695612de2b7054bb583dd19a7235d/options.zsh#L1-L43). - **Adoption:** across all 57 plugin and library entrypoints in the z-shell organization, `f` is tested 31 times, `b` four times, `P` once, and `X` never. ## Portable core The portable core begins here. Conformance does not depend on any interface in the preceding optional loader reference. The rules in this section through [Tests](#tests) define the manager-independent contract. ## Entrypoints and repository layout Provide one authoritative, sourceable Zsh entrypoint. A file named after the repository, such as `example.plugin.zsh`, is widely recognized: [Antidote searches for it first](https://github.com/mattmc3/antidote/blob/db19ea3aa9ad83dbe6ac465ecce0a0afc5a752a5/antidote.zsh#L765-L780), while [Oh My Zsh identifies plugins by that name](https://github.com/ohmyzsh/ohmyzsh/blob/b54a71977574cfcf659cc2f15a5e6422f17a8da7/oh-my-zsh.sh#L81-L98). Entrypoint discovery is not universal. For example, zcomet has its own ordered [entrypoint search](https://github.com/agkozak/zcomet/blob/3dfe6f837479c6731e74400a233739500ec6d648/zcomet.zsh#L214-L240), and Znap exposes [`znap.zsh` as its entrypoint](https://github.com/marlonrichert/zsh-snap/blob/7a954d507c02269e0c42737a460e5a94dc9b2992/znap.zsh#L1-L8). Therefore: 1. State the exact source command in the README. 2. Do not depend on a manager sourcing the "first" matching file. 3. Keep alternate entrypoints, if supplied, as thin wrappers around one implementation. 4. Make repeated loading harmless, or reject it with a clear diagnostic. Resolve the entrypoint directory from the sourced file, not from the caller's working directory. The standard expression accepts an optional manager-supplied `ZERO`, preserves ordinary source behavior through `$0`, falls back to `%N`, and makes the result absolute without resolving symlinks: Resolve the authoritative entrypoint ```zsh () { builtin emulate -L zsh local -r source_path=${1:a} local -r plugin_dir=${source_path:h} # Plugin initialization. } "${ZERO:-${${0:#$ZSH_ARGZERO}:-${(%):-%N}}}" ``` The anonymous function provides local scope, and `emulate -L zsh` establishes native Zsh option behavior while localizing option, pattern, and trap changes. `local -r` makes the two path invariants explicit and readonly. Zsh parses `local name=value` as a declaration assignment, so the unquoted scalar value remains one word. Keep the complete source-path expression at the call site; see [the loader interoperability reference](#zero-handling) for manager evaluation details and the official [Zsh filename modifiers](https://zsh.sourceforge.io/Doc/Release/Expansion.html#Modifiers) for `:a`, `:A`, and `:P`. A maintainable layout is: Example plugin layout ```text example/ ├── example.plugin.zsh # authoritative entrypoint ├── lib/ # optional private, eagerly sourced helpers │ └── setup.zsh ├── functions/ # optional autoload functions │ └── example_command ├── completions/ # optional completion functions │ └── _example ├── bin/ # optional user-invoked executables ├── LICENSE └── README.md ``` The directory names describe execution, not merely organization: - `lib/` contains private implementation files that the entrypoint must source eagerly. Do not add it to `fpath`, and do not retain setup-only functions after loading. - `functions/` contains functions loaded through Zsh autoload, normally one function per file. Use it for work not needed during every shell startup. - `completions/` contains native `_command` completion functions and follows completion-system ownership rules. - `bin/` contains programs the user invokes explicitly. Plugin loading must not execute them as initialization. Omit a directory when the plugin does not need that execution profile. A different directory name must have equally explicit documented semantics; it must not blur eager sourcing and autoloading. Document which directories a direct-source installation should add to `fpath` or `PATH`. A plugin may add its own `functions/` or `completions/` directory to `fpath` after checking that the exact path is absent. Do not mutate `PATH` during ordinary loading; document `bin/` for the user or manager instead. ## Loading contract and security Loading a plugin must be safe and predictable. During ordinary loading, a plugin must not: - access the network, or download or install packages; - evaluate untrusted text, or text fetched at load time; - mutate `PATH`; - write into its own checkout, which may be read-only or manager-controlled; - change global options as an incidental effect; - change the working directory without restoring it, or disturb the directory stack; - print routine success output; - prompt interactively when the shell is non-interactive. Send actionable diagnostics to standard error and stay silent on success. A shell that starts cleanly should print nothing. Defer cache creation and expensive discovery until the feature is actually used, rather than doing the work at load time. **Do not access the network** while loading. Network activity must be an explicit user action, such as a command the user runs, not a side effect of starting a shell. **Do not automatically prepend** or append to `PATH`. Document the plugin's `bin/` directory for explicit user or manager configuration. Treat all user-supplied and externally-derived text as untrusted data, never as code. ### Sourcing versus evaluating The rule differs by who is doing the loading. A **plugin or a user** must not use `eval` merely to load a file. Use the builtin and end the options so a leading dash or a path containing one cannot be reinterpreted: Load a file as a plugin or a user ```zsh source -- "$file" ``` A **plugin manager** may load a plugin by evaluating its text, for example with `eval "$( ## Function and option scope **Zsh guarantee:** `emulate -L zsh` establishes native Zsh emulation and localizes option changes to the function. The `-R` flag is a separate operation that resets all settable options to their default values. See the official [`emulate` documentation](https://zsh.sourceforge.io/Doc/Release/Shell-Builtin-Commands.html#index-emulate). Every non-trivial function should establish the option environment it needs and declare its parameters: Scope a public function ```zsh example_do_work() { emulate -L zsh setopt localtraps pipefail local input=$1 local -a results local -A metadata # Function implementation. } ``` Use `builtin` or `command` when calling a command whose name may be shadowed. Quote substitutions unless intentional Zsh splitting or globbing is required. An entrypoint can use a temporary loader function to contain options and temporary parameters: Contain entrypoint setup ```zsh _example_load() { emulate -L zsh setopt localtraps # Register the plugin's persistent functions, hooks, and state. } () { _example_load "$@" local loader_status=$? unfunction _example_load return "$loader_status" } "$@" ``` See [Standard recommended options](#standard-recommended-optionszsh-options) for the specific option set, and the [Zsh Scripting Standard](https://github.com/z-shell/.github/blob/main/.github/instructions/zsh-scripting.instructions.md) for the full normative rules. ## Names and persistent state Choose one stable, portable ASCII project identifier and document it in the README. Prefix every persistent function, parameter, widget, style context, hook callback, and other shell-visible name with that identifier. Public names use `example_...`; private callbacks and state use `_example_...`: Use a project namespace ```zsh typeset -gA _example_state _example_state[enabled]=1 example_refresh() { emulate -L zsh # Public operation. } _example_precmd() { emulate -L zsh # Internal callback. } ``` Prefix matching must respect a name boundary. An identifier of `example` owns `example_refresh` and `_example_precmd`; it does not own `exampled` merely because the bytes begin the same way. Maintain a documented public shell surface. It lists every public function, parameter, widget, style context, hook, alias, path entry, and unload function. Everything else must be private, temporary, or local. Loading must not retain setup-only functions, and a plugin must not create generic helper names that unrelated plugins could reasonably choose. Zsh has no plugin-local namespace for persistent callbacks, so a small number of project-prefixed globals can be necessary. Minimize and explicitly allowlist them rather than encoding arrays into one scalar or retaining every helper from an eagerly sourced library. See [Standard parameter naming](#standard-parameter-naming), [Shared `Plugins` hash](#standard-plugins-hash), [Standard function name-space prefixes](#standard-function-name-space-prefixes), and [Preventing parameter pollution](#preventing-parameter-pollution). ## Configuration contract Public configuration must use one coherent project-owned interface. For ordinary declarative Zsh configuration, use a namespaced `zstyle` context: Configure a plugin through one style context ```zsh zstyle ':example:config' mode compact zstyle ':example:config' features history matching ``` Read scalar values with `zstyle -s`, arrays with `zstyle -a`, booleans with `zstyle -t`, and document defaults, value types, accepted values, and when each setting is read. Supply defaults as local lookup fallbacks rather than registering them in the global style database, where an exact default could override a broader user pattern. Contexts must begin with the project identifier, and a plugin must not reuse another subsystem's style context. Use a project-owned global parameter only when persistent runtime state needs a Zsh type or mutation model that styles cannot provide. Such parameters are private implementation state, not a second public configuration interface. Keep real indexed and associative arrays when their shape matters. A conforming plugin must not expose a scatter of independent global parameters for ordinary settings. It also must not use exported environment variables as a substitute for namespacing. Environment variables are appropriate only when a value must cross an `exec` boundary or be inherited by a child process. Runtime commands may provide a public mutation interface when validation, atomic updates, or immediate reconfiguration is required. They must write the same documented configuration model rather than introduce a parallel one. ### Organizing plugin configuration The file in which a user stores plugin configuration is not part of portable conformance. Users with a small configuration may keep style declarations in `.zshrc`. To keep that startup file concise, the recommended companion file is: Recommended plugin configuration file ```text ${ZDOTDIR:-${XDG_CONFIG_HOME:-$HOME/.config}/zsh}/plugin-config.zsh ``` [`ZDOTDIR`](https://zsh.sourceforge.io/Doc/Release/Parameters.html#index-ZDOTDIR) keeps the file beside an intentionally relocated Zsh configuration. When `ZDOTDIR` is unset, the path follows the [XDG user-configuration location](https://specifications.freedesktop.org/basedir/0.8/). The name describes the file's purpose; `zstyle` is the configuration mechanism, not the subject of the file. Source the file from `.zshrc` before loading a plugin manager or directly sourcing plugins, because a plugin may read its settings once during initialization: Load plugin configuration before plugins ```zsh source -- "${ZDOTDIR:-${XDG_CONFIG_HOME:-$HOME/.config}/zsh}/plugin-config.zsh" # Load the plugin manager and plugins after this point. ``` Group declarations by project identifier and keep a repository or documentation link plus any useful local note beside each group: Group settings by plugin ```zsh # example/example # https://github.com/example/example # Use the compact display; the documented default is full. zstyle ':example:config' mode compact ``` The shell configuration owns this file. A plugin must not locate or source it. When one file becomes difficult to maintain, users may split it into `plugin-config.d/.zsh` fragments and source them in deterministic order before loading plugins. This is an optional organization technique, not a portable plugin interface. ## Completions and `compinit` ownership Store each native completion in a file named `_command`. Make the directory available on `fpath` before completion initialization, either through the plugin's exact-path membership check, explicit user configuration, or manager configuration. The plugin should not run `compinit` during ordinary loading. The shell configuration, framework, or manager owns completion initialization and should normally perform it once after constructing `fpath`. This avoids repeated startup work and preserves the owner's security policy. **Zsh guarantee:** `compinit` checks completion files and directories for ownership and unsafe write permissions and can rerun those checks through `compaudit`. Review the official [completion security rules](https://zsh.sourceforge.io/Doc/Release/Completion-System.html#Use-of-compinit). Do not bypass them with `compinit -u` inside a plugin. Oh My Zsh provides a representative framework-owned sequence that builds `fpath`, autoloads `compaudit` and `compinit`, and then applies its [completion security policy](https://github.com/ohmyzsh/ohmyzsh/blob/b54a71977574cfcf659cc2f15a5e6422f17a8da7/oh-my-zsh.sh#L75-L135). If a feature needs completion immediately after `compinit` has already run, document the integration step. Do not silently reinitialize the entire completion system. ## Lifecycle and resource ownership Treat every persistent side effect as an owned resource: - hooks and ZLE hook widgets; - functions, aliases, widgets, key bindings, and styles; - temporary files, cache files, and lock files; - background workers, coprocesses, and timers; - file descriptors and signal traps; - modules loaded by the plugin and special parameters created by those modules; - entries intentionally added to `path`, `fpath`, or other global arrays. Provide a namespaced cleanup function whose name is derived from the documented project identifier, for example `example_plugin_unload`. It must stop only known worker process IDs, wait for them when appropriate, close only stored file descriptors, remove only named hooks and functions, and tolerate partial initialization. Capture prior state before the first mutation. During unload, restore a prior value only when the current value is still the value the plugin installed. If the user or another component changed it after load, preserve the newer value. Remove a project-created container only when it is still project-owned and empty; never delete state another component subsequently adopted. An unload contract is exact, not best effort. Test it in a clean `zsh -f` process by taking a baseline before load, recording the declared load surface, unloading, and comparing the final shell to the baseline. The observation harness must initialize any modules or special parameters it needs before the baseline so it does not attribute its own effects to the plugin. At minimum, compare functions, parameters and attributes, aliases, options, traps, loaded modules, hooks, widgets, key bindings, styles, `path`, and `fpath`. Also test repeated source, partial initialization failure, hostile caller options, non-interactive loading, and user changes made after load. Update and migration work should be explicit commands or manager hooks, not ordinary load-time behavior. ## Startup performance Keep the entrypoint small. Prefer autoloaded functions for work that is not needed during every shell startup. Avoid subprocesses, repository scans, unconditional completion generation, and synchronous network access while loading. Measure before optimizing: Profile shell startup ```zsh zmodload zsh/zprof source ./example.plugin.zsh zprof ``` The official [`zsh/zprof` module](https://zsh.sourceforge.io/Doc/Release/Zsh-Modules.html#The-zsh_002fzprof-Module) reports function call counts and times. Also compare end-to-end startup with a representative `.zshrc`; micro benchmarks can miss completion, process, and I/O costs. If work runs asynchronously, record every worker PID and file descriptor as soon as it is created. Cleanup must terminate and reap only those workers, close only those descriptors, and remove any associated temporary files. ## Cache, state, and data directories Use the XDG base-directory variables when the platform and user configuration provide them: Derive project-owned storage paths ```zsh example_prepare_storage() { emulate -L zsh local cache_dir=${XDG_CACHE_HOME:-$HOME/.cache}/example local state_dir=${XDG_STATE_HOME:-$HOME/.local/state}/example local data_dir=${XDG_DATA_HOME:-$HOME/.local/share}/example # Create only the directory needed by the explicit operation. } ``` Use cache for reproducible data, state for persistent operational state, and data for durable user data. Create directories lazily with restrictive, user-appropriate permissions. Do not write into the plugin checkout during loading. A manager installation prefix is not a substitute for these user directories. ## Versions and feature compatibility Declare the oldest supported Zsh version in the README and continuous integration. Prefer feature checks over assumptions: Check an optional feature ```zsh if [[ -o interactive ]]; then if zmodload zsh/zle && (( ${+builtins[zle]} )); then # ZLE integration is available. fi fi ``` When a strict version check is necessary, autoload Zsh's `is-at-least` helper rather than comparing version strings manually: Enforce a minimum Zsh version ```zsh autoload -Uz is-at-least if ! is-at-least 5.8; then print -u2 -- 'example requires Zsh 5.8 or newer' return 1 fi ``` Fail without leaving partial hooks, workers, or state. Document degraded behavior for optional features. ## Tests At minimum, automate these checks: Portable smoke tests ```bash zsh -n ./example.plugin.zsh zsh -f -c 'source ./example.plugin.zsh' zsh -f -c 'source ./example.plugin.zsh; source ./example.plugin.zsh' zsh -f -c 'setopt ksharrays shwordsplit globsubst; source ./example.plugin.zsh' zsh -f -c 'setopt nofunctionargzero posixargzero; source ./example.plugin.zsh' ``` The last line verifies the scoped [entrypoint form](#entrypoints-and-repository-layout): under `posix_argzero` the parameter `0` is read-only, so a plugin that assigns to it fails there. Run the suite on every supported Zsh version. Add assertions for exported functions and parameters, hook registration, repeated loading, explicit cleanup, and non-interactive behavior. Test interactive and ZLE features in a pseudo-terminal rather than weakening their guards. For each failure path, verify that the plugin returns non-zero with an actionable diagnostic and leaves no worker, open descriptor, temporary file, or partially registered hook. ## Zsh plugin-programming best practices This page defines a Zsh plugin, and also serves as an information source for plugin creators. This section covers practices that apply specifically to plugin code. Scope of this section These are plugin-facing conventions. General Zsh authoring rules, covering options, quoting, expansion, arithmetic, traps, descriptors, and safety, live in the canonical [Zsh Scripting Standard](https://github.com/z-shell/.github/blob/main/.github/instructions/zsh-scripting.instructions.md). Worked idioms live in the [Zsh Native Scripting Handbook](https://wiki.zshell.dev/community/zsh_handbook/index.md). ### Use of `add-zsh-hook` to install hooks **Zsh guarantee:** the distributed `add-zsh-hook` function manages multiple callbacks for the supported hook entries, which are `chpwd`, `periodic`, `precmd`, `preexec`, `zshaddhistory`, `zshexit`, and `zsh_directory_name`. Its `-d` option removes a callback. For the meaning of each entry see the official [hook function documentation](https://zsh.sourceforge.io/Doc/Release/User-Contributions.html#Manipulating-Hook-Functions). The invocation syntax is: add-zsh-hook syntax ```zsh add-zsh-hook [ -L | -dD ] [ -Uzk ] hook function ``` Autoload it before use, register a named callback, and remove that exact callback during cleanup: Register and remove a shell hook ```zsh autoload -Uz add-zsh-hook _example_precmd() { emulate -L zsh # Update plugin state. } add-zsh-hook precmd _example_precmd example_plugin_unload() { emulate -L zsh autoload -Uz add-zsh-hook add-zsh-hook -d precmd _example_precmd unfunction _example_precmd example_plugin_unload unset _example_state } ``` Keep cleanup idempotent and list each owned resource explicitly. Do not compare the global function table before and after loading and then delete every new function; another component may have created one. See [Preventing function pollution](#preventing-function-pollution) for the safe narrowed form. ### Use of `add-zle-hook-widget` to install Zle Hooks The Zle editor is the part of Zsh responsible for receiving text from the user. It is based on widgets, which are Zsh functions allowed to run in Zle context, plus a few differences such as the `$WIDGET` parameter that the editor sets automatically. **Zsh guarantee:** `add-zle-hook-widget` adds widgets to documented ZLE hooks and supports `-d` for removal. See [`add-zle-hook-widget` in the user-contributions manual](https://zsh.sourceforge.io/Doc/Release/User-Contributions.html#index-add_002dzle_002dhook_002dwidget). The syntax is: add-zle-hook-widget syntax ```zsh add-zle-hook-widget [ -L | -dD ] [ -Uzk ] hook widget_name ``` The call resembles `add-zsh-hook`. The differences are that it takes a `widget_name` rather than a function name, and that `hook` is one of `isearch-exit`, `isearch-update`, `line-pre-redraw`, `line-init`, `line-finish`, `history-line-set`, or `keymap-select`. Their meanings are explained in the [Zsh documentation: Special Widgets](https://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Special-Widgets). Use this function because it allows installing **multiple** hooks per hook entry. Before it existed, the only way was to define a widget named after one of the special widgets. It has been available since Zsh `5.3` and should be used instead. Only configure line-editor behavior in an interactive shell with ZLE available: Register an interactive ZLE hook ```zsh if [[ -o interactive ]]; then if zmodload zsh/zle && (( ${+builtins[zle]} )); then autoload -Uz add-zle-hook-widget _example_line_init() { emulate -L zsh # ZLE-only behavior. } add-zle-hook-widget line-init _example_line_init fi fi ``` Cleanup must repeat the guards, autoload the helper, remove the exact hook with `add-zle-hook-widget -d`, delete any project-owned widgets with `zle -D`, and unfunction the callbacks. Do not assume that `zle` can run in a non-interactive shell or outside an active widget. ### Standard parameter naming Use descriptive lowercase names for local parameters and explicit `typeset`, `local`, or `integer` declarations for their types. Use the stable project identifier for persistent parameters, with a leading underscore for private state, such as `_example_state` and `_example_items`. Capitalization is not a type system in Zsh. The shell's `REPLY` and `reply` conventions remain relevant when implementing APIs that specify those names, but a plugin must not rely on capitalization alone to communicate a parameter's type or ownership. Declare the type and namespace explicitly. Native completion functions retain the required `_command` file and function naming convention. ### Shared `Plugins` hash Older z-shell plugins used a shared hash named `Plugins` as a registry for plugin paths and state. It has no ownership boundary: one plugin can create the parameter, another can add keys, and neither can determine safely whether the container should disappear during unload. A version 2 portable plugin must not create, require, or write this shared hash. A plugin manager may maintain its own registry as an optional manager profile, but portable plugin code must work without observing or mutating it. Use the [configuration contract](#configuration-contract) for public settings and project-prefixed typed parameters only for necessary private runtime state. ### Standard recommended [options](https://zsh.sourceforge.io/Doc/Release/Options.html) The following snippet is recommended at the beginning of each of the main functions provided by the plugin: Recommended option baseline ```zsh builtin emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace} builtin setopt extended_glob warn_create_global typeset_silent no_short_loops rc_quotes no_auto_pushd ``` `emulate -L zsh` emulates a clean Zsh environment local to the function it is called from. The flags are: - `-L`: makes the emulation local to the function, so it does not affect the shell environment outside it. This is the flag used above. - `-R`: additionally resets all options to their default values, as if the shell had just started. Use `emulate -LR zsh` when you want that stronger reset; it is a different operation from `-L` alone. > Reference: [Zsh shell builtin commands](https://zsh.sourceforge.io/Doc/Release/Shell-Builtin-Commands.html) The emulation is then altered with the following options: - `${=${options[xtrace]:#off}:+-o xtrace}` keeps `xtrace` active if it was already active at the entry to the function, so tracing is not silently lost. - `extended_glob` enables one of the main Zsh features, the advanced built-in globbing mechanism. - `warn_create_global` prints a warning each time a global variable is defined without an explicit `typeset`, `local`, or `declare` call. It catches typos and missing localizations. - `typeset_silent` allows calling `typeset` or `local` more than once on the same variable. Without it, the second call prints the variable contents. It allows declaring variables inside loops, near the place of use. - `no_short_loops` disables the short-loops syntax, because when enabled it limits the parser's ability to detect errors. See this [zsh-workers post](https://www.zsh.org/mla/workers/2011/msg01050.html) for details. - `rc_quotes` adds the ability to insert an apostrophe into an apostrophe-quoted string by doubling it, so `'a string''s example'` yields `a string's example`. - `no_auto_pushd` disables automatically pushing the directory passed to `cd` onto the directory stack, so internal directory changes made by the plugin do not pollute the global stack. For the full normative rules on options and emulation, including when option state must outlive a single function call, see `zsh/options/localize` and `zsh/options/declare-correctness-state` in the [Zsh Scripting Standard](https://github.com/z-shell/.github/blob/main/.github/instructions/zsh-scripting.instructions.md). ### Standard recommended variables It is good to localize the following variables at the entry of the main function of a plugin: Localize pattern-matching parameters ```zsh local MATCH REPLY; integer MBEGIN MEND local -a match mbegin mend reply ``` The variables starting with `m` and `M` are used by substitutions employing the `(#b)` and `(#m)` flags respectively. They should not leak to the global scope. Their automatic creation would also trigger a warning from the `warn_create_global` option. The `reply` and `REPLY` parameters are normally used to return an array or a scalar from a function. That is the standard way of passing values out of functions. Their use is naturally limited to functions called from the main function of a plugin. They should not be used to pass data around between prompts, so it is natural to localize them in the main function. ### Standard function name-space prefixes Use portable ASCII names derived from the stable project identifier: - `example_action` for a documented public function; - `_example_callback` for a private hook, widget, or style callback; - `_example_helper` for a private persistent helper when autoloading or later callbacks genuinely require it; - `_command` only for the native completion function for `command`. Public and private names are part of the same shell command namespace, so the underscore marks visibility but does not replace the project identifier. An unprefixed `_callback` is not project-owned. Do not assign semantic roles through punctuation-only or non-ASCII prefixes. Names beginning with `.`, `+`, `/`, or similar role markers are harder to autoload, type, search, and represent consistently as file names. Manager API names beginning with `@`, such as the optional [unload](#run-on-unload-call) and [update](#run-on-update-call) callbacks, are manager-owned names, not a pattern for plugin-defined functions. #### Historical function-name prefixes Earlier revisions proposed symbolic prefixes for private, hook, output, debugging, and API functions. The anchor remains so historical source comments still reach an explanation, but those prefixes are not part of version 2 portable conformance. Refactored plugins should replace them with the ASCII project-owned forms above. ### Preventing function pollution When writing a larger autoload function, it is often the case that the function contains definitions of other functions. When the main function finishes executing, those sub-functions remain defined, which pollutes the command namespace. The following snippet, added to the main function, unsets the sub-functions on leaving it: Unset project sub-functions on exit ```zsh example_main() { emulate -L zsh local -a _example_pre=( ${(k)functions} ) { # Define and use _example_* sub-functions here. } always { unset -f -- ${(M)${(k)functions[@]:|_example_pre}:#_example_*} 2>/dev/null } } ``` The snippet works as follows: 1. `local -a _example_pre=( ${(k)functions} )` records the functions defined at entry, so the list excludes anything the body is about to define. Keeping it `local` means the record itself does not leak. 2. The `always { }` block runs on normal completion and on error alike, so no `EXIT` and `INT` trap pair is required and no trap state escapes the function. 3. `${(k)functions[@]:|_example_pre}` subtracts the entry list from the current list using the `:|` array-subtraction operator, leaving only functions defined inside the block. 4. `${(M)…:#_example_*}` then keeps only the names matching the project's own prefix, and `unset -f` removes them. Reading older plugins Published plugins often name the saved list `prjef` or a project-specific variant such as `rustef`. In that convention `prj` stands for the project name and `ef` for "entry functions". `_example_pre` above is the same idea with a clearer name. The older name is fine; what matters is that the list is `local` and that the cleanup is filtered by prefix. Do not omit the prefix filter Step 4 is not cosmetic. Without it, the subtraction deletes every function created during the block, including ones the plugin did not create. An `autoload` triggered inside the block is a common case: ```shell-session $ zsh -f -c 'f(){ local -a p=( ${(k)functions} ) { autoload -Uz is-at-least; is-at-least 5.8 } always { unset -f -- "${(k)functions[@]:|p}" 2>/dev/null } }; f; print ${+functions[is-at-least]}' 0 ``` `is-at-least` was destroyed by cleanup that did not create it. With the `_example_*` filter in place, it survives and only the project's own sub-functions are removed. Replace `_example` with your own project prefix. ### Preventing parameter pollution When writing a plugin one often needs to keep state during the Zsh session. It is natural to use global parameters for this. As the number of parameters grows, one may want to limit it. A single private project-owned parameter can be sufficient for flat scalar runtime state: One project-owned hash for flat state ```zsh typeset -gA _example_state _example_state[loaded]=1 _example_state[mode]=fast ``` This replaces a scatter of individual globals with one inspectable parameter. It remains private runtime state and must not become a second public configuration interface. Use the [configuration contract](#configuration-contract) for user settings. Do not use this to encode arrays or nested structures Earlier revisions of this page recommended flattening real arrays and hashes into delimiter-encoded keys, such as `_example_state[some_array__1]`, and described the technique as unproblematic. Testing does not support that. Three traps apply, verified on Zsh 5.9.2: 1. **Sorting silently misorders the result.** The obvious `(o)` sort is lexicographic, which is wrong for numeric indices. Numeric `(on)` is required, and nothing warns you: ```shell-session $ # keys arr__1 arr__2 arr__3 arr__9 arr__10 arr__11 $ print -r -- ${(o)keys} arr__1 arr__10 arr__11 arr__2 arr__3 arr__9 $ print -r -- ${(on)keys} arr__1 arr__2 arr__3 arr__9 arr__10 arr__11 ``` A plugin that flattens an array and sorts with `(o)` has an ordering bug that stays invisible until the tenth element exists. 2. **Key order is not insertion order.** Two hashes filled with the same keys in opposite orders enumerate identically. Key order is a property of the hash, not of how it was filled, and the manual does not specify it, so there is no "read the keys back" shortcut. 3. **The delimiter is ambiguous.** Zsh hash keys accept arbitrary content, `__` included, so `_example_state[user__name]` and `_example_state[user__name__first]` cannot be decoded to a unique structure. Additionally, `${#array}`, `${array[-1]}`, and `${array[2,4]}` all work directly on a real array; flattened, each becomes a pattern scan plus a numeric sort plus manual arithmetic. Keep real arrays and real hashes whenever order or shape matters. ## Optional manager interoperability profiles This appendix records how named managers implement the optional loader interfaces above. It does not add anything to the portable core. Detect each facility before using it and retain direct-source behavior. Exact capability strings and behavior are manager implementation details, not Zsh guarantees. ### Zi profile: canonical for z-shell **Status: implemented in Zi's active source.** The canonical z-shell manager is [Zi](https://github.com/z-shell/zi). Its bootstrap independently declares `zsh_loaded_plugins`, exports `ZPFX` and `PMSPEC`, and derives its own location with a `ZERO` fallback in [`zi.zsh`](https://github.com/z-shell/zi/blob/1ab7a2f152243e3bb27839a70f9d17aaa570cf3f/zi.zsh#L7-L49). Its loader records entries in [`zsh_loaded_plugins`](https://github.com/z-shell/zi/blob/1ab7a2f152243e3bb27839a70f9d17aaa570cf3f/lib/zsh/autoload.zsh#L1170-L1181). Zi also calls a plugin-named `*_plugin_unload` function and processes its own stored unload callback in [`lib/zsh/autoload.zsh`](https://github.com/z-shell/zi/blob/1ab7a2f152243e3bb27839a70f9d17aaa570cf3f/lib/zsh/autoload.zsh#L708-L765). Zi sets `PMSPEC=0fuUpiPsX`. Its `@zsh-plugin-run-on-unload` and `@zsh-plugin-run-on-update` functions store manager callback strings, as shown in [Zi's callback source](https://github.com/z-shell/zi/blob/1ab7a2f152243e3bb27839a70f9d17aaa570cf3f/zi.zsh#L1121-L1133). For z-shell projects, test the portable core by direct sourcing and add Zi tests for any Zi profile behavior you intentionally support. ### Zinit profile: separate alternative project **Status: implemented independently in Zinit's active source.** [Zinit](https://github.com/zdharma-continuum/zinit) is an alternative project under `zdharma-continuum`; it is not Zi. Zinit separately declares `zsh_loaded_plugins`, derives `ZINIT[ZERO]`, exports `ZPFX`, and sets `PMSPEC=0uUpiPsf` in its [`zinit.zsh` bootstrap](https://github.com/zdharma-continuum/zinit/blob/fb12fbc83af3c6fbba94bc98a429cb93e89b546e/zinit.zsh#L13-L189). Zinit separately implements [`@zsh-plugin-run-on-unload` and `@zsh-plugin-run-on-update`](https://github.com/zdharma-continuum/zinit/blob/fb12fbc83af3c6fbba94bc98a429cb93e89b546e/zinit.zsh#L1287-L1299) and invokes a plugin-named unload function in [`zinit-autoload.zsh`](https://github.com/zdharma-continuum/zinit/blob/fb12fbc83af3c6fbba94bc98a429cb93e89b546e/zinit-autoload.zsh#L2453-L2503). Support for these names in both projects is shared convention, not evidence that Zi and Zinit are the same manager. Test them separately if your plugin opts into both profiles. ### zcomet and zgenom profiles **zcomet status: implemented, with unload explicitly described by its source as an incomplete attempt.** zcomet sets `PMSPEC=0fbuiPs` in [`zcomet.zsh`](https://github.com/agkozak/zcomet/blob/3dfe6f837479c6731e74400a233739500ec6d648/zcomet.zsh#L20-L28). Its loader [supplies `ZERO` and appends `zsh_loaded_plugins`](https://github.com/agkozak/zcomet/blob/3dfe6f837479c6731e74400a233739500ec6d648/zcomet.zsh#L201-L205). Its [`zcomet_unload` function](https://github.com/agkozak/zcomet/blob/3dfe6f837479c6731e74400a233739500ec6d648/functions/zcomet_unload#L3-L34) conditionally invokes `*_plugin_unload`, but the source itself says the routine needs work. Do not infer broader lifecycle guarantees. **zgenom status: implemented for the listed variables; no unload claim is made here.** zgenom sets a `PMSPEC` value according to its automatic `bin` setting, defines `ZPFX`, and initializes `zsh_loaded_plugins` in [`options.zsh`](https://github.com/jandamm/zgenom/blob/d99d5dc4b27695612de2b7054bb583dd19a7235d/options.zsh#L1-L43). Its loader supplies `ZERO` and appends the repository ID before sourcing in [`functions/zgenom-load`](https://github.com/jandamm/zgenom/blob/d99d5dc4b27695612de2b7054bb583dd19a7235d/functions/zgenom-load#L8-L32). The differing `PMSPEC` values demonstrate why plugins must not treat any letter list as a universal capability registry. ## Review checklist Before publishing a plugin, confirm that: - one documented entrypoint works with `source` under `zsh -f`; - self-location uses the scoped entrypoint form and remains valid under `posix_argzero`; - `lib/`, `functions/`, `completions/`, and `bin/` follow their documented eager, autoload, completion, and executable profiles; - `functions/` and `completions/` use exact `fpath` membership checks, while ordinary loading does not mutate `PATH`; - a project-identifier-derived unload function exists, and every hook, widget, worker, descriptor, and file has explicit cleanup; - functions localize options and parameters; - the README declares one stable ASCII project identifier and the complete public shell surface; - persistent names and state are project-owned and setup-only helpers do not remain after load; - public configuration uses one namespaced style context or a justified runtime command, not scattered global parameters; - portable code neither requires nor mutates shared manager or plugin registry parameters; - completion initialization remains under user, framework, or manager control; - non-interactive loading does not touch ZLE; - loading performs no network access, untrusted evaluation, package installation, or unguarded `PATH` mutation; - cache, state, and data use appropriate user directories; - startup cost has been measured with `zsh/zprof` and end-to-end timing; - supported Zsh versions and hostile option combinations are tested; - a clean-process lifecycle test proves the declared load surface and exact ownership-aware restoration after unload; and - every deliberately supported optional manager interface is feature-tested, has an independent profile test, and retains direct-source behavior. ## Next Steps - Read the canonical [Zsh Scripting Standard](https://github.com/z-shell/.github/blob/main/.github/instructions/zsh-scripting.instructions.md) for normative Zsh authoring, review, and safety rules. - Read the [Zsh Native Scripting Handbook](https://wiki.zshell.dev/community/zsh_handbook/index.md) for worked idioms. - Read the official [Zsh function documentation](https://zsh.sourceforge.io/Doc/Release/Functions.html) for autoloading and hooks. - Review [Zi's source](https://github.com/z-shell/zi) when adding canonical z-shell integration. - Review each alternative manager's active source rather than assuming Zi behavior applies to it. --- --- title: ✨ Completion Configuration description: Configure Zsh completion behavior and styles with compinit and zstyle. canonical_url: https://wiki.zshell.dev/community/zsh-guide/completion markdown_url: https://wiki.zshell.dev/community/zsh-guide/completion/index.md locale: en source_path: community/01_zsh_guide/03_completion.mdx --- # ✨ Completion Configuration ## Initialize completion Zsh's modern completion system is initialized by `compinit`. Load it once, after adding all required completion directories to `fpath`: ~/.zshrc ```zsh autoload -Uz compinit compinit ``` See the [Completion System manual](https://zsh.sourceforge.io/Doc/Release/Completion-System.html) for initialization options and security checks. ## Configure completion with zstyle The completion system reads `zstyle` values from contexts such as `:completion:*`. A more specific context overrides a broader one, which lets you tune individual completers, commands, or tags. ### Approximate matching ~/.zshrc ```zsh zstyle ':completion:*' completer _complete _match _approximate zstyle ':completion:*:match:*' original only zstyle -e ':completion:*:approximate:*' max-errors \ 'reply=($((($#PREFIX + $#SUFFIX) / 3 > 7 ? 7 : ($#PREFIX + $#SUFFIX) / 3))numeric)' ``` ### Grouping, descriptions, and matching ~/.zshrc ```zsh zstyle ':completion:*:matches' group yes zstyle ':completion:*:options' description yes zstyle ':completion:*:options' auto-description '%d' zstyle ':completion:*:corrections' format ' %F{green}-- %d (errors: %e) --%f' zstyle ':completion:*:descriptions' format ' %F{yellow}-- %d --%f' zstyle ':completion:*:messages' format ' %F{purple}-- %d --%f' zstyle ':completion:*:warnings' format ' %F{red}-- no matches found --%f' zstyle ':completion:*:default' list-prompt '%S%M matches%s' zstyle ':completion:*' group-name '' zstyle ':completion:*' verbose yes zstyle ':completion:*' matcher-list \ 'm:{a-zA-Z}={A-Za-z}' \ 'r:|[._-]=* r:|=*' \ 'l:|=* r:|=*' zstyle ':completion:*:functions' ignored-patterns '(_*|pre(cmd|exec))' zstyle ':completion:*' use-cache true ``` ### Menu selection ~/.zshrc ```zsh zstyle ':completion:*' menu select ``` ### Colors When `LS_COLORS` is defined, split its colon-separated value into the array expected by `list-colors`: ~/.zshrc ```zsh zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS} ``` ## Ubuntu's global compinit call Ubuntu's system-wide Zsh configuration can call `compinit` automatically. If your own `.zshrc` initializes completion, prevent the duplicate call by setting this distro-specific variable in `$ZDOTDIR/.zshenv` (normally `~/.zshenv`): ~/.zshenv ```zsh skip_global_compinit=1 ``` This variable is an Ubuntu packaging convention, not a portable Zsh setting. The current behavior is documented in the [Debian package source](https://sources.debian.org/src/zsh/5.9-8/debian/zshrc/). --- --- title: ⚙️ Configuration and Options description: Configure Zsh options, history behavior, and interactive shell preferences. canonical_url: https://wiki.zshell.dev/community/zsh-guide/configuration markdown_url: https://wiki.zshell.dev/community/zsh-guide/configuration/index.md locale: en source_path: community/01_zsh_guide/02_configuration.mdx --- # ⚙️ Configuration and Options ## Set and unset options Zsh option names are case-insensitive, and underscores are ignored. For example, `allexport` and `ALL_EXPORT` refer to the same option. Prefix a name with `no` to invert it, so `setopt no_beep` is equivalent to `unsetopt beep`. See the [Zsh Options manual](https://zsh.sourceforge.io/Doc/Release/Options.html) for the complete option reference. ## History configuration The following is a practical starting point for interactive history: ~/.zshrc ```zsh setopt append_history # Append history instead of replacing the history file. setopt extended_history # Save timestamps and command durations. setopt hist_expire_dups_first # Expire duplicates first when trimming history. setopt hist_find_no_dups # Do not display an entry already found in a search. setopt hist_ignore_all_dups # Remove older duplicates when adding an entry. setopt hist_ignore_dups # Do not record an entry identical to the previous one. setopt hist_ignore_space # Do not record commands that start with a space. setopt hist_reduce_blanks # Remove extra blanks from stored commands. setopt hist_save_no_dups # Do not write duplicate entries to the history file. setopt hist_verify # Load history expansions into the editor before running them. # Choose one immediate-write mode: setopt inc_append_history # Write each command immediately for independent sessions. # setopt share_history # Import and append commands across active sessions. ``` Choose one history-sharing mode Do not enable `INC_APPEND_HISTORY` and `SHARE_HISTORY` together. `SHARE_HISTORY` already appends commands incrementally and adds extended timestamps while importing commands from other sessions. ## Other interactive options Review each option before enabling it, because options change shell-wide behavior. ~/.zshrc ```zsh setopt auto_cd # Change directory by entering a directory name. setopt auto_list # List choices after an ambiguous completion. setopt auto_pushd # Push the previous directory onto the directory stack. setopt bang_hist # Enable csh-style history expansion with !. setopt interactive_comments # Allow comments in interactive shells. setopt multios # Use implicit tees or cats for multiple redirections. setopt no_beep # Do not beep on errors. setopt prompt_subst # Expand parameters and substitutions when drawing the prompt. setopt pushd_ignore_dups # Avoid duplicate entries in the directory stack. setopt pushd_minus # Exchange the meanings of + and - in stack indices. ``` --- --- title: ⌨️ Key Bindings description: Inspect, add, and remove Zsh Line Editor key bindings with bindkey. canonical_url: https://wiki.zshell.dev/community/zsh-guide/key-bindings markdown_url: https://wiki.zshell.dev/community/zsh-guide/key-bindings/index.md locale: en source_path: community/01_zsh_guide/04_key_bindings.mdx --- # ⌨️ Key Bindings ## Inspect key bindings The Zsh Line Editor (ZLE) stores bindings in keymaps. Run `bindkey` to list the selected keymap, or `bindkey -L` to print bindings as commands that can be reused in `.zshrc`. Key sequences can use caret notation, escape sequences, octal, hexadecimal, or Unicode forms. Common examples include: - `\e` or `\E`: Escape - `^[`: Escape, often produced by an Alt-key sequence - `^?`: Delete - `^X`: Control-X Terminal emulators can encode modified keys differently. Use `cat -v` or a ZLE diagnostic widget to inspect the sequence generated by your terminal before adding a binding. ## Add a key binding Pass a key sequence and widget name to `bindkey`: ~/.zshrc ```zsh bindkey '^R' history-incremental-search-backward ``` Use `bindkey -M keymap` when the binding should target a keymap other than the selected one. ## Remove a key binding Use `bindkey -r` to unbind a specified string in the selected keymap: ~/.zshrc ```zsh bindkey -r '^R' ``` Add `-R` if the argument should be interpreted as a range. The `-d` flag does not delete an individual binding. It deletes all existing keymaps and restores the default keymap state, so it should not be used as an individual-key removal command. See the [Zsh Line Editor manual](https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Keymaps) for keymap selection, binding forms, and range behavior. --- --- title: ⏱️ Startup Profiling description: Profile Zsh startup with zprof, xtrace timestamps, and lightweight checkpoints. canonical_url: https://wiki.zshell.dev/community/zsh-guide/startup-profiling markdown_url: https://wiki.zshell.dev/community/zsh-guide/startup-profiling/index.md locale: en source_path: community/01_zsh_guide/05_startup_profiling.mdx --- # ⏱️ Startup Profiling ## Profile functions with zprof The `zsh/zprof` module records timing information for shell functions. Load it near the top of `.zshrc`, then print the report after the code being measured: ~/.zshrc ```zsh zmodload zsh/zprof # Load the functions or configuration to measure here. zprof ``` Profiling continues while the module is loaded. Start a fresh Zsh process for each comparison so one run does not include earlier function calls. See the [`zsh/zprof` module reference](https://zsh.sourceforge.io/Doc/Release/Zsh-Modules.html#The-zsh_002fzprof-Module) for the report columns and sorting options. ## Trace startup lines For line-by-line timing, guard Zsh's execution trace behind a variable. Place this near the top of `.zshrc`: ~/.zshrc ```zsh typeset -g PROFILE_STARTUP=${PROFILE_STARTUP:-false} if [[ $PROFILE_STARTUP == true ]]; then zmodload zsh/zprof PS4=$'%D{%M%S%.} %N:%i> ' exec 3>&2 2>"$HOME/startlog.$$" setopt xtrace prompt_subst fi ``` Place the matching cleanup near the bottom: ~/.zshrc ```zsh if [[ $PROFILE_STARTUP == true ]]; then unsetopt xtrace exec 2>&3 3>&- zprof >"$HOME/zshprofile.$(date +%s)" fi ``` Run a new interactive Zsh with `PROFILE_STARTUP=true zsh -i`. The trace is written to `$HOME/startlog.`, and the function report is written to `$HOME/zshprofile.`. ## Add lightweight checkpoints For coarse comparisons, use Zsh's floating-point `SECONDS` parameter and an array: ~/.zshrc ```zsh typeset -ga ZLOGS typeset -F4 SECONDS=0 zmsg() { ZLOGS+=("$1: $((SECONDS * 1000)) ms") } # Run a section to measure. zmsg 'Loaded functions' # Run another section. zmsg 'Loaded something else' zmsg 'Done' ``` Print the collected checkpoints after startup: ```zsh print -rl -- $ZLOGS ``` --- --- title: 🧪 ZUnit description: ZUnit — a unit testing framework for Zsh projects with @test syntax, assertions, TAP output, and CI integration. canonical_url: https://wiki.zshell.dev/community/zunit markdown_url: https://wiki.zshell.dev/community/zunit/index.md locale: en source_path: community/03_zunit/index.mdx --- # 🧪 ZUnit ZUnit is a unit testing framework for Zsh projects, maintained by the [Z-Shell organization](https://github.com/z-shell). It brings a concise `@test` syntax inspired by [Bats](https://github.com/bats-core/bats-core), a rich assertion library, per-test `@setup`/`@teardown` hooks, TAP output, HTML reports, and a `zunit init` scaffolding command. ![ZUnit: write @test blocks, run assertions, see results](https://wiki.zshell.dev/img/svg/zunit-banner.svg) Scaffold in seconds Run `zunit init --github-actions` to generate a ready-to-use GitHub Actions workflow alongside the project scaffold — CI from day one. ## Documentation [![](https://wiki.zshell.dev/img/svg/cards/packages.svg) ### Installation Manual install, build from source, dependencies, and Zi integration. Install →](https://wiki.zshell.dev/community/zunit/zunit-installation/index.md)[![](https://wiki.zshell.dev/img/svg/cards/zi-console.svg) ### Test Syntax `@test`, `@setup`/`@teardown` hooks, and helper functions. Learn syntax →](https://wiki.zshell.dev/community/zunit/zunit-test-syntax/index.md)[![](https://wiki.zshell.dev/img/svg/cards/flexible.svg) ### Assertions All ~25 assertion functions with examples — equals, contains, match, status. See assertions →](https://wiki.zshell.dev/community/zunit/zunit-assertions/index.md)[![](https://wiki.zshell.dev/img/svg/cards/speed.svg) ### Running Tests CLI usage, flags, TAP output, HTML reports, and filtering. Run tests →](https://wiki.zshell.dev/community/zunit/zunit-running-tests/index.md)[![](https://wiki.zshell.dev/img/svg/cards/annexes.svg) ### Configuration `.zunit.yml` key reference — directories, TAP, HTML, time limits. Configure →](https://wiki.zshell.dev/community/zunit/zunit-configuration/index.md)[![](https://wiki.zshell.dev/img/svg/cards/ecosystem.svg) ### CI Integration GitHub Actions and Travis CI workflow examples ready to drop in. Set up CI →](https://wiki.zshell.dev/community/zunit/zunit-ci/index.md) ## Quick start ```zsh # 1. Install git clone https://github.com/z-shell/zunit.git cd zunit && ./build.zsh && cp ./zunit /usr/local/bin # 2. Scaffold a new project cd my-project zunit init # 3. Write a test (tests/example.zunit) # #!/usr/bin/env zunit # @test 'addition works' { # assert 2 equals 2 # } # 4. Run zunit ``` --- --- title: ✅ Assertions description: Complete reference for all ZUnit assertion functions. canonical_url: https://wiki.zshell.dev/community/zunit/zunit-assertions markdown_url: https://wiki.zshell.dev/community/zunit/zunit-assertions/index.md locale: en source_path: community/03_zunit/03_assertions.mdx --- # ✅ Assertions Assertions are called through the `assert` helper: ```zunit assert [...] ``` If an assertion fails, the test stops immediately and is marked as failed. ## String assertions ### `same_as` Passes when `$value` is equal to `$comparison` (string equality). ```zunit assert "$output" same_as 'expected string' ``` ### `different_to` Passes when `$value` is **not** equal to `$comparison`. ```zunit assert "$result" different_to 'forbidden' ``` ### `contains` Passes when `$value` contains `$comparison` as a substring. ```zunit assert "$output" contains 'hello' ``` ### `does_not_contain` Passes when `$value` does **not** contain `$comparison`. ```zunit assert "$output" does_not_contain 'error' ``` ### `is_substring_of` Passes when `$value` is a substring of `$comparison` (reverse of `contains`). ```zunit assert 'hell' is_substring_of 'hello world' ``` ### `is_not_substring_of` Passes when `$value` is **not** a substring of `$comparison`. ```zunit assert 'xyz' is_not_substring_of 'hello world' ``` ### `matches` Passes when `$value` matches the regex `$pattern`. ```zunit assert "$output" matches '^[0-9]+$' ``` ### `does_not_match` Passes when `$value` does **not** match the regex `$pattern`. ```zunit assert "$output" does_not_match '^Error' ``` ### `is_empty` Passes when `$value` is an empty string or unset. ```zunit assert "$output" is_empty ``` ### `is_not_empty` Passes when `$value` is non-empty. ```zunit assert "$result" is_not_empty ``` ## Integer assertions ### `equals` Passes when the two integers are equal (`-eq`). ```zunit assert $state equals 0 ``` ### `not_equal_to` Passes when the two integers are **not** equal (`-ne`). ```zunit assert $count not_equal_to 0 ``` ### `is_positive` Passes when the integer is greater than zero. ```zunit assert $count is_positive ``` ### `is_negative` Passes when the integer is less than zero. ```zunit assert $diff is_negative ``` ### `is_greater_than` Passes when `$value` is greater than `$comparison` (`-gt`). ```zunit assert $count is_greater_than 5 ``` ### `is_less_than` Passes when `$value` is less than `$comparison` (`-lt`). ```zunit assert $count is_less_than 100 ``` ## Array assertions ### `in` Passes when `$value` is found in the array. ```zunit local -a fruits=(apple banana cherry) assert 'banana' in "${fruits[@]}" ``` ### `not_in` Passes when `$value` is **not** found in the array. ```zunit assert 'grape' not_in "${fruits[@]}" ``` ## Hash assertions ### `is_key_in` Passes when `$value` is a key in the associative array. ```zunit local -A config=(host localhost port 5432) assert 'host' is_key_in "${(kv)config[@]}" ``` ### `is_not_key_in` Passes when `$value` is **not** a key in the hash. ```zunit assert 'password' is_not_key_in "${(kv)config[@]}" ``` ### `is_value_in` Passes when `$value` is a value in the associative array. ```zunit assert 'localhost' is_value_in "${(kv)config[@]}" ``` ### `is_not_value_in` Passes when `$value` is **not** a value in the hash. ```zunit assert 'remotehost' is_not_value_in "${(kv)config[@]}" ``` ## Filesystem assertions All filesystem assertions accept relative paths (resolved against the test directory) or absolute paths. ### `exists` Passes when the path exists (file, directory, or symlink). ```zunit assert 'output.txt' exists ``` ### `is_file` Passes when the path exists and is a regular file. ```zunit assert 'output.txt' is_file ``` ### `is_dir` Passes when the path exists and is a directory. ```zunit assert '_output' is_dir ``` ### `is_link` Passes when the path exists and is a symbolic link. ```zunit assert 'current' is_link ``` ### `is_readable` Passes when the path exists and is readable by the current user. ```zunit assert 'config.yml' is_readable ``` ### `is_writable` Passes when the path exists and is writable. ```zunit assert 'output.txt' is_writable ``` ### `is_executable` Passes when the path exists and is executable. ```zunit assert 'myscript.zsh' is_executable ``` --- --- title: 🔄 CI Integration description: Running ZUnit tests in CI — GitHub Actions (recommended) and Travis CI (legacy). canonical_url: https://wiki.zshell.dev/community/zunit/zunit-ci markdown_url: https://wiki.zshell.dev/community/zunit/zunit-ci/index.md locale: en source_path: community/03_zunit/06_ci.mdx --- # 🔄 CI Integration ## Generate CI config with `zunit init` The fastest way to get a CI workflow is to use the scaffolding command: ```zunit zunit init --github-actions # GitHub Actions (recommended) zunit init --travis # Travis CI (legacy) ``` Both flags can be combined with the regular `zunit init` run. ## GitHub Actions (recommended) .github/workflows/zunit.yml ```yaml --- name: "ZUnit" on: push: pull_request: workflow_dispatch: {} permissions: contents: read jobs: zunit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install dependencies run: | sudo apt-get update sudo apt-get install -yq zsh mkdir -p .bin curl -fsSL 'https://github.com/z-shell/zunit/releases/latest/download/zunit' > .bin/zunit curl -fsSL 'https://raw.githubusercontent.com/z-shell/src/d38202ee20efeec42900ce26f747a3024028845f/public/zsh/snippets/revolver' > .bin/revolver curl -fsSL 'https://raw.githubusercontent.com/z-shell/src/d38202ee20efeec42900ce26f747a3024028845f/public/zsh/snippets/color.zsh' > .bin/color chmod u+x .bin/{color,revolver,zunit} - name: Test run: PATH="$PWD/.bin:$PATH" zunit --tap tests ``` tip The `--tap` flag produces machine-readable output. You can pipe it through a TAP reporter (e.g., `tap-junit`) to generate JUnit XML for GitHub Actions test summary annotations. ### Using a pinned ZUnit release Replace `latest/download/zunit` with a versioned URL to pin to a specific release: ```yaml curl -fsSL 'https://github.com/z-shell/zunit/releases/download/v0.9.0/zunit' > .bin/zunit ``` ## Travis CI (legacy) warning Travis CI is a legacy option. GitHub Actions is the recommended CI platform for new projects. .travis.yml ```yaml addons: apt: packages: - zsh install: - mkdir .bin - curl -L https://github.com/z-shell/zunit/releases/latest/download/zunit > .bin/zunit - curl -L https://raw.githubusercontent.com/z-shell/src/d38202ee20efeec42900ce26f747a3024028845f/public/zsh/snippets/revolver > .bin/revolver - curl -L https://raw.githubusercontent.com/z-shell/src/d38202ee20efeec42900ce26f747a3024028845f/public/zsh/snippets/color.zsh > .bin/color before_script: - chmod u+x .bin/{color,revolver,zunit} - export PATH="$PWD/.bin:$PATH" script: zunit ``` ## macOS (GitHub Actions) ZUnit works on macOS via `brew install zsh` (Zsh is already available on `macos-latest` runners, but may be an older version): ```yaml jobs: zunit-macos: runs-on: macos-latest steps: - uses: actions/checkout@v4 - name: Install dependencies run: | mkdir -p .bin curl -fsSL 'https://github.com/z-shell/zunit/releases/latest/download/zunit' > .bin/zunit curl -fsSL 'https://raw.githubusercontent.com/z-shell/src/d38202ee20efeec42900ce26f747a3024028845f/public/zsh/snippets/revolver' > .bin/revolver curl -fsSL 'https://raw.githubusercontent.com/z-shell/src/d38202ee20efeec42900ce26f747a3024028845f/public/zsh/snippets/color.zsh' > .bin/color chmod u+x .bin/{color,revolver,zunit} - name: Test run: PATH="$PWD/.bin:$PATH" zunit --tap tests ``` --- --- title: ⚙️ Configuration description: ZUnit configuration file reference — all .zunit.yml keys and their defaults. canonical_url: https://wiki.zshell.dev/community/zunit/zunit-configuration markdown_url: https://wiki.zshell.dev/community/zunit/zunit-configuration/index.md locale: en source_path: community/03_zunit/05_configuration.mdx --- # ⚙️ Configuration ZUnit reads configuration from `.zunit.yml` in the project root. All keys are optional; the file itself is optional — ZUnit falls back to built-in defaults when it is absent. ## Generating the config ```zunit zunit init ``` This creates `.zunit.yml` with all keys set to their defaults (see below), plus `tests/`, `tests/_support/bootstrap`, and `tests/_output/`. ## Key reference .zunit.yml ```yaml tap: false directories: tests: tests output: tests/_output support: tests/_support time_limit: 0 fail_fast: false allow_risky: false verbose: false ``` ### `tap` **Type:** boolean — **Default:** `false` When `true`, all output is written in [TAP](https://testanything.org/) format. Equivalent to passing `--tap` on the command line. ### `directories.tests` **Type:** string — **Default:** `tests` Path (relative to project root) where ZUnit looks for test files (`*.zunit`). ### `directories.output` **Type:** string — **Default:** `tests/_output` Directory where text and HTML report files are written when `--output-text` or `--output-html` is used. ### `directories.support` **Type:** string — **Default:** `tests/_support` Directory containing support files. ZUnit sources `bootstrap` from this directory before running each test file, if it exists. ### `time_limit` **Type:** integer (seconds) — **Default:** `0` (disabled) Maximum time allowed per test. Tests that exceed this limit are marked as errors. Set to `0` to disable. Equivalent to `--time-limit ` on the command line. ### `fail_fast` **Type:** boolean — **Default:** `false` When `true`, ZUnit stops immediately after the first failure. Equivalent to `--fail-fast`. ### `allow_risky` **Type:** boolean — **Default:** `false` When `true`, suppresses warnings for risky tests (tests that contain no assertions). Equivalent to `--allow-risky`. ### `verbose` **Type:** boolean — **Default:** `false` When `true`, prints the full stdout/stderr output of each test. Equivalent to `--verbose`. ## Bootstrap script `tests/_support/bootstrap` (or the path set in `directories.support`) is sourced by ZUnit before running each test file. Use it to set environment variables, define helper functions, or load shared fixtures. tests/\_support/bootstrap ```zsh #!/usr/bin/env zsh # Shared setup — sourced before every test file export PROJECT_ROOT="$PWD" source "$PROJECT_ROOT/my-library.zsh" ``` info Command-line flags always take precedence over `.zunit.yml` values. --- --- title: 📦 Installation description: How to install ZUnit — manual, with Zi, or via the init scaffolding command. canonical_url: https://wiki.zshell.dev/community/zunit/zunit-installation markdown_url: https://wiki.zshell.dev/community/zunit/zunit-installation/index.md locale: en source_path: community/03_zunit/01_installation.mdx --- # 📦 Installation ## Dependencies ZUnit requires: - **Zsh** (any reasonably recent version) - **[Revolver](https://github.com/z-shell/src/blob/d38202ee20efeec42900ce26f747a3024028845f/public/zsh/snippets/revolver)** — a progress spinner, must be available in `$PATH` ## Manual install ```sh git clone https://github.com/z-shell/zunit.git cd zunit ./build.zsh chmod u+x ./zunit cp ./zunit /usr/local/bin ``` Install Revolver alongside it: ```sh curl -fsSL https://raw.githubusercontent.com/z-shell/src/d38202ee20efeec42900ce26f747a3024028845f/public/zsh/snippets/revolver \ > /usr/local/bin/revolver chmod u+x /usr/local/bin/revolver ``` ## Local bin install (no root) If you prefer to keep dependencies local to a project (useful for CI): ```sh mkdir -p .bin curl -fsSL "https://github.com/z-shell/zunit/releases/latest/download/zunit" > .bin/zunit curl -fsSL "https://raw.githubusercontent.com/z-shell/src/d38202ee20efeec42900ce26f747a3024028845f/public/zsh/snippets/revolver" > .bin/revolver curl -fsSL "https://raw.githubusercontent.com/z-shell/src/d38202ee20efeec42900ce26f747a3024028845f/public/zsh/snippets/color.zsh" > .bin/color chmod u+x .bin/{color,revolver,zunit} export PATH="$PWD/.bin:$PATH" ``` ## Install with Zi ~/.zshrc ```zi zi ice from"github" as"program" pick"zunit" zi load z-shell/zunit ``` ## Project scaffolding Once ZUnit is installed, `zunit init` sets up a new project in one command: ```zunit cd my-project zunit init ``` This creates: ```text .zunit.yml — project config tests/ example.zunit — example test file _support/ bootstrap — bootstrap script (sourced before each test file) _output/ — report output directory ``` To also generate a GitHub Actions workflow: ```zunit zunit init --github-actions ``` See [Configuration](https://wiki.zshell.dev/community/zunit/zunit-configuration/index.md) for the `.zunit.yml` key reference and [CI Integration](https://wiki.zshell.dev/community/zunit/zunit-ci/index.md) for the generated workflow details. Legacy package-manager recipes Older recipes may reference historical coordinates such as `zunit-zsh/zunit` or `z-shell/zunit`. These are not the canonical source. Prefer the manual install or the `z-shell/zunit` Zi snippet shown above. --- --- title: ▶️ Running Tests description: How to run ZUnit tests — CLI usage, all flags, output modes, and result summary. canonical_url: https://wiki.zshell.dev/community/zunit/zunit-running-tests markdown_url: https://wiki.zshell.dev/community/zunit/zunit-running-tests/index.md locale: en source_path: community/03_zunit/04_running-tests.mdx --- # ▶️ Running Tests ## Basic usage Run the full suite from the project root (ZUnit picks up `.zunit.yml` automatically): ```zunit zunit ``` Pass a specific file, directory, or glob: ```zunit zunit tests/example.zunit zunit tests zunit 'tests/**/*.zunit' ``` ## Synopsis ```zunit zunit run [options] [tests...] ``` `zunit` without a subcommand defaults to `zunit run`. ## Options
FlagDescription
-h, --helpPrint usage and exit
-v, --versionPrint version and exit
-f, --fail-fastStop immediately after the first failure
-t, --tapOutput results in TAP-compatible format (stdout)
--verbosePrint full output from each test
--output-textWrite a TAP-format text report to the output directory
--output-htmlWrite an HTML report to the output directory
--allow-riskySuppress warnings for risky tests (tests with no assertions)
--time-limit <n>Set a per-test time limit of n seconds
## Examples ```zunit # Stop on first failure zunit --fail-fast # TAP output (useful for CI parsers) zunit --tap tests # Full output + HTML report zunit --verbose --output-html tests # Time-limit each test to 10 seconds zunit --time-limit 10 tests ``` ## Result summary After a run, ZUnit prints a table to stdout (unless `--tap` is active): ```text 42 tests run in 1234ms Results ✔ Passed 38 ✘ Failed 2 ‼ Errors 1 ● Skipped 1 ‼ Warnings 0 ``` Exit code is `0` when all tests pass, non-zero on any failure or error. ## Output files When `--output-text` or `--output-html` is used, reports are written to the directory configured under `directories.output` in `.zunit.yml` (default: `tests/_output`). tip Combine `--tap` with a TAP reporter (e.g., `tap-junit`) in CI to get structured test result artifacts. See [CI Integration](https://wiki.zshell.dev/community/zunit/zunit-ci/index.md) for ready-to-use workflow examples. --- --- title: ✍️ Test Syntax description: ZUnit test file syntax — @test blocks, @setup/@teardown hooks, and helper functions. canonical_url: https://wiki.zshell.dev/community/zunit/zunit-test-syntax markdown_url: https://wiki.zshell.dev/community/zunit/zunit-test-syntax/index.md locale: en source_path: community/03_zunit/02_test-syntax.mdx --- # ✍️ Test Syntax ## File structure Every ZUnit test file must begin with the ZUnit shebang: ```zunit #!/usr/bin/env zunit ``` The rest of the file contains `@test`, `@setup`, and `@teardown` blocks — each delimited with `{` and `}`. ## `@test` blocks ```zunit #!/usr/bin/env zunit @test 'my first test' { assert 'hello' same_as 'hello' } ``` - The label in quotes is used in output and reports. - The body can contain any valid Zsh code. - A test **passes** when it exits with code `0`. - A test **fails** when it exits with a non-zero code. ## `@setup` and `@teardown` `@setup` runs **before each test** in the file. `@teardown` runs **after each test**, even if the test fails. ```zunit #!/usr/bin/env zunit @setup { SOME_VAR='rainbows' } @teardown { unset SOME_VAR } @test 'check SOME_VAR' { assert $SOME_VAR same_as 'rainbows' } @test 'change SOME_VAR' { SOME_VAR='unicorns' assert $SOME_VAR same_as 'unicorns' } @test 'SOME_VAR is reset between tests' { # @setup ran again, so SOME_VAR is 'rainbows' again run assert $SOME_VAR same_as 'unicorns' assert $state equals 1 } ``` info `@setup` and `@teardown` scope is per-file, not per-suite. Each test file has its own independent `@setup`/`@teardown`. ## Helper functions These functions are available inside every `@test`, `@setup`, and `@teardown` block. ### `run` Runs a command and captures its output and exit code without failing the test: ```zunit @test 'run captures output' { run echo 'hello world' assert $state equals 0 assert "$output" same_as 'hello world' assert "${lines[1]}" same_as 'hello world' } ``` After `run`: - `$state` — the exit code of the command - `$output` — full stdout (and stderr) as a string - `$lines` — array of output lines ### `load` Sources a file relative to the test directory (or as an absolute path): ```zunit @test 'load a helper' { load '_support/helpers' assert $MY_HELPER same_as 'loaded' } ``` ZUnit appends `.zsh` automatically if the file without the extension is not found. ### `pass`, `fail`, `error`, `skip` Explicit test outcome shortcuts: ```zunit @test 'explicit pass' { pass } @test 'explicit fail' { fail 'something went wrong' } @test 'explicit error' { error 'unexpected condition' # exit code 78 } @test 'skip conditionally' { [[ -z $CI ]] && skip 'only runs in CI' assert $CI is_not_empty } ```
FunctionExit codeEffect
pass0Marks test as passed immediately
fail <msg>1Marks test as failed with message
error <msg>78Marks test as errored with message
skip <msg>48Marks test as skipped with reason
--- --- title: 🎉 Introduction description: Introduction to Zi — a Swiss Army Knife for Zsh, formerly known as zplugin and zinit. canonical_url: https://wiki.zshell.dev/docs markdown_url: https://wiki.zshell.dev/docs/index.md locale: en source_path: docs/index.mdx --- # 🎉 Introduction Zi is a Swiss Army Knife for Zsh — a blazing-fast plugin manager with Turbo mode, built-in package management, annexes, and full control over your shell environment. ![Zi: install, turbo mode, extend everything](https://wiki.zshell.dev/img/svg/docs-banner.svg) Why Zi? **50–80% faster** Zsh startup with Turbo mode. Manage plugins, packages, completions, and prompts from a single tool — without root access, without cluttering `$FPATH`. ## Explore the Docs [![](https://wiki.zshell.dev/img/svg/cards/speed.svg) ### Getting Started Install Zi, load your first plugins, and migrate from Oh My Zsh or Prezto. Get started →](https://wiki.zshell.dev/docs/category/-getting-started)[![](https://wiki.zshell.dev/img/svg/cards/flexible.svg) ### Guides Commands reference, customization, benchmarks, and syntax guides for power users. Read the guides →](https://wiki.zshell.dev/docs/category/-guides)[![](https://wiki.zshell.dev/img/svg/cards/zi-console.svg) ### Zi Code Reference Internals, function reference, and annotated source for contributors and advanced users. Browse the code →](https://wiki.zshell.dev/docs/code/index.md) ## Summary [![Z-Shell Metrics](https://z-shell.github.io/.github/metrics/plugin/metrics.svg)![Z-Shell Repositories](https://z-shell.github.io/.github/metrics/plugin/repositories_metrics.svg)](https://github.com/orgs/z-shell) --- --- title: 🔖 Code documentation description: The documentation lists all functions, interactions between them, their comments, and features. canonical_url: https://wiki.zshell.dev/docs/code markdown_url: https://wiki.zshell.dev/docs/code/index.md locale: en source_path: docs/zi_code.mdx --- # 🔖 Code documentation info Documentation is automatically updated every `Thursday` at `4:30 UTC` at [z-shell/docs](https://github.com/z-shell/docs).
FileDocument formatDescription
zi.zshadoc, pdf, htmlThe main script which is always loaded, in .zshrc
side.zshadoc, pdf, htmlFunctions, loaded by install.zsh and autoload.zsh scripts
install.zshadoc, pdf, htmlFunctions used only when installing a plugin or snippet
autoload.zshadoc, pdf, htmlFunctions used only in interactive Zi invocations
additional.zshadoc, pdf, htmlAdditional support for functions
--- --- title: ⚡️ Installation description: Installation Guide canonical_url: https://wiki.zshell.dev/docs/getting_started/installation markdown_url: https://wiki.zshell.dev/docs/getting_started/installation/index.md locale: en source_path: docs/getting_started/01_installation.mdx --- # ⚡️ Installation ## Automated setup tip - Verify the sha256 [checksum](https://raw.githubusercontent.com/z-shell/src/main/public/checksum.txt) for file: lib/sh/install.sh - If required append -b <tag> or -b <branch> e.g: ```sh sh -c "$(curl -fsSL get.zshell.dev)" -- -i skip -b main ``` - Minimal - Loader - Repository - Annex - ZUnit Install and include minimal configuration to the .zshrc: ```sh sh -c "$(curl -fsSL get.zshell.dev)" -- ``` Install and include minimal configuration with [loader](#loader): ```sh sh -c "$(curl -fsSL get.zshell.dev)" -- -a loader ``` The installer will download the loader and add the snippet below to the .zshrc file. ```zsh if [[ -n ${XDG_CONFIG_HOME:-} && $XDG_CONFIG_HOME == /* ]]; then ZI_LOADER_CONFIG_HOME="$XDG_CONFIG_HOME/zi" else ZI_LOADER_CONFIG_HOME="$HOME/.config/zi" fi if [[ -r "$ZI_LOADER_CONFIG_HOME/init.zsh" ]]; then source "$ZI_LOADER_CONFIG_HOME/init.zsh" && zzinit fi unset ZI_LOADER_CONFIG_HOME ``` Then reload the shell with: `exec zsh`. All done! Clone repository using default or if set [custom](https://wiki.zshell.dev/docs/guides/customization/index.md#customizing-paths) values: ```sh sh -c "$(curl -fsSL get.zshell.dev)" -- -i skip ``` Install and include minimal configuration with recommended [annexes](https://wiki.zshell.dev/ecosystem/annexes/overview/index.md): ```sh sh -c "$(curl -fsSL get.zshell.dev)" -- -a annex ``` Install and include minimal configuration with recommended [annexes](https://wiki.zshell.dev/ecosystem/annexes/overview/index.md) and setup [z-shell/zunit](https://github.com/z-shell/zunit): ```sh sh -c "$(curl -fsSL get.zshell.dev)" -- -a zunit ``` ## Manual Setup Related - [🏗 Configuration management](https://wiki.zshell.dev/docs/guides/customization/index.md#customizing-paths) ### Prepare Set up the install location and create a directory: The assignment below explicitly selects a fresh XDG-style home. It does not migrate an existing `$HOME/.zi` installation. For automatic legacy detection, use the loader above or source your existing Zi checkout without overriding `ZI[HOME_DIR]`. ```zsh typeset -Ag ZI if [[ -n ${XDG_DATA_HOME:-} && $XDG_DATA_HOME == /* ]]; then ZI[HOME_DIR]="$XDG_DATA_HOME/zi" else ZI[HOME_DIR]="$HOME/.local/share/zi" fi ZI[BIN_DIR]="${ZI[HOME_DIR]}/bin" command mkdir -p -- "${ZI[BIN_DIR]}" ``` For security reasons run function compaudit to check if the [completion system](https://zsh.sourceforge.io/Doc/Release/Completion-System.html#Use-of-compinit) would use files owned by root or by the current user, or files in directories that are world or group-writable. If failed, then set the current user as the owner of directories, then remove group/others write permissions, and clone the repository: ```zsh compaudit | xargs chown -R "$(whoami)" "${ZI[HOME_DIR]}" compaudit | xargs chmod -R go-w "${ZI[HOME_DIR]}" command git clone https://github.com/z-shell/zi.git "${ZI[BIN_DIR]}" ``` ### Enable To enable Zi, source the zi.zsh from the previously set up directory placing the following snippet in the .zshrc file: ~/.zshrc ```zsh typeset -A ZI : ${ZI[HOME_DIR]:="${XDG_DATA_HOME:-${HOME}/.local/share}/zi"} : ${ZI[BIN_DIR]:="${ZI[HOME_DIR]}/bin"} source "${ZI[BIN_DIR]}/zi.zsh" ``` ### Completions Enable Zi completions by placing the following snippet in the .zshrc file: caution The snippet below must be placed after after enabling Zi. ~/.zshrc ```zsh autoload -Uz _zi (( ${+_comps} )) && _comps[zi]=_zi ``` ## Post-install After a fresh install, it is recommended to reload the shell and recompile Zi with: - exec zsh -il - zi self-update Run zi -h for available commands or [explore](https://wiki.zshell.dev/community/category/-collection) wiki to [extend](https://wiki.zshell.dev/ecosystem/index.md), [customize](https://wiki.zshell.dev/docs/guides/customization/index.md) and [create](https://wiki.zshell.dev/community/zsh_plugin_standard/index.md) 👍 🎉. If you have any issue or need help 🤦‍♂️, lets [discuss](https://github.com/orgs/z-shell/discussions/new) it or open an [issue](https://github.com/z-shell/zi/issues/new/choose) on GitHub. It helps us to improve and make Zi better. Don't forget to help the project: share, contribute, or [translate](https://digitalclouds.crowdin.com/z-shell) 🌐 🥰 🤓. Let's glue a toolchain that works for us 🚀. ## Have ideas? ### Suggest or request at [playground](https://github.com/z-shell/playground) ```sh sh -c "$(curl -fsSL get.zshell.dev)" -- -a ??? ``` ## Need warm-up? ### [Docker Alpine](https://github.com/z-shell/zd/pkgs/container/zd) ```sh docker run --rm -it ghcr.io/z-shell/zd:latest ``` ### Turbo Zi in Docker If you create a Docker image that uses Zi, install Turbo-loaded plugins before the shell starts interactively, with the @zi-scheduler function in such a way, that it: - Install plugins without waiting for the prompt (i.e. it's script friendly). - Install all plugins instantly, without respecting the wait argument. To accomplish this, use burst argument and call the @zi-scheduler function: ```docker RUN zsh -i -c -- '@zi-scheduler burst || true' ``` > - An example: [Dockerfile](https://github.com/robobenklein/configs/blob/master/Dockerfile) > - In action: [Playground](https://github.com/z-shell/playground) ## Zi Module: zpmod The module transparently and automatically compiles sourced scripts and lists of all sourced files with the time the sourcing took in milliseconds on the left. - [⚙️ Wiki: zpmod](https://wiki.zshell.dev/ecosystem/plugins/zsh-modules/index.md#i-classfa-brands-fa-githubi-z-shellzpmodzpmod-repo) - [📦 Source: zpmod](https://github.com/z-shell/zpmod) ## Available links [Status page](https://status.zshell.dev/) ✅ ### Installer
ServiceURL
Short URLhttps://get.zshell.dev
GitHub RAWhttps://raw.githubusercontent.com/z-shell/src/main/public/sh/install.sh
### Loader
ServiceURL
Short URLhttps://init.zshell.dev
GitHub RAWhttps://raw.githubusercontent.com/z-shell/src/main/public/zsh/init.zsh
--- --- title: ♻️ Migration description: Migration Guide canonical_url: https://wiki.zshell.dev/docs/getting_started/migration markdown_url: https://wiki.zshell.dev/docs/getting_started/migration/index.md locale: en source_path: docs/getting_started/03_migration.mdx --- # ♻️ Migration ## Oh-My-Zsh ### OMZ shorthand syntax ~/.zshrc ```zi zi snippet # Raw syntax with URL zi snippet OMZ:: # Shorthand OMZ:: (http://github.com/ohmyzsh/ohmyzsh/raw/master/) zi snippet OMZL:: # Shorthand OMZ::lib (http://github.com/ohmyzsh/ohmyzsh/raw/master/lib) zi snippet OMZT:: # Shorthand OMZ::themes (http://github.com/ohmyzsh/ohmyzsh/raw/master/themes) zi snippet OMZP:: # Shorthand OMZ::plugins (http://github.com/ohmyzsh/ohmyzsh/raw/master/plugins) ``` ### OMZ library Importing the [clipboard](https://github.com/ohmyzsh/ohmyzsh/blob/master/lib/clipboard.zsh) and [termsupport](https://github.com/ohmyzsh/ohmyzsh/blob/master/lib/termsupport.zsh) from the OMZ library example: Raw syntax: ~/.zshrc ```zi zi snippet https://github.com/ohmyzsh/ohmyzsh/blob/master/lib/clipboard.zsh zi snippet https://github.com/ohmyzsh/ohmyzsh/blob/master/lib/termsupport.zsh ``` OMZ shorthand syntax: ~/.zshrc ```zi zi snippet OMZ::lib/clipboard.zsh zi snippet OMZ::lib/termsupport.zsh ``` OMZL shorthand syntax: ~/.zshrc ```zi zi snippet OMZL::clipboard.zsh zi snippet OMZL::termsupport.zsh ``` ### OMZ plugins ~/.zshrc ```diff - plugins=( - git - dotenv - rake - rbenv - ruby -) + zi snippet OMZP::git + zi snippet OMZP::dotenv + zi snippet OMZP::rake + zi snippet OMZP::rbenv + zi snippet OMZP::ruby ``` Example of more advanced, conditional turbo loading: ~/.zshrc ```zi zi is-snippet wait lucid for \ atload"unalias grv g" \ OMZP::{git,sudo,encode64,extract} \ if'[[ -d /opt/google-cloud-sdk ]]' \ OMZP::gcloud \ if'[[ -f /etc/os-release ]] && source /etc/os-release && [[ "$ID" = arch ]]' \ OMZP::archlinux \ if'[[ -d ~/.nvm ]]' \ OMZP::nvm \ if'[[ -d ~/.ssh ]]' \ OMZP::ssh-agent \ if'[[ -d ~/.gnupg ]]' \ OMZP::gpg-agent \ if'[[ "$OSTYPE" = *-gnu ]]' \ OMZP::gnu-utils \ has'pip' \ OMZP::pip \ has'python' \ OMZP::python ``` tip Bundle the example above to a single file: `zi snippet ### OMZ themes Themes are stored in the `themes` directory and loaded in the background with the simple syntax: ~/.zshrc ```zsh ZSH_THEME="robbyrussell" ``` However, Zi doesn't support the `ZSH_THEME` variable natively. To use **themes** created for OMZ requires loading shown below as it would be the same as OMZ does in the background. > Some themes may require additional configuration it can be determined from the theme configuration file. - Load `git` library - Load the `git` plugin - Load library dependencies - Enable `setopt prompt_subst` If any of the above are not in order or missing, the theme will break similar as shown below: ```text … $(build_prompt) … ``` If the `Git` library is not loaded or loaded in the wrong order, then it may appear similar to the following: ```text ........:1: command not found: git_prompt_status ........:1: command not found: git_prompt_short_sha ``` If you encounter any issue with the theme, OMZ support libraries are to be loaded - If your theme isn't colored when it should, you will want to load `theme-and-appearance.zsh` - If you encounter an error message similar to: ```text zsh: command not found: ruby_prompt_info ``` You need to load `prompt_info_functions.zsh` All together it looks like this: ~/.zshrc ```zi zi snippet OMZL::git.zsh zi snippet OMZP::git zi snippet OMZL::theme-and-appearance.zsh zi snippet OMZL::prompt_info_functions.zsh ``` Then load the prompt: ```zi setopt prompt_subst zi snippet OMZT::robbyrussell ``` ### External theme sample: [NicoSantangelo/Alpharized](https://github.com/nicosantangelo/Alpharized) Load with OMZ: ~/.zshrc ```zsh ZSH_THEME="alpharized" ``` Load `git` library from OMZ: ~/.zshrc ```zi zi snippet OMZL::git.zsh ``` Load `git` plugin from OMZ: ~/.zshrc ```zi zi snippet OMZP::git zi cdclear -q ``` Then load the prompt: ~/.zshrc ```zi setopt prompt_subst zi light NicoSantangelo/Alpharized ``` ## Prezto ### PZT shorthand syntax ~/.zshrc ```zi zi snippet # Raw syntax with URL zi snippet PZT:: # Shorthand PZT:: (https://github.com/sorin-ionescu/prezto/tree/master/) zi snippet PZTM:: # Shorthand PZT::modules/ (https://github.com/sorin-ionescu/prezto/blob/master/modules/) ``` ### PZT modules Importing the [environment](https://github.com/sorin-ionescu/prezto/blob/master/modules/environment/README.md) and [terminal](https://github.com/sorin-ionescu/prezto/blob/master/modules/terminal/README.md) Prezto modules example: Raw syntax ~/.zshrc ```zi zi snippet https://github.com/sorin-ionescu/prezto/blob/master/modules/environment/init.zsh zi snippet https://github.com/sorin-ionescu/prezto/blob/master/modules/terminal/init.zsh ``` PZT shorthand syntax: ~/.zshrc ```zi zi snippet PZT:: zi snippet PZT::modules/environment zi snippet PZT::modules/terminal ``` PZTM shorthand syntax: ~/.zshrc ```zi zi snippet PZTM:: zi snippet PZTM::environment zi snippet PZTM::terminal ``` Prezto modules: ~/.zshrc ```diff - zstyle ':prezto:load' pmodule 'git' - zstyle ':prezto:load' pmodule 'environment' 'terminal' + zi snippet PZTM::git + zi is-snippet for PZTM::environment PZTM::terminal ``` Available Prezto modules:
Module nameDescription
archiveProvides functions to list and extract archives.
autosuggestionsIntegrates zsh-autosuggestions plugin into Prezto.
command-not-foundLoads the command-not-found tool on macOS or Debian-based distributions.
completionSets TAB completion and provides additional completions from the zsh-completions.
directorySets directory options and defines directory aliases.
dnfDefines dnf aliases.
dockerDefines docker aliases and functions.
dpkgDefines dpkg aliases and functions.
editorSets key bindings.
emacsEnables Emacs dependency management.
environmentSets general shell options and defines environment variables.
fasdMaintains a frequently used file and directory list for fast access.
gitEnhances the Git by providing aliases, functions and by exposing repository status information to prompts.
gnu-utilityProvides for the interactive use of GNU utilities on non-GNU systems.
gpgProvides for an easier use of GPG by setting up gpg-agent.
haskellEnables local Haskell package installation.
helperProvides helper functions for developing modules.
history-substring-searchIntegrates zsh-history-substring-search into Prezto.
historySets history options and defines history aliases.
homebrewDefines Homebrew aliases.
macportsDefines MacPorts aliases and adds MacPorts directories to path variables.
nodeProvides utility functions for Node.js and loads npm completion.
ocamlInitializes OCaml package management.
osxDefines macOS aliases and functions.
pacmanProvides aliases and functions for the Pacman package manager and frontends.
perlEnables local Perl module installation on macOS and defines aliases.
promptLoads prompt themes.
pythonEnables local Python and local Python package installation.
railsDefines Ruby on Rails aliases.
rsyncDefines rsync aliases.
rubyConfigures Ruby local gem installation, loads version managers, and defines aliases.
screenDefines GNU Screen aliases and provides for auto launching it at start-up.
spectrumProvides for easier use of 256 colors and effects.
sshProvides for an easier use of SSH by setting up ssh-agent.
syntax-highlightingIntegrates zsh-syntax-highlighting into Prezto.
terminalSets terminal window and tab titles.
tmuxDefines tmux aliases and provides for auto launching it at start-up.
utilityDefines general aliases and functions.
wakeonlanThis module provides a wrapper around the wakeonlan tool.
yumDefines yum aliases.
Use `blockf` to prevent any unnecessary additions to `fpath`, as Zi manages `fpath`. Zsh completion styles For general `zstyle` guidance, see the Community [Completion Configuration](https://wiki.zshell.dev/community/zsh-guide/completion/index.md) guide. Available ## Zgen ### Load OMZ library ~/.zshrc ```diff - zgen oh-my-zsh + zi snippet OMZL:: ``` ### Load OMZ plugins ~/.zshrc ```diff - zgen oh-my-zsh + zi snippet OMZP:: ``` ### Load Prezto modules ~/.zshrc ```diff - zgen prezto + zi snippet PZTM:: ``` Load repositories as prezto plugins: ~/.zshrc ```diff - zgen pmodule + zi ice ver"" + zi load ``` ### Summarized Zgen info For the `location`: refer [src, pick, multisrc](https://wiki.zshell.dev/docs/guides/syntax/standard/index.md#src-pick-multisrc) ice-modifier. ~/.zshrc ```diff - zgen load [location] [branch] + zi ice ver"[branch]" + zi load ``` ## Zplug Basics ~/.zshrc ```diff - zplug , tag1:, tag2: + zi ice tag1"" tag2"" + zi load ``` ### Tag comparison - `as` => `as` - `use` => `pick`, `src`, `multisrc` - `ignore` => None - `from` => `from` - `at` => `ver` - `rename-to` => `mv`, `cp` - `dir` => Selection(`pick`, …) with rename - `if` => `if` - `hook-build` => `atclone`, `atpull` - `hook-load` => `atload` - `frozen` => None - `on` => None - `defer` => `wait` - `lazy` => `autoload` - `depth` => `depth` --- --- title: ☑️ General Overview description: General overview of Zi usage canonical_url: https://wiki.zshell.dev/docs/getting_started/overview markdown_url: https://wiki.zshell.dev/docs/getting_started/overview/index.md locale: en source_path: docs/getting_started/02_overview.mdx --- # ☑️ General Overview This overview will cover the basics for: 1. [Oh-My-Zsh & Prezto](https://wiki.zshell.dev/search?q=Oh+My+Zsh+%26+Prezto) 2. [Completions](https://wiki.zshell.dev/search?q=completions) 3. [Turbo mode](https://wiki.zshell.dev/search?q=turbo+mode) 4. [Ice modifiers](https://wiki.zshell.dev/search?q=ice+modifiers) ## Plugin and snippet loading ```zi zi load z-shell/H-S-MW zi light zsh-users/zsh-syntax-highlighting ``` The above commands show two ways of basic plugin loading. If you want to source local or remote files (using a direct URL), you can do so with a `snippet`. ```zi zi snippet ``` Such lines should be added to `.zshrc`. Snippets are cached locally, use the `-f` option to download a fresh version of a snippet, or `zi update {URL}`. Use `zi update --all` to update all snippets and plugins. Using `load` causes reporting to be enabled – you can track what the plugin does, view the information with `zi report {plugin-name}`, and then also unload the plugin with `zi unload {plugin-name}`. Using `light` is a faster loading without tracking and reporting about the plugin but also withdrawing the ability to unload it. Using `load` or `light`: ```zi zi load # Load with reporting/investigating. zi light # Load without reporting/investigating. ``` Plugin history-search-multi-word loaded with investigating: ```zi zi load z-shell/H-S-MW ``` Two regular plugins loaded without investigating: ```zi zi light zsh-users/zsh-autosuggestions zi light z-shell/F-Sy-H ``` Snippet: ```zi zi snippet https://gist.githubusercontent.com/hightemp/5071909/raw/ ``` note In turbo mode loading, the slowdown by plugin tracking is done in the background and does not affect the user experience, i.e., loading with `zi light` and `zi load` has the same effect. ## Oh-My-Zsh, Prezto To load Oh-My-Zsh and Prezto plugins, use the `snippet` feature. Snippets are **single files** downloaded by `curl`, `wget`, etc., automatic detection of the download tool is being performed, directly from the URL: ```zi zi snippet 'https://github.com/robbyrussell/oh-my-zsh/raw/master/plugins/git/git.plugin.zsh' zi snippet 'https://github.com/sorin-ionescu/prezto/blob/master/modules/helper/init.zsh' ``` Also, for Oh-My-Zsh and Prezto, you can use `OMZ::` and `PZT::` shorthands: ```zi zi snippet OMZ::plugins/git/git.plugin.zsh zi snippet PZT::modules/helper/init.zsh ``` ## Snippets and Performance Using `curl`, `wget`, etc. allows us to almost completely avoid code dedicated to Oh-My-Zsh and Prezto, and also to other frameworks. It delivers better performance as has a low footprint on memory and shorter loading times. ## Ice Modifiers The command `zi ice` provides [ice modifiers](https://wiki.zshell.dev/search?q=ice+modifiers) for the single Zi command, i.e., `zi ice ; zi load some/plugin`, after loading some/plugin the ice-modifier has to be set again. The logic is that "ice" is something that’s added, e.g. to a drink or a coffee, and in the Zi sense this means that ice is a modifier added to the next Zi command, and also something that melts, so it doesn’t last long, – and in the Zi use it means that the modifier lasts for only single next Zi command. Using one other ice modifier "**pick**" users can explicitly **select the file to source**. The content of the ice-modifier is simply put into `"…"`, `'…'`, `$'…'`. No need for `":"` after the ice-modifier name (although it's allowed: as the equal sign `=`, e.g. `pick="init.zsh"` or `pick=init.zsh`). This way editors like `vim` and `emacs` and also `zsh-users/zsh-syntax-highlighting` and `z-shell/F-Sy-H` will highlight the contents of ice-modifiers. ## About as"program" A plugin might not be a file for sourcing, but a command to be added to `$PATH`. To obtain this effect, use ice-modifier `as` with value `program` (or an alias value `command`). ```zi zi ice as"program" cp"httpstat.sh -> httpstat" pick"httpstat" zi light b4b4r07/httpstat ``` The above command will add plugin directory to `$PATH`, copy file `httpstat.sh` into `httpstat` and add execution rights (`+x`) to the file selected with `pick`, i.e. to `httpstat`. Another ice-mod exists, `mv`, which works like `cp` but **moves** a file instead of **copying** it. `mv` is run before `cp`. tip The `cp` and `mv` ices (and also some other ones, like `atclone`) are being run when the plugin or snippet is being *installed*. To test them again first delete the plugin or snippet (example: `zi delete PZT::modules/osx`). ## Ice modifier: atpull'…' Copying file is safe for doing later updates – original files of the repository are unmodified and `Git` will report no conflicts. However, `mv` also can be used, if a proper `atpull`, an ice-modifier ran at **update** of the plugin: ```zi zi ice as"program" mv"httpstat.sh -> httpstat" \ pick"httpstat" atpull'!git reset --hard' zi light b4b4r07/httpstat ``` If `atpull` starts with an exclamation mark, then it will be run before `git pull`, and before `mv`. Nevertheless, `atpull`, `mv`, and `cp` are run **only if new commits are to be fetched**. So in summary, when the user runs `zi update b4b4r07/httpstat` to update this plugin, and there are new commits, what happens first is that `git reset --hard` is run – and it **restores** original `httpstat.sh`, **then** `git pull` is ran and it downloads new commits (doing fast-forward), **then** `mv` is running again so that the command is `httpstat` not `httpstat.sh`. This way the `mv` ice can be used to induce permanent changes into the plugin's contents without blocking the ability to update it with `git` in the case of snippets. info For exclamation marks to not be expanded by Zsh an interactive session, use `'…'` not `"…"` to enclose contents of `atpull` [ice-modifier](https://wiki.zshell.dev/search?q=ice-modifier). ## Ice modifier: subscribe'…' Ice modifier defers the loading of a plugin while checking the modification time of the given file(s), and when it changes, it then triggers the loading of the plugin or a snippet. Copy and paste the example below to the terminal or add it to the `.zshrc` file and reload the shell with `exec zsh`. ```zi zi ice subscribe'{~/files-*,/tmp/files-*}' id-as'z-sub' lucid \ atload'+zi-message "{profile}I have been loaded{nl}\ {auto}\`Zi Rocks ♥\`"' notify"Yes that is cool ♥ " zi load z-shell/0 ``` Update file as subscribed above to test the ice modifier: ```sh touch ~/files-1 ``` The plugin or snippet will be sourced as many times as the file gets updated. ## Snippets as'…' program Commands can also be added to `$PATH` using **snippets**: ```zi zi ice mv"httpstat.sh -> httpstat" \ pick"httpstat" as"program" zi snippet https://github.com/b4b4r07/httpstat/blob/master/httpstat.sh ``` tip Snippets also support `atpull`. There’s also an `atinit` ice-modifier, executed before each loading of plugin or snippet. ## Snippets as'…' completion By using the `as'…'` ice modifier with the value `completion` you can point the `snippet` subcommand directly to a completion file: ```zi zi ice as"completion" zi snippet https://github.com/docker/cli/blob/master/contrib/completion/zsh/_docker ``` ## The completion management Zi allows disabling and enabling each completion in every plugin. Try installing a popular plugin that provides completions: ```zi zi ice blockf zi light zsh-users/zsh-completions ``` The first command, the `blockf` ice, will block the traditional method of adding completions. Zi uses this method, based on symlinks instead of adding several directories to `$fpath`. Zi will automatically **install** completions of a newly downloaded plugin. To uninstall and install completions: Uninstall: ```zi zi cuninstall zsh-users/zsh-completions ``` Install: ```zi zi creinstall zsh-users/zsh-completions ``` ### Listing available completions To see what completions **all** plugins provide, in tabular formatting and with the name of each plugin: ```zi zi clist ``` This command is adapted for plugins like `zsh-users/zsh-completions`, which provide many completions – listing will have `3` completions per line, and a smaller number of terminal pages can be occupied like this: ![Zi completion list](https://wiki.zshell.dev/img/cast/svg/zi_clist.svg) To show more completions per line by providing an **argument** to `clist`, e.g.: `zi clist 6`, will show: ![Zi completion list 6](https://wiki.zshell.dev/img/cast/svg/zi_clist_6.svg) ### Enabling / disabling - completions Completions can be disabled and other completion will be used, e.g. Zsh builtin. The commands are very basic, they only need completion **name**: Disable `cmake` completion: ```zi zi cdisable cmake ``` Enable `cmake` completion: ```zi zi cenable cmake ``` Command `zi csearch` will **search** all plugin directories for available completions: ![Zi completion search](https://wiki.zshell.dev/img/cast/svg/zi_csearch.svg) ## Turbo Mode (Zsh >= 5.3) The ice-modifier `wait` allows the user to postpone the loading of a plugin to the moment when the processing of `.zshrc` is finished and the first prompt is shown. It is like Windows – during startup, it shows a desktop even though it still loads data in the background. This has drawbacks but is for sure better than a blank screen for 10 minutes. But in Zi, there are no drawbacks of this approach – no lags, freezes, etc. – the command line is fully usable while the plugins are being loaded, for any number of plugins. info Turbo will speed up Zsh startup by **50%–80%**. For example, instead of 200 ms, it'll be 40 ms. note Zsh 5.3 or greater is required. To use turbo mode add `wait` ice to the target plugin in one of the following ways: ```zi PS1="READY > " zi ice wait'!0' zi load halfo/lambda-mod-zsh-theme ``` This sets plugin `halfo/lambda-mod-zsh-theme` to be loaded `0` seconds after `.zshrc`. It will fire up after c.a. 1 ms of showing the basic prompt `READY >`. You probably won't load the prompt in such a way, however, it is a good example in which turbo mode can be observed. The exclamation mark causes Zi to reset the prompt after loading the plugin – commonly needed for themes. Using `zsh-users/zsh-autosuggestions` without any drawbacks: ```zi zi ice wait lucid atload'!_zsh_autosuggest_start' zi light zsh-users/zsh-autosuggestions ``` ### Turbo mode is the key to the performance It can be loaded asynchronously, which makes a huge difference when the amount of plugins increases. Usually used as `zi ice wait''`. note The `wait` is equivalent to `wait'0'`. ```zi zi ice wait zi load z-shell/H-S-MW ``` Load after 2 seconds: ```zi zi ice wait'2' zi load z-shell/H-S-MW ``` Also can be used in `light` and `snippet`: ```zi zi ice wait zi snippet https://gist.githubusercontent.com/hightemp/5071909/raw/ ``` ### Turbo mode & lucid Turbo and lucid are the most used options because turbo mode is verbose and may require an option for quiet and this can be achieved with the `lucid`. ```zi zi ice wait lucid zi load z-shell/H-S-MW ``` ## Turbo mode with sophisticated prompts For some, mostly advanced themes the initialization of the prompt is being done in a `precmd`\-hook, i.e.; in a function that gets called before each prompt. The hook is installed by the [add-zsh-hook](https://wiki.zshell.dev/community/zsh_plugin_standard/index.md#use-of-add-zsh-hook-to-install-hooks) Zsh function by adding its name to the `$precmd_functions` array. To make the prompt fully initialized after turbo mode loading in the middle of the prompt the same situation as with the `zsh-autosuggestions` plugin, the hook should be called from `atload'…'` ice. First, find the name of the hook function by examining the `$precmd_functions` array. For example, for the `robobenklein/zinc` theme, they'll be two functions: `prompt_zinc_setup` and `prompt_zinc_precmd`: print $precmd\_functions ```text _zsh_autosuggest_start prompt_zinc_setup prompt_zinc_precmd ``` Then, add them to the ice list in the `atload'…'` ice: ```zi zi ice wait'!' lucid nocd \ atload'!prompt_zinc_setup; prompt_zinc_precmd' zi load robobenklein/zinc ``` The exclamation mark in `atload'!…'` is to track the functions allowing the plugin to be unloaded, as described in the [standard syntax guide](https://wiki.zshell.dev/docs/guides/syntax/standard/index.md#atclone-atpull-atinit-atload). It might be useful for the multi-prompt setup described next. ### Summary of turbo mode Autosuggestions use the `precmd` hook, which is being called right after processing `.zshrc` – `precmd` hooks are being called **right before displaying each prompt**. Turbo mode with the empty `wait` ice will postpone the loading `1` ms after that, so `precmd` will not be called at that first prompt. This makes autosuggestions inactive at the first prompt. **However** the given `atload'…'` ice-modifier fixes this, it calls the same function that `precmd` would, right after loading autosuggestions, resulting in the same behavior of the plugin. The ice called `lucid` causes the under-prompt message saying `Loaded zsh-users/zsh-autosuggestions` that normally appears for every Turbo-loaded plugin to not show. ## Automatic condition based - load & unload Ices `load` and `unload` allow defining when you want plugins active or inactive: Load when in `~/tmp`: ```zi zi ice load'![[ $PWD = */tmp* ]]' unload'![[ $PWD != */tmp* ]]' \ atload'!promptinit; prompt sprint3' zi load z-shell/zprompts ``` ![Zi load at /tmp](https://wiki.zshell.dev/img/cast/svg/zi_load_at_tmp.svg) Load when NOT in `~/tmp`: ```zi zi ice load'![[ $PWD != */tmp* ]]' unload'![[ $PWD = */tmp* ]]' zi load russjohnson/angry-fly-zsh ``` ![Zi load not at /tmp](https://wiki.zshell.dev/img/cast/svg/zi_load_not_tmp.svg) Two prompts, each active in different directories. This technique can be used to have plugin-sets, e.g. by defining parameter `$PLUGINS` with possible values like `cpp`, `web`, `admin` and by setting `load` / `unload` conditions to activate different plugins on `cpp`, on `web`, etc. note - The difference with `wait` is that `load` / `unload` are constantly active, not only till the first activation. Note that for the unloading of a plugin to work the plugin needs to be loaded with tracking, so `zi load …` and not `zi light …`. Tracking causes a slight slowdown, however, this doesn’t influence Zsh startup time when using turbo mode. ### A Glance at the prompts tip See: [multiple prompts](https://wiki.zshell.dev/docs/guides/customization/index.md#multiple-prompts) or more information. It contains more real-world examples of a multi-prompt setup, which is close to what the author uses in his setup. This is [powerlevel10k](https://github.com/romkatv/powerlevel10k), [pure](https://github.com/sindresorhus/pure), [starship](https://github.com/starship/starship) sample: Load powerlevel10k theme: ~/.zshrc ```zi zi ice depth"1" zi light romkatv/powerlevel10k ``` Load pure theme: > will pick the `async.zsh` library and will source it. ~/.zshrc ```zi zi ice pick"async.zsh" src"pure.zsh" zi light sindresorhus/pure ``` Load starship theme: > - pick `starship` binary as a command, from the GitHub release. > - setup `starship` using `atclone` and create `init.zsh` and `completion`. > - the `atpull'…'` behavior same as `atclone'…'` and but is used when running `zi update`. > - `src` will source `init.zsh`. ~/.zshrc ```zi zi ice as"command" from"gh-r" \ atclone"./starship init zsh > init.zsh; ./starship completions zsh > _starship" \ atpull"%atclone" src"init.zsh" zi light starship/starship ``` ### Common use cases Load the pure theme, with the **zsh-async** library that's bundled with it. ~/.zshrc ```zi zi ice pick"async.zsh" src"pure.zsh" zi light sindresorhus/pure ``` Binary release in the archive, from GitHub. After automatic unpacking, it provides the program "fzf". ~/.zshrc ```zi zi ice from"gh-r" as"program" zi light junegunn/fzf ``` One other binary release needs renaming from `docker-compose-Linux-x86_64`. This can be done by [ice modifier](https://wiki.zshell.dev/search?q=ice+modifiers): `mv'{from} -> {to}'`. There are multiple packages per single version for OS X, Linux, and Windows – the ice-modifier `bpick` is utilized to select the Linux package – in this case - not required, Zi will grep operating system name and architecture automatically when there's no `bpick`. ~/.zshrc ```zi zi ice from"gh-r" as"program" mv"docker* -> docker-compose" bpick"*linux*" zi load docker/compose ``` Handle completions without loading any plugin, see the `clist` command. This one is to be run just once, in an interactive session. ~/.zshrc ```zi zi creinstall %HOME/my_completions ``` If you are interested to try out more then check out the [playground repository](https://github.com/z-shell/playground) where users have uploaded the `~/.zshrc` and other Zi configurations. Feel free to [submit](https://github.com/z-shell/playground/issues/new?template=request-to-add-zshrc-to-the-zi-configs-repo.md) your `~/.zshrc` configuration. Additional examples: [collection](https://wiki.zshell.dev/community/gallery/collection/index.md). --- --- title: ⏲ Zi plugin benchmarking description: Profile Zi plugin loading and inspect Zi analytics. canonical_url: https://wiki.zshell.dev/docs/guides/benchmark markdown_url: https://wiki.zshell.dev/docs/guides/benchmark/index.md locale: en source_path: docs/guides/03_benchmark.mdx --- # ⏲ Zi plugin benchmarking info Run `zi analytics` to see the available commands for statistics and reporting. ## Profile plugins ~/.zshrc ```zi zi ice atinit'zmodload zsh/zprof' \ atload'zprof | head -n 20; zmodload -u zsh/zprof' zi light z-shell/F-Sy-H ```
SyntaxDescription
atinit'…'loads the zsh/zprof module, shipped with Zsh, before loading the plugin – this starts the profiling.
atload'…'works after loading the plugin – shows profiling results zprof / head, unloads zsh/zprof - this stops the profiling.
While in effect, only a single plugin, in this case, `z-shell/F-Sy-H`, will be profiled. The rest plugins will go on completely normally, as when plugins are loaded with `light` - reporting is disabled. Less code is being run in the background, the automatic data gathering, during loading of a plugin, for the reports and the possibility to unload the plugin will be activated and the functions will not appear in the `zprof` report. Example `zprof` report: ```text num calls time self name ---------------------------------------------------------------------------- 1) 1 57,76 57,76 57,91% 57,76 57,76 57,91% _zsh_highlight_bind_widgets 2) 1 25,81 25,81 25,88% 25,81 25,81 25,88% compinit 3) 4 10,71 2,68 10,74% 8,71 2,18 8,73% --zi-shadow-autoload 4) 43 2,06 0,05 2,07% 2,06 0,05 2,07% -zi-add-report 5) 8 1,98 0,25 1,98% 1,98 0,25 1,98% compdef 6) 1 2,85 2,85 2,85% 0,87 0,87 0,87% -zi-compdef-replay 7) 1 0,68 0,68 0,68% 0,68 0,68 0,68% -zi-shadow-off 8) 1 0,79 0,79 0,79% 0,49 0,49 0,49% add-zsh-hook 9) 1 0,47 0,47 0,47% 0,47 0,47 0,47% -zi-shadow-on 10) 3 0,34 0,11 0,35% 0,34 0,11 0,35% (anon) 11) 4 10,91 2,73 10,94% 0,20 0,05 0,20% autoload 12) 1 0,19 0,19 0,19% 0,19 0,19 0,19% -fast-highlight-fill-option-variables 13) 1 25,98 25,98 26,05% 0,17 0,17 0,17% zicompinit 14) 1 2,88 2,88 2,89% 0,03 0,03 0,03% zicdreplay 15) 1 0,00 0,00 0,00% 0,00 0,00 0,00% -zi-load-plugin ----------------------------------------------------------------------------------- ``` The first column is the time in milliseconds: - It denotes the amount of time spent in a function in total - For example, `--zi-shadow-autoload` consumed 10.71 ms of the execution time The fourth column is also a time in milliseconds, but it denotes the amount of time spent on executing only of function's **own code**, it doesn't count the time spent in **descendant functions** that is called from the function: - For example, `--zi-shadow-autoload` spent 8.71 ms on executing only its code The table is sorted in the **self-time** column. ## Profile `.zshrc` startup General Zsh startup profiling now lives in the Community [Startup Profiling](https://wiki.zshell.dev/community/zsh-guide/startup-profiling/index.md) guide. ### Method 1 See [Trace startup lines](https://wiki.zshell.dev/community/zsh-guide/startup-profiling/index.md#trace-startup-lines). ### Method 2 See [Lightweight checkpoints](https://wiki.zshell.dev/community/zsh-guide/startup-profiling/index.md#lightweight-checkpoints). --- --- title: 🛠 Commands description: Zi subcommands and functionality canonical_url: https://wiki.zshell.dev/docs/guides/commands markdown_url: https://wiki.zshell.dev/docs/guides/commands/index.md locale: en source_path: docs/guides/01_commands.mdx --- # 🛠 Commands ## Updates To update and recompile Zi run `zi self-update` in the command line. To update all plugins and snippets, issue `zi update`. To update all in parallel (up to 40 at the time) `zi update -p 40` If you wish to update only a single plugin/snippet instead issue `zi update `. A list of commits will be shown if any. Some plugins require acting each time they're updated. One way you can do this is by using the `atpull'…'` ice modifier. For example, writing `zi ice atpull'./configure'` before loading a plugin will execute `./configure` after a successful update. Refer to [ice-modifiers](https://wiki.zshell.dev/search?q=ice-modifiers) for more information. The ice-modifiers for any plugin or snippet are stored in their directory in a `._zi` subdirectory, hence the plugin doesn't have to be loaded to be correctly updated. There's one other file created there, `.zi_lstupd` – it holds the log of the new commits pulled-in in the last update. Self-update & compile: ```zi zi self-update ``` Update plugins and snippets: ```zi zi update --all zi update --reset zi update --quiet ``` Update plugins or snippets: ```zi zi update --plugins zi update --snippets ``` Update specific plugins. Default is GitHub but can specify any with ice [from'…'](https://wiki.zshell.dev/search?q=from): ```zi zi update / ``` Plugin parallel update plugins: ```zi zi update --parallel ``` Increase the number of jobs in a concurrent set to 40 ```zi zi update --parallel 40 ``` ## Compinit note Calling `compinit` once is a huge performance gain, for example, shell startup time with double `compinit`: **0.980** sec, with `cdreplay` and single `compinit`: **0.156** sec. ### Calling `compinit` without turbo mode With no turbo mode in use, compinit can be called normally, i.e.: as `autoload compinit; compinit`. This should be done after loading all plugins and before possibly calling `zi cdreplay`. The `cdreplay` subcommand is provided to re-play all caught `compdef` calls. The `compdef` calls are used to define a completion for a command. For example, `compdef _git git` defines that the `git` command should be completed by a `_git` function. The `compdef` function is provided by the `compinit` call. As it should be called later, after loading all of the plugins, Zi provides its own `compdef` function that catches (i.e.: records in an array) the arguments of the call, so that the loaded plugins can freely call `compdef`. Then, the `cdreplay` (compdef-replay) can be used, after `compinit` will be called (and the original `compdef` function will become available), to execute all detected `compdef` calls. ~/.zshrc ```zi typeset -A ZI : ${ZI[HOME_DIR]:="${XDG_DATA_HOME:-${HOME}/.local/share}/zi"} : ${ZI[BIN_DIR]:="${ZI[HOME_DIR]}/bin"} source "${ZI[BIN_DIR]}/zi.zsh" zi load "some/plugin" (…) compdef _gnu_generic fd # this will be intercepted by ZI, because as the compinit # isn't yet loaded, thus there's no such function `compdef'; yet # ZI provides its own `compdef' function which saves the # completion-definition for later possible re-run with `zi # cdreplay' or `zicdreplay' (the second one can be used in hooks # like atload'…', atinit'…', etc.) (…) zi load "other/plugin" autoload -Uz compinit compinit zi cdreplay -q # -q is for quiet; actually, run all the `compdef's saved before #`compinit` call (`compinit' declares the `compdef' function, so # it cannot be used until `compinit' is run; ZI solves this # via intercepting the `compdef'-calls and storing them for later # use with `zi cdreplay') ``` ### Calling `compinit` with turbo mode If you load completions using `wait'…'` [turbo mode](https://wiki.zshell.dev/search?q=turbo+mode) then you can add `atinit'zicompinit'` to the syntax-highlighting plugin (which should be the last one loaded, as their (2 projects, [zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting) & [F-Sy-H](https://github.com/z-shell/F-Sy-H)) documentation state), or `atload'zicompinit'` to last completion-related plugin. `zicompinit` is a function that just runs `autoload compinit; compinit`, created for convenience. Alternatively, the `zicompinit` can be replaced with `zicompinit_fast` which checks the cached `.zcompdump` and determines when to regenerate the file. This restricts checking it once a day, as compinit doesn't always need to modify the compdump and compiles mapped to share in the background in multiple shells. There's also `zicdreplay` which will replay any caught compdefs so you can also do: `atinit'zicompinit; zicdreplay'`, etc. It is recommended to run the `compinit` call in the `atinit` or `atload` hook of the last related plugin with the use of the helper functions `zicompinit`,`zicdreplay` & `zicdclear` as shown below: ~/.zshrc ```zi typeset -A ZI : ${ZI[HOME_DIR]:="${XDG_DATA_HOME:-${HOME}/.local/share}/zi"} : ${ZI[BIN_DIR]:="${ZI[HOME_DIR]}/bin"} source "${ZI[BIN_DIR]}/zi.zsh" # Load using the for-syntax zi wait lucid for \ "some/plugin" zi wait lucid for \ "other/plugin" zi wait lucid atload"zicompinit; zicdreplay" blockf for \ zsh-users/zsh-completions ``` ### Ignoring compdefs If you want to ignore compdefs provided by some plugins or snippets, place their load commands before commands loading other plugins or snippets, and issue `zi cdclear` (or `zicdclear`, designed to be used in hooks like `atload'…'`): ```zi typeset -A ZI : ${ZI[HOME_DIR]:="${XDG_DATA_HOME:-${HOME}/.local/share}/zi"} : ${ZI[BIN_DIR]:="${ZI[HOME_DIR]}/bin"} source "${ZI[BIN_DIR]}/zi.zsh" zi snippet OMZP::git zi cdclear -q # <- forget completions provided by Git plugin zi load "some/plugin" (…) zi load "other/plugin" autoload -Uz compinit compinit zi cdreplay -q # <- execute compdefs provided by rest of plugins zi cdlist # look at gathered compdefs ``` The `cdreplay` is important if you use plugins like `OMZP::kubectl` or `asdf-vm/asdf` because these plugins call `compdef`. Following commands are passed to `zi …` to obtain described effects. ## Loading and unloading
CommandDescription
load '…'Load plugin, can also receive absolute local path.
light -b '…'Light plugin load, without reporting/investigating. -b – investigate bindkey-calls only. 1
unload -q '…'Unload plugin loaded with zi load …. -q – quiet.
snippet -f URLSource local (full path) or remote file (URL). -f – don't use cache (force re-download). 2
## Completions management
CommandDescription
clist columns or completions columnsList completions in use, with columns completions per line. zi clist 5 will for example print 5 completions per line. Default is 3.
cdisable '…'Disable completion.
cenable '…'Enable completion.
creinstall -q -Q '…'Install completions for the plugin, can also receive absolute local path. -q – quiet. -Q - quiet all.
cuninstall '…'Uninstall completions for the plugin.
csearchSearch for available completions from any plugin.
compinitRefresh installed completions.
cclearClear stray and improper completions.
cdlistShow compdef replay list.
cdreplay -qReplay compdefs (to be done after compinit). -q – quiet.
cdclear -qClear compdef replay list. -q – quiet.
## Tracking of the active session
CommandDescription
dtrace, dstartStart investigating what's going on in the session.
dstopStop investigating what's going on in the session.
dunloadRevert changes recorded between dstart and dstop.
dreportReport what was going on in the session.
dclearClear report of what was going on in the session.
## Reports and statistics
CommandDescription
times -s -m -aStatistics on plugin load times, sorted in order of loading. -s – use seconds instead of milliseconds. -m – show plugin loading moments and -a both.
zstatusOverall ZI status.
report '…' --allShow plugin report. --all – do it for all plugins.
loadedShow loaded plugins
list keywordFilter loaded plugins with only 'keyword'
lsList snippets in a formatted and colorized manner. Requires tree program.
status '…' or URL --allGit status for plugin. --all – do it for all plugins.
recently time-specShow plugins that changed recently, the argument is e.g. 1 month 2 days.
bindkeysLists bindkeys set up by each plugin.
## Compiling
CommandDescription
compile '…' --allCompile plugin. --all – compile all plugins.
uncompile '…' --allRemove compiled version of the plugin. --all – do it for all plugins.
compiledList plugins that are compiled.
## Other commands
CommandDescription
self-updateUpdates and compiles Zi.
update -q -r '…' or --allUpdate all plugins and snippets with --all – for quiet -q – execute git reset --hard before pulling changes with -r.
ice '…'Add ice to next command, argument e.g.: from"gitlab".
delete '…' or --clean --allRemove plugin or snippet from disk (good to forget wrongly passed ice-modifiers) --all – delete plugins and snippets that are not loaded with --clean.
cd '…'Jump into the plugin's directory. Also support snippets if fed with URL.
edit '…'Edit plugin's file with set $EDITOR.
glance '…'Look at plugin's source (pygmentize, source-highlight).
stress '…'Test plugin for compatibility with a set of options.
changes '…'View plugin's git log.
create '…'Create plugin (also together with GitHub repository).
srv service-id {command}Control a service, command can be: stop,start,restart,next,quit; next moves the service to another Z shell.
recall '…' URLFetch saved ice modifiers and construct zi ice '…' command.
env-whitelist -v -h {env..}Allows to specify names or patterns of variables left unchanged during an unload – verbose -v – help -h.
moduleManage binary Zsh module shipped with ZI, see zi module help.
add-fpath fpath -f --front '…' sub-directoryAdds given plugin (not yet snippet) directory to $fpath. If the second argument is given, it is appended to the directory path. 3
run -l plugin {command}Runs the given command in the given plugin's directory. 4
## Help & manual
CommandDescription
-h, helpUsage information.
manManual.
version, --version, or -VShow the exact Git tag, the abbreviated commit for an untagged checkout, or unknown without Git metadata.
## Commands available using ^TAB [completion](https://wiki.zshell.dev/docs/getting_started/installation/index.md#enable-zi-completions) zi ^TAB ```jsx add-fpath -- Add plugin folder to $fpath analytics -- Statistics, benchmarks and information bindkeys -- Lists bindkeys set up by each plugin cclear -- Clear stray and improper completions cd -- Go into plugin directory cdclear -- Clear compdef replay list cdisable -- Disable completion cdlist -- Show compdef replay list cdreplay -- Replay compdefs (to be done after compinit) cenable -- Enable completion changes -- View the plugin git log compile -- Compile plugin (or all plugins if --all passed) compiled -- Show which plugins are compiled compinit -- Refresh installed completions completions clist -- List completions in use create -- Create plugin (also together with Github repository) creinstall -- Install completions for plugin csearch -- Search for available completions from any plugin cuninstall -- Uninstall completions for plugin dclear -- Clear report of what was going on in session delete -- Delete plugin dreport -- Report what was going on in session dstart dtrace -- Start tracking what's going on in session dstop -- Stop tracking what's going on in session dunload -- Revert changes recorded between dstart and dstop edit -- Edit plugin's file with $EDITOR env-whitelist -- Allows to specify names (also patterns) of variables left unchanged during an unload. -v - verbose glance -- View the plugin source help -- Usage information icemods -- Shows ice-modifiers registered by annex light -- Light load plugin list -- List loaded plugins load -- Load plugin loaded -- Show loaded plugins ls -- List snippets in formatted and colorized manner man -- Manpage module -- Manage binary Zsh module, see 'zi module help' for more info recall -- Fetch saved ice modifiers and construct 'zi ice ...' command recently -- Show plugins that changed recently, argument is e.g. 1 month 2 days report -- Show plugins report (or all plugins if --all passed) run -- Execute code inside plugin's folder self-update -- Updates and compiles ❮ ZI ❯ snippet -- Source (or add to PATH with --command) local or remote file (-f: force - do not use cache) srv -- Control a service, command can be: stop,start,restart,next,quit; next'' moves the service to another Zshell status -- Git status for plugin (or all plugins if --all passed) stress -- Test the plugin for compatibility with set of options subcmds -- Shows subcommands registered by annex times -- Statistics on plugin loading times uncompile -- Remove compiled version of plugin (or of all plugins if --all passed) unload -- Unload plugin update -- Git update plugin (or all plugins and snippets if --all passed) zstatus -- Check and provide status information ``` ## Footnotes 1. There's also `light-mode` ice which can be used to induce the no-investigating (i.e.: *light*) loading, regardless of the command used. [↩](#user-content-fnref-1) 2. The URL can use the following shorthands: `PZT::` (Prezto), `PZTM::` (Prezto module), `OMZ::` (Oh-My-Zsh), `OMZP::` (OMZ plugin), `OMZL::` (OMZ library), `OMZT::` (OMZ theme), e.g.: `PZTM::environment`, `OMZP::git`, etc. [↩](#user-content-fnref-2) 3. The `'…'` can be an absolute path, i.e.: it's possible to also add regular directories. If the option `-f` or `--front` is given, the directory path is prepended instead of appended to `$fpath`. [↩](#user-content-fnref-3) 4. If the option `-l` will be given then the plugin should be skipped – the option will cause the previous plugin to be reused. [↩](#user-content-fnref-4) --- --- title: 🏗 Zi configuration management description: Configure Zi paths, settings, local plugins, and loading behavior. canonical_url: https://wiki.zshell.dev/docs/guides/customization markdown_url: https://wiki.zshell.dev/docs/guides/customization/index.md locale: en source_path: docs/guides/02_customization.mdx --- # 🏗 Zi configuration management ## Hash parameter Related - [standard parameter naming](https://wiki.zshell.dev/community/zsh_plugin_standard/index.md#standard-parameter-naming) Set the initial hash definition and custom values, before enabling Zi. Example: ```zsh typeset -A ZI ZI[BIN_DIR]="some/custom/path/to/bin" source "${ZI[BIN_DIR]}/zi.zsh" ``` ### Customize paths
Hash FieldDefaultDescription
ZI[HOME_DIR]Legacy $HOME/.zi, otherwise XDG data ziWhere Zi should create all working directories
ZI[BIN_DIR]$ZI[HOME_DIR]/binDirectory where Zi code resides
ZI[COMPLETIONS_DIR]$ZI[HOME_DIR]/completionsCompletion working directory
ZI[CACHE_DIR]${XDG_CACHE_HOME:-$HOME/.cache}/ziCache directory
ZI[CONFIG_DIR]${XDG_CONFIG_HOME:-$HOME/.config}/ziDirectory for configuration files
ZI[MAN_DIR]$ZPFX/manDirectory to store manpages
ZI[LOG_DIR]$ZI[CACHE_DIR]/logDirectory to store log files
ZI[PLUGINS_DIR]$ZI[HOME_DIR]/pluginsPlugins working directory
ZI[SNIPPETS_DIR]$ZI[HOME_DIR]/snippetsSnippets working directory
ZI[ZCOMPDUMP_PATH]${ZI[CACHE_DIR]}/.zcompdumpPath to .zcompdump file
ZI[ZMODULES_DIR]$ZI[HOME_DIR]/zmodulesZsh modules working directory
ZPFX$ZI[HOME_DIR]/polarisDirectory to store binary and related files
:::info Loader defaults If you install with the [loader](https://wiki.zshell.dev/docs/getting_started/installation/index.md#loader), `init.zsh` sets `ZI[REPOSITORY]`, `ZI[STREAM]`, `ZI[HOME_DIR]`, and `ZI[BIN_DIR]` before Zi is cloned, because those four decide what to fetch and where. It mirrors the core home resolver. Every other field in this table, including cache and configuration, is owned by `zi.zsh`. To override any field, assign it in your `.zshrc` **before** sourcing the loader or `zi.zsh`. ::: #### Path precedence and XDG validity Zi resolves paths in this order: 1. A non-empty explicit `ZI[...]` path, or explicit `ZPFX`, wins. 2. For `ZI[HOME_DIR]`, a recognized legacy `$HOME/.zi` installation remains active so code, plugins, snippets, completions, and modules are not split. 3. Otherwise Zi uses the matching XDG base when its value is an absolute path. 4. An unset, empty, or relative XDG value uses the specification fallback: `$HOME/.local/share`, `$HOME/.cache`, or `$HOME/.config`. Zi does not use `ZDOTDIR` for application data, cache, or configuration. It is the home for Zsh startup files, not a Zi storage base. XDG destinations do not need to exist in advance; Zi creates its new application roots privately and does not change permissions on a pre-existing base directory. `XDG_ZI_HOME`, `XDG_ZI_CACHE`, and `XDG_ZI_CONFIG` are exported compatibility outputs derived from the resolved `ZI[...]` values. They are not configuration inputs. Set `ZI[HOME_DIR]`, `ZI[CACHE_DIR]`, or `ZI[CONFIG_DIR]` instead. #### Legacy and both-present homes Zi never moves, merges, deletes, or overwrites an installation merely because an XDG location is available. If only a recognized `$HOME/.zi` installation exists, Zi keeps using it and `zi zstatus` displays a migration hint outside the shell startup path. If both legacy and XDG homes contain Zi data, Zi uses an explicit configuration or the sourced `ZI[BIN_DIR]` identity. An external checkout without either identity conservatively selects the legacy home. `zi zstatus` reports the ambiguity and selected root. To resolve it, set `ZI[HOME_DIR]` before sourcing the loader or `zi.zsh`; do not combine roots manually while shells are using them. An automatic migration command is not part of path resolution. Follow [z-shell/zi#429](https://github.com/z-shell/zi/issues/429) for the planned explicit, recoverable migration workflow. Until it is released, keep a backup and treat changing `ZI[HOME_DIR]` as selecting another root, not as moving the existing one. ### Modify settings
Hash FieldDefaultDescription
ZI[OPTIMIZE_OUT_DISK_ACCESSES]undefinedIf set to 1, will skip checking if a turbo-loaded object exists on the disk. This option can give a performance gain of about 10 ms out of 150 ms (e.g: Zsh will start up in 140 ms instead of 150 ms).
ZI[COMPINIT_OPTS]undefinedOptions for compinit call (e.g: done by zicompinit), commonly used with -C to speed up loading
ZI[MUTE_WARNINGS]undefinedIf set to 1, mutes some warnings, specifically the plugin already registered warning
ZI[PKG_OWNER]z-shellOwner of the packages (zi pack …)
ZI[LOADER_HISTORY]1Loader only. Set to 0 before sourcing init.zsh to leave HISTFILE, SAVEHIST, and HISTSIZE entirely to your own configuration
## Non-GitHub (Local) Plugins Use the `create` subcommand with user name `_local` (the default) to create the plugin's skeleton in `$ZI[PLUGINS_DIR]`. It will be not connected with the GitHub repository (because of the user name being `_local`). To enter the plugin's directory use the `cd` command with just the plugin's name (without `_local`, it's optional). If the username is not `_local`, then Zi will create a repository also on GitHub and set up the correct repository origin. ## Extending Git Several projects provide git extensions. Installing them with Zi has many benefits: - all files are under `$HOME` – no administrator rights are needed, - declarative setup (like Chef or Puppet) – copying `.zshrc` to a different account brings also git-related setup, - easy update by e.g: `zi update --all`. Below is a configuration that adds multiple git extensions, loaded in Turbo mode, 1 second after prompt, with the use of the [bin-gem-node](https://github.com/z-shell/z-a-bin-gem-node) annex: ~/.zshrc ```zi zi as'null' wait'1' lucid for \ sbin Fakerr/git-recall \ sbin cloneopts paulirish/git-open \ sbin paulirish/git-recent \ sbin davidosomething/git-my \ sbin iwata/git-now \ sbin atload'export _MENU_THEME=legacy' \ arzzen/git-quick-stats \ sbin'bin/git-dsf;bin/diff-so-fancy' \ z-shell/zsh-diff-so-fancy \ make'PREFIX=$ZPFX install' \ tj/git-extras ``` The target directory for installed files is `$ZPFX` - `${ZI[HOME_DIR]}/polaris` by default. With [meta-plugins](https://github.com/z-shell/z-a-meta-plugins) consisting of: Annexes: 1. [z-shell/z-a-readurl](https://github.com/z-shell/z-a-readurl), 2. [z-shell/z-a-patch-dl](https://github.com/z-shell/z-a-patch-dl), 3. [z-shell/z-a-rust](https://github.com/z-shell/z-a-rust), 4. [z-shell/z-a-bin-gem-node](https://github.com/z-shell/z-a-bin-gem-node). Git tools: 1. [paulirish/git-open](https://github.com/paulirish/git-open), 2. [paulirish/git-recent](https://github.com/paulirish/git-recent), 3. [davidosomething/git-my](https://github.com/davidosomething/git-my), 4. [arzzen/git-quick-stats](https://github.com/arzzen/git-quick-stats), 5. [iwata/git-now](https://github.com/iwata/git-now), 6. [tj/git-extras](https://github.com/tj/git-extras), 7. [wfxr/forgit](https://github.com/wfxr/forgit). just run: ```zi zi light-mode for z-shell/z-a-meta-plugins @annexes @ext-git ``` ## [Zsh option](https://wiki.zshell.dev/community/zsh-guide/configuration/index.md): `setopt` This Zsh-specific guidance now lives in the Community [Configuration and Options](https://wiki.zshell.dev/community/zsh-guide/configuration/index.md) guide. ### History optimization See [History configuration](https://wiki.zshell.dev/community/zsh-guide/configuration/index.md#history-configuration). ### Other tweaks See [Other interactive options](https://wiki.zshell.dev/community/zsh-guide/configuration/index.md#other-interactive-options). ## Style the [completion system](https://zsh.sourceforge.io/Doc/Release/Completion-System.html#Completion-System-Configuration) with: `zstyle` This Zsh-specific guidance now lives in the Community [Completion Configuration](https://wiki.zshell.dev/community/zsh-guide/completion/index.md) guide. ### Fuzzy matching of completions See [Approximate matching](https://wiki.zshell.dev/community/zsh-guide/completion/index.md#approximate-matching). ### Pretty completions See [Grouping, descriptions, and matching](https://wiki.zshell.dev/community/zsh-guide/completion/index.md#grouping-descriptions-and-matching). ### Do menu-driven completion See [Menu selection](https://wiki.zshell.dev/community/zsh-guide/completion/index.md#menu-selection). ### Color completion for \[some things\]\[color-completion-using-zsh-modules-on\] See [Completion colors](https://wiki.zshell.dev/community/zsh-guide/completion/index.md#completion-colors). ## Disabling System-Wide `compinit` Call (Ubuntu) See [Ubuntu's global compinit call](https://wiki.zshell.dev/community/zsh-guide/completion/index.md#ubuntu-global-compinit). ## Multiple prompts
SyntaxDescription
load'…'Condition that when fulfilled will cause the plugin to be loaded.
unload'…'Same as above, but will unload the plugin.
note `zi light …` loads the plugin without tracking it, while `zi load` tracks the plugin. To be able to unload the plugin, it has to be loaded with `zi load …` instead of `zi light …`.
SyntaxDescription
atload'!…'Run the precmd hooks to make the prompts fully initialized when loaded in the middle of the prompt.
precmdHooks are normally run before each new prompt.
info Exclamation mark causes the effects of the functions to be tracked. To allow better unloading, conditions are checked every second, you can use conditions like:
ConditionDescription
![[ $PWD == *github* ]]Change prompt after changing directory to *github*.
![[ $MYPROMPT = 1 ]]Change prompt when variable MYPROMPT = 1 is true.
![[ … ]]The exclamation mark causes the prompt to be reset after loading or unloading the plugin pick'/dev/null' – disable sourcing of the default-found file.
multisrc'…'Source multiple files.
lucidDon't show the under-prompt message that says e.g: Loaded geometry-zsh/geometry.
nocdDon't cd into the plugin's directory when executing the atload'…'.
atload'…'This ice can make the path that's displayed by the theme point to that directory.
### Loading and unloading themes (8 examples) 1 - zprompts ```zi zi lucid load'![[ $MYPROMPT = 1 ]]' unload'![[ $MYPROMPT != 1 ]]' \ atload'!promptinit; typeset -g PSSHORT=0; prompt sprint3 yellow red green blue' nocd for \ z-shell/zprompts ``` 2 - lambda-mod-zsh-theme ```zi zi lucid load'![[ $MYPROMPT = 2 ]]' unload'![[ $MYPROMPT != 2 ]]' nocd for \ halfo/lambda-mod-zsh-theme ``` 3 - lambda-gitster ```zi zi lucid load'![[ $MYPROMPT = 3 ]]' unload'![[ $MYPROMPT != 3 ]]' nocd for \ ergenekonyigit/lambda-gitster ``` 4 - geometry ```zi zi lucid load'![[ $MYPROMPT = 4 ]]' unload'![[ $MYPROMPT != 4 ]]' \ atload'!geometry::prompt' nocd \ atinit'GEOMETRY_COLOR_DIR=63 GEOMETRY_PATH_COLOR=63' for \ geometry-zsh/geometry ``` 5 - pure ```zi zi lucid load'![[ $MYPROMPT = 5 ]]' unload'![[ $MYPROMPT != 5 ]]' \ pick"/dev/null" multisrc"{async,pure}.zsh" atload'!prompt_pure_precmd' nocd for \ sindresorhus/pure ``` 6 - agkozak-zsh-theme ```zi zi lucid load'![[ $MYPROMPT = 6 ]]' unload'![[ $MYPROMPT != 6 ]]' \ atload'!_agkozak_precmd' nocd atinit'AGKOZAK_FORCE_ASYNC_METHOD=subst-async' for \ agkozak/agkozak-zsh-theme ``` 7 - zinc ```zi zi load'![[ $MYPROMPT = 7 ]]' unload'![[ $MYPROMPT != 7 ]]' \ compile"{zinc_functions/*,segments/*,zinc.zsh}" nocompletions \ atload'!prompt_zinc_setup; prompt_zinc_precmd' nocd for \ robobenklein/zinc ``` 8 - git-prompt ```zi zi lucid load'![[ $MYPROMPT = 8 ]]' unload'![[ $MYPROMPT != 8 ]]' \ atload'!_zsh_git_prompt_precmd_hook' nocd for \ woefe/git-prompt.zsh ``` --- --- title: 💬 Zi messages description: Render reliable, styled Zi messages and progress updates. canonical_url: https://wiki.zshell.dev/docs/guides/messages markdown_url: https://wiki.zshell.dev/docs/guides/messages/index.md locale: en source_path: docs/guides/04_messages.mdx --- # 💬 Zi messages `+zi-message` is Zi's public message renderer. It preserves message text, applies optional semantic markup, and writes the result to one selected file descriptor. Safe automatic formatting is enabled by default. ```zsh +zi-message "Installed z-shell/zi in 245ms" +zi-message --level=success "Update completed" +zi-message "{warn}Warning:{rst} configuration is missing" ``` Use `+zi-progress` for a replaceable progress line. It shares the same renderer and options, but terminates output with a carriage return instead of a newline. ```zsh +zi-progress "Downloading 42%" ``` ## Automatic formatting The default `safe` mode recognizes forms whose meaning does not depend on the current directory or mutable shell state: - URLs with a scheme, including paths, queries, and fragments - Zi commands and built-in ice modifiers - integers, decimals, and durations ending in `ms`, `s`, `m`, `h`, or `d` - balanced single-quoted, double-quoted, and backtick spans Safe mode deliberately leaves ambiguous text such as `owner/repo`, `usr/bin`, `1..2`, and `...` unchanged. This makes the default predictable across machines and shell sessions. ```zsh +zi-message --auto=safe "Fetch https://example.com/a?q=1 in 12.5ms" ``` The `contextual` mode additionally recognizes registered annex commands and ices, shell commands, aliases, reserved words, functions, installed plugin IDs, known OMZ and PZT aliases, and existing paths. Its output can therefore change with `$PATH`, the current directory, installed plugins, or loaded shell state. ```zsh +zi-message --auto=contextual "Run my-function on ./local-file" ``` Disable automatic formatting for a call with `--auto=off`. Configure the default for all calls with `zstyle`: ```zsh zstyle ':zi:message' auto safe # Other values: contextual, off ``` The inline tags `{auto}`, `{contextual}`, and `{no-auto}` change the mode for the following span. `{auto}` means safe mode. An explicit style tag takes precedence over automatic formatting until `{rst}` or another automatic-mode tag resumes automatic formatting. ## Options
OptionEffect
-nDo not append the normal +zi-message newline.
-lJoin separate message operands with newlines instead of spaces.
-u FD or -uFDWrite the complete message to file descriptor FD.
--auto=off|safe|contextualSelect automatic formatting for this call.
--color=auto|always|neverSelect the color policy for this call.
--level=plain|debug|info|warn|error|successApply a semantic base style.
--literalDisable all markup parsing while preserving the input bytes.
--End option parsing. Use it before data that may begin with -.
The compatibility option `-r` is accepted and has no additional effect, because output is always emitted literally rather than through Zsh prompt expansion. Separate operands are joined with one space by default. Existing whitespace inside each operand, including tabs and newlines, is preserved. The renderer does not interpret `%F`, `%B`, backslashes, command substitutions, or parameter syntax. For dynamic text that must not be interpreted as Zi markup, combine `--literal` and `--`: ```zsh +zi-message --literal -- "$value" ``` `--literal` preserves control bytes too. If `$value` comes from an untrusted source, sanitize or reject terminal control characters before displaying it. ## Markup A markup tag has the form `{name}`. Unknown tags remain literal, so ordinary brace text is not silently deleted. Common semantic style tags include:
PurposeTags
Commands and options{cmd}, {bcmd}, {ice}, {opt}, {flag}
Objects and paths{pname}, {pid}, {file}, {dir}, {url}
Data{data}, {data2}, {num}, {time}, {version}, {var}
Status{info}, {warn}, {error}, {failure}, {happy}, {note}
Quoting and emphasis{apo}, {quo}, {bapo}, {b}, {u}, {it}, {faint}
Reset{rst}
`{pid}` is an alias for `{pname}`. Existing Zi palette names exposed as `ZI[col-NAME]` also work as `{NAME}`. Structural tags insert characters rather than selecting a style:
TagOutput
{nl}Newline
{tab}Spaced tab
{bspc}Backspace
{bar}, {th-bar}Terminal-width horizontal rule
{…}, {-…}Ellipsis forms
{ndsh}, {mdsh}, {mmdsh}Dash forms
{lr}, {↔}Left-right marker
Unicode structural tags use ASCII fallbacks when the active locale is not UTF-8. ## Color policy and destinations The default `auto` color policy emits ANSI color only when the selected output descriptor is a terminal, the terminal reports color support, `TERM` is not `dumb`, and `NO_COLOR` is unset. Zi uses a compatible basic palette for terminals reporting fewer than 256 colors. ```zsh zstyle ':zi:message' color auto # Other values: always, never ``` `--color=always` is an explicit per-call override, including when `NO_COLOR` is set. `--color=never` guarantees plain output. The reset sequence, message body, and terminator are all written to the same selected descriptor; Zi does not leak a newline or reset to standard output. ```zsh +zi-message -u2 --level=error "Update failed" ``` Invalid options, modes, levels, and descriptors return status `2`. A write failure returns the underlying output error status. --- --- title: 🗒 Zi bindmap description: Remap plugin key bindings with Zi's bindmap ice. canonical_url: https://wiki.zshell.dev/docs/guides/syntax/bindkey markdown_url: https://wiki.zshell.dev/docs/guides/syntax/bindkey/index.md locale: en source_path: docs/guides/syntax/10_bindkey.mdx --- # 🗒 Zi bindmap ## Bindkey General Zsh key-binding guidance now lives in the Community [Key Bindings](https://wiki.zshell.dev/community/zsh-guide/key-bindings/index.md) guide. ## Delete key binding See [Remove a key binding](https://wiki.zshell.dev/community/zsh-guide/key-bindings/index.md#remove-a-key-binding). The Community guide also explains why `bindkey -r`, not `bindkey -d`, removes an individual binding. ## The `bindmap'…'` keybindings Sometimes plugins call [bindkey](https://wiki.zshell.dev/search?q=bindkey) to assign keyboard shortcuts. This can cause problems because multiple plugins can bind the same keys. Also, the user might want a different binding(s), which will require complicated, additional `bindkey` commands in `.zshrc`. Zi provides a solution to this problem – the ability to remap the bindkeys with a short [ice-modifier](https://wiki.zshell.dev/search?q=ice+modifier) `bindmap'…'`. ### Examples for `bindmap'…'` Map Ctrl-G instead of Ctrl-R for the history searcher. ```zi zi bindmap'^R -> ^G' for z-shell/history-search-multi-word ``` Map Ctrl-Shift-Left and Ctrl-Shift-Right used by URxvt instead of the Xterms' ones. Load with the bindkey-tracking ↔ with light-loading for anything else. Could also separate the bindmaps with a semicolon, i.e.: ```zi bindmap'"\\e[1\;6D" -> \\e[1\;5D ; "\\e[1\;6C" -> ^[[1\;5C' \ ``` ```zi zi wait light-mode trackbinds bindmap'"\\e[1\;6D" -> \\e[1\;5D"' \ bindmap'"\\e[1\;6C" -> ^[[1\;5C' pick'dircycle.zsh' for \ michaelxmcbride/zsh-dircycle ``` Map space to regular space and Ctrl-Space to the `globalias` widget, which expands the alias entered on the left, provided by OMZ globalias plugin. ```zi zi bindmap='!" " -> magic-space; !"^ " -> globalias' nocompletions \ depth=1 pick=plugins/globalias/globalias.plugin.zsh for \ ohmyzsh/ohmyzsh ``` ### Explanation The `bindmap'…'` ice has two modes of operation: normal and exclamation-mark (`bindmap'!…'`). In the first mode, the remapping is being done from-key to-key, i.e.: `bindmap'fromkey -> to-key'`. The given key is changed to the second given key in the `bindkey` command while loading the plugin. In the second mode, the remapping is being done from-key to-widget, e.g: `bindmap'!from-key -> to-widget'`. In this mode, the given key is being mapped to the given widget instead of the widget specified in the `bindkey` command e.g.: Instead of: ```zsh bindkey "^ " magic-space bindkey " " globalias ``` The actual call that'll be done will be: ```zsh bindkey "^ " globalias bindkey " " magic-space ``` For the `bindmap='!" " -> magic-space; !"^ " -> globalias'` ice. ### Using `bindmap'…'` in light mode When the investigation mode is on i.e.: - when the full loading mode is being used, default in the `for` syntax, and when `zi load …` is used, then the `bindmap'…'` ice works normally. In the non-investigation: - the [light mode](https://wiki.zshell.dev/search?q=light+mode) – activated when `zi light …` or the `light-mode` ice is being used – the `bindmap'…'` is unavailable, unless the `trackbinds` ice is specified: With the use of the light-mode ice and the for-syntax: ```zi zi light-mode for trackbinds bindmap'^R -> ^G' \ z-shell/history-search-multi-word ``` With the use of the traditional syntax: ```zi zi ice trackbinds bindmap'^R -> ^G' zi light z-shell/history-search-multi-word ``` ### Using the UPAR shorthands There are four special values that can be used on the left side of the bind-map: UPAR, DOWNAR, LEFTAR, RIGHTAR. They'll match up arrow, down arrow, etc. So that it's possible to do: ```zi zi bindmap='LEFTAR -> ^F; RIGHTAR -> ^G' … ``` The benefit of using the UPAR, … shorthands is that they cover multiple possible cursor-key codes for each of the cursor keys so that they'll work regardless of the terminal is used. --- --- title: ✨ Zi "for" syntax description: Use Zi's concise for syntax to load groups of plugins and snippets. canonical_url: https://wiki.zshell.dev/docs/guides/syntax/for markdown_url: https://wiki.zshell.dev/docs/guides/syntax/for/index.md locale: en source_path: docs/guides/syntax/02_for.mdx --- # ✨ Zi "for" syntax The `for` syntax is the most popular, more concise, and more optimized. The single command will work the same as the [standard syntax](https://wiki.zshell.dev/docs/guides/syntax/standard/index.md) invocation. It allows providing common/default ice-modifiers for a **set of plugins** or to source **multiple files** with the ices: [src, pick, multisrc](https://wiki.zshell.dev/docs/guides/syntax/standard/index.md#src-pick-multisrc). tip To find more information about anything use [search](https://wiki.zshell.dev/search?q=ice) or just CTRL+K. ```zi zi light-mode for \ zsh-users/zsh-autosuggestions \ z-shell/F-Sy-H \ z-shell/H-S-MW \ pick"async.zsh" src"pure.zsh" \ sindresorhus/pure ``` It is best presented by real-world examples: ```zi zi wait"3" lucid for as"null" \ sbin Fakerr/git-recall \ sbin paulirish/git-open \ sbin paulirish/git-recent \ sbin davidosomething/git-my \ make"PREFIX=$ZPFX install" iwata/git-now \ make"PREFIX=$ZPFX" tj/git-extras ``` The above single command installs 6 plugins ([git extension](https://wiki.zshell.dev/search?q=git+ext) packages), with the base ices `as"null" wait"3" lucid` that are common to all of the plugins and 6 plugin-specific add-on ice-modifiers. Load a few useful binary packages from the [GitHub releases](https://wiki.zshell.dev/search?q=GH-R), utils: ```zi zi for as"null" wait"2" lucid from"gh-r" \ mv"exa* -> exa" sbin ogham/exa \ mv"fd* -> fd" sbin"fd/fd" @sharkdp/fd \ sbin"fzf" junegunn/fzf ``` note - `sbin'…'` is an [ice](https://wiki.zshell.dev/search?q=ice) added by the [bin-gem-node](https://wiki.zshell.dev/search?q=bin+gem+node) [annex](https://wiki.zshell.dev/search?q=annex), it provides the command to the command line without altering `$PATH`. - If the name of the command is the same as the name of the plugin, the ice contents can be skipped. [Turbo](https://wiki.zshell.dev/search?q=turbo+mode) load some plugins, without any plugin-specific ices: ```zi zi wait lucid for \ hlissner/zsh-autopair \ urbainvaes/fzf-marks ``` Load two [Oh-My-Zsh](https://wiki.zshell.dev/search?q=oh+my+zsh) files as [snippets](https://wiki.zshell.dev/search?q=snippets), in turbo mode: ```zi zi wait lucid for \ OMZ::lib/git.zsh \ atload"unalias grv" \ OMZ::plugins/git/git.plugin.zsh ``` Popular plugin set with [turbo](https://wiki.zshell.dev/search?q=turbo+mode) and The "For": ```zi zi wait lucid light-mode for \ atinit"zicompinit; zicdreplay" \ z-shell/F-Sy-H \ atload"_zsh_autosuggest_start" \ zsh-users/zsh-autosuggestions \ blockf atpull'zi creinstall -q .' \ zsh-users/zsh-completions ```
SyntaxDescription
waitLoad 0 seconds (about 5 ms exactly) after prompt (turbo mode).
lucidSilence the under-prompt messages ("Loaded {name of the plugin}").
light-modeLoad the plugin in light mode. 1.
atpull'…'Execute after updating the plugin – the command in the ice will install any new completions.
atinit'…'Execute code before loading plugin.
atload'…'Execute code after loading the plugin.
zicompinitEquals to autoload compinit; compinit.
zicdreplayExecute compdef … calls by plugins. More below 2.
## Oh-My-Zsh, [turbo](https://wiki.zshell.dev/search?q=turbo+mode) Oh-My-Zsh and the The "For" syntax ### Without [turbo mode](https://wiki.zshell.dev/search?q=turbo+mode) and The "For" ```zi # A. setopt prompt_subst # B. zi snippet OMZL::git.zsh # C. zi ice atload"unalias grv" zi snippet OMZP::git # D. zi for OMZL::prompt_info_functions.zsh OMZT::gnzh # E. zi snippet OMZP::colored-man-pages # F. zi ice as"completion" zi snippet OMZP::docker/_docker # G. zi ice atinit"zicompinit; zicdreplay" zi light z-shell/F-Sy-H ``` ### With [turbo mode](https://wiki.zshell.dev/search?q=turbo+mode) and The "For" ```zi # A. setopt prompt_subst # B, C. zi wait lucid for \ OMZL::git.zsh \ atload"unalias grv" \ OMZP::git # Provide a simple prompt till the theme loads to visualize the effect. PS1="READY >" # D. zi wait'!' lucid for \ OMZL::prompt_info_functions.zsh \ OMZT::gnzh # E, F, G. zi wait lucid for \ atinit"zicompinit; zicdreplay" \ z-shell/fast-syntax-highlighting \ OMZP::colored-man-pages \ as"completion" \ OMZP::docker/_docker ``` info **A** - Most themes use this option. **B, C** - OMZ themes use this library and some others use also the plugin. It provides many aliases – `atload'…'` showing how to disable some of them (e.g.: to use the program `rgburke/grv`). **D** - Set OMZ theme. Loaded separately because the theme needs the `!` passed to the `wait` ice to reset the prompt after loading the snippet in turbo mode. **E, F, G** - Some plugins: 1. syntax-highlighting, loaded possibly early for a better user experience). 2. example functional plugin. 3. docker completion. The above setup loads everything after the prompt, because of the preceding `wait` ice. That is called **turbo mode**, which shortens Zsh startup time by 50%-80%, e.g. instead of 200 ms, it'll be getting your shell started up after **40 ms**. Try both setups on the daily basis to notice the difference. The features of Zi can do much more than this simple example. ### `zi-turbo '…' for …` The `zi-turbo` is a function to simplify `wait`: ```zi zi-turbo() { zi depth'3' lucid ${1/#[0-9][a-c]/wait"${1}"} "${@:2}" } ``` Then use the `for` syntax in the imposed loading order: ```zsh zi-turbo '0a' for \ OMZL::git.zsh \ OMZL::compfix.zsh \ OMZL::functions.zsh \ zi-turbo '0b' for \ OMZL::prompt_info_functions.zsh OMZL::spectrum.zsh \ OMZL::clipboard.zsh OMZL::termsupport.zsh OMZL::directories.zsh zi-turbo '0c' for \ OMZP::sudo OMZP::encode64 \ atload"unalias grv g" OMZP::git \ OMZP::gcloud OMZP::nvm OMZP::gem OMZP::rust zi-turbo '1a' for \ MichaelAquilina/zsh-you-should-use ``` ## Summary In general, [turbo mode](https://wiki.zshell.dev/search?q=turbo+mode) can be optionally enabled only for a subset of plugins or for all plugins. Syntax-highlighting plugins, like [F-Sy-H](https://github.com/z-shell/F-Sy-H) or [zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting), theoretically expect to be loaded last, even after the completion initialization as `compinit` function. However, in practice, you just have to ensure that such plugin is loaded after plugins that are issuing `compdef` – which means completions that aren't using the underscore-starting function file; the completion initialization still has to be performed before the syntax-highlighting plugin, hence the `atinit'…'` ice, which will load `compinit` right before loading the plugin, the syntax-highlighting and suggestions plugins are loaded early for a better user experience. ## Footnotes 1. Then the tracking of plugin, activity report gathering, accessible via the `zi report {plugin-name}` subcommand) is being disabled. Note that for turbo mode, the performance gains are almost `0`, so in this mode, you can load all plugins with the tracking and the `light-mode` ice can be removed from the command. [↩](#user-content-fnref-1) 2. They were recorded and `compinit` can be called later. `compinit` provides the `compdef` function, so it must be run before issuing the taken-over `compdef`s with `zicdreplay`. [↩](#user-content-fnref-2) --- --- title: 🧊 Ice Modifiers description: Ice Modifiers Documentation canonical_url: https://wiki.zshell.dev/docs/guides/syntax/ice-modifiers markdown_url: https://wiki.zshell.dev/docs/guides/syntax/ice-modifiers/index.md locale: en source_path: docs/guides/syntax/03_ice_modifiers.mdx --- # 🧊 Ice Modifiers FAQ: What is ice? ![What is ice](/assets/ideal-img/ice_180x170.788ead7.180.png) The **ice** is something that melts in a drink, though in Zi syntax, it means adding an **ice-modifier** that's temporary because it disappears – which means that the **ice-modifier** will last only for the next Zi command. An ice-modifiers are [passed](https://wiki.zshell.dev/docs/guides/syntax) to `zi ice …` to obtain described effects, additionally can be added with [annexes](https://wiki.zshell.dev/ecosystem/annexes/overview/index.md). To see all available ice-modifiers run `zi icemods`. Some ice-modifiers are highlighted and clicking on them will take you to the appropriate Wiki page for an extended explanation. You may safely assume that given ice works with both plugins and snippets unless explicitly stated otherwise. ## Ice effects
Ice-modifierDescription
asCan be as"program" (alias: as"command"), and will cause to add script/program to $PATH instead of sourcing (see pick). Can also be as"completion" – use with plugins or snippets in whose only underscore-starting _* files you are interested in. 1
id-asNickname a plugin or snippet, e.g. create a short handler for the long-URL snippet.
teleidEffective remote-ID (i.e.: URL, GitHub username/repo, package name, etc.).
compilePattern (possible {…} expansion, like {a/*,b*}) to select additional files to compile, e.g. compile"(pure | async).zsh"forsindresorhus/pure.
nocompileDon't try to compile pick-pointed files. If passed the exclamation mark (i.e. nocompile'!'), then do compile, but after make'…' and atclone'…' (useful if Makefile installs some scripts, to point pick'…' at the location of their installation).
serviceMake the following plugin or snippet a service, which will run in the background, and only in a single Zshell instance. See #zservice topic.
reset-promptReset the prompt after loading the plugin/snippet (by issuing zle .reset-prompt). Note: normally it's sufficient to precede the value of wait'…' ice with !.
bindmapTo hold ;-separated strings like Key(s)A -> Key(s)B, e.g. ^R -> ^T; ^A -> ^B. In general, bindmap'…' changes bindings (done with the bindkey builtin) the plugin does. The example would cause the plugin to map Ctrl-T instead of Ctrl-R, and Ctrl-B instead of Ctrl-A. Does not work with snippets.
trackbindsShadow but only bindkey calls even with zi light …, i.e. even with investigating disabled (fast loading), to allow bindmap to remap the key-binds. The same effect has the zi light -b …, i.e. additional -b option to the light-subcommand. Does not work with snippets.
wrapTakes a ;-separated list of function names to be investigated (meaning gathering report and unloading data) once during execution. It works by wrapping the functions with an investigating-enabling and disabling snippet of code. 2
aliasesLoad the plugin with the aliases mechanism enabled. Use plugins that define and use aliases in their scripts.
light-modeLoad the plugin without investigating, i.e., the same as the light command. Useful with the "for" syntax, where there is no load nor light subcommand
extractPerforms archive extraction supporting multiple formats like zip, tar.gz, etc., and OS X dmg images. 3
substSubstitute the given string into another string when sourcing the plugin script, e.g.: zi subst'autoload → autoload -Uz' ….
autoloadAutoload the given functions (from their files). Equivalent to calling atinit'autoload the-function'. Supports renaming of the function – pass '… → new-name' or '… -> new-name', e.g.: zi autoload'fun → my-fun; fun2 → my-fun2'.
## Cloning options
Ice-modifierDescription
protoChange protocol to git,ftp,ftps,ssh, rsync, etc. The default is https. Does not work with snippets.
fromClone plugin from a given site. Supported are from"github" (default), …"github-rel", …"gitlab", …"bitbucket", …"notabug" (short names: gh, gh-r, gl, bb, nb). Can also be a full domain name e.g: for GitHub enterprise. Does not work with snippets.
verUsed with from"gh-r" (i.e. downloading a binary release, e.g. for use with as"program") – selects which version to download. Default is latest, can also be explicit ver"latest". Works also with regular plugins, and checkouts e.g. ver"branch", i.e. a specific version. Does not work with snippets.
bpickUsed to select which release from GitHub Releases to download, e.g. zi ice from"gh-r" as"program" bpick"*Darwin*"; zi load docker/compose. Does not work with snippets.
depthPass --depth to git. I.e., limit how much history to download. Does not work with snippets.
cloneoptsPass the contents of cloneopts to git clone. Defaults to --recursive. I.e., change cloning options. Pass empty ice to disable recursive cloning. Does not work with snippets.
pulloptsPass the contents of pullopts to git pull used when updating plugins. Does not work with snippets.
## Selection of files (source '…')
Ice-modifierDescription
pickSelect the file to source, or the file to set as a command, when using snippet --command or the ice as"program". More below 4.
srcSpecify an additional file to source after the main file or after setting up command via as"program". It is not a pattern but a plain filename.
multisrcAllows specifying multiple files for sourcing, enumerated with spaces as the separators. More below 5.
## Conditional loading
Ice-modifierDescription
waitPostpone loading a plugin or snippet. For wait'1', loading is done 1 second after the prompt. 6.
loadA condition to check which should cause the plugin to load. 7.
unloadA condition to check to cause the plugin to unload. More below 8.
cloneonlyDon't load the plugin/snippet, only download it.
ifLoad plugin/snippet only when a given condition is true. Example: 9.
hasLoad plugin or snippet only when given command is available (in $PATH), e.g. zi ice has'git' ….
subscribePostpone loading of a plugin or snippet until the given file(s) get updated, e.g. subscribe'{~/files-*,/tmp/files-*}'.
trigger-loadCreates a function that loads the associated plugin/snippet, with an option. More below 10.
## Plugin output
Ice-modifierDescription
silentMute plugin's or snippet's stderr & stdout. Also, skip the loaded … message under the prompt for wait, etc. loaded plugins, and completion-installation messages.
lucidSkip loaded … message under prompt for wait, etc. loaded plugins (a subset of silent).
notifyOutput given message under-prompt after successfully loading a plugin/snippet. In case of problems with the loading, output a warning message and the return code. If starts with ! it will then always output the given message. Hint: if the message is empty, then it will just notify about problems.
## Completions
Ice-modifierDescription
blockfDisallow plugin to modify fpath. Useful when a plugin wants to provide completions traditionally. Manage completions using Zi and block the plugins to expose them.
nocompletionsSkip plugin completions detection and installation. Completions can be installed anytime using: zi creinstall {plugin-name}.
## Command execution after cloning, updating or loading
Ice-modifierDescription
mvMove file after cloning or update (only for new commits). Example: mv "fzf-* -> fzf". It uses -> as a separator for old and new file names. Also works with snippets.
cpCopy file after cloning or update (only for new commits). Example: cp "docker-c* -> dcompose". Ran after mv.
atcloneRun command after cloning, within plugin's directory, e.g. zi ice atclone"echo cloned". Ran also after downloading the snippet.
atpullRun command after updating (only for new commits), within the plugin's directory. If starts with "!" then the command will be run before mv & cp ices and before git pull. Otherwise is run after mv & cp ices. Use the atpull'%atclone' to repeat atclone ice-modifier.
atinitRun command after directory setup (cloning, checking, etc.) of the plugin/snippet before loading it.
atloadRun the given command within the plugin's directory after loading. Can be used with snippets. Passed code can be preceded with !, to be investigated (when using load, not light).
run-atpullAlways run the atpull hook (when updating), not exclusively for new commits.
nocdDon't switch the current directory to the plugin's directory when evaluating the above ice-modifiers atinit'…', atload'…', etc.
makeRun the make command after cloning or updating and executing the mv, cp, atpull, atclone ice-modifiers. Can obtain argument, e.g. make"install PREFIX=/opt". If the value starts with ! then make is run before atclone and atpull ice-modifiers, e.g. make'!'.
countdownCauses an interruptive (Ctrl-C) countdown 5…4…3…2…1…0 to be displayed before executing atclone'…', atpull'…' and make ices-modifiers.
resetInvokes git reset --hard HEAD for plugins before pulling any new changes. This way git will not report conflicts if some changes were done by e.g.: atclone'…' ice-modifier. For file snippets and gh-r plugins, it invokes rm -rf *.
## Sticky-Emulation Of other shells
Ice-modifierDescription
sh, !shSource the plugin's (or snippet's) script with sh emulation so that also all functions declared within the file will get a sticky emulation assigned and invoked with the sh emulation set-up. The !sh version switches additional options that are rather not important from the portability perspective.
bash, !bashThe same as sh, but with the SH_GLOB option disabled, for "Bash" regular expressions to work.
ksh, !kshThe same as sh, but emulating the ksh shell.
csh, !cshThe same as sh, but emulating the csh shell.
## Footnotes 1. The third possible value is `as"null"` – a shorthand for `pick"/dev/null" nocompletions` – i.e.: it disables the default script-file sourcing and also the installation of completions. [↩](#user-content-fnref-8) 2. In summary, `wrap` allows to extend the investigating beyond the moment of loading of a plugin. An example use is to `wrap` a precmd function of a prompt (like `_p9k_precmd()` of powerlevel10k) or other plugins that *postpones its initialization till the first prompt* (like e.g.: zsh-autosuggestions). **Does not work with snippets.** [↩](#user-content-fnref-9) 3. If it has no value, then it works in the *auto* mode – it automatically extracts all files of known archive extensions IF they aren't located deeper than in a sub-directory (this is to prevent extraction of some helper archive files, typically located somewhere deeper in the tree). If no such files will be found, then it extracts all found files of known **type** – the type is being read by the `file` Unix command. If not empty, then takes the names of the files to extract. Refer to the Wiki page for further information. [↩](#user-content-fnref-10) 4. This pattern will alphabetically match and choose the first file e.g: `zi ice pick"*.plugin.zsh"; zi load …`. [↩](#user-content-fnref-1) 5. Example: `multisrc'misc.zsh grep.zsh'` and also using brace-expansion syntax: `multisrc'{misc,grep}.zsh'` also supports patterns. [↩](#user-content-fnref-2) 6. For `wait'[[ … ]]'`, `wait'(( … ))'`, loading is done when given condition is meet. For `wait'!…'`, the prompt is reset after load. Zsh can start 80% (i.e.: 5x) faster thanks to postponed loading. **Fact:** when `wait` is used without a value, it works as `wait'0'`. [↩](#user-content-fnref-3) 7. It will load once, the condition can be still true, but will not trigger the second load, unless the plugin is unloaded earlier, see `unload`. E.g.: `load'[[ $PWD = */github* ]]'`. [↩](#user-content-fnref-4) 8. It will unload once, then only if loaded again e.g: `unload'[[ $PWD != */github* ]]'`. [↩](#user-content-fnref-5) 9. Example: `zi ice if'[[ -n "$commands[otool]" ]]'; zi load …` or `zi ice if'[[ $OSTYPE = darwin* ]]'; zi load …`. [↩](#user-content-fnref-6) 10. To use the option, precede the ice content with `!` to automatically forward the call afterward, to a command of the same name as the function. Can obtain multiple functions to create – separate with `;`. [↩](#user-content-fnref-7) --- --- title: 🔀 Zi standard syntax description: Use Zi's standard command and ice-modifier syntax. canonical_url: https://wiki.zshell.dev/docs/guides/syntax/standard markdown_url: https://wiki.zshell.dev/docs/guides/syntax/standard/index.md locale: en source_path: docs/guides/syntax/01_standard.mdx --- # 🔀 Zi standard syntax ## Introduction Zi provides two syntax types for structured statements or expressions: - Standard syntax - The ["For"](https://wiki.zshell.dev/docs/guides/syntax/for/index.md) syntax It is up to the user which syntax to use, but it is highly recommended to familiarize yourself with both of them. In this example, we will use an empty repository [z-shell/0](https://github.com/z-shell/0) to practice the basics of the standard syntax. - Execute the following command in your terminal: ```zi zi load z-shell/0 ``` Successfully installed the Zsh plugin which usually contains all the setup instructions as described in the [Zsh plugin standard](https://wiki.zshell.dev/community/zsh_plugin_standard/index.md). A snippet is a single file with a portion of reusable source code, machine code, or text and requires a full path or URL to the file. > - Execute the following command in your terminal: ```zi zi snippet https://raw.githubusercontent.com/z-shell/src/main/public/zsh/snippets/welcome.zsh ``` Success! But not always everything is so easy and simple, also sometimes we want certain things to happen at certain times or conditions. This can be achieved using [ice-modifiers](https://wiki.zshell.dev/docs/guides/syntax/ice-modifiers/index.md). The top line contains ice-modifiers, and the bottom line is the plugin. > - Execute the following commands in your terminal: ```zi zi ice id-as'zsh/plugin' atinit'print "Hello World!"' zi load z-shell/0 ``` This registered the plugin under the [plugin ID](#id-as) `zsh/plugin` instead of `z-shell/0`. This will work as expected e.g. `zi update zsh/plugin`, `zi remove zsh/plugin`, etc. The "Hello World!" printed before loading the plugin Let's install again with more ice-modifiers. > - Execute the following commands in your terminal: ```zi zi ice id-as'final/countdown' \ atinit'+zi-message "{bapo}Cloned!"' \ atclone'+zi-message "{quos}Boom!"' \ atload'+zi-message "{apo}Loaded!"' countdown zi load z-shell/0 ``` ## Order of execution The order of execution of related ice-modifiers is as follows: ```zi atinit'' → atpull'!' → make'!!' → mv'' → cp'' → make'!' → atclone'' / atpull'' → make'!' → [ plugin script loading ] → src'' → multisrc'' → atload'' ``` ### A few remarks - The syntax automatically detects if the object is a snippet or a plugin, by checking if the object is an URL, i.e.: if it starts with `http*://` or `OMZ::`, etc. - To load a local-file snippet (which will be treated as a local-directory plugin by default) use the `is-snippet` ice, - To load a plugin in `light` mode use the `light-mode` ice. - If the plugin name collides with an ice name, precede the plugin name with `@`, e.g.: `@sharkdp/fd` (collides with the `sh` ice, ZI will take the plugin name as `sh"arkdp/fd"`), see the next section for an example. ### Syntax alternatives Zi supports alternatives such as the equal (`=`) syntax: ```zi zi ice id-as=equal atload="print Hello World" zi load z-shell/0 ``` The colon (`:`) syntax: ```zi zi ice id-as:colon atload:"print Hello World" zi load z-shell/0 ``` And also – in conjunction with all of the above – the GNU syntax: ```zi zi ice id-as=GNU --atload="print Hello World" zi load z-shell/0 ``` The syntax alternatives can utilize the highlighting of editors like Vim – and have the strings and ice expressions colorized with a distinct color. However, with [zi-vim-syntax](https://github.com/z-shell/zi-vim-syntax) the syntax definition can be superseded with the highlighting specifically for Zi. syntax definition can be superseded with the highlighting specifically for Zi. ### Utilizing "make" Vim repository on GitHub – a typical source code that needs compilation, Zi can manage the run of `./configure` and other `make` stuff. Ice-modifier `pick` adds the binary program to `$PATH`. You could also install the package under the path $ZPFX. ~/.zshrc ```zi zi ice as"program" atclone"rm -f src/auto/config.cache; ./configure" \ atpull"%atclone" make pick"src/vim" zi light vim/vim ``` The `make'…'` ice could also be: `make"install PREFIX=$ZPFX"`, if "install" wouldn't be the only, default target. info [$ZPFX](https://wiki.zshell.dev/community/zsh_plugin_standard/index.md#global-parameter-with-prefix) is provided by Zi, it is set to `${ZI[HOME_DIR]}/polaris` by default. However, it can be changed by specifying the `$ZPFX=` target. ~/.zshrc ```zi zi ice as"program" pick"$ZPFX/bin/git-*" make"PREFIX=$ZPFX" zi light tj/git-extras ``` The `Makefile` of the project above has only 2 tasks: 1. Install the target. 2. Build scripts that are required for installation. The `Makefile` with 2 tasks, can use: 1. `make"all install PREFIX=…"`, 2. `pick'…'` will `chmod +x` all matching files and add `$ZPFX/bin/` to `$PATH`. ### Compiling programs ```zi zi ice as"program" atclone"rm -f src/auto/config.cache; ./configure" \ atpull"%atclone" make pick"src/vim" zi light vim/vim ```
SyntaxDescription
as'program'Add file selected by pick'…' to $PATH, and do not source it.
atclone'…'Execute code after downloading.
atpull'%atclone'Execute the same code atclone'…' is given, but after successful update.
makeRun make after atclone'…' and atpull'…' (note: make'!' will execute before them).
pick'src/vim'Set the executable flag on src/vim, hint that src/ should be added to $PATH.
The same but with **installation** (`make install`) under [$ZPFX](https://wiki.zshell.dev/community/zsh_plugin_standard/index.md#global-parameter-with-prefix) by default: ```zi zi ice as'program' atclone'rm -f src/auto/config.cache; \ ./configure --prefix=$ZPFX' atpull'%atclone' make'all install' pick'$ZPFX/bin/vim' zi light vim/vim ```
SyntaxDescription
as'program'As above.
atclone'…'As above plus pass --prefix=$ZPFX to ./configure, to set the installation directory.
atpull'%atclone'As above.
makeAs above, but also run the install target.
pick'src/vim'as above, but for a different path $ZPFX/bin/vim.
### LS\_COLORS A repository [trapd00r/LS\_COLORS](https://github.com/trapd00r/LS_COLORS) provides a file with color definitions for GNU `ls` command, and also for [ogham/exa](https://github.com/ogham/exa). Typically one does `eval $( dircolors -b $HOME/LS_COLORS)` to process this file and set the environment for `ls`. This means `dircolors` is run by every shell startup. It costs much time to create a fork and program, i.e., the `dircolors` binary needs to be loaded to obtain and process the color definitions. The following invocation solves this problem: ```zi zi ice atclone'dircolors -b LS_COLORS > clrs.zsh' \ atpull'%atclone' pick"clrs.zsh" nocompile'!' \ atload'zstyle ":completion:*" list-colors ${(s.:.)LS_COLORS}' zi light trapd00r/LS_COLORS ```
SyntaxDescription
atclone'…'Generate shell script, passing it to eval. More: 1
atpull'%atclone'Do the same at any update of the plugin. More: 2
pick"clrs.zsh"Source the previously generated file clrs.zsh.
nocompile'!'Invokes compilation after the atclone'…' and the exclamation mark causes this.
atload'…'Additionally sets up the Zsh completion to use the colors provided by the trapd00r package.
This way, except for the plugin installation and update, `dircolors` isn't run, just normal sourcing is done. The everyday sourced file, i.e. `clrs.zsh`, is being compiled to speed up the loading. ### Direnv The project [direnv/direnv](https://github.com/direnv/direnv) registers itself in the Z shell to modify the environment on directory change. This registration is most often done by `eval "$(direnv hook zsh)"` added to `.zshrc`. ```zi zi ice as"program" make'!' atclone'./direnv hook zsh > zhook.zsh' \ atpull'%atclone' src"zhook.zsh" zi light direnv/direnv ``` - `make'!'` – execute `make` before `atclone'…'` and before `atpull'…'` (see `make` above), - `src'zhook.zsh'` – source file `zhook.zsh`. In general, `direnv` works by hooking up to Zsh. The code that does this is provided by the program `direnv` (built by `make'…'`). Above `atclone'…'` puts this code into file `zhook.zsh`, `src''` sources it. This way `direnv hook zsh` is executed only on clone and update, and Zsh starts faster. #### Glance at the 'for' syntax The drawback of this standard procedure is that the `direnv` binary is run on every shell startup and significantly slows it down. Zi allows to solve this in the following way: ```zi zi as"program" make'!' atclone'./direnv hook zsh > zhook.zsh' \ atpull'%atclone' pick"direnv" src"zhook.zsh" for \ direnv/direnv ```
SyntaxDescription
make'!'Compile direnv, the exclamation mark means: run the make first, before atclone'…' and atpull'…' hooks.
atclone'…'As soon as the plugin is installed generate the registration code and save it to zhook.zsh, instead of passing it to eval.
atpull'%atclone'The atclone'…' runs on installation while atpull'…' runs on update of the plugin.
src'zhook.zsh'Load generated registration code
pick'direnv'Ensure +x permission on the binary
as'program'The plugin is a program, there's no main file to the source.
In this method, the registered code is generated once on every installation or update, then sourced without running `direnv` itself. The project is also available as a binary [GitHub releases](https://github.com/direnv/direnv/releases/). This distribution can be installed by: ```zi zi from"gh-r" as"program" mv"direnv* -> direnv" \ atclone'./direnv hook zsh > zhook.zsh' atpull'%atclone' \ pick"direnv" src="zhook.zsh" for \ direnv/direnv ```
SyntaxDescription
from'gh-r'Install from direnv from GitHub Github releases.
mv'direnv* -> direnv'After installation, rename direnv.linux-386 or similar file to direnv.
atclone'…', atpull'…'Same above example.
pick'direnv'Same above example.
as'program'Same above example.
## `extract'…'` A swiss-knife tool for unpacking all kinds of archives – the `extract'…'` ice. It works in two modes – automatic mode and fixed mode. Automatic mode: It is active if the ice is empty (or contains only flags). It works as follows: 1. At first, a recursive search for files of known [file extensions](#supported-file-formats) located not deeper than in a sub-directory is being performed. All such found files are then extracted. - The directory-level limit is to skip extraction of some helper archive files, which are typically located somewhere deeper in the directory tree. 2. **If** no such files will be found, then a recursive search for files of known archive **types** will be performed. This is done by running the `file` Unix command on each file in the plugin or snippet directory and then grepping the output for strings like `Zip`, `bzip2`, etc. All such discovered files are then extracted. - The directory-level requirement is imposed during this stage. The files located deeper than in a sub-directory are omitted. 3. If no archive files will be discovered then no action is being performed and also no warning message is being printed. Fixed mode: It is active when a filename is being passed as the `extract`'s argument, e.g.: `zi extract=archive.zip for z-shell/null`. Multiple files can be specified – separated by spaces. In this mode all and only the specified files are being extracted. Filenames with spaces: The filenames with spaces are supported when correctly passing such filenames to an `extract` with the non-breaking spaces for the original in-filename. The non-breaking space is easy to type by pressing right ALT and the SPACE. Flags: The value of the ice can begin with two special characters: 1. Exclamation mark (`!`), i.e.: `extract='!…'` – it'll cause the files to be moved one directory level up upon unpacking, 2. Two exclamation marks (`!!`), i.e.: `extract='!!…'` – it'll cause the files to be moved two directory-level up upon unpacking, 3. Dash (`-`), i.e.: `extract'-…'` – it'll prevent removal of the archive after unpacking. - This flag allows comparing timestamps with the server in case of snippet-downloaded file – it will prevent unnecessary downloads during `zi update`, as the timestamp of the archive file on the disk will be first compared with the HTTP last-modification time header. The flags can be combined in any order: `extract'!-'`. ## `ziextract` Sometimes a more uncommon unpacking operation is needed. In such a case you can directly use the function that implements the ice – it is called `ziextract`. It recognizes the following options: 1. `--auto` – runs the automatic extraction. 2. `--move` – performs the one-directory-level-up move of the files after unpacking. 3. `--move2` – performs the two-directory-level-up move of the files after unpacking. 4. `--norm` - prevents the archive file removal. 5. And also one option specific only to the function: `--nobkp`, which prevents clearing the plugin's directory before the extraction. – All files besides the archive are being moved into the `._backup` directory after extraction is done. - `extract` ice also skips creating the backup **if** more than one archive is found or given as the argument. ### Supported file formats Zip, rar, tar.gz, tar.bz2, tar.xz, tar.7z, tar, tgz, tbz2, gz, bz2, txz, xz, 7z, exe, deb, OS X (dmg). ## `from'…'` To install and load a plugin whose repository is private - e.g: requires providing credentials to log in – use the `from'…'` ice in the following way: ```zi zi ice from"user@github.com" zi load user/fsh-auto-themes ``` Current preset:
Ice nameDomain name / URL
gegitee.com
giteegitee.com
githubgithub.com
ghgithub.com
gitlabgitlab.com
glgitlab.com
notabugnotabug.org
nbnotabug.org
bitbucketbitbucket.org
bbbitbucket.org
github-relgithub.com/$remote_url_path/releases
gh-rgithub.com/$remote_url_path/releases
cygwincygwin
note If the `from'…'` ice isn't one of the above tables, then **it is treated as a domain name** and inserted into the domain position into the `git clone` URL: ```sh git clone https://{from-ice-contents}/user/plugin ``` In order to change the protocol, use the `proto'…'` ice. ## `id-as'…'` Load a plugin or snippet with a nickname with the `id-as'…'` ice-modifier. For example, one could try to load [docker/compose](https://github.com/docker/compose) from GitHub binary releases: ```zi zi ice as"program" from"gh-r" mv"docker-c* -> docker-compose" zi light "docker/compose" ``` This registers the plugin under the ID `docker/compose`. Now suppose the user would want to also load a completion from the project's GitHub repository (not the binary release catalog) which is also available under the GitHub URL **…/docker/compose**. The two IDs, both being "docker/compose", will collide. The solution to this problem – the `id-as'…'` (to be read as *identify-as*) ice to which this document is devoted: by using the `id-as'…'` ice the user can resolve the conflict by loading the completion under a kind of a *nickname*, for example under "*dc-complete*", by issuing the following commands: ```zi zi ice as"completion" id-as"dc-complete" zi load docker/compose ``` The plugin (of the type `completion`) is now seen under ID `dc-complete`: ```zi zi list | grep -i dc-complete dc-complete ``` Issuing `zi report dc-complete` will work as with regular command: ```zi zi report dc-complete Plugin report for dc-complete ------------------------------- Completions: _docker-compose [enabled] ``` The same method applies to nickname snippets. For instance, use it to create handy IDs in place of long URLs: ```zi zi ice as"program" id-as"git-unique" zi snippet https://github.com/Osse/git-scripts/blob/master/git-unique ``` The commands `zi update git-unique`, and `zi delete git-unique` will work as expected and e.g. `zi times` will show the *nickname*\-ID `git-unique` instead of the long URL. - `id-as'auto'`: There's a special value to the `id-as'…'` ice – `auto`. It causes the nickname to be automatically set to the last component of the plugin name or snippet URL. For example: ```zi zi ice as"program" id-as"auto" zi snippet https://github.com/Osse/git-scripts/blob/master/git-unique ``` will work the same as before, e.g: if the ice used was `id-as'git-unique'`. Will work as if id-as'zsh-autopair' was passed: ```zi zi ice wait lucid id-as"auto" zi load hlissner/zsh-autopair ``` - empty `id-as'…'`: An empty `id-as'…'` will work the same as `id-as'auto'` as if id-as'zsh-autopair' was passed, e.g: ```zi zi ice wait lucid id-as zi load hlissner/zsh-autopair ``` ## `wait'…'` note Turbo mode, i.e. the `wait'…'` is ice that implements it - needs Zsh >= 5.3. ```zi zi ice wait'0' # or just: zi ice wait zi light wfxr/forgit ``` - waits for prompt, - instantly ("0" seconds) after prompt loads given plugin. ```zi zi ice wait'[[ -n ${ZLAST_COMMANDS[(r)cras*]} ]]' zi light z-shell/zi-crasis ``` - screencast that presents the feature: ![Crasis example screencast](https://wiki.zshell.dev/img/cast/svg/crasis_01.svg) - `$ZLAST_COMMANDS` is an array built by [F-Sy-H](https://github.com/z-shell/F-Sy-H), it contains commands currently entered at prompt, - `(r)` searches for an element that matches a given pattern (`cras*`) and returns it, - `-n` means: not-empty, so it will be true when users enter "cras", - after 1 second or less, Zi will detect that the `wait'…'` condition is true, and load the plugin, which provides command *crasis*, ```zi zi ice wait'[[ $PWD = */github || $PWD = */github/* ]]' zi load unixorn/git-extra-commands ``` it waits until the user enters a `github` directory. Turbo mode also supports a suffix – the letter a, `b`, or `c`. The meaning is illustrated by the following example: ```zi zi ice wait"0b" as"command" pick"wd.sh" atinit"echo Firing 1" lucid zi light mfaerevaag/wd zi ice wait"0a" as"command" pick"wd.sh" atinit"echo Firing 2" lucid zi light mfaerevaag/wd ``` will output: ```text Firing 2 Firing 1 ``` As can be seen, the second plugin has been loaded first. That's because there are now three sub-slots (the `a`, `b`, and `c`) into which the plugin/snippet loadings can be put. Plugins from the same time slot with suffix `a` will be loaded before plugins with suffix `b`, etc. In other words, instead of `wait'1'`, you can enter `wait'1a'`, `wait'1b'`, and `wait'1c'` – this **imposes the loading order** of the **commands** regardless of actual execution time. ## `src'…'` `pick'…'` `multisrc'…'` Normally `src'…'` can be used to specify the additional file to the source: ```zi zi ice pick'powerless.zsh' src'utilities.zsh' zi light martinrotter/powerless ```
SyntaxDescription
pick'…'Provide the main file to the source - like *.sh, otherwise alphabetically first matched file is sourced.
src'…'Provide a second file to the source - not a pattern - plain file name.
### The `multisrc'…'` ice Loads **multiple** files enumerated with spaces as the separator (e.g. `multisrc'misc.zsh grep.zsh'`) and also using brace-expansion syntax (e.g. `multisrc'{misc,grep}.zsh')`. Example: ```zi zi ice pick'completion.zsh' \ multisrc'git.zsh functions.zsh {history,grep}.zsh' zi snippet OMZ::lib/completion.zsh ``` All possible ways to use the `multisrc'…'` ice-modifier: ```zi zi ice depth'1' multisrc='lib/{functions,misc}.zsh' pick'/dev/null' zi load robbyrussell/oh-my-zsh ``` Can use patterns: ```zi zi ice multisrc'{funct*,misc}.zsh' pick'/dev/null' zi light some/plugin ``` ```zi zi ice multisrc'misc.zsh functions.zsh' pick'/dev/null' zi light some/plugin ``` Will use the array's value at the moment of plugin load: > This can matter when using turbo mode. ```zi array=({functions,misc}.zsh) zi ice multisrc"\$array" pick'/dev/null' zi light some/plugin ``` Compatible with KSH\_ARRAYS option: ```zi array=({functions,misc}.zsh) zi ice multisrc"${array[*]}" pick'/dev/null' zi light some/plugin ``` Hack with Zi: the ice's contents are simply `eval`\-uated like follows: eval "reply=($multisrc)". So it might get handy on an occasion to pass code there, but first, you must close the paren and then don't forget to assign `reply`, and to provide a trailing opening paren. In the code be careful to not redefine any variable used internally by Zi – e.g.: `i` is safe: ```zi array=({functions,misc}.zsh) zi ice multisrc'); local i; for i in $array; do reply+=( ${i/.zsh/.sh} ); done; ((1)' pick'/dev/null' zi light some/plugin ``` Extended with the [for](https://wiki.zshell.dev/docs/guides/syntax/for/index.md) syntax which can in some situations replace a typical `multisrc'…'` loading. The idea of this syntax is to source multiple snippets with a single command. Instead of: ```zi zi ice multisrc'(functions|misc|completion).zsh' zi snippet OMZ::lib/completion.zsh ``` it's possible to write: ```zi zi for \ OMZL::functions.zsh \ OMZL::misc.zsh \ OMZL::completion.zsh ``` which is somewhat easier on the eyes. Important Property The multiple snippets loaded with the `for` syntax are being loaded *separately*, which means that they will not cause a longer keyboard blockage, which could have been noticeable – when loading in turbo mode. The Zi scheduler will distribute the work over time and will allow activation of the keyboard in between the snippets. The `multisrc'…'` way doesn't work this way – sourcing many files may cause a noticeable keyboard freeze (in turbo mode). ## `atclone'…'` `atpull'…'` `atinit'…'` `atload'…'` There are four code-receiving ice-modifiers: `atclone'…'`, `atpull'…'`, `atinit'…'`, `atload'…'`. Their role is to **receive a portion of Zsh code and execute it in specific moments of the plugin life-cycle**.
SyntaxExecution moment
atclone'…'after cloning the associated plugin or snippet to the disk.
atpull'…'after updating the associated plugin or snippet.
atinit'…'before loading of the associated plugin or snippet.
atload'…'after loading of the associated plugin or snippet.
For convenience, you can use each of the ices multiple times in a single `zi ice …` invocation – all commands will run in the given order. The `atpull'…'` ice recognizes a special value: `%atclone`, so the code looks: `atpull'%atclone'`. It causes the contents of the `atclone'…'` ice to be copied into the contents of the `atpull'…'` ice. This is handy when the same tasks have to be performed on clone **and** on the update of plugin or snippet, like e.g.: in the [direnv example](#direnv). ### `atload'!…'` with exclamation mark preceded The [wrap'…'](#wrap) The ice-modifier allows the track and unload of plugins that defer their initialization to a function and run later after sourcing the plugin's script – When the function call, the plugin is then fully initialized. However, if the function is being called from the `atload'…'` ice, then the *exclamation mark*\-preceded method can be used with `atload'…'` contents. The exclamation mark causes the effects of the execution of the code passed to `atload'…'` ice to be recorded. ### Use case for `atload'…'` For example, in the following invocation: ```zi zi ice id-as'test' atload'!PATH+=:~/share' zi load z-shell/null ``` the `$PATH` is being changed within `atload'…'` ice. Zi's tracking registers `$PATH` changes and withdraws them on the plugin unload and shows loading information: zi report test ```zsh Report for test plugin ---------------------- Source (reporting enabled) PATH elements added: /home/sg/share ``` As it can be seen, the `atload'…'` code is being correctly tracked and can be unloaded & viewed. Below is the result of using the `unload'…'` subcommand to unload the `test` plugin: zi unload test ```zsh --- Unloading plugin: test --- Removing PATH element /home/user/share Unregistering plugin test Plugin report saved to $LASTREPORT ``` The same example as in the [wrap'…'](#use-case-for-wrap) article, but using the *exclamation mark*\-preceded `atload'…'` instead of `wrap'…'`: Load when - `MYPROMPT == 4` ```zi zi ice load'![[ $MYPROMPT = 4 ]]' unload'![[ $MYPROMPT != 4 ]]' \ atload'!source ~/.p10k.zsh; _p9k_precmd' zi load romkatv/powerlevel10k ``` ## `wrap'…'` The `wrap' …'` ice-modifier allows extending the tracking (e.g.: the gathering of the report and unloading data) of a plugin beyond the moment of sourcing its main file(s). It works by wrapping the given functions with a tracking-enabling and disabling snippet of code. This is useful especially with prompts, as they very often do their initialization in the first call to their `precmd` [hook](https://zsh.sourceforge.net/Doc/Release/Functions.html#Hook-Functions) function. For example, [romkatv/powerlevel10k](https://github.com/romkatv/powerlevel10k) works this way. The ice takes a list of function names, with the elements separated by `;`: ```zi zi ice wrap"func1;func2;…" ``` ### Use case for `wrap'…'` Therefore, to load and unload for the example powerlevel10k prompt in the fashion of [multiple prompts](https://wiki.zshell.dev/docs/guides/customization/index.md#multiple-prompts) article, the `precmd` function of the plugin – called `_p9k_precmd`, to get the name of the function do `echo $precmd_functions` after loading a theme, should be passed to `wrap'…'` ice. Load when `MYPROMPT == 4` ```zi zi ice load'![[ $MYPROMPT = 4 ]]' unload'![[ $MYPROMPT != 4 ]]' \ atload'source ~/.p10k.zsh; _p9k_precmd' wrap'_p9k_precmd' zi load romkatv/powerlevel10k ``` This way the actions done during the first call to `_p9k_precmd()` will be normally recorded, which can be viewed in the report of the [romkatv/powerlevel10k](https://github.com/romkatv/powerlevel10k) theme: zi report romkatv/powerlevel10k ```zsh Report for romkatv/powerlevel10k plugin --------------------------------------- Source powerlevel10k.zsh-theme (reporting enabled) Autoload is-at-least with options -U -z (…) Note: === Starting to track function: _p9k_precmd === Zle -N p9k-orig-zle-line-finish _zsh_highlight_widget_zle-line-finish Note: a new widget created via zle -N: p9k-orig-zle-line-finish Zle -N -- zle-line-finish _p9k_wrapper__p9k_zle_line_finish Autoload vcs_info with options -U -z Zstyle :vcs_info:* check-for-changes true (…) Zstyle :vcs_info:* get-revision false Autoload add-zsh-hook with options -U -z Zle -F 22_gitstatus_process_response_POWERLEVEL9K Autoload_gitstatus_cleanup_15877_0_16212/docs/guides/syntax/wrap Zle -N -- zle-line-pre-redraw _p9k_wrapper__p9k_zle_line_pre_redraw Note: a new widget created via zle -N: zle-line-pre-redraw Zle -N -- zle-keymap-select _p9k_wrapper__p9k_zle_keymap_select Note: === Ended tracking function:_p9k_precmd === Functions created: +vi-git-aheadbehind +vi-git-remotebranch (…) ``` Summary of `wrap'…'`: As it can be seen, the creation of four additional Zle-widgets has been recorded - `Zle -N …` lines. They will be properly deleted/restored on the plugin unload with `MYPROMPT=3` as an example and the shell state will be clean, ready to load a new prompt. ## Footnotes 1. Save it to a file. The `atclone'…'` is being run on the **installation** while the `atpull'…'` hook is being run on an **update** of the [**trapd00r/LS\_COLORS**](https://github.com/trapd00r/LS_COLORS) plugin. [↩](#user-content-fnref-1) 2. The `%atclone` is just a special string that denotes the `atclone'…'` hook and is copied onto the `atpull'…'` hook. [↩](#user-content-fnref-2) --- --- title: 🌐 Ecosystem description: Zi's ecosystem — annexes that extend the plugin manager, curated packages, and a gallery of Zsh plugins. canonical_url: https://wiki.zshell.dev/ecosystem markdown_url: https://wiki.zshell.dev/ecosystem/index.md locale: en source_path: ecosystem/index.mdx --- # 🌐 Ecosystem Zi's ecosystem extends the plugin manager with three building blocks: **annexes** that add new capabilities, **packages** that bundle complex setups into a single ice, and a curated **plugin gallery**. ![Zi ecosystem: Annexes, Packages, Plugins](https://wiki.zshell.dev/img/svg/ecosystem-banner.svg) Start with annexes The [bin-gem-node](https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md) annex automatically manages Ruby gems, Node modules, and Python virtualenvs. Install it once via [meta-plugins](https://wiki.zshell.dev/ecosystem/annexes/meta-plugins/index.md) and everything just works. ## What's in the Ecosystem [![](https://wiki.zshell.dev/img/svg/cards/annexes.svg) ### Annexes Hook into Zi's lifecycle to add bin-gem-node, Rust crate management, patch-dl, readurl, and more. Browse annexes →](https://wiki.zshell.dev/ecosystem/category/-annexes)[![](https://wiki.zshell.dev/img/svg/cards/packages.svg) ### Packages Curated ice configurations for popular tools — fzf, neovim, zsh-system-completions, and more, ready to load. Browse packages →](https://wiki.zshell.dev/ecosystem/category/-packages)[![](https://wiki.zshell.dev/img/svg/cards/meta-plugins.svg) ### Plugins Featured Zsh plugins — syntax highlighting, autosuggestions, history search, and console tools. Browse plugins →](https://wiki.zshell.dev/ecosystem/plugins/diff-so-fancy/index.md) --- --- title: 🌀 Bin Gem Node description: Annex - Bin Gem Node documentation. canonical_url: https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node markdown_url: https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md locale: en source_path: ecosystem/annexes/1_bin_gem_node.mdx --- # 🌀 Bin Gem Node An annex provides the following functionality: 1. Run programs and scripts without adding anything to `$PATH`, 2. Install: [Ruby Gems](https://github.com/rubygems/rubygems), [Node](https://github.com/npm/cli), and [Python](https://python.org/) modules, with automatically set: - [$GEM\_HOME](https://guides.rubygems.org/command-reference/#gem-environment) - [$NODE\_PATH](https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders) - [$VIRTUALENV](https://docs.python.org/3/tutorial/venv.html) 3. Run programs, scripts, and functions with automatic `cd` into the plugin or snippet directory, plus also with automatic standard output & standard error redirecting. 4. Source scripts through an automatically created function with the above `$GEM_HOME`, `$NODE_PATH`, `$VIRTUALENV`, and `cd` features available, 5. Create the so-called `shims` known from [rbenv](https://github.com/rbenv/rbenv) – the same feature as the first item of this enumeration – of running a program without adding anything to `$PATH` with all of the above features, however through an automatic **script** created in `$ZPFX/bin`, not a **function** (the first item uses a function-based mechanism), 6. Automatic updates of Ruby gems and Node modules during regular plugin and snippet updates with `zi update …`. The [sbin](#sbin-1) ice-modifier that creates forwarder-scripts instead of forwarder-functions created by the [fbin](#fbin-2) ice-modifier turned out to be the proper, best method for exposing binary programs and scripts. This way there is no need to add anything to `$PATH` – `z-a-bin-gem-node` will automatically create a function that will wrap the binary and provide it on the command line as if it was being placed in the `$PATH`. As previously mentioned, the function can automatically export `$GEM_HOME`, `$NODE_PATH`, `$VIRTUALENV` shell variables and also automatically cd into the plugin or snippet directory right before executing the binary and then cd back to the original directory after the execution is finished. As previously mentioned, instead of the function an automatically created script – the so-called `shim` – can be used for the same purpose and with the same functionality, so that the command is accessible practically fully normally – not only in the live Zsh session, only within which the functions created by [fbin](#fbin-2) exist, but also from any Zsh script. Suppose that we want to install the `junegunn/fzf` plugin from GitHub Releases, which contains only a single file – the `fzf` binary for the selected architecture. It is possible to do it in the standard way – by adding the plugin's directory to the `$PATH`. ```zi zi ice as'program' from'gh-r' zi load junegunn/fzf ``` After this command, the `$PATH` variable will contain e.g.: print $PATH ```zsh /home/sall/.zi/plugins/junegunn---fzf:/bin:/usr/bin:/usr/sbin:/sbin ``` For many such programs loaded as plugins, the PATH can become quite cluttered. I've had 26 entries before switching to `z-a-bin-gem-node`. To solve this, load with the use of [sbin](#sbin-1) ice-modifier provided and handled by `z-a-bin-gem-node`: ```zi zi ice as'program' from'gh-r' sbin'fzf' zi load junegunn/fzf ``` The `$PATH` will remain unchanged and a forwarder-script of `fzf` shim will be created in `$ZPFX/bin` (`${ZI[HOME_DIR]}/polaris/bin` by default), which is being already added to the `$PATH` by Zi when it is being sourced: cat $ZPFX/bin/fzf ```zsh #!/usr/bin/env zsh function fzf { local bindir="/home/sall/.zi/plugins/junegunn---fzf" "$bindir"/"fzf" "$@" } fzf "$@" ``` Running the script will forward the call to the program accessed through an embedded path to it. Thus, no `$PATH` changes are needed.
Ice modifierDescription
sbinCreates shims for binaries and scripts.
fbinCreates functions for binaries and scripts.
gemInstalls and updates gems + creates functions for gems binaries.
nodeInstalls and updates node_modules + creates functions for binaries of the modules.
pipInstalls and updates python packages into a virtualenv + creates functions for binaries of the packages.
fmodCreates wrapping functions for other functions.
fsrcCreates functions that source given scripts.
fercThe same as fsrc, but using an alternate script-loading method.
Function wrappers for binaries, scripts, gems, node\_modules, python packages, etc:
FlagDescription
gSet $GEM_HOME variable to {plugin-dir}.
nSet $NODE_PATH variable to {plugin-dir}/node_modules.
pSet $VIRTUALENV variable to {plugin-dir}/venv.
ccd to the plugin's directory before running the program and then cd back after it has been run.
NAppend &>/dev/null to the call of the binary, i.e. redirect both standard output and standard error to /dev/null.
EAppend 2>/dev/null to the call of the binary, i.e. redirect standard error to /dev/null.
OAppend >/dev/null to the call of the binary, i.e. redirect standard output to /dev/null.
View all currently registered: - ice-modifiers: `zi icemods` - subcommand: `zi subcmds` ## `SBIN'…'` ```zi sbin'[{g|n|c|N|E|O}:]{path-to-binary}[ -> {name-of-the-script}]; …' ``` Creates the so-called `shim` known from `rbenv` – a wrapper script that forwards the call to the actual binary. The script is created always under the same, standard, and single `$PATH` entry: `$ZPFX/bin` (which is `${ZI[HOME_DIR]}/polaris/bin` by default). The flags have the same meaning as with `fbin'…'` ice. ```zi zi ice as'program' from'gh-r' sbin'fzf' zi load junegunn/fzf ``` cat $ZPFX/bin/fzf ```zsh #!/usr/bin/env zsh function fzf { local bindir="/home/sall/.zi/plugins/junegunn---fzf" local -xU PATH="$bindir":"$PATH" "$bindir"/"fzf" "$@" } fzf "$@" ``` note - as'program' (an alias: as'command') - used for the plugin to be added to $PATH when a plugin is not a file for sourcing. The [sbin](#sbin-1) ice-modifier can be empty, it will then try to create the shim for the trailing component of the [id-as](https://wiki.zshell.dev/docs/guides/syntax/standard/index.md#id-as) ice, e.g.: - `id_as'exts/git-my'` → it'll check if a file `git-my` exists and if yes, will create the function `git-my`. - `paulirish/git-open` → it'll check if a file `git-open` exists and if yes, will create the function `git-open`. The same trailing component would be set for the snippet URL, for any alphabetically first and executable file. ## `FBIN'…'` ```zi fbin'[{g|n|c|N|E|O}:]{path-to-binary}[ -> {name-of-the-function}]; …' ``` Creates a wrapper function of the name the same as the last segment of the path or as `{name-of-the-function}`. ```zi zi ice from"gh-r" fbin"g:fzf -> myfzf" nocompile zi load junegunn/fzf ``` which myfzf ```zsh myfzf () { local bindir="/home/sall/.zi/plugins/junegunn---fzf" local -x GEM_HOME="/home/sall/.zi/plugins/junegunn---fzf" local -xU PATH="/home/sall/.zi/plugins/junegunn---fzf"/bin:"$bindir":"$PATH" "$bindir"/"fzf" "$@" } ``` note - `nocompile` ice-modifier is used to skip file compilation when it is not required. ## `GEM'…'` ```zi gem'{gem-name}; …' gem'[{path-to-binary} <-] !{gem-name} [-> {name-of-the-function}]; …' ``` Installs the gem of name `{gem-name}` with `$GEM_HOME` set to the plugin's or snippet's directory. In other words, the gem and its dependencies will be installed locally in that directory. In the second form, it also creates a wrapper function identical to the one created with `fbin'…'` ice. ```zi zi ice gem'!asciidoctor' id-as'asciidoctor' nocompile zi load z-shell/0 ``` which asciidoctor ```zsh asciidoctor () { local bindir="/home/sall/.zi/plugins/asciidoctor/bin" local -x GEM_HOME="/home/sall/.zi/plugins/asciidoctor" local -xU PATH="/home/sall/.zi/plugins/asciidoctor"/bin:"$bindir":"$PATH" "$bindir"/"asciidoctor" "$@" } ``` note - `z-shell/0` - an empty repository to aid Zi's hooks, in this case, used to store the `asciidoctor` gem. - `id-as'asciidoctor'` - used to assign a name instead of the `z-shell/0`. - `nocompile` - used to skip file compilation when it is not required. ## `NODE'…'` ```zi node'{node-module}; …' node'[{path-to-binary} <-] !{node-module} [-> {name-of-the-function}]; …' ``` Installs the node module of name `{node-module}` inside the plugin's or snippet's directory. In the second form, it also creates a wrapper function identical to the one created with `fbin'…'` ice. ```zi zi ice node'remark <- !remark-cli -> remark; remark-man' id-as'remark' nocompile zi load z-shell/0 ``` which remark ```zsh remark () { local bindir="/home/sall/.zi/plugins/remark/node_modules/.bin" local -x NODE_PATH="/home/sall/.zi/plugins/remark"/node_modules local -xU PATH="/home/sall/.zi/plugins/remark"/node_modules/.bin:"$bindir":"$PATH" "$bindir"/"remark" "$@" } ``` In this case, the name of the binary program provided by the node module is different from its name, hence the second form with the `b <- a -> c` syntax has been used. note - `z-shell/0` - an empty repository to aid Zi's hooks, in this case, used to store the `remark` Node module. - `id-as'remark'` - used to assign a name instead of the `z-shell/0`. - `nocompile` - used to skip file compilation when it is not required. ## `PIP'…'` ```zi pip'{pip-package}; …' pip'[{path-to-binary} <-] !{pip-package} [-> {name-of-the-function}]; …' ``` Installs the node module of name `{pip-package}` inside the plugin's or snippet's directory. In the second form, it also creates a wrapper function identical to the one created with `fbin'…'` ice. ```zi zi ice pip'youtube-dl <- !youtube-dl -> youtube-dl' id-as'youtube-dl' nocompile zi load z-shell/0 ``` which youtube-dl ```zsh youtube-dl () { local bindir="/home/sall/.zi/plugins/youtube-dl/venv/bin" local -x VIRTUALENV="/home/sall/.zi/plugins/youtube-dl"/venv local -xU PATH="/home/sall/.zi/plugins/youtube-dl"/venv/bin:"$bindir":"$PATH" "$bindir"/"youtube-dl" "$@" } ``` note - `z-shell/0` - an empty repository to aid Zi's hooks, in this case, used to store the `youtube-dl` pip package. - `id-as'youtube-dl'` - used to assign a name instead of the `z-shell/0`. - `nocompile` - used to skip file compilation when it is not required. ## `FMOD'…'` ```zi fmod'[{g|n|c|N|E|O}:]{function-name}; …' fmod'[{g|n|c|N|E|O}:]{function-name} -> {wrapping-function-name}; …' ``` It wraps the given function with the ability to set `$GEM_HOME`, etc. – the meaning of the `g`, `n`, and `c` flags is the same as in the `fbin'…'` ice. Example: ```zi myfunc() { pwd; ls -1 }; zi ice fmod'cgn:myfunc' id-as'myfunc' nocompile zi load z-shell/0 ``` which myfunc ```zsh myfunc () { local -x GEM_HOME="/home/sall/.zi/plugins/myfunc" local -x NODE_PATH="/home/sall/.zi/plugins/myfunc"/node_modules local oldpwd="/home/sall" () { setopt local_options no_auto_pushd builtin cd -q "/home/sall/.zi/plugins/myfunc" } "myfunc--za-bgn-orig" "$@" () { builtin setopt local_options no_auto_pushd builtin cd -q "$oldpwd" } } ``` myfun ```zsh /home/sall/.zi/plugins/z-shell---0 docs/ LICENSE README.md ``` note - `z-shell/0` - an empty repository to aid Zi's hooks, in this case, used to store the `myfunc` function files. - `id-as'myfunc'` - used to assign a name instead of the `z-shell/0`. - `nocompile` - used to skip file compilation when it is not required. ## `FSRC'…'` ```zi fsrc'[{g|n|c|N|E|O}:]{path-to-script}[ -> {name-of-the-function}]; …' ``` ## `FERC'…'` ```zi ferc'[{g|n|c|N|E|O}:]{path-to-script}[ -> {name-of-the-function}]; …' ``` Creates a wrapper function that at each invocation sources the given file. The second ice, `FERC'…'` works the same with the single difference that it uses `eval "$(<{path-to-script})"` instead of `source "{path-to-script}"` to load the script. ```zi zi ice fsrc"myscript -> myfunc" ferc"myscript" nocompile zi load z-shell/0 ``` which myfunc ```zsh myfunc () { local bindir="/home/sall/.zi/plugins/z-shell---0" local -xU PATH="$bindir":"$PATH" () { source "$bindir"/"myscript" } "$@" } ``` which myscript ```zsh myscript () { local bindir="/home/sall/.zi/plugins/z-shell---0" local -xU PATH="$bindir":"$PATH" () { eval "$(<"$bindir"/"myscript")" } "$@" } ``` note - `nocompile` ice-modifier is used to skip file compilation when it is not required. The ices can be empty as the trailing component will be assigned with [id-as](https://wiki.zshell.dev/docs/guides/syntax/standard/index.md#id-as) ice-modifier the same way as described in the [sbin](#sbin-1). ## `shim-list` An annex provides a subcommand – `shim-list` for shims currently stored in `$ZPFX/bin` management: Available flags are: ```zi zi shim-list [ -t | -i | -o | -s | -h ] ```
FlagDescription
-t --this-dirInstructs Zi to look for shims in the current directory instead of $ZPFX/bin.
-i --from-icesNormally the code looks for the shim files by examining their contents (more info 1).
-o --one-lineDisplay the list of shim files without line breaks, in a single line, after spaces.
-s --shortDon't show the plugin/snippet that the shim belongs to.
-h --helpShows usage information.
## Cygwin support The [sbin](#sbin-1) ice-modifier has an explicit Cygwin support – it creates additional, **extra shim files** – Windows batch scripts that allow running the shielded applications from e.g.: Windows run dialog – if the `${ZI[HOME_DIR]}/polaris/bin` directory is being added to the Windows `PATH` environment variable, for example (it is a good idea to do so, IMHO). The Windows shims have the same name as the standard ones (which are also being created, normally) plus the `.cmd` extension. You can test the feature by e.g.: installing Firefox from the Zi package via: ```zi zi pack=bgn for firefox ``` ## Install bin-gem-node Source - [ z-shell/z-a-bin-gem-node ](https://github.com/z-shell/z-a-bin-gem-node) - Default - Unscoped Add the following snippet in the `.zshrc` file: ```zi zi light z-shell/z-a-bin-gem-node ``` Add the following snippet in the `.zshrc` file to install using the [unscope](https://wiki.zshell.dev/ecosystem/annexes/unscope/index.md) annex: ```zi zi light z-shell/z-a-unscope bgn ``` This will register the [shim-list](#shim-list) subcommand and following ice-modifiers: ## Footnotes 1. shims created by the `bin-gem-node` annex have a fixed structure, this option instructs Zi to show the list of shims that results from the [sbin](#sbin-1) ice-modifier of the loaded plugins. If a plugin for example has `sbin'git-open'`, means that such shim has already been created. [↩](#user-content-fnref-1) --- --- title: 🌀 Default Ice description: Annex - Default Ice documentation canonical_url: https://wiki.zshell.dev/ecosystem/annexes/default-ice markdown_url: https://wiki.zshell.dev/ecosystem/annexes/default-ice/index.md locale: en source_path: ecosystem/annexes/3_default_ice.mdx --- # 🌀 Default Ice An annex delivers the capability to set **default ices** for the next `zi` command, e.g: set default-ices: ```zi zi default-ice lucid from"gh-r" ``` this will download from GitHub releases (gh-r) and also use the lucid ice by default: ```zi zi wait for \ sbin junegunn/fzf-bin \ sbin"**/pk" peco/peco ``` caution The `wait` ice cannot be made default by using this subcommand. ## `default-ice` An annex provides a subcommand – `default-ice` which has the following synopsis: ```text — default-ice [ -s | -c | -g | -t | -q | -h ] [ -s ] — Show currently set default ices [ -c ] — Reset default ices [ -g ] — Return current ices in REPLAY hash [ -t ] — Show statistics [ -q ] — Hide all messages [ -h ] — This message ``` ## Install default-ice Source - [ z-shell/z-a-default-ice ](https://github.com/z-shell/z-a-default-ice) - Default Add the following snippet in the `.zshrc` file: ```zi zi light z-shell/z-a-default-ice ``` This will register the [default-ice](#default-ice) subcommand. --- --- title: 🌀 Eval description: Annex - Eval documentation. canonical_url: https://wiki.zshell.dev/ecosystem/annexes/eval markdown_url: https://wiki.zshell.dev/ecosystem/annexes/eval/index.md locale: en source_path: ecosystem/annexes/19_eval.mdx --- # 🌀 Eval The output of a slow initialization command is redirected to a file located within the plugin or snippets directory and sourced while loading. The next time the plugin or snippet is loaded, this file will be sourced skipping the need to run the initialization command. The ice-modifier `eval'…'` provided and handled by this annex creates a `cache` in the plugin or snippets directory which stores the output of the command and the cache is regenerated when: 1. The plugin or snippet is updated. 2. The cache file is removed. 3. When running `zi recache`. note The optional preceding `!` flag means to store command output regardless of exit code. Otherwise `eval'…'` will avoid caching the output of code which returns a non-zero exit code. ## Example invocations - No Eval + Zi - Eval + Zi ```zi zi ice as"command" from"gh-r" \ atclone"./zoxide init --cmd x zsh > init.zsh" \ atpull"%atclone" src"init.zsh" nocompile'!' zi light ajeetdsouza/zoxide ``` ```zi zi ice atclone"dircolors -b LS_COLORS > init.zsh" \ atpull"%atclone" pick"init.zsh" nocompile'!' \ atload'zstyle ":completion:*" list-colors ${(s.:.)LS_COLORS}' zi light trapd00r/LS_COLORS ``` ```zi zi ice as"command" from"gh-r" \ eval"./zoxide init --cmd x zsh" zi light ajeetdsouza/zoxide ``` ```zi zi ice eval"dircolors -b LS_COLORS" \ atload'zstyle ":completion:*" list-colors ${(s.:.)LS_COLORS}' zi light trapd00r/LS_COLORS ``` - No Zi + Eval - Zi + Eval ```zsh if [[ "${+commands[kubectl]}" == 1 ]]; then eval $(kubectl completion zsh) fi ``` ```zi zi ice id-as"kubectl_completion" has"kubectl" \ eval"kubectl completion zsh" run-atpull zi light z-shell/null ``` ## Install eval Source - [ z-shell/z-a-eval ](https://github.com/z-shell/z-a-eval) - Default - Enable Completion Add the following snippet in the `.zshrc` file: ```zi zi light z-shell/z-a-eval ``` Add the following snippet in the `.zshrc` file: > Set value `Z_A_USECOMP=1` to enable TAB completion for subcommand `recache`. ```zi zi ice atinit'Z_A_USECOMP=1' zi light z-shell/z-a-eval ``` This will register subcommand `recache` and `eval'…'` ice-modifier. --- --- title: 🌀 Link Bin description: Annex - Link Bin documentation. canonical_url: https://wiki.zshell.dev/ecosystem/annexes/linkbin markdown_url: https://wiki.zshell.dev/ecosystem/annexes/linkbin/index.md locale: en source_path: ecosystem/annexes/8_linkbin.mdx --- # 🌀 Link Bin An annex exposes a binary program without modifying `$PATH` – `z-a-linkbin` and automatically creates a hard or soft link to the binary at `$ZPFX/bin` exposing the program to the command line as if it were being placed in `$PATH`. The command can then be accessed normally – not only in the live Zsh session but also from any Zsh script. The ice-modifier `lbin''` provided by the annex creates `links` for binaries and scripts. It creates the `link` that calls the actual binary. The link is created always under the same, standard and single `$PATH` entry: `$ZPFX/bin` ## Soft link note The optional preceding `!` flag means creating a soft link instead of a hard link. Example: ```zi zi ice from'gh-r' as'program' \ lbin'!fzf' zi load junegunn/fzf ``` Check the output: ```sh ls -l $ZPFX/bin/ | awk '{print $(NF-2),$(NF-1),$NF}' fzf --version ``` ## Hard link note The ice-modifier can contain globs as it will expand these when searching for the binary. Example: ```zi zi ice from'gh-r' as'program' \ lbin'**fzf -> myfzf' zi load junegunn/fzf ``` Check the output: ```sh ls -l $ZPFX/bin/ | awk '{print $(NF-2),$(NF-1),$NF}' myfzf --version ``` ## Auto nickname link If ice-modifier [id-as](https://wiki.zshell.dev/docs/guides/syntax/standard/index.md#id-as) is empty, then will try to create the link with a nickname as follows: 1. Trailing component of the `id-as` ice-modifier, e.g.: `id-as'exts/git-my'` → it will check if a file `git-my` exists and if yes, create the link `git-my`. 2. The plugin name, e.g.: for `paulirish/git-open` it'll check if a file `git-open` exists and if yes, create the link `git-open`. 3. Trailing component of the snippet URL. 4. For any alphabetically first executable file. The above also applies if just `!` were passed. ## Install linkbin Source - [ z-shell/z-a-linkbin ](https://github.com/z-shell/z-a-linkbin) - Default Add the following snippet in the `.zshrc` file: ```zi zi light z-shell/z-a-linkbin ``` This will register the `lbin'…'` ice-modifier. --- --- title: 🌀 Meta Plugins description: Annex meta-plugins documentation canonical_url: https://wiki.zshell.dev/ecosystem/annexes/meta-plugins markdown_url: https://wiki.zshell.dev/ecosystem/annexes/meta-plugins/index.md locale: en source_path: ecosystem/annexes/2_meta_plugins.mdx --- # 🌀 Meta Plugins An annex has the curated, optimal [ice-modifiers](https://wiki.zshell.dev/docs/guides/syntax/ice-modifiers/index.md) lists automatically applied. For more details refer to [z-a-meta-plugins.plugin.zsh](https://github.com/z-shell/z-a-meta-plugins/blob/main/z-a-meta-plugins.plugin.zsh) file. tip - To create your group of plugins as meta-plugins propose them in a new [issue](https://github.com/z-shell/z-a-meta-plugins/issues/new) - Prefix `@` used to avoid syntax conflicts, e.g: `zi light @` - Before installing any plugin visit the original repository where available to verify that system is supported and meets other requirements ## Usage of meta-plugins The following snippets are examples of how to install meta-plugins: ```zi zi light @annexes ``` ```zi zi light-mode for @annexes @zsh-users @console-tools ``` ```zi zi light-mode for z-a-meta-plugins \ @annexes @ext-git @zsh-users ``` ```zi zi light-mode for @annexes \ skip'zsh-completions' @zsh-users \ skip'vivid exa tig' @console-tools ``` ## Available meta-plugins
Meta-plugin nameConsisting plugins
@annexesbin-gem-node, readurl, patch-dl, rust, default-ice, unscope
@annexes+@annexes + submods, test
@console-toolsdircolors-material (package), fd, bat, hexyl, hyperfine, vivid, exa, ripgrep, tig
@developer-toolscolor, revolver, zunit, gitignore.plugin.zsh, tig
@ext-gitgit-open, git-recent, git-my, git-quick-stats, git-now, git-extras, forgit
@fuzzyfzf (package), fzy (package), skim, peco
@fuzzy-srcfzf-go, fzy, skim-cargo, peco-go
@ohmyzsh-libOMZL::git, OMZL::history, OMZL::vcs_info, OMZL::clipboard, OMZL::completion, OMZL::theme-and-appearance, OMZL::prompt_info_functions, OMZL::termsupport, OMZL::key-bindings, OMZL::compfix, OMZL::directories, OMZL::functions
@preztoPZTM::archive, PZTM::directory, PZTM::utility
@py-utilspyenv (package)
@romkatvpowerlevel10k
@rust-utilsrust-toolchain, cargo-extensions
@sharkdpfd, bat, hexyl, hyperfine, vivid
@z-shellF-Sy-H, H-S-MW, zsh-diff-so-fancy
@z-shell+zsh-select, zconvey, zui, zflai
@zsh-userszsh-syntax-highlighting, zsh-autosuggestions, zsh-completions
@zsh-users+fastF-Sy-H, zsh-autosuggestions, zsh-completions, z-shell/zsh-fancy-completions
@zunitcolor, revolver, zunit
Zi compatibility `@prezto` requires a Zi build with Git sparse-checkout support for GitHub directory snippets, introduced by [z-shell/zi#417](https://github.com/z-shell/zi/pull/417). The archive module is installed as a directory snapshot; the directory and utility modules remain single-file snippets. ### Summary of the meta-plugins It consumes time to: - Constantly, over and over collect some new interesting plugins to install/load. - Over and over reconstruct the new findings on the new machines. - Constantly extend and tweak the ice list of each plugin, so that it's hard on the eyes, especially for an outsider.
ProblemSolution
(1) finding new pluginsThe annex contains a curated, broad list of plugins, e.g.: all the console tools like fd, fzf, exa, ripgrep, etc.,
(2) reconstructing the findings in new environmentsIt's easy to say and memorize e.g.: zi for console-tools – one label pulls a group of plugins and also the curated, optimal, default ice lists for each of them,
(3) constant increase of complexity of the commandsThe provided, hopefully, best/optimal ices for each plugin are handled transparently and automatically; care is given to each ice list so that the plugin loads without any glitches (e.g.: without the "No files for compilation found." message and other, even such slight issues).
Other unique benefits of the meta-plugins annex:
BenefitDescription
Plugin dependenciesThe meta-plugins implement a dependency mechanism: selecting a from-source built ogham/exa will automatically pull in also the Rust compiler (available under the meta-plugin name: rust-toolchain).
Flexible disabling of chosen sub-plugins in any meta-pluginA meta-plugin can contain many sub-plugins and it's possible to skip installing some of them by the skip'plugin-1 plugin-2…' ice, e.g.: zi skip'ripgrep fd' for console-tools. This way despite that some of the meta plugins are broad the user still has control over what's and how much is being installed.
Common from-source meta pluginsFor the plugins that provide the binary programs it is often the case that a meta-plugin exists that'll build the program from the source (e.g.: fuzzy meta-plugin and its fuzzy-src counterpart). This might be handy e.g.: if there's no binary for our machine.
## Install meta-plugins Source - [ z-shell/z-a-meta-plugins ](https://github.com/z-shell/z-a-meta-plugins) - Default Add the following snippet in the `.zshrc` file: ```zi zi light z-shell/z-a-meta-plugins ``` This will register the `skip'…'` ice-modifier. --- --- title: 🌀 What can Annexes do? description: Annex Introduction canonical_url: https://wiki.zshell.dev/ecosystem/annexes/overview markdown_url: https://wiki.zshell.dev/ecosystem/annexes/overview/index.md locale: en source_path: ecosystem/annexes/0_overview.mdx --- # 🌀 What can Annexes do? 1. Add a new Zi subcommand (i.e. the [command](https://wiki.zshell.dev/docs/guides/commands/index.md) that’s placed after the function `zi …` when calling Zi). 2. Add new [ice-modifiers](https://wiki.zshell.dev/docs/guides/syntax/ice-modifiers/index.md). 3. Register four types of hooks: 3.1. `atclone` hook – run after cloning any plugin or downloading any snippet. 3.2. `atpull` hook – run after pulling new commits (i.e. updating) for any plugin/snippet. 3.3. `atinit` hook – run before loading any plugin/snippet, after it has been set up (i.e. downloaded). 3.4. `atload` hook – run after loading any plugin/snippet. 4. Register hooks for generating help text, shown by the `zi icemods` subcommand. ## Recommended annexes ### Common 1. [z-a-bin-gem-node](https://github.com/z-shell/z-a-bin-gem-node) 2. [z-a-readurl](https://github.com/z-shell/z-a-readurl) 3. [z-a-patch-dl](https://github.com/z-shell/z-a-patch-dl) 4. [z-a-rust](https://github.com/z-shell/z-a-rust) ### Additional 1. [z-a-submods](https://github.com/z-shell/z-a-submods) 2. [z-a-unscope](https://github.com/z-shell/z-a-unscope) 3. [z-a-test](https://github.com/z-shell/z-a-test) tip Use [meta-plugins](https://wiki.zshell.dev/ecosystem/annexes/meta-plugins/index.md) to install common annexes as a group: ```zi zi light-mode for z-shell/z-a-meta-plugins @annexes ``` To install common and additional annexes: ```zi zi light-mode for z-shell/z-a-meta-plugins @annexes+rec ``` ## How to code them? Below is an example body of an `atclone` hook taken from [submods](https://github.com/z-shell/z-a-submods) annex. It shows how to: 1. Obtain the arguments passed to the hook. 2. Use an [ice-modifier](https://wiki.zshell.dev/docs/guides/syntax/ice-modifiers/index.md). 3. It also shows a useful snippet that will trim the whitespace in array elements (see `# (4) …` in the code). 4. Utilize the last hook argument – the plugin’s/snippet’s containing directory. ```zi emulate -L zsh -o extended_glob -o warn_create_global -o typeset_silent [[ -z "${ZI_ICE[submods]}" ]] && return 0 # (1) – get arguments [[ "$1" = plugin ]] && \ local type="$1" user="$2" plugin="$3" id_as="$4" dir="$5" hook="$6" || \ local type="$1" url="$2" id_as="$3" dir="$4" hook="$6" # type: snippet # (2) – we're interested only in plugins/snippets # which have the submods'' ice in their load command [[ -z ${ZI_ICE[submods]} ]] && return 0 local -a mods parts local mod from # (3) – process the submods'' ice mods=( ${(@s.;.)ZI_ICE[submods]} ) for mod in "${mods[@]}"; do parts=( "${(@s:->:)mod}" ) # (4) Remove only leading and trailing whitespace parts=( "${parts[@]//((#s)[[:space:]]##|[[:space:]]##(#e))/}" ) print "\nCloning submodule: ${parts[1]} to dir: ${parts[2]}" from="https://github.com" parts[1]="${from}/${parts[1]}" # (5) – the use of the input argument: `$dir' command git -C "$dir" clone --progress "${parts[1]}" "${parts[2]}" done ``` The recommended method of creating a hook is to place its body into a file that starts with a right arrow `→` ([more information](https://wiki.zshell.dev/community/zsh_plugin_standard/index.md#the-proposed-function-name-prefixes), and also a `za-` prefix, e.g. `→za-myproject-atclone-hook` and then to mark it for autoloading via `autoload -Uz →za-myproject-atclone-hook`. Then register the hook, presumably in the `myproject.plugin.zsh` file, with the API call: `@zi-register-annex`: ```zi @zi-register-annex myproject hook:atclone \ →za-myproject-atclone-handler \ →za-myproject-atclone-help-handler \ "submods''" # register a new ice-modifier: submods'' ``` The general syntax of the API call is: ```zi @zi-register-annex {project-name} \ {hook: \ {name-of-the-handler-function} \ {name-of-the-HELP-handler-function} \ "{ice-mod1}|{ice-mod2}|…" < hook-type >| subcommand: < new-subcommand-name > } ``` The last argument, i.e. the `|`\-separated ice list, is optional. That’s all! After this loading the plugin `myproject` will set up the new [ice-modifier](https://wiki.zshell.dev/docs/guides/syntax/ice-modifiers/index.md) `submods` that will have syntax `submods'{user}/{plugin} –> {output-dir}; …'` and will clone submodules when installing the original plugin or snippet! Example of the [submods](https://github.com/z-shell/z-a-submods) ice-modifier to load a plugin with additional submodules: ```zi zi ice submods'zsh-users/zsh-autosuggestions -> external' zi load some/plugin ``` Check out the project which fully implements this idea, [z-a-submods](https://github.com/z-shell/z-a-submods). It e.g. also implements the `atpull` hook, i.e. supports the automatic update of the submodules. The `z-a-*` prefix is recommended for projects which indicate annexes. ## Unregistering an annex An annex that follows the [Zsh Plugin Standard](https://wiki.zshell.dev/community/zsh_plugin_standard/index.md#unload-function) unload contract removes its handler functions when it is unloaded. Removing a handler without also removing its registration leaves Zi dispatching to a function that no longer exists, so the registration has to go first: `@zi-unregister-annex`: ```zi myproject_plugin_unload() { @zi-unregister-annex myproject hook:atclone unfunction →za-myproject-atclone-handler \ →za-myproject-atclone-help-handler \ myproject_plugin_unload } ``` The general syntax mirrors the registration call, using the same project name and hook type that were given to `@zi-register-annex`: ```zi @zi-unregister-annex {project-name} { hook: < hook-type >| subcommand: < subcommand-name > } ``` Unregistering something that was never registered is a no-op, not an error, so an unload function does not need to guard the call. warning Removing a handler without unregistering it corrupts later loads. Zi calls the stored handler unconditionally, a missing function returns `127`, and Zi folds that error into its return value and shifts its argument list. Every plugin loaded afterwards in that session is affected, and the first one fails with `Error: No plugin or snippet ID given.` info The ice-modifiers an annex contributed at registration are not withdrawn. Registration appends them to a shared list without recording which annex contributed which entry, so a leftover ice name stays recognised but dispatches to nothing. ## Summary There are 2 or 3 subtypes for each of the hooks: 1. `atinit` or `!atinit` – the `!` version is run before the `atinit` ice-modifier (i.e. before `zi ice atinit'echo this!'; …`), while the normal version runs after it. 2. `atload` or `!atload` – analogous to the `atinit` case: the `!` version runs before the `atload` ice-modifier (while the normal version runs after it). 3. `atclone` or `!atclone` – analogous to the `atinit` and `atload` cases. 4. `atpull`, `!atpull`, or `%atpull` – the first two are being run **only when there are new commits to be downloaded** during the update. The `%` version is being **always** run, regardless of whether the update will pull any actual commits or not, and it is being run **after** the `atpull` ice-modifier. --- --- title: 🌀 Patch DL description: Annex - Patch DL documentation canonical_url: https://wiki.zshell.dev/ecosystem/annexes/patch-dl markdown_url: https://wiki.zshell.dev/ecosystem/annexes/patch-dl/index.md locale: en source_path: ecosystem/annexes/4_patch-dl.mdx --- # 🌀 Patch DL An annex downloads files and applies patches and adds two ice-modifiers: first: ```zi zi ice dl'{URL} [-> {optional-output-file-name}]; …' … ``` second: ```zi zi ice patch'{file-name-with-the-patch-to-apply}; …' … ``` The annex will download the given `{URL}` under the path `{optional-output-file-name}` (if no file name given, then it is taken from last segment of the URL) in case of the `dl'…'` ice-modifier, and apply a patch given by the `{file-name-with-the-patch-to-apply}` in case of the `patch'…'` ice-modifier. You can use this functionality to download and apply patches. For example, to install `fbterm`, two patches are being needed, one to fix the operation, the other one to fix the build: ```zi zi ice as"command" pick"$ZPFX/bin/fbterm" \ dl"https://bugs.archlinux.org/task/46860?getfile=13513 -> ins.patch" \ dl"https://aur.archlinux.org/cgit/aur.git/plain/0001-Fix-build-with-gcc-6.patch?h=fbterm-git" \ patch"ins.patch; 0001-Fix-build-with-gcc-6.patch" \ atclone"./configure --prefix=$ZPFX" \ atpull"%atclone" make"install" reset zi load izmntuk/fbterm ``` This command will result in: ![fbterm](https://wiki.zshell.dev/img/png/content/annex/patch-dl/fbterm-ex.png) ## Install patch-dl Source - [ z-shell/z-a-patch-dl ](https://github.com/z-shell/z-a-patch-dl) - Default Add the following snippet in the `.zshrc` file: ```zi zi light z-shell/z-a-patch-dl ``` This will register the `dl'…'` and `patch'…'` ice-modifiers. --- --- title: 🌀 Read URL description: Annex - Read URL documentation. canonical_url: https://wiki.zshell.dev/ecosystem/annexes/readurl markdown_url: https://wiki.zshell.dev/ecosystem/annexes/readurl/index.md locale: en source_path: ecosystem/annexes/5_readurl.mdx --- # 🌀 Read URL An annex allows automatically downloading the newest version of a file to which the URL is hosted on a webpage. It works as follows: Invoke `snippet` (or simply pass the `https://…` address using the `for` syntax) on the web page that hosts the URL to the file to download, provide `dlink'…'` ice with the expected file-download URL replacing the version with the `%VERSION%` keyword, also provide `as'…'` ice with one of the following values: 1. `readurl`, 2. `readurl|command`, 3. `readurl|completion`, 4. `readurl|null`. note The part after the `|` has the same meaning as in the normal `as'…'` ice. ## Examples ```zi zi id-as=fzf as='readurl|command' for \ dlink='/junegunn/fzf/releases/download/%VERSION%/fzf-%VERSION%-linux_amd64.tar.gz' \ https://github.com/junegunn/fzf/releases/ ``` The snippet is just an example. The same effect is obtained by loading as the `junegunn/fzf` plugin with `from'gh-r'` ice. As it can be seen, the `dlink'…'` can be a relative or an absolute path and also a full URL (i.e.: beginning with the `https://…` prefix). ### Intermediate download page Sometimes, like it is in the case of the [terraform](https://releases.hashicorp.com/terraform) command, the final download link isn't on the download page, but on a page, that's listed on it. In such a case use the `dlink0'…'` ice to provide the pattern for the additional, intermediate download page, e.g.: ```zi zi id-as=terraform as='readurl|command' extract for \ dlink0='/terraform/%VERSION%/' \ dlink='/terraform/%VERSION%/terraform_%VERSION%_linux_386.zip' \ https://releases.hashicorp.com/terraform/ ``` ### Skipping `dlink'…'` ice Sometimes the URL of the download page differs from the URL of the archive in just a few `/`\-sections. In such a case, it is possible to skip the `dlink'…'` ice by appending a `++`\-separated fragment of the archive URL, like so: ```zi zi as'readurl|command' extract for \ http://domain.com/download-page++/archive.zip ``` If the archive URL has some different `/`\-sections, then it's possible to strip the conflicting ones from the download URL by using `+++`, `++++`, etc. – the number of the `/`\-section that'll be stripped equals to the number of the `+` minus 2. So, for example: ```zi zi as'readurl|command' extract for \ http://domain.com/download-page/removed-section+++/archive.zip ``` ### Sorting the matched URLs / package versions Sometimes the download page doesn't list the package versions from newest to oldest, but in some other order. In such case, it's possible to sort the URLs / package versions by prepending the chosen `dlink` ice (`dlink0'…'` or `dlink'…'`) with the exclamation mark (`dlink'!…'`, etc.). See the next section for an example: ### Filtering the matched URLs Sometimes some unwanted URLs match the `dlink'…'`/`dlink0'…'` regex/pattern. In such a case it's possible to filter them out by appending a filtering regex to the `dlink'…'` ice as: `dlink='the-main-regex~%the-unwanted-URLs-regex%'` (or the same for `dlink0'…'`). An example package that can benefit from this is the [Open Shift](https://www.openshift.com/) client, which doesn't sort the URLs from latest to the oldest – hence the exclamation mark (`!`) prepend – and it has special URLs like `stable-4.4` or `candidate-4.5` together with the regular version URLs (like `4.5.0-rc.1`): ```zi zi id-as"ocp" as"readurl|command" for \ dlink0'!%VERSION%~%(stable|latest|fast|candidate).*%' \ dlink"openshift-client-windows-%VERSION%.zip" \ https://mirror.openshift.com/pub/openshift-v4/clients/ocp/ ``` The above snippet of Zsh code / Zi invocation will sort the URLs (`dlink0'!…'`) and then filter out the special ones from the results (via `…~%(stable|latest|fast|candidate).*%`), this way selecting the latest version of the Open Shift client. ### Other examples [Pulumi](https://www.pulumi.com/), a tool to create, deploy and manage modern cloud software. ```zi zi id-as'pulumi' as'readurl|null' for \ dlink='https://get.pulumi.com/releases/sdk/pulumi-%VERSION%-linux-x64.tar.gz' \ sbin'pulumi*' \ https://www.pulumi.com/docs/get-started/install/versions/ ``` ## Install readurl Source - [ z-shell/z-a-readurl ](https://github.com/z-shell/z-a-readurl) - Default Add the following snippet in the `.zshrc` file: ```zi zi light z-shell/z-a-readurl ``` This will register the `dlink'…'` and `dlink0'…'` ice-modifiers and also the special `as'readurl|…'` value of the `as'…'`. --- --- title: 🌀 Rust description: An annex installs rust and cargo packages. canonical_url: https://wiki.zshell.dev/ecosystem/annexes/rust markdown_url: https://wiki.zshell.dev/ecosystem/annexes/rust/index.md locale: en source_path: ecosystem/annexes/9_rust.mdx --- # 🌀 Rust An annex installs rust and cargo packages locally inside the plugin or snippet directories. ## Usage of the annex The Zi annex provides ice-modifiers `rustup` and `cargo'…'`. The first one installs rust inside the plugin's folder using the official `rustup` installer and the second one has the following syntax: ```zi cargo'[{name-of-the-binary-or-path} <-] [[!][c|n|e|o]:]{crate-name} [-> {shim-script-name}]'` ```
FlagDescription
Nredirect both standard output and error to /dev/null
Eredirect standard error to /dev/null
Oredirect standard output to /dev/null
cchange the current directory to the plugin's or snippet's directory before executing the command
As the examples showed, the name of the binary to run and the shim name are by default equal to the name of the crate. Specifying `{binary-name} <- …` and/or `… -> {shim-name}` allows to override them. The crate can create so-called *shims* – scripts that are exposed to the standard `$PATH`. The shim script is a wrapper around the binary that is installed by the crate. The shim script is created in the plugin's or snippet's directory and is named after the crate. The shim script is a shell script that sets up the environment variables and then runs the binary. Example of the *shim* script: ```zsh #!/usr/bin/env zsh function lsd { local bindir="/root/.zi/plugins/z-shell---null/bin" local -x PATH="/root/.zi/plugins/z-shell---null"/bin:"$PATH" # -x means export local -x RUSTUP_HOME="/root/.zi/plugins/z-shell---null"/rustup CARGO_HOME="/root/.zi/plugins/z-shell---null" "$bindir"/"lsd" "$@" } lsd "$@" ``` As it can be seen shim ultimately provides the binary to the command line.
Use case examples

Set up rust and the lsd crate with a shim lsd exposing the binary:

zi ice rustup cargo'!lsd'
zi load z-shell/0

Set up rust and the exa crate with a shim ls exposing the exa binary:

zi ice rustup cargo'!exa -> ls'
zi load z-shell/0

Set up rust and the exa and lsd crates:

zi ice rustup cargo'exa;lsd'
zi load z-shell/0

Set up rust, then the exa and lsd crates, with their binaries exposed by altering $PATH:

zi ice rustup cargo'exa;lsd' as"command" pick"bin/(exa|lsd)"
zi load z-shell/0

Set up rust and then the exa crate with shim standard error redirected to /dev/null:

zi ice rustup cargo'!E:exa'
zi load z-shell/0

Just install rust and make it available globally in the system:

zi ice id-as"rust" wait"0" lucid rustup as"command" pick"bin/rustc" atload="export \
  CARGO_HOME=\$PWD RUSTUP_HOME=\$PWD/rustup"
zi load z-shell/0

A little more complex rustup configuration that uses bin-gem-node annex and installs the cargo completion provided with rustup, using the for syntax:

zi id-as=rust wait=1 as=null sbin="bin/*" lucid rustup \
  atload="[[ ! -f ${ZI[COMPLETIONS_DIR]}/_cargo ]] && zi creinstall rust; \
  export CARGO_HOME=\$PWD RUSTUP_HOME=\$PWD/rustup" for \
z-shell/0
## Install rust Source - [ z-shell/z-a-rust ](https://github.com/z-shell/z-a-rust) - Default Add the following snippet in the `.zshrc` file: ```zi zi light z-shell/z-a-rust ``` This will register the `rustup` and `cargo'…'` ice-modifiers. --- --- title: 🌀 Submods description: Annex - Submods documentation. canonical_url: https://wiki.zshell.dev/ecosystem/annexes/submods markdown_url: https://wiki.zshell.dev/ecosystem/annexes/submods/index.md locale: en source_path: ecosystem/annexes/6_submods.mdx --- # 🌀 Submods An annex delivers the capability to clone additional submodules while installing a plugin or snippet. The submodules are then automatically updated on the `zi update …` command. Synopsis: ```zi submods'{user}/{plugin} -> {output directory}; …` ``` An example command utilizing the annex and its ice-modifier to load a plugin with additional submodules: ~/.zshrc ```zi zi ice submods'zsh-users/zsh-autosuggestions -> external' zi load some/plugin ``` ## Install submods Source - [ z-shell/z-a-submods ](https://github.com/z-shell/z-a-submods) - Default Add the following snippet in the `.zshrc` file: ```zi zi light z-shell/z-a-submods ``` This will register the `submods'…'` ice-modifier. --- --- title: 🌀 Unscope description: Annex - Unscope IDs documentation canonical_url: https://wiki.zshell.dev/ecosystem/annexes/unscope markdown_url: https://wiki.zshell.dev/ecosystem/annexes/unscope/index.md locale: en source_path: ecosystem/annexes/7_unscope.mdx --- # 🌀 Unscope An annex allows the installation of plugins without specifying the GitHub user name, as follows: 1. On the installation of a plugin without any slashes (/) in its name the annex will query the GitHub API searching for `*/{the-name}`, sorting on stars. 2. It first requires at least 10 forks on the candidates, then 2, then 0. 3. After finding the best result it sets it as the **full** remote-id of the plugin, storing the ID on disk for later automatic use. 4. For security, for such GH-API request to be made a newly added (by this annex) ice: `ghapi` is required to be given. 5. Otherwise only the static database of mappings of short-plugin nicknames to the full scoped IDs will be searched. It contains many mappings, like, e.g.: `vi-reg → zsh-vi-more/evil-registers`, and some of the popular plugins, like, e.g.: `zsh-syntax-highlighting → zsh-users/zsh-syntax-highlighting` and more. ## Static mappings info Fill [request](https://github.com/z-shell/z-a-unscope/issues/new/choose) to add new repositories with scoped IDs. Besides the GitHub-API querying, there's also a fixed, curated list of mappings of short names to the full GitHub IDs:
Short (Nick-) NameGitHub ID / scoped ID
nullz-shell/null
z-a-readurlz-shell/z-a-readurl
readurlz-shell/z-a-readurl
rdurlz-shell/z-a-readurl
z-a-patch-dlz-shell/z-a-patch-dl
patch-dlz-shell/z-a-patch-dl
z-a-submodsz-shell/z-a-submods
submodsz-shell/z-a-submods
z-a-rustz-shell/z-a-rust
rustz-shell/z-a-rust
z-a-bin-gem-nodez-shell/z-a-bin-gem-node
bin-gem-nodez-shell/z-a-bin-gem-node
bgnz-shell/z-a-bin-gem-node
metaz-shell/z-a-meta-plugins
metaplgz-shell/z-a-meta-plugins
meta-pluginsz-shell/z-a-meta-plugins
archivePZTM::archive
archPZTM::archive
directoryPZTM::directory
dirPZTM::directory
environmentPZTM::environment
envPZTM::environment
utilityPZTM::utility
utilPZTM::utility
fast-syntax-highlightingz-shell/fast-syntax-highlighting
f-sy-hz-shell/fast-syntax-highlighting
fshz-shell/fast-syntax-highlighting
history-search-multi-wordz-shell/history-search-multi-word
hsmwz-shell/history-search-multi-word
zuiz-shell/zui
ZUIz-shell/zui
zconveyz-shell/zconvey
zconvz-shell/zconvey
zbrowsez-shell/zbrowse
zzcompletez-shell/zzcomplete
zzcompz-shell/zzcomplete
zzcomz-shell/zzcomplete
zsh-autosuggestionszsh-users/zsh-autosuggestions
autosuggestionszsh-users/zsh-autosuggestions
autosugzsh-users/zsh-autosuggestions
asugzsh-users/zsh-autosuggestions
z-asugzsh-users/zsh-autosuggestions
zsh-syntax-highlightingzsh-users/zsh-syntax-highlighting
z-sy-hzsh-users/zsh-syntax-highlighting
zsh-autocompletemarlonrichert/zsh-autocomplete
autocompletemarlonrichert/zsh-autocomplete
autocompmarlonrichert/zsh-autocomplete
aucommarlonrichert/zsh-autocomplete
acommarlonrichert/zsh-autocomplete
z-aucommarlonrichert/zsh-autocomplete
z-acommarlonrichert/zsh-autocomplete
zsh-autopairhlissner/zsh-autopair
autopairhlissner/zsh-autopair
aupairhlissner/zsh-autopair
aupahlissner/zsh-autopair
z-aupahlissner/zsh-autopair
evil-registerszsh-vi-more/evil-registers
evil-regzsh-vi-more/evil-registers
vi-regzsh-vi-more/evil-registers
viregzsh-vi-more/evil-registers
vi-motionszsh-vi-more/vi-motions
evil-motzsh-vi-more/vi-motions
vi-motzsh-vi-more/vi-motions
vimotzsh-vi-more/vi-motions
vi-incrementzsh-vi-more/vi-increment
evil-inczsh-vi-more/vi-increment
vi-inczsh-vi-more/vi-increment
viinczsh-vi-more/vi-increment
vi-quotezsh-vi-more/vi-quote
evil-qtezsh-vi-more/vi-quote
vi-qtezsh-vi-more/vi-quote
viqtezsh-vi-more/vi-quote
directory-markszsh-vi-more/directory-marks
evil-dir-markszsh-vi-more/directory-marks
vi-dir-markszsh-vi-more/directory-marks
vi-dirmazsh-vi-more/directory-marks
vidirmazsh-vi-more/directory-marks
fdsharkdp/fd
shark-fdsharkdp/fd
batsharkdp/bat
shark-batsharkdp/bat
exaogham/exa
zsh-completionszsh-users/zsh-completions
completionszsh-users/zsh-completions
compszsh-users/zsh-completions
## Install unscope Source - [ z-shell/z-a-unscope ](https://github.com/z-shell/z-a-unscope) - Default Add the following snippet in the `.zshrc` file: ```zi zi light z-shell/z-a-unscope ``` This will allow scoped IDs to be searched and resolved. --- --- title: 📦 Synopsis description: Introduction to packages. canonical_url: https://wiki.zshell.dev/ecosystem/packages/synopsis markdown_url: https://wiki.zshell.dev/ecosystem/packages/synopsis/index.md locale: en source_path: ecosystem/packages/01_synopsis.mdx --- # 📦 Synopsis The motivation for adding packages functionality: 1. Zi is a flexible and feature-rich plugin manager, however, users often feel overwhelmed by its configuration. 2. It has multiple package-manager-like features, such as: - get the plugin's Git repository OR release-package URL, - get the list of the recommended [ice-modifiers](https://wiki.zshell.dev/docs/guides/syntax/ice-modifiers/index.md) for the plugin, - there can be multiple lists of [ice-modifiers](https://wiki.zshell.dev/docs/guides/syntax/ice-modifiers/index.md), - the ice-modifiers list is stored in profiles; there's at least one profile, default, - the [ice-modifiers](https://wiki.zshell.dev/docs/guides/syntax/ice-modifiers/index.md) can be selectively overridden. - automatically provide so-called shims (i.e.: forwarder scripts) for the binaries, - extend `$PATH` to expose the binaries, - it can run `Makefile` and more. 3. In general, Zi has many hooks which allow surprising things, however, their content often evolves to a gradually better one and it's hard to keep track of all the current versions. info The [bin-gem-node](https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md) annex is recommended, otherwise, some packages will fail to install due to missing functionality. ## The [any-gem](https://github.com/z-shell/any-gem) and [any-node](https://github.com/z-shell/any-node) packages They allow the installation of any Gem(s) or Node module(s) locally in a newly created plugin directory. For example: ```zi zi pack param='GEM -> rails' for any-gem zi pack param='MOD -> doctoc' for any-node ``` If the installation is used in the `.zshrc` file then use `id-as'…'`, then Zi knows that the package is already installed. note The Unicode arrow is allowed in Zi syntax as in the example below. ```zi zi id-as=jekyll pack param='GEM → jekyll' for any-gem ``` The binaries will be exposed without altering the PATH via shims. Shims are correctly removed when deleting a plugin with `zi delete …` The so-called packages are GitHub repositories holding a `package.json` file with the meta-data in them. This way you don't have to (but still can) specify ice-modifiers, which might be handy when the [ice-modifiers](https://wiki.zshell.dev/docs/guides/syntax/ice-modifiers/index.md) list is long and complex. ## Introductory example This way, instead of the following command used to install `fzf`: ```zi zi lucid as=program pick="$ZPFX/bin/(fzf|fzf-tmux)" \ atclone="cp shell/completion.zsh _fzf_completion; \ cp bin/(fzf|fzf-tmux) $ZPFX/bin" \ make="PREFIX=$ZPFX install" for \ junegunn/fzf ``` you only need: ```zi zi pack for fzf ``` to get the complete setup of the fuzzy finder, including: - the completion - the additional executable script `fzf-tmux` The installation is like with package-manager because you don't need to invoke Zi anymore once installed to use `fzf` (that's because `fzf` is just a binary program and not e.g.: a shell function). You can also update the package with `zi update fzf` – it'll cause the project to refresh and rebuild, like with a "normal" package manager such as `apt-get`. However, it'll be more like to `emerge` from Gentoo, because the installation will be from the source… unless… the user will pick up a binary installation by profile argument specified in the `pack'…'` ice. ## Pros of using the Zi package for regular software installations Using Zi to install software where one could use a regular package manager has several advantages: 1. **Pro:** The Zi packages typically use the URLs to the official and *latest* distributions of the software (e.g.: the [ecs-cli](https://github.com/z-shell/ecs-cli) package, which uses the URL: `https://amazon-ecs-cli.s3.amazonaws.com/ecs-cli-linux-amd64-latest` when installing on Linux). 2. **Pro:** You can influence the installation easily by specifying Zi ice-modifiers, e.g.: ```zi zi pack=bgn atclone="cp fzy.1 $ZI[MAN_DIR]/man1" for fzy ``` to install also the man page for the `fzy` fuzzy finder (this omission in the package will be fixed soon). 3. **Pro:** The installation is much more flexible than a normal package manager. Example available degrees of freedom: - to install from Git or release-tarball, or a binary-release file, - to install via shims or via extending `$PATH`, or by copying to `$ZPFX/bin`, - to download files and apply patches to the source by using the `patch-dl` annex features. 4. **Pro:** The installations are located in the user home directory, which doesn't require root access. Also, for Gems and Node modules, they are installed in their plugin directory, which can have advantages (e.g.: isolation allowing e.g: easy removal by `rm -rf …`). 5. **Con:** You're somewhat "on your own", with no support from any package maintainer. Thus, summing up 1. with 4., it might be nice/convenient too, for example, have the latest ECS CLI binary installed in the home directory, without using root access and always the latest, and – summing up with 2. and 3. – to, for example, have always the latest `README` downloaded by additional ice: `dl'https://raw.githubusercontent.com/aws/amazon-ecs-cli/master/README.md'` (and then to have the `README` converted into a man page by the `remark` Markdown processor or other via an `atclone''` ice, as the tool doesn't have any official man page). ## Adding your package 1. Contact the author to have the repository at the [Z-Shell](https://github.com/z-shell) organization or set the [ZI\[PKG\_OWNER\]](https://wiki.zshell.dev/docs/guides/customization/index.md#modify-settings). 2. Populate the `package.json` – I suggest grabbing the one for `fzf` or `doctoc` and doing a few substitutions like [doctoc](https://github.com/z-shell/doctoc) → `your-project` and then simply filling the `default` profile in the `zi-ices` object – it is same as passing ice-modifiers to `zi ice …` but in JSON. 3. The project name in the `package.json` should start with `zsh-`. The prefix will be skipped when specifying it with Zi. 4. Commit and push. --- --- title: 📦 Usage description: Zi packages usage information. canonical_url: https://wiki.zshell.dev/ecosystem/packages/usage markdown_url: https://wiki.zshell.dev/ecosystem/packages/usage/index.md locale: en source_path: ecosystem/packages/02_usage.mdx --- # 📦 Usage ## Package repositories For all the available packages use [GitHub search](https://github.com/search?q=topic%3Azpackage+org%3Az-shell&type=Repositories).
Package NameDescription
any-nodeThe any Node module(s) locally in a newly created plugin directory.
any-gemThe any Gem(s) locally in a newly created plugin directory.
aprThe Apache Portable Runtime (APR) library.
fzfThe fzf command-line fuzzy finder.
fzyThe fzy command-line fuzzy finder.
pyenvThe pyenv Python virtual environment manager.
remarkThe remark Markdown processor.
doctocThe doctoc Markdown processor.
ls_colorsThe LS_COLORS and setup a zsh-completion system color scheme.
dircolors-materialThe dircolors-material and set up a zsh-completion system color scheme.
asciidoctorThe asciidoctor Markdown processor.
system-completionsMoves the stock Zsh completions under the control of Zi.
brew-completionsThe Homebrew Shell Completion under the control of Zsh & Zi.
ecs-cliThe AWS ECS CLI.
subversionThe Subversion client.
github-issuesThe GitHub Issues client.
github-issues-srvThe GitHub Issues server.
firefox-devThe Firefox Developer Edition.
zshThe Zsh mirror of zsh-users.
nbBookmarking, and archiving with linking, tagging, search, Git syncing, Pandoc conversion, and more.
zsh-binPackage of statically-linked, hermetic, relocatable - romkatv/zsh-bin.
## Package profiles ### Apache Portable Runtime (APR) library
Package source:Source TarballBinaryGitNodeGem
Status:

(default)

Download, build and install the latest Apache Portable Runtime. ```zi zi pack for apr ``` ### `asciidoctor` Markdown processor
Package source:Source TarballBinaryGitNodeGem
Status:

(default)

Download the Gem of asciidoctor locally with the [bin-gem-node](https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md) annex. > Using the `@` prefix because of collision with the as'' ice. ```zi zi pack for @asciidoctor ``` ### AWS ECS CLI
Package source:Source TarballBinaryGitNodeGem
Status:

(default)

- Default - Annex Download the binary of the Amazon-ECS-CLI command. ```zi zi pack for ecs-cli ``` Download the ECS-CLI binary with the use of the bin-gem-node annex. ```zi zi pack"bgn" for ecs-cli ``` ### `dircolors-material` color scheme
Package sourceTarballBinaryGitNodeGem
Status:

(default)

- Default - No completion - No color swap - Minimal Download the default profile. ```zi zi pack for dircolors-material ``` Download the "no-zsh-completion" profile. ```zi zi pack"no-zsh-completion" for dircolors-material ``` Download the "no-color-swaps" profile. ```zi zi pack"no-color-swaps" for dircolors-material ``` Download the minimal profile without altering the original theme. ```zi zi pack"minimal" for dircolors-material ``` ### `doctoc` Markdown processor
Package source:Source TarballBinaryGitNodeGem
Status:

(default)

A download default profile with the Node package of doctoc. ```zi zi pack for doctoc ``` ### Firefox Developer Edition
Package source:Source TarballBinaryGitNodeGem
Status:

(default)

- Default - Annex Download the firefox-dev latest binary. ```zi zi pack for firefox-dev ``` Download the firefox-dev latest binary with use of the [bin-gem-node](https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md) annex. ```zi zi pack"bgn" for firefox-dev ``` ### `fzf` command-line fuzzy finder
Package source:Source TarballBinaryGitNodeGem
Status:

(default)

- Default - Key bindings - Annex - Annex + key bindings - Annex + git - Binary - Annex + binary Download the package with the default profile. ```zi zi pack for fzf ``` Download the package with the default profile + key bindings. ```zi zi pack"default+keys" for fzf ``` Download the package with the [bin-gem-node](https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md) annex. ```zi zi pack"bgn" for fzf ``` Download the package with the [bin-gem-node](https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md) annex and with the key bindings. > The "+keys" variants are available for each profile. ```zi zi pack"bgn+keys" for fzf ``` Download with the [bin-gem-node](https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md) annex from GitHub repository. ```zi zi pack"bgn" git for fzf ``` Download the binary from the GitHub releases. ```zi zi pack"binary" for fzf ``` Download the binary from the GitHub releases and install using [bin-gem-node](https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md) + shims. ```zi zi pack"bgn-binary" for fzf ``` ### `fzy` command-line fuzzy finder
Package source:TarballBinaryGitNodeGem
Status:

(default)

- Default - Annex - Annex + git - Default Override Download the package with the default profile. ```zi zi pack for fzy ``` Download the package with the [bin-gem-node](https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md) annex. ```zi zi pack"bgn" for fzy ``` Download with the [bin-gem-node](https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md) annex from GitHub repository. ```zi zi pack"bgn" git for fzy ``` Download normal ice list and override atclone'' ice to skip the contrib scripts ```zi zi pack"bgn" atclone'' for fzy ``` ### `LS_COLORS` color scheme
Package source:TarballGitNodeGem
Status:🚫

(default)

- Default - No completion - No color swap Download the default profile. ```zi zi pack for ls_colors ``` Download the "no-zsh-completion" profile. ```zi zi pack"no-zsh-completion" for ls_colors ``` Download the "no-dir-color-swap" profile. ```zi zi pack"no-dir-color-swap" for ls_colors ``` ### Feature-rich note‑taking (`nb`)
Package source:Source TarballGitNodeGem
Status:

(default)

🚫
Default profile are using [bin-gem-node](https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md) to set shims. ```zi zi pack for nb ``` ### Python virtual environment manager - `pyenv`
Package source:Source TarballBinaryGitNodeGem
Status:

(default)

- Default - Annex - Annex + git Download the tarball with the default ice list. ```zi zi pack for pyenv ``` Download the binary from the GitHub releases with the [bin-gem-node](https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md) annex. ```zi zi pack"bgn" for pyenv ``` Download with the [bin-gem-node](https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md) annex from GitHub repository. ```zi zi pack"bgn" git for pyenv ``` ### `remark` Markdown processor
Package source:TarballGitNodeGem
Status:🚫🚫

(default)

- Default - Man only - HTML only Download the Node package of remark-CLI, remark-man and remark-HTML ```zi zi pack for remark ``` Download the Node package of remark-CLI and remark-man ```zi zi pack"man-only" for remark ``` Download the Node package of remark-CLI and remark-HTML ```zi zi pack"html-only" for remark ``` ### Subversion
Package source:Source TarballBinaryGitNodeGem
Status:

(default)

Download, build and install the latest Subversion. > Dependency of Subversion: [APR](https://github.com/z-shell/apr) ```zi zi pack for subversion ``` ### Zsh mirror of zsh-users
Package source:Source TarballBinaryGitNodeGem
Status:

(default)

- Default - Specific version Install the newest Zsh. ```zi zi pack for zsh ``` Install preferred Zsh version. ```zi zi pack"5.9" for zsh zi pack"5.8.1" for zsh zi pack"5.8" for zsh zi pack"5.7.1" for zsh zi pack"5.6.2" for zsh zi pack"5.5.1" for zsh zi pack"5.4.2" for zsh zi pack"5.3.1" for zsh ``` ### Statically-linked, hermetic, relocatable Zsh
Package source:Source TarballBinaryGitNodeGem
Status:

(default)

- Default - Annex - Rootless Requires **root** access to install Zsh at `/usr/local` and will attempt to register it as a login shell. ```zi zi pack for zsh-bin ``` Does not require **root** access, when install using [bin-gem-node](https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md) to set shims. ```zi zi pack"bgn" for zsh-bin ``` Does not require **root** access, will install to `~/.local`. ```zi zi pack"rootless" for zsh-bin ``` --- --- title: ⚙️ Diff So Fancy description: Git diffs human-readable instead of machine-readable. canonical_url: https://wiki.zshell.dev/ecosystem/plugins/diff-so-fancy markdown_url: https://wiki.zshell.dev/ecosystem/plugins/diff-so-fancy/index.md locale: en source_path: ecosystem/plugins/diff-so-fancy.mdx --- # ⚙️ Diff So Fancy ## [z-shell/zsh-diff-so-fancy](https://github.com/z-shell/zsh-diff-so-fancy) The wrapper around [so-fancy/diff-so-fancy](https://github.com/so-fancy/diff-so-fancy) to use with Zsh helps to improve code quality and spot defects faster. The plugin adds the subcommand `dsf` to `git`, and provides the command `fancy-diff` to pipe the `diff` output to diff-so-fancy: - `git dsf` to see the diff output. - `fancy-diff ` to pipe the `diff` output. - `diff-so-fancy -h` to see the help. ![Zsh Diff So Fancy](https://wiki.zshell.dev/img/cast/gif/dsf/zsh-diff-so-fancy.gif) ### Install Diff-So-Fancy - Zi - Zgen - Oh-My-Zsh Add the following to your `.zshrc` file: Using [bin-gem-node](https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md) annex (recommended): ```zi zi ice as'null' sbin'bin/*' zi light z-shell/zsh-diff-so-fancy ``` Standard installation: ```zi zi ice as'program' pick'bin/*' zi light z-shell/zsh-diff-so-fancy ``` This will add `diff-so-fancy`, `fancy-diff`, and `git-dsf` to `$PATH` and automatically equip `git` with the subcommand `dsf`. Add the following to your `.zshrc` file in the same place you're doing your other `zgen load` calls. ```zsh zgen load z-shell/zsh-diff-so-fancy ``` Clone the Repository: ```sh git clone https://github.com/z-shell/zsh-diff-so-fancy.git \ ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/zsh-diff-so-fancy ``` And add `zsh-diff-so-fancy` to your plugin list: .zshrc ```zsh plugins=(... zsh-diff-so-fancy) ```
Configuration
diff-so-fancy --colors

Configure git-diff to use diff-so-fancy and suggested colors:

diff-so-fancy --set-defaults

Use diff-so-fancy in patch mode (interoperable with git add --patch):

diff-so-fancy --patch

Configure git to use diff-so-fancy for all diff operations:

git config --global core.pager "diff-so-fancy | less --tabs=4 -RFXS"

Configure git to use diff-so-fancy for git add --patch:

git config --global interactive.diffFilter "diff-so-fancy --patch"
Options

markEmptyLines

Should the first block of an empty line be colored? (Default: true)

git config --bool --global diff-so-fancy.markEmptyLines false

changeHunkIndicators

Simplify git header chunks to a more human-readable format. (Default: true)

git config --bool --global diff-so-fancy.changeHunkIndicators false

stripLeadingSymbols

Should the pesky + or - at the line start be removed? (Default: true)

git config --bool --global diff-so-fancy.stripLeadingSymbols false

useUnicodeRuler

By default, the separator for the file header uses Unicode line-drawing characters. If this is causing output errors on your terminal, set this to false to use ASCII characters instead. (Default: true)

git config --bool --global diff-so-fancy.useUnicodeRuler false

rulerWidth

By default, the separator for the file header spans the full width of the terminal. Use this setting to set the width of the file header manually.

git config --global diff-so-fancy.rulerWidth 47    # git log's commit header width
--- --- title: ⚙️ Feature-rich syntax highlighting description: Feature-rich Syntax Highlighting for Zsh canonical_url: https://wiki.zshell.dev/ecosystem/plugins/f-sy-h markdown_url: https://wiki.zshell.dev/ecosystem/plugins/f-sy-h/index.md locale: en source_path: ecosystem/plugins/f-sy-h.mdx --- # ⚙️ Feature-rich syntax highlighting ## [z-shell/f-sy-h](https://github.com/z-shell/F-Sy-H) ## Install F-Sy-H - Zi - Zgen - Oh-My-Zsh - Standalone Add the following to your `.zshrc` file. ```zi zi light z-shell/F-Sy-H ``` Load the plugin in [turbo mode](https://wiki.zshell.dev/docs/getting_started/overview/index.md#turbo-mode-zsh--53): ~/.zshrc ```zi zi wait lucid for \ atinit"ZI[COMPINIT_OPTS]=-C; zicompinit; zicdreplay" \ z-shell/F-Sy-H \ blockf \ zsh-users/zsh-completions \ atload"!_zsh_autosuggest_start" \ zsh-users/zsh-autosuggestions ``` Add the following to your `.zshrc` file in the same place you're doing your other `zgen load` calls in. ```zsh zgen load z-shell/F-Sy-H ``` Clone the Repository: ```sh git clone https://github.com/z-shell/F-Sy-H.git \ ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/F-Sy-H ``` And add `F-Sy-H` to your plugin list. Clone the Repository. ```sh git clone https://github.com/z-shell/F-Sy-H.git \ ~/some/path/to/fsh ``` And add the following to your `.zshrc` file. ```zsh source ~/some/path/to/fsh/F-Sy-H.plugin.zsh ``` ## Compatibility and loading F-Sy-H uses the portable project identifier `fsh` and supports Zsh 5.8 or newer. Its authoritative entrypoint is `F-Sy-H.plugin.zsh`. Direct sourcing and plugin-manager loading expose the public `fsh_theme` and `fsh_plugin_unload` functions. The `functions/`, `completions/`, and private `chroma/` directories are added to `fpath` when absent. Interactive loading wraps ZLE widgets, installs a `preexec` hook, and loads the required Zsh modules. Non-interactive loading defines the shell API without changing widgets or installing the hook. The plugin has no public aliases or public parameters. Loading performs no network request and does not create the theme work directory. The explicit `fsh_theme` command creates storage only when it persists theme state. Saved `current_theme.ini`, `theme_overlay.ini`, and `secondary_theme.local.ini` files are parsed as data; legacy executable `*.zsh` theme caches are ignored. `fsh_plugin_unload` removes plugin-owned hooks, widgets, functions, parameters, modules, and `fpath` entries. State changed by another component after loading is preserved. ## Performance Performance differences can be observed in this Asciinema recording, where a `10 kB` function is being edited. ## Syntax highlighting features ### Themes List the shipped themes and their declared backgrounds: ```zsh fsh_theme --list ``` Apply a theme and place the sample snippet above on the command line: ```zsh fsh_theme --test clean ``` Apply a theme without the sample: ```zsh fsh_theme clean ``` #### Theme guide for F-Sy-H Themes are declarative [INI files](https://github.com/z-shell/F-Sy-H/tree/main/themes). Start from a shipped theme so required styles and current fallbacks are retained: ```zsh theme_dir=${XDG_CONFIG_HOME:-$HOME/.config}/f-sy-h mkdir -p -- "$theme_dir" fsh_theme --copy-shipped-theme default "$theme_dir/custom" # Edit custom.ini, then apply it. fsh_theme "$theme_dir/custom.ini" ``` The shipped set is discovered at runtime, so use `fsh_theme --list` instead of relying on a hardcoded count. ##### Rendering contract Shipped themes declare their rendering assumptions: ```ini [theme] palette = xterm-256 foreground = #ffffff background = #000000 ``` `palette` is either `xterm-256`, with exact `#rrggbb` foreground and background values, or `terminal-ansi16`, with both values set to `default`. External themes may omit the complete `[theme]` section for compatibility, but a partial metadata declaration is invalid. A style value is a comma-separated combination of:
FormAccepted values
Namedred, green, blue, yellow, cyan, magenta, black, white, or default
Indexed0 through 255
TruecolorSix-digit #rrggbb
Backgroundbg: before any named, indexed, or truecolor value
Attributebold, blink, conceal, reverse, standout, underline, each no- form, or none
For example, `#ff0055,bold` sets a truecolor foreground and `bg:17,underline` combines an indexed background with an attribute. ##### Styles and fallbacks Theme sections group styles by where they apply:
SectionStyle group
[base]General tokens, separators, diagnostics, and recursive input
[command-point]Commands, aliases, functions, keywords, and shell syntax
[paths]Paths, directories, separators, and globbing
[brackets]Paired brackets and nesting levels
[arguments]Options and quoted arguments
[in-string]Escapes and expansions inside strings
[other]Variables, assignments, and history expansion
[math]Arithmetic variables, numbers, and errors
[for-loop]Loop variables, numbers, operators, and separators
[case]Case inputs, parentheses, and conditions
The exhaustive style order and each style's fallback chain live in the [canonical theme schema](https://github.com/z-shell/F-Sy-H/blob/main/lib/theme-schema.zsh). A missing style follows that declared chain and then `default`. Use [the default shipped theme](https://github.com/z-shell/F-Sy-H/blob/main/themes/default.ini) as the complete authoring template instead of copying a style table from documentation. From an F-Sy-H checkout, the [theme validator](https://github.com/z-shell/F-Sy-H/blob/main/tools/validate-themes.zsh) checks value syntax, required styles, metadata, contrast, semantic distinguishability, and colour-vision-deficiency separation: ```zsh zsh -f tools/validate-themes.zsh /path/to/theme.ini ``` The validator's JSON Lines record includes a `nearcolor256` object. It maps each distinct truecolor style literal to the xterm-256 index selected by the installed [`zsh/nearcolor` module](https://zsh.sourceforge.io/Doc/Release/Zsh-Modules.html#The-zsh_002fnearcolor-Module), or is empty when the theme has no truecolor styles. F-Sy-H attempts to load this optional module when the terminal does not advertise truecolor support. The approximation excludes terminal-defined indices 0 through 15 because their colours are not predictable, and automatic truecolor detection remains terminal-dependent. ##### Overlays and paths An overlay uses the same format but declares only the styles it replaces. A theme path whose base name contains `overlay` is treated as a partial overlay. Example overlay file: ```ini ; overlay.ini [base] commandseparator = yellow,bold comment = 17 [command-point] function = green command = 180 ``` Theme paths accept these shorthands:
ShorthandLocation
CONFIG:${XDG_CONFIG_HOME:-$HOME/.config}/f-sy-h/
CACHE:${XDG_CACHE_HOME:-$HOME/.cache}/f-sy-h/
LOCAL:/usr/local/share/f-sy-h/
HOME:$HOME/.f-sy-h/
OPT:/opt/local/share/f-sy-h/
For example, use `fsh_theme CONFIG:overlay` to load `${XDG_CONFIG_HOME:-$HOME/.config}/f-sy-h/overlay.ini` as an overlay. The `.ini` extension is optional. #### Secondary theme The optional `secondary` key names a theme used for recursively highlighted `eval` arguments and command substitutions. For example, the default theme uses `free`: ```ini ; default.ini [base] default = none unknown-token = 210,bold ; ... secondary = free ``` Recursive highlighting can use the alternate palette to distinguish embedded code: ![Syntax highlighting command substitution](https://wiki.zshell.dev/img/plugins/fsh/cmdsubst.png) In the above screen-shot the interior of `$( ... )` uses different colors than the rest of the code. Example for `eval`: ![Syntax highlighting eval](https://wiki.zshell.dev/img/plugins/fsh/eval_cmp.png) The first line highlights the `eval` argument as a regular string. The second line uses recursive highlighting with the configured secondary theme. ### Variables Comparing to the project `zsh-users/zsh-syntax-highlighting` (the upper image): ![Syntax highlighting parameter](https://wiki.zshell.dev/img/plugins/fsh/parameter.png) ![Syntax highlighting in string](https://wiki.zshell.dev/img/plugins/fsh/in_string.png) ### Brackets ![Syntax highlighting brackets](https://wiki.zshell.dev/img/plugins/fsh/brackets.gif) ### Conditions Comparing to the project `zsh-users/zsh-syntax-highlighting` (the upper line): ![Syntax highlighting conditions](https://wiki.zshell.dev/img/plugins/fsh/cplx_cond.png) ### Strings Exact highlighting that recognizes quoting. ![Syntax highlighting strings](https://wiki.zshell.dev/img/plugins/fsh/ideal-string.png) #### here-strings ![Syntax highlighting here-strings](https://wiki.zshell.dev/img/plugins/fsh/herestring.png) ### `exec` descriptor-variables Comparing to the project `zsh-users/zsh-syntax-highlighting` (the upper line): ![Syntax highlighting exec](https://wiki.zshell.dev/img/plugins/fsh/execfd_cmp.png) ### The for-loops and alternate syntax (brace `{`/`}` blocks) ![Syntax highlighting loops](https://wiki.zshell.dev/img/plugins/fsh/for-loop-cmp.png) ### Function definitions Comparing to the project `zsh-users/zsh-syntax-highlighting` (the upper 2 lines): ![Syntax highlighting function](https://wiki.zshell.dev/img/plugins/fsh/function.png) ### Recursive `eval` and `$( )` highlighting Comparing to the project `zsh-users/zsh-syntax-highlighting` (the upper line): ![Syntax highlighting eval](https://wiki.zshell.dev/img/plugins/fsh/eval_cmp.png) ## Chroma functions ### Command specific highlighting #### Autoload ![Syntax highlighting autoload](https://wiki.zshell.dev/img/cast/gif/fsh/fsh-autoload.gif) #### Awk ![Syntax highlighting awk](https://wiki.zshell.dev/img/cast/gif/fsh/fsh-awk.gif) #### Docker ![Syntax highlighting docker](https://wiki.zshell.dev/img/cast/gif/fsh/fsh-docker.gif) #### Git commit ![Syntax highlighting git commit](https://wiki.zshell.dev/img/cast/gif/fsh/fsh-git-commit.gif) #### Git checkout ![Syntax highlighting git checkout](https://wiki.zshell.dev/img/cast/gif/fsh/fsh-git-checkout.gif) #### Grep ![Syntax highlighting grep](https://wiki.zshell.dev/img/cast/gif/fsh/fsh-grep.gif) #### Make ![Syntax highlighting make](https://wiki.zshell.dev/img/cast/gif/fsh/fsh-make.gif) #### Perl ![Syntax highlighting perl](https://wiki.zshell.dev/img/cast/gif/fsh/fsh-perl.gif) #### Sh ![Syntax highlighting sh -c](https://wiki.zshell.dev/img/cast/gif/fsh/fsh-sh-c.gif) The [Chroma registry](https://github.com/z-shell/F-Sy-H/blob/main/lib/highlight.zsh) maps command names to their handlers. Inspect that mapping with `fsh_chroma list`: ```text COMMAND TARGET KIND STATUS docker _fsh_chroma_docker dedicated ready npm _fsh_chroma_subcommand generic ready ``` `dedicated` means the command has a command-specific handler or parser. `generic` means it uses the shared `_fsh_chroma_subcommand` fallback, which highlights the first non-option word as a subcommand but does not validate it or parse command-specific options. Registry totals therefore include lightweight generic entries and must not be read as a count of dedicated command coverage. `STATUS` reports whether each target is `ready`, `missing`, or intentionally `disabled`. ### Fpath highlighting ![Syntax highlighting fpath](https://wiki.zshell.dev/img/cast/gif/fsh/fsh-fpath.gif) ### Case highlighting ![Syntax highlighting case](https://wiki.zshell.dev/img/cast/gif/fsh/fsh-case.gif) ### Math highlighting ![Syntax highlighting math](https://wiki.zshell.dev/img/plugins/fsh/math.gif) ### Zcalc highlighting ![Syntax highlighting zcalc](https://wiki.zshell.dev/img/plugins/fsh/zcalc.png) ### Custom Working Directory Set the theme work directory before loading the plugin: ```zsh zstyle ':fsh:config' work-dir "${XDG_CACHE_HOME:-$HOME/.cache}/f-sy-h" ``` The default is `${XDG_CACHE_HOME:-$HOME/.cache}/f-sy-h`. `fsh_theme` stores parsed INI state there only when an explicit theme command needs to persist a main theme, overlay, or local secondary theme. ### Chroma guide for F-Sy-H This guide explains how to create detailed highlighting for a **specific program**. #### Keywords - `chroma` - a shorthand for `chroma function` – the thing that colorizes selected commands, like `git`, `grep`, etc. invocations, see `chroma function` below, - `big loop` - main highlighting code, a loop over tokens, and at least 2 large structure constructs (big `if` and `case`); it is advanced, e.g. parses `case` statements, here-string, it constitutes 90% of the F-Sy-H project, - `chroma function` - a plugin function that is called when a specific command occurs (e.g. when a user enters `git` at the command line) suppressing activity of `big loop` (i.e. no standard highlighting unless requested), - `token` - the result of splitting the whole command line (i.e. `$BUFFER`, the Zle variable) into bits called tokens, which are words in general, separated by spaces on the command line. #### Overview of functioning 1. Big loop is working – token by token processes command line, changes states (e.g. enters state "inside case statement") and in the end decides on the color of the token currently processed. 2. Big loop occurs a command that has a chroma, e.g. `git`. 3. Big loop enters "chroma" state, calls associated chroma function. 4. Chroma takes care of the "chroma" state, and ensures it will be set also for the next token. 5. "chroma" state is active, so all following tokens are routed to the chroma (in general skipping big-loop, see next items), 6. When processing of a single token is complete, the associated chroma returns 0 (shell-truth) to request no further processing by the big loop. 7. It can also return 1 so that a single, current token will be passed into a big-loop for processing (to do a standard highlighting). #### Chroma function arguments - `$1` - 0 or 1, denoting if it's the first call to the chroma, or the following one, - `$2` - the current token, also accessible by `$\__arg` from the upper scope - basically a private copy of `$__arg`; the token can be eg.: "grep", - `$3` - a private copy of `$_start_pos`, i.e. the position of the token in the command line buffer, used to add region\_highlight entry (see man) because Zsh colorizes by \_ranges\* applied onto command line buffer (e.g. `from-10 to-13 fg=red`), - `$4` - a private copy of `$_end_pos` from the upper scope; denotes where the current token ends (at which index in the string is the command line). So example invocation could look like this: ```zsh _fsh_chroma_example 1 "grep" "$_start_pos" "$_end_pos" ``` Big-loop will be doing such calls for the user, after occurring a specific chroma-enabled command (like e.g. `awk`), and then until chroma will detect the end of this chroma-enabled command (end of the whole invocation, with arguments, etc.; in other words, when e.g. new line or `;`\-character occurs, etc.). #### Example of chroma function Use the shipped [`_fsh_chroma_example`](https://github.com/z-shell/F-Sy-H/blob/main/chroma/_fsh_chroma_example) as the current template. It contains the supported function arguments, state handling, return statuses, and highlighting update sequence in one source file. #### Declarative Chroma definitions Git and Zi use declarative associative arrays instead of handwritten token loops. Their definitions live in [`chroma/_fsh_chroma_git`](https://github.com/z-shell/F-Sy-H/blob/main/chroma/_fsh_chroma_git) and [`chroma/_fsh_chroma_zi`](https://github.com/z-shell/F-Sy-H/blob/main/chroma/_fsh_chroma_zi). The shared [`_fsh_chroma_main` parser](https://github.com/z-shell/F-Sy-H/blob/main/chroma/_fsh_chroma_main) reads an array named `_fsh_chroma__def`, and the registry selects it with `_fsh_chroma_main%`. The format is maintainer-facing Zsh source. Definition files are trusted code because validation sources them to obtain the associative array and its handler functions. ##### Top-level entries
EntryMeaning
subcommandsA Zsh pattern, or ::function returning the patterns in reply
subcmd:NULLNodes active before a recognized subcommand
subcmd:<selector>//-separated nodes active for one subcommand or a parenthesized | pattern
subcmd:*Catch-all nodes for recognized subcommands without a more specific entry
subcmd-hookA function called after a subcommand is recognized
subcommands-blacklistA comma-separated list of subcommands that return to the normal highlighter
<LABEL>_<POSITION>_...A node describing an option or positional argument
Each referenced node must be a key in the same definition array. ##### Node names and positions A node name has this form: ```text LABEL_POSITION_KIND[MODIFIER] ``` - `POSITION` is a decimal argument position or `#` for any position. A recognized subcommand is not counted. - `KIND` is `opt` for a token beginning with `-`, or `arg` for a positional token. - `0_opt` handles options before the first positional argument. Later options can use their current numeric position or `#`. - A trailing `*` lets matching continue to the next candidate node. Without it, the first matching node wins. - A trailing `^` marks an option node that can add or delete active nodes. For example, `URL_1_arg` handles the first argument after a subcommand, while `FILE_#_arg` can handle any positional argument. ##### Records and delimiters Route entries list nodes with `//`: ```zsh subcmd:clone "CLONE_0_opt // URL_1_arg // DIRECTORY_2_arg // NO_MATCH_#_opt" ``` An option node contains one or more selector clauses separated by `||`. `<<>>` separates the selector from the action for the option and, when present, a second action for its value: ```zsh CLONE_0_opt " (--depth=|-b|--branch=) <<>> NO-OP // ::_fsh_chroma_option_action <<>> __style=\${_fsh_theme_name}optarg-string // NO-OP || (--quiet|-q) <<>> NO-OP // ::_fsh_chroma_option_action" ``` An argument node contains one action record. An optional Zsh pattern before `:::::` limits the record to an expected value: ```zsh URL_1_arg "NO-OP // ::_fsh_chroma_verify_url" MODE_2_arg "(fast|safe) ::::: __style=\${_fsh_theme_name}subcommand // NO-OP" ``` Every action record has `ACTION // HANDLER`. The supported action vocabulary is `NO-OP` or `__style=`. A handler is `NO-OP` or `::function`; the function receives the subcommand, start, end, and token values. Referenced handlers must be defined by the parser or the definition file. Directive clauses change the active node list. For option selectors, append `:add` or `:del` to the selector: ```zsh "MODE_0_opt^" " --safe <<>> NO-OP // ::_fsh_chroma_option_action || --safe:add <<>> SAFE_FILE_1_arg // NO_MATCH_#_arg || --safe:del <<>> DEFAULT_FILE_1_arg" ``` Argument records place the operation after another `<<>>`: ```zsh COMMAND_1_arg "run ::::: __style=\${_fsh_theme_name}subcommand // NO-OP <<>> add:RUN_TARGET_2_arg // NO_MATCH_#_arg" ``` ##### Validate definitions From an F-Sy-H checkout, validate every declarative definition: ```zsh zsh -f tools/validate-chromas.zsh ``` The validator rejects malformed records, unsupported actions, undefined handlers, and references to undefined nodes. CI runs the same check. A nested source format or generated associative array is intentionally not part of the contract; add one only if direct maintenance of these two arrays becomes a measured problem. --- --- title: ⚙️ History search for multiple words description: Search history for multiple keywords, entries that match all keywords will be found and syntax highlighted canonical_url: https://wiki.zshell.dev/ecosystem/plugins/h-s-mw markdown_url: https://wiki.zshell.dev/ecosystem/plugins/h-s-mw/index.md locale: en source_path: ecosystem/plugins/h-s-mw.mdx --- # ⚙️ History search for multiple words ## [z-shell/h-s-mw](https://github.com/z-shell/h-s-mw) The plugin allows to search history for multiple keywords, Ctrl+R initiates the search and matched keywords will be found and highlighted. ![History search for multiple words](https://wiki.zshell.dev/img/cast/gif/hsmw/hsmw-1.gif) ## Install H-S-MW - Zi - Zgen - Oh-My-Zsh - Standalone Add the following to your `.zshrc` file: ~/.zshrc ```zi zi light z-shell/H-S-MW ``` Reload the shell with `exec zsh` or open a new terminal. Add the following to your `.zshrc` file in the same place you're doing your other `zgen load` calls. ~/.zshrc ```zsh zgen load z-shell/H-S-MW ``` Clone the repository: ```sh git clone https://github.com/z-shell/H-S-MW.git \ ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/H-S-MW ``` And add `H-S-MW` to your plugin list: ~/.zshrc ```zsh plugins=(... H-S-MW) ``` Clone the repository: ```sh git clone https://github.com/z-shell/H-S-MW.git \ ~/some/path/to/hsmw ``` And add the following to your `.zshrc` file: ~/.zshrc ```zsh source ~/some/path/to/fsh/H-S-MW.plugin.zsh ``` ## Customizing ### Context viewing Add `zstyle` to `~/.zshrc`: `zstyle :plugin:history-search-multi-word `, where `` is one of:
ValueDescription
reset-prompt-protect 1See all occurrences of a command together with surrounding commands
page-size "8"Number of entries to show (default is $LINES/3)
page-size "LINES/4"Pages size relative to screen height
highlight-color "fg=yellow,bold"Color to highlight matched, searched text (default bg=17 on 256-color)
synhl "yes"Whether to perform syntax highlighting (default true)
active "underline"Effect on active history entry. Try standout, bold, bg=blue (default underline)
check-paths "yes"Whether to check paths for existence and mark with magenta (default true)
clear-on-cancel "no"Whether pressing Ctrl+c or ESC should clearly enter query (default true)
Example: ~/.zshrc ```zsh zstyle :plugin:history-search-multi-word reset-prompt-protect 1 zstyle ":history-search-multi-word" page-size "8" ``` tip For a better experience adjust history using [options](https://zsh.sourceforge.io/Doc/Release/Options.html), for example: ~/.zshrc ```zsh setopt extended_history # record timestamp of command in HISTFILE setopt hist_expire_dups_first # delete duplicates first when HISTFILE size exceeds HISTSIZE setopt hist_ignore_all_dups # remove older duplicate entries from the history setopt hist_ignore_dups # ignore duplicated commands history list setopt hist_ignore_space # ignore commands that start with space setopt hist_reduce_blanks # remove superfluous blanks from history items setopt hist_save_no_dups # do not write a duplicate event to the history file setopt inc_append_history # allow multiple terminal sessions to append to one history setopt inc_append_history # write to the history file immediately, not when the shell exits. setopt share_history # share command history data ``` ### Features
Refreshing prompt

Use zle reset-prompt in sched calls, in the presence of z-shell/F-Sy-H, zsh-users/zsh-syntax-highlighting, zsh-users/zsh-autosuggestions, and other plugins that hook up into Z-Shell by overloading ZLE widgets.

For example, to refresh the clock in prompt every second:

PROMPT=%B%F{yellow}%D{%H:%M:%S}%B%b%f

schedprompt() {
  zle && zle reset-prompt
  sched +1 schedprompt
}

zmodload -i zsh/sched
schedprompt

The reset-prompt-protect zstyle needs to be set to 1 for correct cooperation with H-S-MW. Alternatively, you could use zle .reset-prompt (i.e. with the dot in front) to call the original, not an overloaded reset-prompt widget (created by z-shell/F-Sy-H, zsh-users/zsh-autosuggestions, etc.).

Customizing syntax highlighting

Syntax highlighting is customized via the HSMW_HIGHLIGHT_STYLES associative array. It has keys like reserved-word, alias, command, path, etc. which are assigned with strings like fg=blue,bold, to configure how given elements are to be colored. The complete list of available keys is at the beginning of hsmw-highlight.

If you assign this array in ~/.zshrc before or after loading H-S-MW you will change the defaults.

Examples of customizing syntax highlighting

Sets path key – paths that exist will be highlighted with background magenta, foreground white, bold:

typeset -gA HSMW_HIGHLIGHT_STYLES
HSMW_HIGHLIGHT_STYLES[path]="bg=magenta,fg=white,bold"

Enable coloring of options of the form "-o" and "--the option", with cyan:

typeset -gA HSMW_HIGHLIGHT_STYLES
HSMW_HIGHLIGHT_STYLES[single-hyphen-option]="fg=cyan"
HSMW_HIGHLIGHT_STYLES[double-hyphen-option]="fg=cyan"

Use 256 colors to highlight command separators (like ";" or "&&"):

HSMW_HIGHLIGHT_STYLES[commandseparator]="fg=241,bg=17"
Blacklisting paths

Hash holding paths that shouldn't be grepped (globbed) – blacklist for slow disks, mounts, etc.:

typeset -gA FAST_BLIST_PATTERNS
FAST_BLIST_PATTERNS[/mount/nfs1/*]=1
FAST_BLIST_PATTERNS[/mount/disk2/*]=1
--- --- title: ⚙️ ZBrowse description: Check variables of a possible loop. canonical_url: https://wiki.zshell.dev/ecosystem/plugins/zbrowse markdown_url: https://wiki.zshell.dev/ecosystem/plugins/zbrowse/index.md locale: en source_path: ecosystem/plugins/zbrowse.mdx --- # ⚙️ ZBrowse ## [z-shell/zbrowse](https://github.com/z-shell/zbrowse) When doing shell work, it is often the case that `echo $variable` is invoked multiple times, to check the result of a loop, etc. With ZBrowse, you just need to press Ctrl-B, which invokes the`ZBrowse` variable browser for the Z shell. ![ZBrowse Preview](https://wiki.zshell.dev/img/cast/gif/zsh/zbrowse.gif) ## Install Zbrowse First, install the [z-shell/zui](https://github.com/z-shell/zui) plugin - a UI library for Z shell. - Zi - Zgen - Oh-My-Zsh - Standalone Add the following to your `.zshrc`. Zi will handle cloning the plugin for you automatically the next time you start Zsh. To update run `zi update z-shell/zbrowse`. ~/.zshrc ```zi zi load z-shell/zbrowse ``` Add the following to your `.zshrc` file in the same place you're doing your other `zgen load` calls. ~/.zshrc ```zsh zgen load z-shell/zbrowse.git ``` Clone the Repository: ~/.zshrc ```zsh git clone https://github.com/z-shell/zbrowse.git \ ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/zbrowse ``` And add `Zbrowse` to your plugin list. **The plugin is "standalone"**, which means that only sourcing it is needed. So to install, unpack ZBrowse somewhere and add to `.zshrc`: ~/.zshrc ```zsh source {where-zbrowse-is}/zbrowse.plugin.zsh ``` --- --- title: ⚙️ ZConvey description: Zsh Plugin ZConvey documentation canonical_url: https://wiki.zshell.dev/ecosystem/plugins/zconvey markdown_url: https://wiki.zshell.dev/ecosystem/plugins/zconvey/index.md locale: en source_path: ecosystem/plugins/zconvey.mdx --- # ⚙️ ZConvey ## [z-shell/zconvey](https://github.com/z-shell/zconvey) ZConvey integrates multiple Zsh sessions. They are given an `ID`, optionally a `NAME` (both unique), and can send commands to each other. Use this to switch all your Zshells to a given directory, via `zc-all cd $PWD`! Also, there's a `zc-bg-notify` **script** (not a function), that will show notification under the prompt of every active Zsh session. You can call this script from any program, Bash or GUI. ![ZConvey Preview](https://wiki.zshell.dev/img/cast/gif/zsh/zconvey.gif) ## [Zstyles](https://wiki.zshell.dev/search?q=zstyle) for ZConvey The values being set are the defaults. They must be set before loading the plugin.
ValueDescription
zstyle ":plugin:zconvey" check_interval "2"How often to check if there are new commands (in seconds)
zstyle ":plugin:zconvey" expire_seconds "22"If the shell is busy for 22 seconds, the received command will expire and not run
zstyle ":plugin:zconvey" greeting "logo"Display logo at Zsh start ("text" – display text, "none" – no greeting)
zstyle ":plugin:zconvey" ask "0"The zc won't ask for missing data ("1" has the same effect as always using -a option)
zstyle ":plugin:zconvey" ls_after_rename "0"Don't execute zc-ls after doing rename (with zc-rename or zc-take)
zstyle ":plugin:zconvey" use_zsystem_flock "1"Should use a faster zsystem's flock when it's possible? (default true)
zstyle ":plugin:zconvey" output_method "feeder"To put commands on the command line, ZConvey can use the small program "feeder" or "zsh" method, which currently doesn't automatically run the command – to use when e.g. feeder doesn't build (unlikely) or when occurring any problems with it
zstyle ":plugin:zconvey" timestamp_from "datetimeUse zsh/datetime module for obtaining timestamp. "date" – use date command (fork)
## ZConvey commands
CommandDescription
zcSends to another session; use the -a option to be asked for a target and a command to send
zc-lsLists all active and named sessions
zc-idShows ID and NAME of current session
zc-allThe same as zc, but targets are all other active sessions (with -f also busy sessions)
zc-takeTakes a name for current or selected sessions, schematically renames any conflicting sessions
zc-renameAssigns a name to a current or selected session; won't rename if there's a session with the same name
zc-bg-notifyIn subdirectory cmds, link it to /usr/local/bin, etc. or load with e.g. Zi
The main command is `zc` (yet it is rather rarely used, I'm always sending to all sessions with `zc-all`). It is used to execute commands on other sessions. `zc-ls` is the main tool to obtain overall information on sessions and `zc-take` is a nice rename tool to quickly name a few sessions. Keyboard shortcut Ctrl-O and Ctrl-I will show current session's `ID` and `NAME` in form of an on-screen logo. ## Install zconvey - Zi - Standalone Add the following to your `.zshrc` file. Zi will clone the plugin the next time you start Zsh. To update issue `zi update z-shell/zconvey`. ```zi zi load z-shell/zconvey ``` Zi can load in [turbo mode](https://wiki.zshell.dev/search?q=turbo+and+lucid), below is an example configuration. ```zi zi ice wait"0" zi light z-shell/zconvey ``` Adding `zc-bg-notify` to `$PATH`: ```zi zi ice wait"0" as"command" pick"cmds/zc-bg-notify" silent zi light z-shell/zconvey ``` The **standalone"** plugin install, unpack `zconvey` somewhere and add to `.zshrc`: ```zsh source {where-zconvey-is}/zconvey.plugin.zsh ``` The plugin integrates with my other plugin [z-shell/zsh-select](https://github.com/z-shell/zsh-select). Install it with e.g. Zi to be able to use the `-a` option for the `zc` command. --- --- title: ⚙️ Zi Console description: A console based on the `zsh/zcurses` Zshell module. canonical_url: https://wiki.zshell.dev/ecosystem/plugins/zi-console markdown_url: https://wiki.zshell.dev/ecosystem/plugins/zi-console/index.md locale: en source_path: ecosystem/plugins/zi_console.mdx --- # ⚙️ Zi Console ## [z-shell/zi-console](https://github.com/z-shell/zi-console) A console for [Zi](https://github.com/z-shell/zi) – based on the `zsh/zcurses` Zshell module allows the user to: - View the currently loaded plugins in a colorful list, in one of 3 different display modes. - Unload and load plugins. - Delete the plugins and snippets from the disk. ## Zi Console keybindings Start the console by Ctrl-O Ctrl-J keyboard shortcut, or by running `ziconsole` function in the shell.
Key(s)Description
Ctrl-U ,Ctrl-DHalf page up; half page down
Ctrl-P ,Ctrl-NPrevious line, centered; next line, centered
Ctrl-LRedraw of whole display
[ , ]Jump to next and previous section (e.g.: next plugin or snippet)
g , GJump to beginning and end of whole interface
< ,> or { ,}Horizontal scroll (i.e.: left or right)
/Show incremental search
F1Jump to result (in incremental search) and back
EscExit incremental search, clearing query
Ctrl-WDelete whole word (in incremental search)
Ctrl-KDelete whole line (in incremental search)
## Zi Console preview ## Install Zi Console > Prerequisites: [ZUI](https://github.com/z-shell/zui) library. - Standard - Turbo mode Standard syntax: ```zi zi load z-shell/zi-console ``` with use of [turbo mode](https://wiki.zshell.dev/docs/getting_started/overview/index.md#turbo-mode-zsh--53) and the [for](https://wiki.zshell.dev/docs/guides/syntax/for/index.md) syntax: ```zi zi wait lucid for z-shell/zi-console ``` The plugin needs the `zsh/curses` Zsh module. You can check if it's available to your Zsh by executing: ```zsh zmodload zsh/curses ``` If the call will return an error, then the `zsh/curses` module isn't available. ### Build the `zsh/curses` module You can build the `zsh/curses`\-equipped Z shell with Zi by the following command: ```zi zi ice id-as"zsh" atclone"./.preconfig CFLAGS='-I/usr/include -I/usr/local/include -g -O2 -Wall' \ LDFLAGS='-L/usr/lib -L/usr/local/lib' ./configure --prefix='$ZPFX'" \ atpull"%atclone" run-atpull make"install" pick"/dev/null" zi load zsh-users/zsh ``` The command will build a custom `zsh` and install it under `$ZPFX` (`${ZI[HOME_DIR]}/polaris` by default). The path `$ZPFX/bin` is already added to `$PATH` by Zi at the first position, so starting `zsh` will run the new Z shell. When on Gentoo, and possibly other systems, the `zsh` can still not have the ncurses library linked. To address this, utilize the [patch-dl](https://github.com/z-shell/z-a-patch-dl) annex and automatically patch the source first: ```zi zi light z-shell/z-a-patch-dl zi ice id-as"zsh" atclone"./.preconfig CFLAGS='-I/usr/include -I/usr/local/include -g -O2 -Wall' \ LDFLAGS='-L/usr/lib -L/usr/local/lib' ./configure --prefix='$ZPFX'" \ dl"http://gist.githubusercontent.com/z-shell/2373494c71cb6d1529344a2ed1a64b03/raw -> curses.patch" \ patch'curses.patch' atpull"%atclone" reset \ run-atpull make"install" pick"/dev/null" zi load zsh-users/zsh ``` Then, to update, rebuild and reinstall the `zsh`, you can do `zi update zsh`. The binary can be safely copied over `/bin/zsh` as it has paths to all needed directories built-in. --- --- title: ⚙️ ZPrompts description: Zsh themes (prompts) that use original Zsh theming subsystem. canonical_url: https://wiki.zshell.dev/ecosystem/plugins/zprompts markdown_url: https://wiki.zshell.dev/ecosystem/plugins/zprompts/index.md locale: en source_path: ecosystem/plugins/zprompts.mdx --- # ⚙️ ZPrompts ## [z-shell/zprompts](https://github.com/z-shell/zprompts) Zsh themes (prompts) that use original Zsh theming subsystem. The previews demonstrate: - ability to change prompt’s colors and other traits (original Zsh feature) - information on the age of unstaged changes - list of oldest unstaged modified files in the repo (3 files, starting from the oldest) ![Zprompts Preview](https://wiki.zshell.dev/img/cast/gif/zsh/zprompts.gif) ![Zprompts Theming Preview](https://wiki.zshell.dev/img/cast/gif/zsh/zprompts_theming.gif) ## Install Zprompts - Zi - Standalone Add the following to your `.zshrc` file with preferred theme e.g: ```zi zi nocd for \ atload'!promptinit; typeset -g PSSHORT=0; prompt sprint3 yellow red green blue' \ z-shell/zprompts ``` To use load the plugin: ```zsh source {where-zprompts-is}/zprompts.plugin.zsh ``` and then invoke (to use theme `scala3`) e.g.: ```zsh promptinit prompt scala3 ``` Please submit your prompt if you find a time to write the `prompt_NAME_setup` file. ## Help for the prompts Each prompt has its help available, e.g. for the theme `scala`: prompt -h scala ```text This prompt is themable. You can invoke it in following way: prompt scala You can provide only N first arguments, N=1..4. The default invocation is: ":: " cyan cyan magenta ``` --- --- title: ⚙️ Command Architect description: Allows to copy segments of commands in history, rearrange segments of the current command, and delete segments of the current command. canonical_url: https://wiki.zshell.dev/ecosystem/plugins/zsh-cmd-architect markdown_url: https://wiki.zshell.dev/ecosystem/plugins/zsh-cmd-architect/index.md locale: en source_path: ecosystem/plugins/zsh_comand_architect.mdx --- # ⚙️ Command Architect ## [z-shell/zsh-cmd-architect](https://github.com/z-shell/zsh-cmd-architect) The Zsh Command Architect allows to copy segments of commands in history, rearrange segments of the current command, and delete segments of the current command. This way user glues commands from parts without using a mouse. Advanced history search (multi-word, without duplicate lines) allows to quickly find the parts. ## Keybindings
Key(s)Description
Ctrl+TStart Zsh Command Architect (Zshell binding)
EnterDelete selected segment (command window) or add selected segment (history window)
[ or ]Move active segment (when in command window)
Shift+left or Shift+rightMove active segment (when in command window)
TabSwitch between the two available windows
g, GBeginning and end of the list
/Start incremental search
EscExit incremental search, clearing filter
<,>, {,}Horizontal scroll
Ctrl+LRedraw of whole display
Ctrl+O, oEnter uniq mode (no duplicate lines)
Ctrl+W(in incremental search) - delete whole word
Ctrl+K(in incremental search) - delete whole line
Ctrl+D, Ctrl+UHalf page up or down
Ctrl+P, Ctrl+NPrevious and next (also done with vim's j,k)
## Install Zsh Command Architect - Zi - Zgen - Manual - Single File - Standalone Add the following to `.zshrc`. The config files will be available in `~/.config/zca`. ~/.zshrc ```zi zi load z-shell/zsh-cmd-architect ``` Add the following to `.zshrc` and issue a `zgen reset` (this assumes that there is a proper `zgen save` construct in `.zshrc`). The config files will be available in `~/.config/zca`. ~/.zshrc ```zsh zgen load z-shell/zsh-cmd-architect ``` After extracting `ZCA` to `{some-directory}` add the following two lines to `~/.zshrc`: ~/.zshrc ```zsh fpath+=( {some-directory} ) source "{some-directory}/zsh-cmd-architect.plugin.zsh" ``` As you can see, no plugin manager is needed to use the `*.plugin.zsh` file. The above two lines of code are all that almost **all** plugin managers do: ~/.zshrc ```zsh source "{some-directory}/zsh-cmd-architect.plugin.zsh" ``` because `ZCA` detects if it is used by **any** plugin manager and can handle the `$fpath` update by itself. Running script `doc/generate_single_file` will create a single-file version of `ZCA`. It can be sourced from `.zshrc`. Don't forget about configuration files (copy them to `~/.config/zca`). ```sh sh -c "$(curl -fsSL https://raw.githubusercontent.com/z-shell/zsh-cmd-architect/main/doc/install.sh)" ``` To update run the command again. `ZCA` will be installed at `~/.config/zca/zsh-cmd-architect`, config files will be copied to `~/.config/zca`. `.zshrc` will be updated with only `4` lines of code that will be added to the bottom. After installing and reloading the shell give `ZCA` a quick try with Ctrl-T. ## Performance `ZCA` is fastest with `Zsh` before `5.0.6` and starting from `5.2`
Fixing tmux, screen and linux vt

If TERM=screen-256color (often a case for tmux and screen sessions) then ncv terminfo capability will have 2nd bit set. This in general means that underline won't work. To fix this by creating your own ncv=0-equipped terminfo file, run:

{ infocmp -x screen-256color; printf '\t%s\n' 'ncv@,'; } > /tmp/t && tic -x /tmp/t

A file will be created in directory ~/.terminfo and will be automatically used, tmux and screen will work. Similar is for Linux virtual terminal:

{ infocmp -x linux; printf '\t%s\n' 'ncv@,'; } > /tmp/t && tic -x /tmp/t

It will not display underline properly, but will instead highlight by a color, which is quite nice. The same will not work for FreeBSD's vt, ZCA will detect if that vt is used and will revert to highlighting elements via reverse mode.

--- --- title: ⚙️ Editing Workbench description: Organized shortcuts for Zsh canonical_url: https://wiki.zshell.dev/ecosystem/plugins/zsh-editing-workbench markdown_url: https://wiki.zshell.dev/ecosystem/plugins/zsh-editing-workbench/index.md locale: en source_path: ecosystem/plugins/zsh_editing_workbench.mdx --- # ⚙️ Editing Workbench ## [z-shell/zsh-editing-workbench](https://github.com/z-shell/zsh-editing-workbench) Organized shortcuts for various command line editing operations, plus new operations as incremental history word completion.
KeysDescription
Alt+wDelete a shell word 1
Alt+tTranspose (swap) shell words
Alt+mCopy previous shell word, or word before that, etc. when used multiple times
Alt+MJust copy previous shell word without iterating to previous ones
Alt+.Copy last shell word from previous line, or line before that, etc. when used multiple times; can be combined with Alt+m
Ctrl+WDelete word according to configured word style 2:
Alt+rTranspose (swap) words according to configured word style (cursor needs to be placed on beginning of word to swap)
Alt+/Complete some word 3 from history
Alt+h, Alt+HComplete shell word from history (custom version)
Alt+JBreak line
Alt+_Undo
## Install Zsh Editing Workbench - Zi - Zgen - Standalone Add the following to `.zshrc`. The config files will be available in `~/.config/zew`. ~/.zshrc ```zi zi load z-shell/zsh-editing-workbench ``` Add `zgen load z-shell/zsh-editing-workbench` to `.zshrc` and issue a `zgen reset` (this assumes that there is a proper `zgen save` construct in `.zshrc`). The config files will be available in `~/.config/zew`. After extracting `ZEW` to `{some-directory}` add the following two lines to `~/.zshrc`: ~/.zshrc ```zsh fpath+=( {some-directory} ) source "{some-directory}/zsh-editing-workbench.plugin.zsh" ```
Configure terminals
  • XTerm

To make Alt key work like expected under XTerm add XTerm*metaSendsEscape: true to your resource file, e.g.:

echo 'XTerm*metaSendsEscape: true' >> ~/.Xresources
  • Konsole

To make Alt key work like expected under Konsole add Konsole*keysym.Meta: Meta to your resource file, e.g.:

echo 'Konsole*keysym.Meta: Meta' >> ~/.config/konsolerc
## Footnotes 1. A **shell word** is a text that Zsh would see as single segment. For example `$(( i + 1 ))` is a single **shell word**. [↩](#user-content-fnref-1) 2. A **word style** defines a way Zsh recognizes segments (words) of text in commands that want to use the style information. The style can be configured in **zew.conf** to be one of: - bash words are built up of alphanumeric characters only. - normal as in normal shell operation: word characters are alphanumeric characters plus any characters present in the string given by the parameter `$WORDCHARS`. - shell words are complete shell command arguments, possibly including complete quoted strings, or any tokens special to the shell. - whitespace words are any set of characters delimited by whitespace. - default restore the default settings; this is the same as 'normal' with default `$WORDCHARS` value. [↩](#user-content-fnref-2) 3. **Some word** is in general a sophisticated word, but not a **shell word**, because of limitations in Zsh history word completion. **Some word** is rather not build from special characters, it works well for normal characters. [↩](#user-content-fnref-3) --- --- title: ⚙️ Modules description: Compiled Zsh modules, including zpmod automatic source compilation and profiling canonical_url: https://wiki.zshell.dev/ecosystem/plugins/zsh-modules markdown_url: https://wiki.zshell.dev/ecosystem/plugins/zsh-modules/index.md locale: en source_path: ecosystem/plugins/zsh_modules.mdx --- # ⚙️ Modules ## [z-shell/zpmod](https://github.com/z-shell/zpmod) info - Required Zsh version: >= v5.8.1 [![CI](https://github.com/z-shell/zpmod/actions/workflows/ci.yml/badge.svg)](https://github.com/z-shell/zpmod/actions/workflows/ci.yml) [![Compatibility](https://github.com/z-shell/zpmod/actions/workflows/compatibility.yml/badge.svg)](https://github.com/z-shell/zpmod/actions/workflows/compatibility.yml) The module is a binary Zsh module transparently and automatically **compiles sourced scripts** and **measures the time of each script sourcing**. ```text source file -> freshness check -> compile or reuse .zwc -> execute -> source-study report ``` zpmod also provides native builtins for batch path metadata, directory listing, file reading, and record-oriented array input. These helpers reduce repeated shell-level filesystem work without requiring a particular plugin manager. ### Measuring Time of sources ```zsh zpmod source-study [-l] ``` > Option -l shows full paths to the files. Issue `zpmod source-study` after loading the module at top of `~/.zshrc` to see a list of files loaded via `source` or `.` builtins, with a duration that each loading lasted, in milliseconds. The module tracks all calls to those builtins and measures the time each call took. This can be used e.g. profile loading of plugins, regardless of the plugin manager used. This feature allows profiling the shell startup. Also, no script can pass through that check and you will obtain a complete list of all loaded scripts, like if Zshell itself was investigating this. **The list can be surprising** 😵‍💫 ```text ⏱️ 3 ms plugin-a.plugin.zsh ⏱️ 12 ms plugin-b.plugin.zsh ``` The values above illustrate the report format. Actual timings depend on the machine and shell configuration. ### Reproducible benchmark ![Bar chart: median startup time was 11.564 milliseconds for plain source, 13.378 milliseconds for the zpmod first run, 4.082 milliseconds for zpmod warm, and 3.760 milliseconds for manual compiled Zsh files.](https://raw.githubusercontent.com/z-shell/zpmod/a2c9e2310f763098ac3f5de186748367d5d17a56/benchmarks/results/v2.0.6-linux-x86_64/benchmark.svg)
ModeMedianp95
Plain source11.564 ms13.331 ms
zpmod first run13.378 ms15.495 ms
zpmod warm4.082 ms4.210 ms
Manual .zwc3.760 ms3.880 ms
The v2.0.6 comparison uses one synthetic 40-script workload. The first run includes compilation, the warm run reuses generated `.zwc` files, and manual `.zwc` is the native Zsh control. It is not a universal startup-speed claim. Read the [complete benchmark report](https://github.com/z-shell/zpmod/blob/a2c9e2310f763098ac3f5de186748367d5d17a56/benchmarks/results/v2.0.6-linux-x86_64/benchmark.md) and [benchmark methodology](https://github.com/z-shell/zpmod/blob/a2c9e2310f763098ac3f5de186748367d5d17a56/benchmarks/README.md) for the raw context and reproduction commands. ### Extending Zi reports ```zsh zpmod [options] ``` Used by zpmod internally to speed up loading plugins with tracking (reporting). It extends the given field {plugin-ID} in `$ZI_REPORTS` hash, with the given string {new-report-body}. ### Debugging To enable debug messages from the module set: ```zsh typeset -g ZI_MOD_DEBUG=1 ``` ### Install zpmod - Zi - Standalone - To start using the module run: `zi module -B`, append `--clean` to run `make distclean`. - To display the instructions on loading the module, run: `zi module -I`. ```zi zi module [-B|--build[--clean]] [-I|--info] [-r|--reset] [-h|--help] [options] zi module -B [--clean] # Build the module, append --clean to run distclean. zi module -I # Display instructions on loading the module. zi module -r # Check timestamps and rebuild the module if needed. ``` This command will compile the module and display instructions on what to add to `~/.zshrc`. Install zpmod as a system package or build it with the repository's CMake helper. Both paths work without Zi or another plugin manager. See the [current standalone installation guide](https://github.com/z-shell/zpmod/blob/main/docs/how-to/install-zpmod-with-cmake.md). ## [z-shell/zgdbm](https://github.com/z-shell/zgdbm) Provides GDBM module as a plugin. `zstyles` - The values being set are the defaults. note Change the values before loading the `zgdbm` plugin. ~/.zshrc ```zsh zstyle ":plugin:zgdbm" cppflags "-I/usr/local/include" # Additional include directory zstyle ":plugin:zgdbm" cflags "-Wall -O2 -g" # Additional CFLAGS zstyle ":plugin:zgdbm" ldflags "-L/usr/local/lib" # Additional library directory ``` ### Install zgdbm ```zi zi light z-shell/zgdbm ``` --- --- title: ⚙️ Navigation Tools description: Multi-word history searcher, `n-cd` – directory bookmark manager, `n-kill` – `htop` like kill utility, and more. canonical_url: https://wiki.zshell.dev/ecosystem/plugins/zsh-navigation-tools markdown_url: https://wiki.zshell.dev/ecosystem/plugins/zsh-navigation-tools/index.md locale: en source_path: ecosystem/plugins/zsh_navigation_tools.mdx --- # ⚙️ Navigation Tools ## [z-shell/zsh-navigation-tools](https://github.com/z-shell/zsh-navigation-tools) ## Available tools
FunctionDescription
n-aliasesAliases, relegates editing to vared
n-cdDir stack and bookmarked directories, allows entering the selected directory
n-functionsFunctions, relegates editing to zed or vared
n-historyHistory, allows to edit and run commands from it
n-killProcesses list, allows sending a signal to the selected process
n-envEnvironment, relegates editing to vared
n-optionsOptions, allows toggling their state
n-panelizeLoads the output of a given command into the list for browsing
All tools support horizontal scroll with <, >, {, } , h, l or left and right cursors. Other keys are:
Key(s)Description
H, ?(from n-history) - run n-help
Ctrl-RStart n-history, the incremental, multi-keyword history searcher (Zsh binding)
Ctrl-ARotate entered words (1+2+3 -> 3+1+2)
Ctrl-FFix mode (approximate matching)
Ctrl-LRedraw of whole display
Ctrl-TBrowse themes (next theme)
Ctrl-GBrowse themes (previous theme)
Ctrl-UHalf page up
Ctrl-DHalf page down
Ctrl-PPrevious element (also done with vim's k)
Ctrl-NNext element (also done with vim's j)
[, ]Jump directory bookmarks in n-cd and typical signals in n-kill
g, GBeginning and end of the list
/Show incremental search
F3Show/hide incremental search
EscExit incremental search, clearing filter
Ctrl-W (in incremental search)Delete whole word
Ctrl-K (in incremental search)Delete whole line
Ctrl-O, oEnter uniq mode (no duplicate lines)
Ctrl-E, eEdit private history (when in private history view)
F1 (in n-history)Switch view
F2, Ctrl-X, Ctrl-/Search predefined keywords (defined in config files)
Set of tools like `n-history` – multi-word history searcher, `n-cd` – directory bookmark manager, `n-kill` – `htop` like kill utility, and more. Based on `n-list`, a tool generates a selectable curses-based list of elements that has access to the current `Zsh` session, i.e. has broad capabilities to work together with it. Feature highlights include incremental multi-word searching, approximate matching, ANSI coloring, themes, unique mode, horizontal scroll, grepping, advanced history management, and various integrations with `Zsh`. ## Install Zsh Navigation Tools - Zi - Zgen - Manual - Single file - Fully manual - Standalone Add the following to `.zshrc`. The config files will be in `~/.config/znt`. ~/.zshrc ```zi zi load z-shell/zsh-navigation-tools ``` Add the following to `.zshrc` and issue a `zgen reset` (this assumes that there is a proper `zgen save` construct in `.zshrc`). ~/.zshrc ```zsh zgen load z-shell/zsh-navigation-tools ``` The config files will be available in `~/.config/znt`. After extracting `ZNT` to `{some-directory}` add the following two lines to `~/.zshrc`: ~/.zshrc ```zsh fpath+=( {some-directory} ) source "{some-directory}/zsh-navigation-tools.plugin.zsh" ``` As you can see, no plugin manager is needed to use the `*.plugin.zsh` file. The above two lines of code are all that almost **all** plugin managers do. What's actually needed is only: ```zsh source "{some-directory}/zsh-navigation-tools.plugin.zsh" ``` because `ZNT` detects if it is used by **any** plugin manager and can handle the `$fpath` update by itself. Running script `doc/generate_single_file` will create a single-file version of `ZNT`. It can be sourced from `.zshrc`. Don't forget about configuration files as described above. Copy (or link) all `n-*` and `znt-*` files to **/usr/share/zsh/site-functions/** (or **/usr/local/share/zsh/site-functions/**, check with `echo $fpath[1]`) and then add: `autoload n-list n-cd n-env n-kill n-panelize n-options n-aliases n-functions n-history n-help` to `~/.zshrc`. Create aliases to avoid typing the minus sign `-`: ```zsh alias naliases=n-aliases ncd=n-cd nenv=n-env nfunctions=n-functions nhistory=n-history alias nkill=n-kill noptions=n-options npanelize=n-panelize nhelp=n-help ``` Don't forget to copy [configuration files](https://github.com/z-shell/zsh-navigation-tools/tree/main/.config/znt). They should go to `~/.config/znt`. Moreover, `n-cd` works together with option `AUTO_PUSHD` and you should have: ```zsh setopt AUTO_PUSHD ``` in `.zshrc` (also recommend `PUSHD_IGNORE_DUPS`). Without the option, `n-cd` will just work as an incremental searcher of directory bookmarks. ```sh sh -c "$(curl -fsSL https://raw.githubusercontent.com/z-shell/zsh-navigation-tools/main/doc/install.sh)" ``` To update run the command again. `ZNT` will be installed at `~/.config/znt/zsh-navigation-tools`, config files will be copied to `~/.config/znt`. `.zshrc` will be updated with only `8` lines of code, which will be added at the bottom. After installing and reloading the shell give `ZNT` a quick try with `Ctrl-R` – this keyboard shortcut will open `n-history`. ## History Widget To have `n-history` as the incremental searcher bound to `Ctrl-R` copy `znt-*` files into the `*/site-functions` dir (unless you do a single file install) and add: ```zsh autoload znt-history-widget zle -N znt-history-widget bindkey "^R" znt-history-widget ``` to `.zshrc`. This is done automatically when using the installer, zgen, antigen, or single file install. Two other widgets exist, `and-cd-widget` and `znt-kill-widget`, they too can be assigned to key combinations (`autoload` is done in `.zshrc` so no need for it): ```zsh zle -N znt-cd-widget bindkey "^B" znt-cd-widget zle -N znt-kill-widget bindkey "^Y" znt-kill-widget ``` ## Configuration `ZNT` has configuration files located in `~/.config/znt`. The files are: ```ini n - aliases.conf; n - cd.conf; n - env.conf; n - functions.conf; n - history.conf; n - kill.conf; n - list.conf; n - options.conf; n - panelize.conf; ``` `n-list.conf` contains main configuration variables: ```zsh # Should the list (text, borders) be drawn in bold local bold=0 # Main color pair (foreground/background) local colorpair="white/black" # Should draw the border? local border=1 # Combinations of colors to try out with Ctrl-T and Ctrl-G # The last number is the bold option, 0 or 1 local -a themes themes=( "white/black/1" "green/black/0" "green/black/1" "white/blue/0" "white/blue/1" "magenta/black/0" "magenta/black/1" ) ``` Read remaining configuration files to see what's in them. Nevertheless, a configuration can be also set from `.zshrc`. There are `5` standard `.zshrc` configuration variables: ```zsh znt_history_active_text - underline or reverse - how should be active element highlighted znt_history_nlist_coloring_pattern - pattern that can be used to colorize elements znt_history_nlist_coloring_color - color with which to colorize znt_history_nlist_coloring_match_multiple - should multiple matches be colorized (0 or 1) znt_history_keywords (array) - search keywords activated with `Ctrl-X`, `F2` or `Ctrl-/`, e.g. ( "git" "vim" ) ``` The above variables will work for the `n-history` tool. For other tools, change `_history_` to e.g. `_cd_`, for the `n-cd` tool. The same works for all `8` tools. The common configuration of the tools uses variables with `_list_` in them: ```zsh znt_list_bold - should draw text in bold (0 or 1) znt_list_colorpair - main pair of colors to be used, e.g "green/black" znt_list_border - should draw borders around windows (0 or 1) znt_list_themes (array) - list of themes to try out with Ctrl-T, e.g. ( "white/black/1" "green/black/0" ) znt_list_instant_select - should pressing enter in search mode leave tool (0 or 1) ``` If you used `ZNT` before `v2.1.12`, remove old configuration files `~/.config/znt/*.conf` so that `ZNT` can update them to the latest versions that support integration with `.zshrc`. If you used installer then run it again (after the removal of configuration files). ## Programming The function `n-list` is used as follows: ```zsh n-list {element1} [element2] ... [elementN] ``` This is all that is needed to be done to have the features like ANSI coloring, incremental multi-word search, unique mode, horizontal scroll, and non-selectable elements (grepping is done outside `n-list`, see the tools for how it can be done). To set up non-selectable entries add their indices into the array `NLIST_NONSELECTABLE_ELEMENTS`: ```zsh typeset -a NLIST_NONSELECTABLE_ELEMENTS NLIST_NONSELECTABLE_ELEMENTS=( 1 ) ``` The result is stored as `$reply[REPLY]` (`$` isn't needed before `REPLY` because of the arithmetic context inside `[]`). The returned array might be different from input arguments as `n-list` can process them via incremental search or uniq mode. `$REPLY` is the index in that possibly processed array. If `$REPLY` equals `-1` it means that no selection has been made (user quitted via `q` key). To set up entries that can be jumped to with `[`,`]` keys add their indices to the `NLIST_HOP_INDEXES` array: ```zsh typeset -a NLIST_HOP_INDEXES NLIST_HOP_INDEXES=( 1 10 ) ``` `n-list` can automatically colorize entries according to a `Zsh` pattern. The following example will colorize all numbers with blue: ```zsh local NLIST_COLORING_PATTERN="[0-9]##" local NLIST_COLORING_COLOR=$'\x1b[00;34m' local NLIST_COLORING_END_COLOR=$'\x1b[0m' local NLIST_COLORING_MATCH_MULTIPLE=1 n-list "This is a number 123" "This line too has a number: 456" ``` Blue is the default color, it doesn't have to be set. See the `zshexpn` man page for more information on `Zsh` patterns. Briefly, comparing to regular expressions, `(#s)` is `^`, `(#e)` is `$`, `#` is `*`, `##` is `+`. The alternative will work when in parenthesis, i.e. `(a|b)`. BTW by using this method you can colorize the output of the tools, via their config files (check out e.g. n-cd.conf, it is using this). ## Performance `ZNT` is fastest with `Zsh` before `5.0.6` and starting from `5.2` tip Zsh plugins may look scary, as they seem to have some "architecture". The plugin is: 1. has its directory added to `fpath` 2. has any first `*.plugin.zsh` file sourced That's it. When one contributes to Oh-My-Zsh or creates a plugin for any plugin manager, he only needs to account for this. The same with doing any non-typical Zsh Navigation Tools installation. caution Be aware of [this](https://github.com/z-shell/zsh-navigation-tools/blob/f49f910d239ae5bc6e1a5bb34930307b4f4e3ffe/zsh-navigation-tools.plugin.zsh#L35-L49) ## Fixing tmux, screen, and Linux vt If `TERM=screen-256color` (often a case for `tmux` and `screen` sessions) then `ncv` terminfo capability will have `2`nd bit set. This in general means that the underline won't work. To fix this by creating your own `ncv=0`\-equipped terminfo file, run: ```sh { infocmp -x screen-256color; printf '\t%s\n' 'ncv@,'; } > /tmp/t && tic -x /tmp/t ``` A file will be created in the directory `~/.terminfo` and will be automatically used, `tmux` and `screen` will work. Similar is for Linux virtual terminal: ```sh { infocmp -x linux; printf '\t%s\n' 'ncv@,'; } > /tmp/t && tic -x /tmp/t ``` It will not display underline properly, but will instead highlight by a color, which is quite nice. The same will not work for FreeBSD's vt, `ZNT` will detect if that `vt` is used and will revert to highlighting elements via `reverse` mode. --- --- title: ⚙️ Select description: A shell command that will display a selection list. canonical_url: https://wiki.zshell.dev/ecosystem/plugins/zsh-select markdown_url: https://wiki.zshell.dev/ecosystem/plugins/zsh-select/index.md locale: en source_path: ecosystem/plugins/zsh_select.mdx --- # ⚙️ Select ## [z-shell/zsh-select](https://github.com/z-shell/zsh-select) A shell command that will display a selection list. It is similar to `selecta`, but uses curses library to do display, and when compared to `fzf`, the main difference is approximate matching instead of fuzzy matching. It is written in Z shell and has its capabilities: - Patterns, allowing multi-term searching - Curses module - Approximate matching Ctrl-F The file `zsh-select` can be copied to any `bin` directory. `Zsh` will serve as say `Ruby`, and `zsh-select` will be a regular program available in the system. Pressing o will make elements uniqe. To search again after pressing enter, press /. Approximate matching mode is activated by Ctrl-F. ![Zsh Select Preview](https://wiki.zshell.dev/img/cast/gif/zsh/zsh-select.gif) ## Install Zsh Select The `zsh-select` will be available in interactive `Zsh` sessions only when using this method. Nevertheless, integration with `Vim` and other methods will work when `Zsh` is your main shell. - Standalone - Zi - Zgen - Vim Simply copy file `zsh-select` to any `bin` directory such as `/usr/local/bin`. Add the following to `.zshrc`. The plugin will be loaded next time you start `Zsh`. To update issue `zi update z-shell/zsh-select` from command line. ~/.zshrc ```zi zi load z-shell/zsh-select ``` Add the following to `.zshrc` and issue a `zgen reset` (this assumes that there is a proper `zgen save` construct in `.zshrc`). ~/.zshrc ```zsh zgen load z-shell/zsh-select ``` Adding the following snippet to `.vimrc` will provide a `\f` keyboard shortcut that will run `zsh-select` as file-selector. Multi-term searching and approximate matching Ctrl-F will be available. The snippet is based on code from the `selecta` GitHub page (MIT license): ```vim " Run a given vim command on the results of fuzzy selecting from a given shell " command. See usage below. function! ZshSelectCommand(choice_command, zshselect_args, vim_command) try let selection = system(a:choice_command . " | zsh-select " . a:zshselect_args) catch /Vim:Interrupt/ " Swallow the ^C so that the redraw below happens; otherwise there will be " leftovers from zshselect on the screen redraw! return endtry redraw! exec a:vim_command . " " . selection endfunction " Find all files in all non-dot directories starting in the working directory. " Fuzzy select one of those. Open the selected file with :e. nnoremap f :call ZshSelectCommand("find * -type f 2>/dev/null", "", ":e") ``` ## Configuring There are a few environment variables that can be set to alter `zsh-select` behavior. The values assigned below are the defaults: ```zsh export ZSHSELECT_BOLD="1" # The interface will be drawn in bold font. Use "0" for no bold export ZSHSELECT_COLOR_PAIR="white/black" # Draw in white foreground, black background. Try e.g.: "white/green" export ZSHSELECT_BORDER="0" # No border around interface, Use "1" for the border export ZSHSELECT_ACTIVE_TEXT="reverse" # Mark current element with reversed text. Use "underline" for marking with underline export ZSHSELECT_START_IN_SEARCH_MODE="1" # Starts Zsh Select with searching active. "0" will not invoke searching at the start. ``` --- --- title: ⚙️ Unique ID description: Provides a **unique number** that identifies a **running** Zshell session. canonical_url: https://wiki.zshell.dev/ecosystem/plugins/zsh-unique-id markdown_url: https://wiki.zshell.dev/ecosystem/plugins/zsh-unique-id/index.md locale: en source_path: ecosystem/plugins/zsh_unique_id.mdx --- # ⚙️ Unique ID ## [z-shell/zsh-unique-id](https://github.com/z-shell/zsh-unique-id) This plugin provides a **unique number** that identifies a **running** Z shell session, in its shell variable `$ZUID_ID`. Besides this unique number, also a unique *codename* is provided, in the shell variable `$ZUID_CODENAME`. Once you load this plugin, the two parameters will be set, and their values will not be available to other Zshell sessions (being thus *unique*). `$ZUID_ID` is a progressing number starting from `1`. `$ZUID_CODENAME` is chosen from a list of predefined codenames, see the default list below. An example use case is to hold logs in files `.../mylog-${ZUID_CODENAME}.log` so that two different Z shells will not write to the same file at the same time. Default code names are: - atlantis (for `ZUID_ID` == `1`) - echelon (for `ZUID_ID` == `2`) - quantum (for `ZUID_ID` == `3` and etc.) - ion - proxima - polaris - solar - momentum - hyper - gloom - velocity - future - enigma - andromeda - saturn - jupiter - aslan - commodore - falcon - persepolis - dharma - samsara - prodigy - ethereal - epiphany - aurora - oblivion Zstyle configuration allows to customize the codenames: ```zsh zstyle :plugin:zuid codenames paper metal wood plastic # first 4 shells will have those codenames ``` ## Install Zsh Unique ID - Standalone - Zi - Zgen - Oh-My-Zsh Unpack `zsh-unique-id` to the chosen location and add to `.zshrc`: ~/.zshrc ```zsh source {where-zsh-unique-id-is}/zsh-unique-id.plugin.zsh ``` Sourcing is recommended, because it can be done early, at top of zshrc, without a plugin manager – to acquire the unique identification as early as possible. Add the following to your `.zshrc` file. Zi will clone the plugin the next time you start zsh. To update issue `zi update z-shell/zsh-unique-id`. ~/.zshrc ```zi zi load z-shell/zsh-unique-id ``` Add `zgen load z-shell/zsh-unique-id` to your `.zshrc` file in the same place you're doing your other `zgen load` calls in. Clone the Repository: ~/.zshrc ```zsh git clone https://github.com/z-shell/zsh-unique-id.git \ ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/zsh-unique-id ``` And add `zsh-unique-id` to your plugin list. --- --- title: ⚙️ ZUI description: The Rapid Application Development textual user interface library for Zsh. canonical_url: https://wiki.zshell.dev/ecosystem/plugins/zui markdown_url: https://wiki.zshell.dev/ecosystem/plugins/zui/index.md locale: en source_path: ecosystem/plugins/zui.mdx --- # ⚙️ ZUI ## CGI+DHTML-like User Interface Library for Zsh / ZCurses ### [z-shell/ZUI](https://github.com/z-shell/zui) This is a RAD (Rapid Application Development) textual user interface library for Zsh. It in many aspects resembles a typical CGI+(D)HTML setup. There are: - Generators ran on the "server" side (basic Zshell-code that is just generating text!), - Event loop that turns the generated text into a document with active elements (buttons, anchors, toggle buttons, text fields, list boxes), - Mechanism to regenerate document parts from the original generators. So, a Zshell code generates text. It is then turned into a document with hyperlinks. DHTML-like calls are possible that will regenerate document parts on the fly. Page can be also reloaded with input data, just like an HTML page. A voiced [video tutorial](https://youtu.be/TfZ8b_RS_Bg) or [file on google drive](https://drive.google.com/file/d/1mg6OPScurIT_AIJPotEzpw1TrnU0OeUZ/view?usp=sharing) shows how to create an application – Nmap network scanner frontend. ### Learning Zsh ZUI will allow you to learn Zsh at the advanced level. The library uses Zshell e.g. Ruby. To write a functional program in Ruby, you need to know the language. To write a command or alias in Zsh, you can spend years not learning anything new. With ZUI you will learn how to use `coproc`, patterns with `(#b)` flag, Zstyles, arrays, hashes, and various substitutions. That said, examples are there to make the process easy, and problems have an easy and advanced way of issue solving. ### API The API consists of [Standard Library](#standard-library), [Utility Library](#utility-library) and [Callbacks](#callbacks). You normally want a few calls from Standard Library – to create buttons and regenerate document parts, and one or two callbacks. The fastest way to learn ZUI is to look at [Hello World example](https://github.com/z-shell/zui/blob/main/demos/zui-demo-hello-world) and other [example codes](https://github.com/z-shell/zui/tree/main/demos) like the [timeout example](https://github.com/z-shell/zui/blob/main/demos/zui-demo-timeout). ## Screenshots ![Hello World](/assets/ideal-img/zui-hello-world-fs8.4771707.630.png) **Text-fields demo, showing what color "default" can do:** ![Text Fields](/assets/ideal-img/zui-text-fields-fs8.5557b83.630.png) **List-boxes demo:** ![List-boxes](/assets/ideal-img/zui-list-boxes-fs8.3982af6.630.png) **History demo – fully functional history tool with the incremental search:** ![History demo](/assets/ideal-img/zui-history-fs8.12e9ab3.630.png) **Text Editor demo, written in 30 minutes:** ![Text Editor demo](/assets/ideal-img/zui-edit-fs8.62825a0.630.png) ## Asciinema Videos on the service `Asciinema`, where you can resize the video like a normal web page, and select/copy text. List boxes, text fields: Text editor written in 30 minutes: Configure/Make wrapper: ## Standard Library Standard Library contains functions to: - Initialize and clean up an application, - Load and set the application's configuration, - Create hyperlinks (buttons, anchors, text fields, list boxes), - Handle hyperlinks (e.g. check if the given text is a hyperlink), - Control document regeneration on-the-fly (the DHTML-like way), - Handle modules (e.g. read module's position in the document). ### Calls of Standard Library Below are descriptions of Standard Library functions. Arguments in triangular brackets are mandatory, in square brackets – optional. * * * #### \-zui\_std\_init ```zsh -zui_std_init [app:"application ID"] [app_name:"Application name"] ``` Initializes application. To be called **before emulate**. Optional argument `app:...` will set `ZUI[app]` – hash field needed by any application. Argument `app_name:` does the same for `ZUI[app_name]` (it is a human-readable application name, displayed in header). * * * #### \-zui\_std\_init2 ```zsh -zui_std_init2 ``` Initialization to be called **after emulate**. `emulate` is the command that makes a function an independent program and each ZUI application should use it. * * * #### \-zui\_std\_stalog ```zsh -zui_std_stalog [Text 2] ... ``` Appends a message to the status window logs. Each text argument has a color assigned – see the `log_colors` [zstyle](#zstyles), it controls the colors. * * * #### \-zui\_std\_special\_text ```zsh -zui_std_special_text [output array] reply+=( "{output string}" ) ``` Quote special characters in the text. This allows to use of strings like `That's` in the document – special character `'` will not disturb content. The default output array is `reply`. * * * #### \-zui\_std\_button\_ext ```zsh -zui_std_button_ext