docs(docs): add iris docs and update shell documentation for iris integration

This commit is contained in:
Prad Nukala committed 2026-10-05 09:15:24 -04:00
1 parent 0692ae332b
commit e0cdcd6159
9 files changed
+134 -92

No files matched your search

+18 -14
View File
@@ -1,9 +1,9 @@
---
title: bash and zsh
description: Secondary shells that share fish's aliases, starship prompt, atuin history, and cd-style helper functions.
description: Secondary shells that share fish's aliases, starship prompt, and cd-style helper functions; zsh also runs under iris like fish.
---
bash and zsh are set up so a shell other than fish feels the same: the same aliases, the same starship prompt, atuin history, ports of the fish helpers that have to change the shell's own directory, and the same picker key bindings.
bash and zsh are set up so a shell other than fish feels the same: the same aliases, the same starship prompt, ports of the fish helpers that have to change the shell's own directory (`zsh/functions/` also ports `czs` and `y`), and the same picker key bindings. zsh matches fish most closely: it runs under [iris](/shell/iris), activates mise and zoxide, and has the <kbd>ctrl-z</kbd> job picker. bash keeps atuin for history.
| Source (repo) | Target |
| --- | --- |
@@ -38,24 +38,30 @@ Interactive shells only (`[[ $- != *i* ]] && return`). In order:
## ~/.zshrc
1. **History.** `HISTFILE=~/.zsh_history`, `HISTSIZE=10000`, `SAVEHIST=10000`, `setopt SHARE_HISTORY HIST_IGNORE_DUPS`.
1. **History.** `HISTFILE=~/.zsh_history`, `HISTSIZE=10000`, `SAVEHIST=10000`, `setopt SHARE_HISTORY HIST_IGNORE_DUPS HIST_IGNORE_SPACE`. `HIST_IGNORE_SPACE` keeps lines that start with a space out of history, as fish does; the <kbd>ctrl-z</kbd> binding's hidden ` __jobs_bind_fg` relies on it.
2. **Completion.** `autoload -Uz compinit && compinit`.
3. The same PATH, Go, Android, and `GITHUB_TOKEN` blocks as `.bashrc`, including `GHQ_ROOT` and `CODE_ROOT`.
3. The same PATH, Go, Android, and `GITHUB_TOKEN` blocks as `.bashrc`, including `GHQ_ROOT` and `CODE_ROOT`, plus `eval "$(mise activate zsh)"` if mise is installed (fish activates mise in `40-tools.fish`; the Go block's `GOROOT` comes from it).
4. Sources `~/.config/shell/aliases.sh`.
5. Sources every `~/.config/zsh/functions/*.zsh`.
6. Sources `~/.config/zsh/keybinds.zsh`.
7. `eval "$(suv init zsh)"` if suvadu is installed.
8. Sources `~/.config/envman/load.sh` if it is non-empty.
9. `eval "$(starship init zsh)"` if starship is installed.
10. `eval "$(tv init zsh)"` if `tv` is installed (<kbd>ctrl-t</kbd> smart autocomplete).
11. **Syntax highlighting.** Sources the first `zsh-syntax-highlighting.zsh` found in `/usr/share/zsh/plugins/zsh-syntax-highlighting/`, `/opt/homebrew/share/zsh-syntax-highlighting/`, or `/usr/local/share/zsh-syntax-highlighting/`. The comment says this must stay last, because it wraps every zle widget defined before it.
12. **atuin.** Sources `~/.atuin/bin/env` and runs `eval "$(atuin init zsh)"`. These lines come after the syntax-highlighting block, so despite the comment, syntax highlighting is not actually last. atuin loads after tv, so it keeps <kbd>ctrl-r</kbd>.
10. Prints quickshell's wallpaper-generated terminal color sequences (`~/.local/state/quickshell/user/generated/terminal/sequences.txt`) if present, as fish does.
11. `eval "$(zoxide init zsh)"` if zoxide is installed (`z`, `zi`), alongside the zoxide fallback in `functions/cd.zsh`.
12. `eval "$(tv init zsh)"` if `tv` is installed (<kbd>ctrl-t</kbd> smart autocomplete).
13. **iris.** If iris is installed, `eval "$(iris init zsh)"`: in an interactive zsh on a tty it `exec`s iris, which starts a second zsh in its pty that runs `.zshrc` again; there the init installs iris's line, cwd, and command hooks. Inside iris (`IRIS_PID` set) it also runs `unsetopt PROMPT_SP`: zsh's partial-line marker pads the line with spaces and a carriage return before each prompt, and iris read that echo as a typed space, so every prompt opened with a stale query and inserted a stray first suggestion on the next key. `IRIS_RESCUE=1 zsh` starts a plain zsh. See [iris](/shell/iris).
14. **Syntax highlighting.** Sources the first `zsh-syntax-highlighting.zsh` found in `/usr/share/zsh/plugins/zsh-syntax-highlighting/`, `/opt/homebrew/share/zsh-syntax-highlighting/`, or `/usr/local/share/zsh-syntax-highlighting/`. It stays last, because it wraps every zle widget defined before it.
atuin is not used in zsh: under iris it fought for <kbd>up</kbd> and <kbd>ctrl-r</kbd>, as in fish.
## zsh key bindings
`keybinds.zsh` ports `fish_user_key_bindings.fish` to zle. Unlike fish, zsh uses the **emacs** keymap: `bindkey -e` pins it so that `$EDITOR=nvim` does not switch zsh into vi mode. `unsetopt flow_control` frees <kbd>ctrl-q</kbd> and <kbd>ctrl-s</kbd> from the terminal.
A generic widget `_zle-run` is registered as `run-<cmd>` for `purple`, `clip-last`, `omp`, `yy`, and `lazygit`. It clears the pending display (`zle -I`), runs the command with the tty on stdin, then `zle reset-prompt`. Widgets run in the shell process, so directory changes made by `gho` and `yy` persist.
A generic widget `_zle-run` is registered as `run-<cmd>` for `purple`, `clip-last`, `omp-session`, `yy`, and `lazygit`. It clears the pending display (`zle -I`), runs the command with the tty on stdin, then `zle reset-prompt`. Widgets run in the shell process, so directory changes made by `gho` and `yy` persist.
Widgets never fire `preexec`/`precmd`, so every widget that runs a program wraps it in `_iris_busy start` / `_iris_busy stop`, which send iris the same `IRIS_CMD_START` / `IRIS_CMD_STOP` messages its own hooks do (a no-op outside iris). Without them iris kept handling keys as if the prompt were idle while tv, yazi, or lazygit owned the screen, swallowing arrows and <kbd>tab</kbd>. See [fish](/shell/fish#key-bindings) for the same helper there.
The tv pickers have their own widgets, because `tv --inline` opens on the line below the prompt and leaves the cursor there on exit. `zle -I` followed by `zle reset-prompt` redraws the prompt where zle last drew it, wiping anything printed below; `zle -I` alone draws a fresh prompt under the output.
@@ -63,7 +69,8 @@ The tv pickers have their own widgets, because `tv --inline` opens on the line b
| --- | --- | --- |
| `tv-git-files` | `tv git-files --inline` | `\033[A`, `reset-prompt` |
| `gho-inline` | `gho` | cwd unchanged: `\033[A`, `reset-prompt`. After a cd: nothing, so the new prompt lands below onefetch |
| `pj-inline`, `pj-down` | `_zle-pj`: `pj` with `PJ_STATE` set to a temp file (`PJ_QUIET=1` for `pj-down`) | `open` (cancelled): `\033[A`, `reset-prompt`. `ran`: nothing, so the task output stays. Empty (no task sources): `reset-prompt` |
| `pj-inline` | `_zle-pj`: `pj` with `PJ_STATE` set to a temp file | `open` (cancelled): `\033[A`, `reset-prompt`. `ran`: nothing, so the task output stays. Empty (no task sources): `reset-prompt` |
| `jobs-fg` | `_zle-jobs`: with one job, that job; with several, `tv jobs --inline` with `TV_JOBS` set to a temp file holding the job table built from `$jobstates`/`$jobtexts` | Cancelled: `\033[A`, `reset-prompt`. Picked: `zle push-input`, then the line ` __jobs_bind_fg <id>` is accepted; `__jobs_bind_fg` erases that echoed line and runs `fg` quietly. Typed text returns at the next prompt |
| Key | Action |
| --- | --- |
@@ -74,7 +81,7 @@ The tv pickers have their own widgets, because `tv --inline` opens on the line b
| <kbd>ctrl-o</kbd> | `gho-inline` (`ghq-repos` cable, cd) |
| <kbd>ctrl-p</kbd> | `pj-inline` (`pj` cable) |
| <kbd>ctrl-t</kbd> | tv smart autocomplete (from `tv init zsh`) |
| <kbd>down</kbd> | `pj-down`: `PJ_QUIET=1 pj` on an empty prompt, otherwise `down-line-or-history` (bound to `^[[B` and `^[OB`) |
| <kbd>ctrl-z</kbd> | `jobs-fg`: `fg` the only background job, or pick one from the `jobs` cable ([television](/tools/television#jobs)); nothing with no jobs. While a job runs, ctrl-z is the tty's suspend, so the key toggles |
| <kbd>ctrl-a</kbd> | `omp-session`: resume or start omp for the current repo ([Scripts](/shell/scripts#omp-session)) |
| <kbd>ctrl-g</kbd> | `lazygit` |
| <kbd>ctrl-q</kbd> | `exit` |
@@ -82,7 +89,7 @@ The tv pickers have their own widgets, because `tv --inline` opens on the line b
<kbd>ctrl-c</kbd> is the tty interrupt character, so `bindkey '^C'` would never fire. The binding is a `TRAPINT` function instead. At the prompt it runs `zle .kill-buffer`, prints the clear sequence, and runs `zle .reset-prompt`. While a command is running, SIGINT keeps its normal behavior (returns `128+SIGINT`).
The fish-only binds <kbd>ctrl-w</kbd> (atuin AI) and <kbd>?</kbd> have no zsh equivalent. <kbd>ctrl-h/j/k/l</kbd> belong to kitty ([kitty](/terminal/kitty)) and are not bound here. The cables are described on the [television](/tools/television) page.
<kbd>ctrl-h/j/k/l</kbd> belong to kitty ([kitty](/terminal/kitty)) and are not bound here. Under iris, <kbd>tab</kbd>, <kbd>shift-tab</kbd>, <kbd>ctrl-r</kbd>, <kbd>up</kbd>, and <kbd>down</kbd> belong to iris; <kbd>up</kbd>/<kbd>down</kbd> keep zsh's defaults outside it. The cables are described on the [television](/tools/television) page.
## bash key bindings
@@ -93,7 +100,6 @@ The fish-only binds <kbd>ctrl-w</kbd> (atuin AI) and <kbd>?</kbd> have no zsh eq
| <kbd>ctrl-o</kbd> | Macro `\C-x1\C-x2\C-x3\C-x0` (below): pick from the `ghq-repos` cable and cd |
| <kbd>ctrl-e</kbd> | `_kb_git_files`: `tv git-files --inline`, then `_kb_up` |
| <kbd>ctrl-p</kbd> | `_kb_pj`: `pj` with `PJ_STATE` set to a temp file; `_kb_up` only when the state is `open` (cancelled) |
| <kbd>down</kbd> | `PJ_QUIET=1 _kb_pj` on an empty prompt, otherwise `history-search-forward` |
| <kbd>ctrl-t</kbd> | `_kb_tv_autocomplete`: runs `tv --autocomplete-prompt … --inline` on the text left of the cursor, `_kb_up`, and inserts the picks there, each `printf %q`-quoted |
`bind -x` runs in the shell process, so a `cd` persists, but readline keeps drawing the prompt it started with. <kbd>ctrl-o</kbd> is therefore a macro:
@@ -103,8 +109,6 @@ The fish-only binds <kbd>ctrl-w</kbd> (atuin AI) and <kbd>?</kbd> have no zsh eq
3. `\C-x3` runs `_kb_gho_show` (`clear`, then `onefetch` in git repos); readline redraws the prompt below the output.
4. `\C-x0` runs `_kb_restore`, which puts the typed text back.
<kbd>down</kbd> (`\e[B` and `\eOB`) is the macro `\C-x4\C-x5`. `bind -x` cannot call readline functions, so `_kb_down` (`\C-x4`) rebinds `\C-x5` to `history-search-forward` when the line is non-empty, or runs `PJ_QUIET=1 _kb_pj` and rebinds `\C-x5` to `redraw-current-line`.
## Shared aliases: aliases.sh
`~/.config/shell/aliases.sh` is rendered from `.chezmoidata/aliases.yaml`. Each YAML `section` becomes a comment, and each item becomes `alias <name>='<cmd>'`. The template then appends shell-specific extras that are not in the YAML: