Skip to content
dots
Esc
↑↓navigate↵open⌘Jpreview
On this page

iris

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 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 execs 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 tab (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, zsh).

The old down → 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 ctrl-p.

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 ctrl-r 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
tab Accept the selected suggestion (replaces the shell’s tab completion)
shift-tab Hide or show the menu for this shell session
ctrl-r Switch between spec and history mode for this shell session (shadows tv init’s history binding)
up / down Move the selection in the menu; on a closed menu, browse iris’s history (navigate-closed = "history", the default)
right Accept ghost text

Every other key in fish’s and zsh’s bindings (ctrl-a, ctrl-e, ctrl-o, ctrl-p, ctrl-z, ctrl-c, …) and fish vi mode’s esc 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.

Was this page helpful?