mirror of
https://github.com/prdlk/dotfiles.git
synced 2026-11-02 19:32:43 +00:00
docs(docs): add iris docs and update shell documentation for iris integration
This commit is contained in:
1 parent
0692ae332b
commit
e0cdcd6159
9 files changed
+134
-92
No files matched your search
@@ -52,6 +52,7 @@ The repo is a [chezmoi](https://chezmoi.io) source directory. It lives at `~/.lo
|
|||||||
- herdr-nvim/
|
- herdr-nvim/
|
||||||
- hunk/
|
- hunk/
|
||||||
- hypr/
|
- hypr/
|
||||||
|
- iris/
|
||||||
- kitty/
|
- kitty/
|
||||||
- lazydocker/
|
- lazydocker/
|
||||||
- lazygit/
|
- lazygit/
|
||||||
|
|||||||
+7
-40
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: atuin
|
title: atuin
|
||||||
description: Shell history search with a daemon, directory-scoped up-arrow, tmux popups, a cyberdream theme, and the atuin AI inline command generator.
|
description: Shell history search for bash with a daemon, directory-scoped up-arrow, tmux popups, and a cyberdream theme.
|
||||||
---
|
---
|
||||||
|
|
||||||
[atuin](https://atuin.sh) replaces shell history search in fish, bash, and zsh. It also provides `atuin ai inline`, a natural-language command generator that the fish config binds to keys.
|
[atuin](https://atuin.sh) replaces shell history search in bash. fish and zsh do not use it: [iris](/shell/iris) wraps both and takes <kbd>up</kbd>, <kbd>down</kbd>, and <kbd>ctrl-r</kbd> itself, so the two fought over the same keys.
|
||||||
|
|
||||||
| Source (repo) | Target | Notes |
|
| Source (repo) | Target | Notes |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
@@ -15,12 +15,9 @@ chezmoi runs in symlink mode, but `private_` files are written as real files, so
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
`atuin` is listed in `.chezmoidata/packages.yaml` for pacman and Homebrew. The shells also expect atuin's standalone installer layout in `~/.atuin/bin`:
|
`atuin` is listed in `.chezmoidata/packages.yaml` for pacman and Homebrew. bash also expects atuin's standalone installer layout in `~/.atuin/bin`: it runs `. "$HOME/.atuin/bin/env"` just before `atuin init`.
|
||||||
|
|
||||||
- fish: `conf.d/atuin.env.fish` sources `~/.atuin/bin/env.fish`.
|
That line does not check that the file exists, so a machine without `~/.atuin/bin` prints a sourcing error at shell start.
|
||||||
- bash and zsh: `. "$HOME/.atuin/bin/env"` just before `atuin init`.
|
|
||||||
|
|
||||||
Neither line checks that the file exists, so a machine without `~/.atuin/bin` prints a sourcing error at shell start.
|
|
||||||
|
|
||||||
### atuin-receipt.json
|
### atuin-receipt.json
|
||||||
|
|
||||||
@@ -40,51 +37,21 @@ The receipt that atuin's [cargo-dist](https://opensource.axo.dev/cargo-dist/) in
|
|||||||
|
|
||||||
| Shell | Where | What |
|
| Shell | Where | What |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| fish | `config.fish`, interactive only | `atuin init fish \| source`, `atuin ai init fish \| source`, and the `?` binding |
|
|
||||||
| fish | `functions/fish_user_key_bindings.fish` | <kbd>ctrl-w</kbd> runs `atuin ai inline` |
|
|
||||||
| bash | end of `~/.bashrc` | `eval "$(atuin init bash)"` |
|
| bash | end of `~/.bashrc` | `eval "$(atuin init bash)"` |
|
||||||
| zsh | end of `~/.zshrc` | `eval "$(atuin init zsh)"` |
|
|
||||||
|
|
||||||
In fish, `40-tools.fish` sources hishtory before atuin, but atuin's init runs later in `config.fish` and takes <kbd>ctrl-r</kbd>. zsh also loads `suv init zsh` (suvadu) if installed.
|
|
||||||
|
|
||||||
### Key bindings
|
### Key bindings
|
||||||
|
|
||||||
| Key | Shells | Action |
|
| Key | Shells | Action |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| <kbd>ctrl-r</kbd> | all | Interactive search (`_atuin_search`) |
|
| <kbd>ctrl-r</kbd> | bash | Interactive search (`_atuin_search`) |
|
||||||
| <kbd>up</kbd> | all | Search opened from the up arrow. `filter_mode_shell_up_key_binding = "directory"` limits it to history from the current directory |
|
| <kbd>up</kbd> | bash | Search opened from the up arrow. `filter_mode_shell_up_key_binding = "directory"` limits it to history from the current directory |
|
||||||
| <kbd>?</kbd> | fish, vi normal mode | On an empty buffer, runs `atuin ai inline --hook` |
|
|
||||||
| <kbd>ctrl-w</kbd> | fish, insert and normal mode | `atuin ai inline`, then repaint |
|
|
||||||
| <kbd>ctrl+shift+p</kbd> | kitty | [`atuin-pick`](/shell/scripts#atuin-pick): directory-filtered search in a split, running the pick in a fresh fish |
|
| <kbd>ctrl+shift+p</kbd> | kitty | [`atuin-pick`](/shell/scripts#atuin-pick): directory-filtered search in a split, running the pick in a fresh fish |
|
||||||
|
|
||||||
Under tmux, atuin's `[tmux]` settings open the search in a tmux popup (80% wide, 60% tall) instead of inline.
|
Under tmux, atuin's `[tmux]` settings open the search in a tmux popup (80% wide, 60% tall) instead of inline.
|
||||||
|
|
||||||
## AI inline
|
## AI inline
|
||||||
|
|
||||||
Commit `a82921c` ("add Atuin AI inline integration and keybinding") added the <kbd>ctrl-w</kbd> bind to `fish_user_key_bindings.fish` and the following to `config.fish`:
|
`atuin ai` is enabled in the config (`[ai] enabled = true`), but no shell loads `atuin ai init` or binds a key to it any more: the fish integration (<kbd>?</kbd> and <kbd>ctrl-w</kbd>) was removed with the move to iris. Run `atuin ai inline` by hand to use it.
|
||||||
|
|
||||||
1. `atuin ai init fish | source`, in the interactive block next to `atuin init fish`.
|
|
||||||
2. A copy of the `_atuin_ai_question_mark` function that `atuin ai init fish` prints, and `bind "?" _atuin_ai_question_mark`.
|
|
||||||
|
|
||||||
The config also sets `[ai] enabled = true`.
|
|
||||||
|
|
||||||
`_atuin_ai_question_mark` works like this:
|
|
||||||
|
|
||||||
- **Buffer empty (or just `?`):** clears the line, prints OSC `133;C` to `/dev/tty` to close the terminal's semantic prompt zone (so shell-integrated terminals do not erase the TUI during a prompt reflow), then runs `atuin ai inline --hook` with stdout and stderr swapped. The TUI draws on the terminal, and the result comes back tagged:
|
|
||||||
|
|
||||||
| Output prefix | Effect |
|
|
||||||
| --- | --- |
|
|
||||||
| `__atuin_ai_print__:` | Print the text, repaint |
|
|
||||||
| `__atuin_ai_cancel__` | Repaint only |
|
|
||||||
| `__atuin_ai_execute__:` | Put the command on the line and execute it |
|
|
||||||
| `__atuin_ai_insert__:` | Put the command on the line for editing |
|
|
||||||
| anything else, non-empty | Put it on the line for editing |
|
|
||||||
|
|
||||||
- **Buffer not empty:** inserts a literal `?` under emacs bindings. Under vi or hybrid bindings, which this config uses, it does nothing.
|
|
||||||
|
|
||||||
`bind "?"` in `config.fish` has no `-M` flag, so it applies to fish's `default` mode, which is vi **normal** mode here. In insert mode <kbd>?</kbd> types a question mark. To ask for a command, press <kbd>Esc</kbd> and then <kbd>?</kbd> on an empty line, or press <kbd>ctrl-w</kbd> from either mode. The <kbd>ctrl-w</kbd> bind calls `atuin ai inline` without `--hook` and replaces fish's default <kbd>ctrl-w</kbd> word deletion.
|
|
||||||
|
|
||||||
bash and zsh do not load `atuin ai init`.
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
|||||||
+18
-14
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: bash and zsh
|
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 |
|
| Source (repo) | Target |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
@@ -38,24 +38,30 @@ Interactive shells only (`[[ $- != *i* ]] && return`). In order:
|
|||||||
|
|
||||||
## ~/.zshrc
|
## ~/.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`.
|
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`.
|
4. Sources `~/.config/shell/aliases.sh`.
|
||||||
5. Sources every `~/.config/zsh/functions/*.zsh`.
|
5. Sources every `~/.config/zsh/functions/*.zsh`.
|
||||||
6. Sources `~/.config/zsh/keybinds.zsh`.
|
6. Sources `~/.config/zsh/keybinds.zsh`.
|
||||||
7. `eval "$(suv init zsh)"` if suvadu is installed.
|
7. `eval "$(suv init zsh)"` if suvadu is installed.
|
||||||
8. Sources `~/.config/envman/load.sh` if it is non-empty.
|
8. Sources `~/.config/envman/load.sh` if it is non-empty.
|
||||||
9. `eval "$(starship init zsh)"` if starship is installed.
|
9. `eval "$(starship init zsh)"` if starship is installed.
|
||||||
10. `eval "$(tv init zsh)"` if `tv` is installed (<kbd>ctrl-t</kbd> smart autocomplete).
|
10. Prints quickshell's wallpaper-generated terminal color sequences (`~/.local/state/quickshell/user/generated/terminal/sequences.txt`) if present, as fish does.
|
||||||
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.
|
11. `eval "$(zoxide init zsh)"` if zoxide is installed (`z`, `zi`), alongside the zoxide fallback in `functions/cd.zsh`.
|
||||||
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>.
|
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
|
## 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.
|
`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.
|
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` |
|
| `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 |
|
| `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 |
|
| 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-o</kbd> | `gho-inline` (`ghq-repos` cable, cd) |
|
||||||
| <kbd>ctrl-p</kbd> | `pj-inline` (`pj` cable) |
|
| <kbd>ctrl-p</kbd> | `pj-inline` (`pj` cable) |
|
||||||
| <kbd>ctrl-t</kbd> | tv smart autocomplete (from `tv init zsh`) |
|
| <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-a</kbd> | `omp-session`: resume or start omp for the current repo ([Scripts](/shell/scripts#omp-session)) |
|
||||||
| <kbd>ctrl-g</kbd> | `lazygit` |
|
| <kbd>ctrl-g</kbd> | `lazygit` |
|
||||||
| <kbd>ctrl-q</kbd> | `exit` |
|
| <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`).
|
<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
|
## 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-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-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>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 |
|
| <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:
|
`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.
|
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.
|
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
|
## 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:
|
`~/.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:
|
||||||
|
|||||||
+26
-24
@@ -3,7 +3,7 @@ title: fish
|
|||||||
description: The primary interactive shell — conf.d layout, environment, vi-mode key bindings, the cyberdream theme, completions, and every autoloaded function.
|
description: The primary interactive shell — conf.d layout, environment, vi-mode key bindings, the cyberdream theme, completions, and every autoloaded function.
|
||||||
---
|
---
|
||||||
|
|
||||||
fish is the primary interactive shell. The config is split into autoloaded files: `conf.d/` for startup snippets, `functions/` for one function per file, `completions/` for completion scripts, and `themes/` for the color theme. `config.fish` runs last and holds the prompt and atuin setup.
|
fish is the primary interactive shell. The config is split into autoloaded files: `conf.d/` for startup snippets, `functions/` for one function per file, `completions/` for completion scripts, and `themes/` for the color theme. `config.fish` runs last and holds the prompt setup and the [iris](/shell/iris) launch.
|
||||||
|
|
||||||
| Source (repo) | Target |
|
| Source (repo) | Target |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
@@ -19,7 +19,7 @@ None of these files are templates. chezmoi runs in symlink mode, so each target
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
`fish` is in `.chezmoidata/packages.yaml` under `packages.arch.pacman` and `packages.darwin.brew`. `.chezmoiscripts/run_onchange_before_10-packages.sh.tmpl` installs it with pacman on Arch and Homebrew on macOS. The tools that fish initializes (starship, zoxide, atuin, fzf, eza, gum, yazi) come from the same manifest. mise, television, hishtory, envman, and the Bun, Go, Android, and Foundry toolchains are not in the manifest; fish initializes each one only if it is present.
|
`fish` is in `.chezmoidata/packages.yaml` under `packages.arch.pacman` and `packages.darwin.brew`. `.chezmoiscripts/run_onchange_before_10-packages.sh.tmpl` installs it with pacman on Arch and Homebrew on macOS. The tools that fish initializes (starship, zoxide, fzf, eza, gum, yazi) come from the same manifest. mise, television, hishtory, envman, iris, and the Bun, Go, Android, and Foundry toolchains are not in the manifest; fish initializes each one only if it is present.
|
||||||
|
|
||||||
### Plugins
|
### Plugins
|
||||||
|
|
||||||
@@ -43,7 +43,6 @@ fish sources `conf.d/*.fish` in name order and then `config.fish`. The numbered
|
|||||||
| `30-ssh-agent.fish` | One ssh-agent shared across shells |
|
| `30-ssh-agent.fish` | One ssh-agent shared across shells |
|
||||||
| `40-tools.fish` | mise, television, starship, zoxide, entire, hishtory, unsloth |
|
| `40-tools.fish` | mise, television, starship, zoxide, entire, hishtory, unsloth |
|
||||||
| `50-terminal-title.fish` | tmux/screen window name from `$PWD` |
|
| `50-terminal-title.fish` | tmux/screen window name from `$PWD` |
|
||||||
| `atuin.env.fish` | Sources `~/.atuin/bin/env.fish` (puts atuin's install dir on `PATH`) |
|
|
||||||
| `inir-env.fish` | `INIR_VENV` and `ILLOGICAL_IMPULSE_VIRTUAL_ENV` → `~/.local/state/quickshell/.venv` |
|
| `inir-env.fish` | `INIR_VENV` and `ILLOGICAL_IMPULSE_VIRTUAL_ENV` → `~/.local/state/quickshell/.venv` |
|
||||||
| `inir-path.fish` | Prepends `~/.local/bin` to `PATH` if missing |
|
| `inir-path.fish` | Prepends `~/.local/bin` to `PATH` if missing |
|
||||||
| `nvm.fish` | nvm.fish plugin events and default-version activation |
|
| `nvm.fish` | nvm.fish plugin events and default-version activation |
|
||||||
@@ -133,7 +132,7 @@ Interactive shells without `SSH_AUTH_SOCK` source `~/.ssh/agent.env.fish` and pr
|
|||||||
5. Source `~/.hishtory/config.fish` if present.
|
5. Source `~/.hishtory/config.fish` if present.
|
||||||
6. Activate the unsloth studio venv (`~/.unsloth/studio/unsloth_studio/bin/activate.fish`) if present.
|
6. Activate the unsloth studio venv (`~/.unsloth/studio/unsloth_studio/bin/activate.fish`) if present.
|
||||||
|
|
||||||
atuin is initialized later, in `config.fish`, and binds <kbd>ctrl-r</kbd> itself.
|
Under [iris](/shell/iris), <kbd>ctrl-r</kbd> never reaches fish: iris takes it to switch its menu between spec and history mode.
|
||||||
|
|
||||||
### 50-terminal-title.fish
|
### 50-terminal-title.fish
|
||||||
|
|
||||||
@@ -169,10 +168,14 @@ The header comment says the file should stay empty, but it holds the following:
|
|||||||
2. **Bottom-pinned prompt.** In interactive shells with `FISH_BOTTOM_PROMPT=1`, `tput cup $LINES 0` moves the cursor to the last row. The comment calls `1` the default, but `10-env.fish` sets `0`, so the prompt stays at the top unless you export `FISH_BOTTOM_PROMPT=1`.
|
2. **Bottom-pinned prompt.** In interactive shells with `FISH_BOTTOM_PROMPT=1`, `tput cup $LINES 0` moves the cursor to the last row. The comment calls `1` the default, but `10-env.fish` sets `0`, so the prompt stays at the top unless you export `FISH_BOTTOM_PROMPT=1`.
|
||||||
3. `fish_add_path ~/.grok/bin` (grok installer block).
|
3. `fish_add_path ~/.grok/bin` (grok installer block).
|
||||||
4. Sources `~/.config/envman/load.fish` if it is non-empty.
|
4. Sources `~/.config/envman/load.fish` if it is non-empty.
|
||||||
5. **atuin.** Interactive shells run `atuin init fish | source` and `atuin ai init fish | source`. See [atuin](/shell/atuin).
|
5. `fish_add_path ~/.cmux/bin` and `~/.cache/.bun/bin`.
|
||||||
6. `fish_add_path ~/.cmux/bin` and `~/.cache/.bun/bin`.
|
6. Sources the first readable `~/.config/herdr/plugins/github/herdr-automatic-rename-*/shell/hook.fish`. This is herdr's automatic pane-rename hook ([herdr](/terminal/herdr)).
|
||||||
7. Sources the first readable `~/.config/herdr/plugins/github/herdr-automatic-rename-*/shell/hook.fish`. This is herdr's automatic pane-rename hook ([herdr](/terminal/herdr)).
|
7. `brew shellenv fish` for `/home/linuxbrew/.linuxbrew`.
|
||||||
8. A copy of the `_atuin_ai_question_mark` function that `atuin ai init fish` emits, followed by `bind "?" _atuin_ai_question_mark`.
|
8. **iris.** If iris is installed: in an interactive fish that iris is not already running (no `IRIS_PID`, no `IRIS_RESCUE`), export `fish_features=no-query-term`, then `iris init fish | source`. The first half of the init `exec`s iris from an interactive fish; iris starts a second fish inside its pty, which runs this whole config again, and there the second half installs iris's cwd and command hooks and turns off fish autosuggestions. `IRIS_RESCUE=1 fish` starts a plain fish. See [iris](/shell/iris).
|
||||||
|
|
||||||
|
`no-query-term` turns off fish's terminal capability query. In kitty that query switches on kitty's keyboard protocol, which sends keys as `CSI u` escape sequences iris cannot parse, so its menu never opened. Feature flags are read at startup, so the export has to happen before `exec iris`; the fish iris starts inherits it.
|
||||||
|
|
||||||
|
atuin is not used in fish: it fought iris for <kbd>up</kbd> and <kbd>ctrl-r</kbd>. bash still initializes it ([atuin](/shell/atuin)).
|
||||||
|
|
||||||
## Key bindings
|
## Key bindings
|
||||||
|
|
||||||
@@ -181,16 +184,14 @@ The header comment says the file should stay empty, but it holds the following:
|
|||||||
| Key | Action |
|
| Key | Action |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| <kbd>ctrl-e</kbd> | `_tv_inline git-files`, then repaint: pick a file in the repo and open it in nvim ([television](/tools/television#git-files)) |
|
| <kbd>ctrl-e</kbd> | `_tv_inline git-files`, then repaint: pick a file in the repo and open it in nvim ([television](/tools/television#git-files)) |
|
||||||
| <kbd>ctrl-f</kbd> | `yy`: yazi, then cd to the directory yazi exited in |
|
| <kbd>ctrl-f</kbd> | `_bind_run yy`: yazi, then cd to the directory yazi exited in |
|
||||||
| <kbd>ctrl-b</kbd> | `purple`, then repaint (an external command; this repo does not provide it) |
|
| <kbd>ctrl-b</kbd> | `_bind_run purple` (an external command; this repo does not provide it) |
|
||||||
| <kbd>ctrl-y</kbd> | `clip-last`: re-run the last command and copy its output |
|
| <kbd>ctrl-y</kbd> | `_bind_run clip-last`: re-run the last command and copy its output |
|
||||||
| <kbd>ctrl-o</kbd> | `_gho_bind`: `gho` (pick a ghq repo or Obsidian vault from the `ghq-repos` cable and cd into it), then repaint |
|
| <kbd>ctrl-o</kbd> | `_gho_bind`: `gho` (pick a ghq repo or Obsidian vault from the `ghq-repos` cable and cd into it), then repaint |
|
||||||
| <kbd>ctrl-p</kbd> | `_pj_bind`: `pj`, the task runner on the `pj` cable ([Scripts](/shell/scripts#pj)), then repaint |
|
| <kbd>ctrl-p</kbd> | `_pj_bind`: `pj`, the task runner on the `pj` cable ([Scripts](/shell/scripts#pj)), then repaint |
|
||||||
| <kbd>ctrl-a</kbd> | `omp-session`, then repaint: resume or start omp for the current repo by branch and session age, opening the `omp-sessions` cable only when nothing qualifies ([Scripts](/shell/scripts#omp-session)) |
|
| <kbd>ctrl-a</kbd> | `_bind_run omp-session`: resume or start omp for the current repo by branch and session age, opening the `omp-sessions` cable only when nothing qualifies ([Scripts](/shell/scripts#omp-session)) |
|
||||||
| <kbd>ctrl-w</kbd> | `atuin ai inline`: natural-language command overlay |
|
|
||||||
| <kbd>ctrl-z</kbd> | `_jobs_bind`: with one background job, `fg` it; with several, pick one from the `jobs` cable ([television](/tools/television#jobs)) and `fg` that; with none, nothing. While a job holds the terminal, ctrl-z is still the tty's suspend, so the key toggles suspend and resume |
|
| <kbd>ctrl-z</kbd> | `_jobs_bind`: with one background job, `fg` it; with several, pick one from the `jobs` cable ([television](/tools/television#jobs)) and `fg` that; with none, nothing. While a job holds the terminal, ctrl-z is still the tty's suspend, so the key toggles suspend and resume |
|
||||||
| <kbd>ctrl-g</kbd> | `lazygit` |
|
| <kbd>ctrl-g</kbd> | `_bind_run lazygit` |
|
||||||
| <kbd>down</kbd> | `_pj_bind_down`: opens the `pj` cable on an idle, empty prompt, otherwise fish's `down-or-search` |
|
|
||||||
| <kbd>ctrl-c</kbd> | Clears the command line, runs `clear` (screen and kitty scrollback), repaints |
|
| <kbd>ctrl-c</kbd> | Clears the command line, runs `clear` (screen and kitty scrollback), repaints |
|
||||||
| <kbd>ctrl-q</kbd> | `exit` |
|
| <kbd>ctrl-q</kbd> | `exit` |
|
||||||
|
|
||||||
@@ -198,12 +199,12 @@ Bindings added by tool init scripts:
|
|||||||
|
|
||||||
| Key | Mode | Action | Source |
|
| Key | Mode | Action | Source |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| <kbd>ctrl-r</kbd> | default and insert | `_atuin_search` | `atuin init fish` |
|
| <kbd>up</kbd> / <kbd>down</kbd> | default and insert | fish presets (`up-or-search`, `down-or-search`); under iris the keys never reach fish | fish |
|
||||||
| <kbd>up</kbd> | default and insert | `_atuin_bind_up` (atuin history, directory filter) | `atuin init fish` |
|
|
||||||
| <kbd>?</kbd> | default (vi normal) | `_atuin_ai_question_mark`: on an empty buffer, opens `atuin ai inline` | `config.fish` |
|
|
||||||
| <kbd>ctrl-t</kbd> | insert | `tv_smart_autocomplete` | `tv init fish` |
|
| <kbd>ctrl-t</kbd> | insert | `tv_smart_autocomplete` | `tv init fish` |
|
||||||
|
|
||||||
`_pj_bind_down` hands the key to `down-or-search` when the buffer is non-empty, the completion pager is open, or history search is active. Otherwise it runs `_pj_bind --quiet` (`PJ_QUIET=1`), so pressing down outside a project does nothing and prints no error.
|
Under iris, <kbd>tab</kbd>, <kbd>shift-tab</kbd>, <kbd>ctrl-r</kbd>, <kbd>up</kbd>, and <kbd>down</kbd> belong to iris and never reach fish, so fish's tab completion, history keys, and the `tv init fish` <kbd>ctrl-r</kbd> history binding are shadowed. Every other key above reaches fish.
|
||||||
|
|
||||||
|
A program started by a key binding never fires `fish_preexec`/`fish_postexec`, so iris would keep handling keys as if the prompt were idle while tv, yazi, or lazygit owned the screen: it swallowed arrows and <kbd>tab</kbd> and could draw its menu over them. Every binding that runs a program therefore tells iris with `_iris_busy start` / `_iris_busy stop`, the same `IRIS_CMD_START` / `IRIS_CMD_STOP` messages iris's own hooks send (a no-op outside iris). `_bind_run CMD` wraps a command that way and repaints; `_tv_inline`, `_gho_bind`, and `_pj_bind` call `_iris_busy` themselves.
|
||||||
|
|
||||||
The pickers run `tv --inline`, which opens on the line below the prompt and leaves the cursor there when it exits. The wrappers put the prompt back before `commandline -f repaint`, writing the cursor moves to `/dev/tty`:
|
The pickers run `tv --inline`, which opens on the line below the prompt and leaves the cursor there when it exits. The wrappers put the prompt back before `commandline -f repaint`, writing the cursor moves to `/dev/tty`:
|
||||||
|
|
||||||
@@ -211,14 +212,14 @@ The pickers run `tv --inline`, which opens on the line below the prompt and leav
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `_tv_inline ARGS` | `tv ARGS --inline` | `\033[A` (cursor up), returns tv's status |
|
| `_tv_inline ARGS` | `tv ARGS --inline` | `\033[A` (cursor up), returns tv's status |
|
||||||
| `_gho_bind` | `gho` | `\033[A` if the cwd did not change; `\n` after a cd, so the prompt lands below onefetch |
|
| `_gho_bind` | `gho` | `\033[A` if the cwd did not change; `\n` after a cd, so the prompt lands below onefetch |
|
||||||
| `_pj_bind [--quiet]` | `pj` with `PJ_STATE` set to a temp file | `\033[A` for `open` (cancelled) or empty (no task sources); `\n` for `ran` (the task printed output) |
|
| `_pj_bind` | `pj` with `PJ_STATE` set to a temp file | `\033[A` for `open` (cancelled) or empty (no task sources); `\n` for `ran` (the task printed output) |
|
||||||
| `_jobs_bind` | `fg` of the only job, or `jobs` with `TV_JOBS` set to a temp file holding the job table | `\033[A` (via `_tv_inline`) when the picker is cancelled |
|
| `_jobs_bind` | `fg` of the only job, or `jobs` with `TV_JOBS` set to a temp file holding the job table | `\033[A` (via `_tv_inline`) when the picker is cancelled |
|
||||||
|
|
||||||
`_jobs_bind` runs `fg` by replacing the command line with ` __jobs_bind_fg <id>` (leading space: kept out of history) and executing it, not from inside the binding. A binding runs while fish's reader still owns the terminal with extended keys on, so a job started with `&` that had never held the terminal got ctrl-z and ctrl-c as escape sequences instead of signals. `__jobs_bind_fg` erases the echoed prompt line (`\033[A\033[2K` on `/dev/tty`) and runs `fg` with its "Send job … to foreground" message sent to `/dev/null`, so resuming leaves nothing on screen. Text already typed is put back at the next prompt by a one-shot `fish_prompt` handler.
|
`_jobs_bind` runs `fg` by replacing the command line with ` __jobs_bind_fg <id>` (leading space: kept out of history) and executing it, not from inside the binding. A binding runs while fish's reader still owns the terminal with extended keys on, so a job started with `&` that had never held the terminal got ctrl-z and ctrl-c as escape sequences instead of signals. `__jobs_bind_fg` erases the echoed prompt line (`\033[A\033[2K` on `/dev/tty`) and runs `fg` with its "Send job … to foreground" message sent to `/dev/null`, so resuming leaves nothing on screen. Text already typed is put back at the next prompt by a one-shot `fish_prompt` handler.
|
||||||
|
|
||||||
`omp-session` (<kbd>ctrl-a</kbd>) does its own `\033[A` when its `omp-sessions` picker is cancelled, so the binding only repaints.
|
`omp-session` (<kbd>ctrl-a</kbd>) does its own `\033[A` when its `omp-sessions` picker is cancelled, so the binding only repaints.
|
||||||
|
|
||||||
zsh and bash bind <kbd>ctrl-o</kbd>, <kbd>ctrl-e</kbd>, <kbd>ctrl-p</kbd>, <kbd>down</kbd>, and <kbd>ctrl-t</kbd> the same way ([bash and zsh](/shell/bash-zsh)). Inside television, <kbd>ctrl-e</kbd> runs the channel's `edit` action and <kbd>ctrl-h</kbd> its `goto_parent_dir` action (`television/config.toml`). The config and cables are on the [television](/tools/television) page.
|
zsh and bash bind <kbd>ctrl-o</kbd>, <kbd>ctrl-e</kbd>, <kbd>ctrl-p</kbd>, and <kbd>ctrl-t</kbd> the same way, and zsh also <kbd>ctrl-z</kbd> ([bash and zsh](/shell/bash-zsh)). Inside television, <kbd>ctrl-e</kbd> runs the channel's `edit` action and <kbd>ctrl-h</kbd> its `goto_parent_dir` action (`television/config.toml`). The config and cables are on the [television](/tools/television) page.
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
Under kitty, <kbd>ctrl-g</kbd> (lazygit overlay), <kbd>ctrl-y</kbd> (yazi split), and <kbd>ctrl-h/j/k/l</kbd> (split navigation) are mapped in `kitty.conf` and never reach fish. The fish binds for those keys only apply in other terminals. See [kitty](/terminal/kitty).
|
Under kitty, <kbd>ctrl-g</kbd> (lazygit overlay), <kbd>ctrl-y</kbd> (yazi split), and <kbd>ctrl-h/j/k/l</kbd> (split navigation) are mapped in `kitty.conf` and never reach fish. The fish binds for those keys only apply in other terminals. See [kitty](/terminal/kitty).
|
||||||
@@ -271,9 +272,10 @@ Every file in `functions/` is autoloaded by name. Several also exist as POSIX sc
|
|||||||
| `_nvm_version_activate` | nvm.fish internal: sets `nvm_current_version` and prepends its `bin` to `PATH` |
|
| `_nvm_version_activate` | nvm.fish internal: sets `nvm_current_version` and prepends its `bin` to `PATH` |
|
||||||
| `_nvm_version_deactivate` | nvm.fish internal: removes a version's `bin` from `PATH` |
|
| `_nvm_version_deactivate` | nvm.fish internal: removes a version's `bin` from `PATH` |
|
||||||
| `_gho_bind` | <kbd>ctrl-o</kbd> handler: `gho`, then cursor fix and repaint (see [Key bindings](#key-bindings)) |
|
| `_gho_bind` | <kbd>ctrl-o</kbd> handler: `gho`, then cursor fix and repaint (see [Key bindings](#key-bindings)) |
|
||||||
| `_pj_bind` | <kbd>ctrl-p</kbd> handler: `pj` with `PJ_STATE`, then cursor fix and repaint. `--quiet` sets `PJ_QUIET=1` |
|
| `_pj_bind` | <kbd>ctrl-p</kbd> handler: `pj` with `PJ_STATE`, then cursor fix and repaint |
|
||||||
| `_jobs_bind` | <kbd>ctrl-z</kbd> handler: `fg` the only job or pick one from the `jobs` cable (see [Key bindings](#key-bindings)) |
|
| `_jobs_bind` | <kbd>ctrl-z</kbd> handler: `fg` the only job or pick one from the `jobs` cable (see [Key bindings](#key-bindings)) |
|
||||||
| `_pj_bind_down` | Down-arrow handler: `_pj_bind --quiet` on an empty idle prompt, else `down-or-search` |
|
| `_bind_run` | Runs `$argv` between `_iris_busy start` and `stop`, then repaints. Used by the <kbd>ctrl-a</kbd>, <kbd>ctrl-b</kbd>, <kbd>ctrl-f</kbd>, <kbd>ctrl-g</kbd>, and <kbd>ctrl-y</kbd> bindings |
|
||||||
|
| `_iris_busy` | `start`/`stop`: writes `IRIS_CMD_START` / `IRIS_CMD_STOP:0` to `$IRIS_FD` so iris stands down while a binding's program runs. No-op outside iris |
|
||||||
| `_tv_inline` | `tv $argv --inline`, then moves the cursor up one line on `/dev/tty` |
|
| `_tv_inline` | `tv $argv --inline`, then moves the cursor up one line on `/dev/tty` |
|
||||||
| `ai` | `gum choose` among `claude`, `opencode`, `codex`, `hermes`, `fractal`. `claude` runs with `--dangerously-skip-permissions` |
|
| `ai` | `gum choose` among `claude`, `opencode`, `codex`, `hermes`, `fractal`. `claude` runs with `--dangerously-skip-permissions` |
|
||||||
| `c` | `clear` with no argument, `cd <dir>` with one |
|
| `c` | `clear` with no argument, `cd <dir>` with one |
|
||||||
@@ -302,7 +304,7 @@ Every file in `functions/` is autoloaded by name. Several also exist as POSIX sc
|
|||||||
| `y` | yazi with `--cwd-file`, then cd to the directory yazi exited in |
|
| `y` | yazi with `--cwd-file`, then cd to the directory yazi exited in |
|
||||||
| `yy` | Same as `y`. Used by the <kbd>ctrl-f</kbd> bind |
|
| `yy` | Same as `y`. Used by the <kbd>ctrl-f</kbd> bind |
|
||||||
|
|
||||||
Functions defined inside `conf.d/` and `config.fish` rather than `functions/`: `q` (20-aliases), `terminal_title` (50-terminal-title), the `pnpmf*` helpers and `__pnpm_pick_pkg` (pnpm-filter), the `_nvm_install`/`_nvm_update`/`_nvm_uninstall` event handlers (nvm), and `starship_transient_prompt_func`, `starship_transient_rprompt_func`, and `_atuin_ai_question_mark` (config.fish).
|
Functions defined inside `conf.d/` and `config.fish` rather than `functions/`: `q` (20-aliases), `terminal_title` (50-terminal-title), the `pnpmf*` helpers and `__pnpm_pick_pkg` (pnpm-filter), the `_nvm_install`/`_nvm_update`/`_nvm_uninstall` event handlers (nvm), and `starship_transient_prompt_func` and `starship_transient_rprompt_func` (config.fish). Under iris, `iris init fish` adds `_iris_fish_prompt`, `_iris_fish_preexec`, and `_iris_fish_postexec`.
|
||||||
|
|
||||||
:::warning[gh-sonr-org-migrate]
|
:::warning[gh-sonr-org-migrate]
|
||||||
`functions/gh-sonr-org-migrate.fish` is a script, not a function definition. Running `gh-sonr-org-migrate` in fish autoloads the file, which runs the whole fish migration flow. Because the file defines no function of that name, fish then falls through to the bash port at `~/.local/bin/gh-sonr-org-migrate` and starts a second run. Cancel the second run at its first `gum` prompt, or call the bash script by its full path.
|
`functions/gh-sonr-org-migrate.fish` is a script, not a function definition. Running `gh-sonr-org-migrate` in fish autoloads the file, which runs the whole fish migration flow. Because the file defines no function of that name, fish then falls through to the bash port at `~/.local/bin/gh-sonr-org-migrate` and starts a second run. Cancel the second run at its first `gum` prompt, or call the bash script by its full path.
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
title: Shell
|
title: Shell
|
||||||
description: fish, bash, and zsh with shared aliases, the starship prompt, atuin history, and the ~/.local/bin scripts.
|
description: fish, bash, and zsh with shared aliases, the starship prompt, iris and atuin, and the ~/.local/bin scripts.
|
||||||
sidebar:
|
sidebar:
|
||||||
label: Overview
|
label: Overview
|
||||||
---
|
---
|
||||||
|
|
||||||
fish is the main interactive shell. bash and zsh are set up to feel the same: they share aliases, the prompt, atuin history, and ports of the cd-style helper functions. General-purpose tools are POSIX or bash scripts in `~/.local/bin`, so each has a single implementation that all three shells call.
|
fish is the main interactive shell. fish and zsh both run under [iris](/shell/iris) for an inline completion menu; bash uses atuin for history. All three share aliases, the prompt, and ports of the cd-style helper functions. General-purpose tools are POSIX or bash scripts in `~/.local/bin`, so each has a single implementation that all three shells call.
|
||||||
|
|
||||||
| Page | Covers |
|
| Page | Covers |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
@@ -14,7 +14,8 @@ fish is the main interactive shell. bash and zsh are set up to feel the same: th
|
|||||||
| [Aliases](/shell/aliases) | Every alias from `.chezmoidata/aliases.yaml` plus the fish-only and shell-specific ones |
|
| [Aliases](/shell/aliases) | Every alias from `.chezmoidata/aliases.yaml` plus the fish-only and shell-specific ones |
|
||||||
| [Scripts](/shell/scripts) | Every general-purpose executable in `~/.local/bin` (`pj`, `ghn`, `gop`, `mtime-sort`, `atuin-pick`, …) |
|
| [Scripts](/shell/scripts) | Every general-purpose executable in `~/.local/bin` (`pj`, `ghn`, `gop`, `mtime-sort`, `atuin-pick`, …) |
|
||||||
| [Starship](/shell/starship) | `~/.config/starship.toml`: format, modules, symbols, cyberdream palette |
|
| [Starship](/shell/starship) | `~/.config/starship.toml`: format, modules, symbols, cyberdream palette |
|
||||||
| [atuin](/shell/atuin) | `~/.config/atuin`: history search settings, AI inline integration, cyberdream theme |
|
| [iris](/shell/iris) | `~/.config/iris/config.toml`: the inline completion menu that wraps fish and zsh, zoxide `cd`, key ownership |
|
||||||
|
| [atuin](/shell/atuin) | `~/.config/atuin`: bash history search settings, cyberdream theme |
|
||||||
|
|
||||||
## Shared across shells
|
## Shared across shells
|
||||||
|
|
||||||
@@ -22,10 +23,11 @@ fish is the main interactive shell. bash and zsh are set up to feel the same: th
|
|||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| Aliases | `conf.d/20-aliases.fish` (hand-maintained) | `~/.config/shell/aliases.sh` (rendered from YAML) | same as bash |
|
| Aliases | `conf.d/20-aliases.fish` (hand-maintained) | `~/.config/shell/aliases.sh` (rendered from YAML) | same as bash |
|
||||||
| Prompt | starship + transient prompt | starship | starship |
|
| Prompt | starship + transient prompt | starship | starship |
|
||||||
| History | atuin (hishtory also sourced if installed) | atuin | atuin, plus suvadu if installed |
|
| History | fish's own history, browsed through iris (hishtory also sourced if installed) | atuin | zsh's own history, browsed through iris, plus suvadu if installed |
|
||||||
| Key bindings | vi mode plus custom binds | atuin defaults only | emacs keymap plus ports of the fish binds |
|
| Completion menu | iris | bash completion | iris |
|
||||||
|
| Key bindings | vi mode plus custom binds | atuin defaults plus picker keys | emacs keymap plus ports of the fish binds |
|
||||||
| cd-style helpers | `functions/*.fish` | `~/.config/bash/functions/*.bash` | `~/.config/zsh/functions/*.zsh` |
|
| cd-style helpers | `functions/*.fish` | `~/.config/bash/functions/*.bash` | `~/.config/zsh/functions/*.zsh` |
|
||||||
| Directory jumping | `cd` wrapper with zoxide fallback, `zoxide init` | `cd` wrapper | `cd` wrapper |
|
| Directory jumping | `cd` wrapper with zoxide fallback, `zoxide init` | `cd` wrapper | `cd` wrapper with zoxide fallback, `zoxide init` |
|
||||||
|
|
||||||
## Theme
|
## Theme
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
---
|
||||||
|
title: iris
|
||||||
|
description: Inline completion menu that wraps fish and zsh in its own pty, with zoxide cd suggestions, and how the shells' bindings coexist with it.
|
||||||
|
---
|
||||||
|
|
||||||
|
[iris](https://github.com/versenilvis/iris) draws an IntelliSense-style completion menu inline in the terminal. It runs the shell inside its own pty and reads keys before the shell does. fish and zsh start it; bash does not.
|
||||||
|
|
||||||
|
| Source (repo) | Target | Notes |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `dot_config/iris/config.toml` | `~/.config/iris/config.toml` | symlink |
|
||||||
|
|
||||||
|
iris is the AUR package `iris-autocomplete`, not in `.chezmoidata/packages.yaml`. State and history live in `~/.local/share/iris` (not in the repo); `state.toml` remembers the last menu mode.
|
||||||
|
|
||||||
|
## Startup
|
||||||
|
|
||||||
|
`config.fish` ends with the iris block and `.zshrc` runs `iris init zsh` just before zsh-syntax-highlighting; both skip it where iris is not installed. The init script has two halves:
|
||||||
|
|
||||||
|
1. In an interactive shell without `IRIS_PID`, it exports `IRIS_ACTIVE_SHELL` and `exec`s `iris`, which starts a second copy of that shell inside its pty. That shell runs the whole config again.
|
||||||
|
2. Inside iris (`IRIS_PID` and `IRIS_FD` set), it installs hooks that report the cwd, the current line (zsh), and command start/stop to iris over `$IRIS_FD`. fish also loses its autosuggestions.
|
||||||
|
|
||||||
|
`IRIS_RESCUE=1 fish` or `IRIS_RESCUE=1 zsh` skips the `exec` and gives a plain shell, for when iris itself misbehaves.
|
||||||
|
|
||||||
|
Two shell settings change for iris:
|
||||||
|
|
||||||
|
| Shell | Setting | Why |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| fish | `fish_features=no-query-term`, exported before `exec iris` | fish's terminal query turns on kitty's keyboard protocol in kitty, which encodes keys as `CSI u` escapes; iris cannot parse them, so the menu never opened in kitty (it did in tmux, which lacks the protocol) |
|
||||||
|
| zsh | `unsetopt PROMPT_SP` inside iris | zsh's partial-line marker pads each prompt with spaces and a carriage return; iris read the echo as a typed space, so prompts opened with a stale query and inserted a stray suggestion (`4channels`) on the next key |
|
||||||
|
|
||||||
|
### Key bindings that run programs
|
||||||
|
|
||||||
|
iris only knows a command is running from the shell's preexec/postexec hooks, and programs started by key bindings never fire those. While tv, yazi, or lazygit ran from a binding, iris kept treating the screen as an idle prompt: it swallowed arrows and <kbd>tab</kbd> (Down never reached a tv picker) and could draw its menu on top. Every such binding now wraps the program in `_iris_busy start` / `_iris_busy stop`, which send `IRIS_CMD_START` and `IRIS_CMD_STOP:0` on `$IRIS_FD`, the same messages the hooks send ([fish](/shell/fish#key-bindings), [zsh](/shell/bash-zsh#zsh-key-bindings)).
|
||||||
|
|
||||||
|
The old <kbd>down</kbd> → `pj` binding is gone from fish, zsh, and bash: it opened the `pj` picker inside iris's pty and the two fought over the screen. `pj` stays on <kbd>ctrl-p</kbd>.
|
||||||
|
|
||||||
|
## Config
|
||||||
|
|
||||||
|
Every key from iris's sample config is listed, so `iris config show` and the file agree. Changes from the defaults:
|
||||||
|
|
||||||
|
| Key | Value | Why |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `core.shell` | `""` | Auto-detect. `IRIS_ACTIVE_SHELL`, exported by `iris init <shell>`, decides which shell starts, so one config serves fish and zsh |
|
||||||
|
| `core.mode` | `"spec"` | Every shell opens with Fig-style completions (subcommands, flags, paths). The default `"last"` restores whatever mode the last shell ended in, so one stray <kbd>ctrl-r</kbd> left every new shell showing history |
|
||||||
|
| `core.atuin-history` | `0` | Shell history only; atuin is not used under iris |
|
||||||
|
| `updater.check-on-startup` | `false` | The package manager owns updates; `iris update` would replace a pacman-owned binary |
|
||||||
|
| `zoxide.extend-cd` | `true` | `cd <partial>` suggests directories from zoxide's database, not just the current directory |
|
||||||
|
|
||||||
|
AI suggestions stay off (`ai.enabled = false`). The Groq provider reads `GROQ_API_KEY` from the environment (`conf.d/secrets.fish`) through `api_key_env`; no key is stored in the file. `debug` stays off because iris then logs every keystroke.
|
||||||
|
|
||||||
|
## Keys
|
||||||
|
|
||||||
|
iris consumes these keys; the shell never sees them while iris runs:
|
||||||
|
|
||||||
|
| Key | iris action |
|
||||||
|
| --- | --- |
|
||||||
|
| <kbd>tab</kbd> | Accept the selected suggestion (replaces the shell's tab completion) |
|
||||||
|
| <kbd>shift-tab</kbd> | Hide or show the menu for this shell session |
|
||||||
|
| <kbd>ctrl-r</kbd> | Switch between spec and history mode for this shell session (shadows `tv init`'s history binding) |
|
||||||
|
| <kbd>up</kbd> / <kbd>down</kbd> | Move the selection in the menu; on a closed menu, browse iris's history (`navigate-closed = "history"`, the default) |
|
||||||
|
| <kbd>right</kbd> | Accept ghost text |
|
||||||
|
|
||||||
|
Every other key in [fish's](/shell/fish#key-bindings) and [zsh's](/shell/bash-zsh#zsh-key-bindings) bindings (<kbd>ctrl-a</kbd>, <kbd>ctrl-e</kbd>, <kbd>ctrl-o</kbd>, <kbd>ctrl-p</kbd>, <kbd>ctrl-z</kbd>, <kbd>ctrl-c</kbd>, …) and fish vi mode's <kbd>esc</kbd> still reach the shell.
|
||||||
|
|
||||||
|
## When the menu appears
|
||||||
|
|
||||||
|
Like Fig, the menu opens by itself about 20 ms after each keystroke on a non-empty line (`ghost-text = 1`: menu plus inline ghost text) and closes on an empty line or while a command runs. It only appears in shells that iris started, so shells opened before iris was set up (for example long-running herdr or tmux panes) show nothing until they are reopened.
|
||||||
+1
-1
@@ -3,5 +3,5 @@ import { defineMeta } from "blume";
|
|||||||
export default defineMeta({
|
export default defineMeta({
|
||||||
title: "Shell",
|
title: "Shell",
|
||||||
icon: "terminal",
|
icon: "terminal",
|
||||||
pages: ["index", "fish", "bash-zsh", "aliases", "scripts", "starship", "atuin"],
|
pages: ["index", "fish", "bash-zsh", "aliases", "scripts", "starship", "iris", "atuin"],
|
||||||
});
|
});
|
||||||
@@ -314,9 +314,9 @@ A task runner that searches every task source in the project and lets you pick o
|
|||||||
|
|
||||||
**Run.** cds into the task's directory and `exec`s the runner. Extra arguments given to `pj` travel to the task through `$PJ_EXTRA` (shell-quoted), with `--` inserted for `npm run`, `mise`, and `devbox`. Tasks named `dev`, `start`, `serve`, `watch`, or namespaced variants like `dev:web` are wrapped in `openlogs` (found on `PATH`, via `mise which`, or in mise's node installs) for a live log tail. Without openlogs they run unwrapped, with a note.
|
**Run.** cds into the task's directory and `exec`s the runner. Extra arguments given to `pj` travel to the task through `$PJ_EXTRA` (shell-quoted), with `--` inserted for `npm run`, `mise`, and `devbox`. Tasks named `dev`, `start`, `serve`, `watch`, or namespaced variants like `dev:web` are wrapped in `openlogs` (found on `PATH`, via `mise which`, or in mise's node installs) for a live log tail. Without openlogs they run unwrapped, with a note.
|
||||||
|
|
||||||
**Env.** `PJ_QUIET=1` suppresses the "no task sources found" error (exit status stays 1). The <kbd>down</kbd> bindings use it. `PJ_STATE=FILE` makes `pj` write `open` to `FILE` before launching the picker, and `pj --run` writes `ran`; the file stays empty when there are no task sources. The key bindings read it to tell no tasks, a cancelled picker, and a task that printed output apart and redraw the prompt to match. `pj` itself does no cursor handling, so typing `pj` behaves like any command.
|
**Env.** `PJ_STATE=FILE` makes `pj` write `open` to `FILE` before launching the picker, and `pj --run` writes `ran`; the file stays empty when there are no task sources. The key bindings read it to tell no tasks, a cancelled picker, and a task that printed output apart and redraw the prompt to match. `pj` itself does no cursor handling, so typing `pj` behaves like any command.
|
||||||
|
|
||||||
**Launched by:** <kbd>ctrl-p</kbd>, and <kbd>down</kbd> on an empty prompt, in fish, zsh, and bash.
|
**Launched by:** <kbd>ctrl-p</kbd> in fish, zsh, and bash.
|
||||||
|
|
||||||
**Needs:** `bash`, `tv`, `mtime-sort`, `jq`, `python3` 3.11+ (for `tomllib`), plus whichever runners the project uses.
|
**Needs:** `bash`, `tv`, `mtime-sort`, `jq`, `python3` 3.11+ (for `tomllib`), plus whichever runners the project uses.
|
||||||
|
|
||||||
|
|||||||
@@ -70,14 +70,14 @@ Every custom cable except `agents` (a fixed list, see [agents](#agents)), `omp-s
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| [`ghq-repos`](#ghq-repos) | `cable/ghq-repos.toml` | <kbd>ctrl-o</kbd> (`gho`) |
|
| [`ghq-repos`](#ghq-repos) | `cable/ghq-repos.toml` | <kbd>ctrl-o</kbd> (`gho`) |
|
||||||
| [`git-files`](#git-files) | `cable/git-files.toml` | <kbd>ctrl-e</kbd> |
|
| [`git-files`](#git-files) | `cable/git-files.toml` | <kbd>ctrl-e</kbd> |
|
||||||
| [`pj`](#pj) | `cable/pj.toml` | <kbd>ctrl-p</kbd>, <kbd>down</kbd> on an empty prompt (`pj`) |
|
| [`pj`](#pj) | `cable/pj.toml` | <kbd>ctrl-p</kbd> (`pj`) |
|
||||||
| [`files`](#files) | `cable/files.toml` | `tv`, <kbd>ctrl-t</kbd> autocomplete |
|
| [`files`](#files) | `cable/files.toml` | `tv`, <kbd>ctrl-t</kbd> autocomplete |
|
||||||
| [`obsidian`](#obsidian) | `cable/obsidian.toml` | `tv obsidian` |
|
| [`obsidian`](#obsidian) | `cable/obsidian.toml` | `tv obsidian` |
|
||||||
| [`agents`](#agents) | `cable/agents.toml` | `tv agents` |
|
| [`agents`](#agents) | `cable/agents.toml` | `tv agents` |
|
||||||
| [`omp-sessions`](#omp-sessions) | `cable/omp-sessions.toml` | `tv omp-sessions`, <kbd>ctrl-a</kbd> (`omp-session`) when it has nothing to resume |
|
| [`omp-sessions`](#omp-sessions) | `cable/omp-sessions.toml` | `tv omp-sessions`, <kbd>ctrl-a</kbd> (`omp-session`) when it has nothing to resume |
|
||||||
| [`jobs`](#jobs) | `cable/jobs.toml` | <kbd>ctrl-z</kbd> in fish (`_jobs_bind`) with two or more background jobs |
|
| [`jobs`](#jobs) | `cable/jobs.toml` | <kbd>ctrl-z</kbd> in fish (`_jobs_bind`) and zsh (`jobs-fg`) with two or more background jobs |
|
||||||
|
|
||||||
The keys are bound in fish (`fish_user_key_bindings.fish`), zsh (`keybinds.zsh`), and bash (`keybinds.bash`); bash has no <kbd>ctrl-a</kbd> binding, and <kbd>ctrl-z</kbd> is fish only. See [fish](/shell/fish#key-bindings) and [bash and zsh](/shell/bash-zsh).
|
The keys are bound in fish (`fish_user_key_bindings.fish`), zsh (`keybinds.zsh`), and bash (`keybinds.bash`); bash has no <kbd>ctrl-a</kbd> or <kbd>ctrl-z</kbd> binding. See [fish](/shell/fish#key-bindings) and [bash and zsh](/shell/bash-zsh). Under [iris](/shell/iris), the bindings tell iris a picker is running (`_iris_busy`), or iris would swallow the arrow keys meant for tv.
|
||||||
|
|
||||||
### ghq-repos
|
### ghq-repos
|
||||||
|
|
||||||
@@ -161,7 +161,7 @@ The preview runs `omp-session --preview`: title, last-write time and cwd, then t
|
|||||||
|
|
||||||
### jobs
|
### jobs
|
||||||
|
|
||||||
The current fish shell's background jobs, stopped or running, newest first. tv runs as a child process and cannot read the shell's job table, so `_jobs_bind` ([fish](/shell/fish#key-bindings)) writes it to a temp file named by `$TV_JOBS` and the source just `cat`s that file; opened any other way the list is empty. Entries are `%<id> <state> <command><TAB><id><TAB><pgid>`; <kbd>enter</kbd> prints the id and the binding runs `fg %<id>`.
|
The current shell's background jobs, stopped or running, newest first. tv runs as a child process and cannot read the shell's job table, so the <kbd>ctrl-z</kbd> binding (`_jobs_bind` in [fish](/shell/fish#key-bindings), `jobs-fg` in [zsh](/shell/bash-zsh#zsh-key-bindings)) writes it to a temp file named by `$TV_JOBS` and the source just `cat`s that file; opened any other way the list is empty. Entries are `%<id> <state> <command><TAB><id><TAB><pgid>`; <kbd>enter</kbd> prints the id and the binding runs `fg %<id>`.
|
||||||
|
|
||||||
The preview finds the job's processes by process group (procps `ps -g` matches sessions, so it filters `ps -e -o pgid,pid`) and shows the first process's cwd and start time, then `ps --forest` with PID, STAT (`T` = stopped), elapsed time, CPU, RSS, and arguments. The preview script avoids `{`/`}` because tv would parse them as templates and leave `{split:…}` unexpanded.
|
The preview finds the job's processes by process group (procps `ps -g` matches sessions, so it filters `ps -e -o pgid,pid`) and shows the first process's cwd and start time, then `ps --forest` with PID, STAT (`T` = stopped), elapsed time, CPU, RSS, and arguments. The preview script avoids `{`/`}` because tv would parse them as templates and leave `{split:…}` unexpanded.
|
||||||
|
|
||||||
@@ -170,4 +170,4 @@ The preview finds the job's processes by process group (procps `ps -g` matches s
|
|||||||
| <kbd>enter</kbd> | Print the job id; `_jobs_bind` runs `fg` on it |
|
| <kbd>enter</kbd> | Print the job id; `_jobs_bind` runs `fg` on it |
|
||||||
| <kbd>ctrl-d</kbd> | `kill` then `reload_source`: SIGTERM to the job's process group, then SIGCONT so a stopped job receives it. Once the group is gone (polled up to 1 s) the row is removed from `$TV_JOBS`, so the reloaded list drops it; a job that ignores SIGTERM stays listed. Fish reports the termination at the next prompt |
|
| <kbd>ctrl-d</kbd> | `kill` then `reload_source`: SIGTERM to the job's process group, then SIGCONT so a stopped job receives it. Once the group is gone (polled up to 1 s) the row is removed from `$TV_JOBS`, so the reloaded list drops it; a job that ignores SIGTERM stays listed. Fish reports the termination at the next prompt |
|
||||||
|
|
||||||
**Needs:** `ps`, fish.
|
**Needs:** `ps`, fish or zsh.
|
||||||
Reference in new issue
Block a user