Figterm is how hook into the user's system to get information about the user's current shell session like:
- What the user has typed
- Environment variables
- Working directory
It is run in each terminal session started by the user, and communicates this information to the mac app, sending update notifications on edit buffer changes, prompts (precmd), and right before a command is executed (preexec)
First, make sure shell integrations are installed. You can use the q_cli to do
this:
q integrations install dotfiles
The run make install to build the binary and move to the right location.
The shell integrations will then launch figterm on each terminal session.
You can verify figterm is running by:
- Running
pstree -p $$and checking, e.g. for afigtermprocess with a childzshprocess. - Running
env | grep FIGand checking theQ_TERMvariable is set to
When you spin up a terminal emulator like iTerm, it launches a shell. If our
shell integrations are properly installed
into a user's dotfiles (.zshrc, .bashrc, etc.) then the shell will source the
pre integration to exec
the figterm binary.
Figterm launches a PTY, or pseudoterminal, (see https://spin0r.wordpress.com/2012/12/28/terminally-confused-part-seven/ for an explanation of PTYs). In short, it forks a child process that execs a shell. The parent process, then, is responsible for acting as a "pseudoterminal", which is roughly a process that a shell reads from and writes to. In figterm, this parent process:
- Acts a pass through layer between shell and terminal emulator
- Creates a representation of the terminal screen state (it's a headless terminal emulator)
- Communicates events with shell context to the macOS app.
Figterm as a Shell & Terminal Intermediary
Without figterm, iTerm and zsh communicate directly: iTerm <-> zsh. iTerm will
forward input from the user to zsh and zsh will send ANSI escape codes to
the terminal iTerm to direct it to move the cursor, change text color, write the
prompt, write command output to the screen, etc.
With figterm, we intercept the shell/terminal emulator communication:
iTerm <-> figterm <-> zsh. The figterm process looks like a shell to iTerm (it
forwards ANSI escape codes from zsh to iTerm), and looks like a terminal to
zsh (it forwards input to zsh).
Figterm as a Headless Terminal
A terminal emulator like iTerm has an internal representation of what is displayed to the terminal screen. This is usually stored as a grid of "cells" that each have attributes like foreground color, background color, character, etc. The terminal processes ANSI escape sequences from the shell to update this representation.
Figterm replicates the processing of these sequences from the shell to create
it's own internal screen representation. We do this with a fork of
Alacritty’s
alacritty_terminal crate.
This lives in crates/alacritty_terminal/.
Our post shell integrations add
hooks that print custom OSC ANSI escape codes that figterm also parses. This
is our mechanism for sending information from the shell to figterm. We send
these codes to figterm to:
- Indicate the start/end of a prompt
- Indicate a command is about to run
- Update context about the shell (env variables, working directory, etc.)
Figterm then uses these internally to annotate screen cells based on whether they are part of a shell prompt, the "edit buffer" that the user has typed, or command output.
Figterm as a Shell Context Provider
Figterm sends 3 types of hooks with shell context to the macOS app:
promptorprecmdevents - sent right before a prompt is displayedpreexecevents - sent right before a command is executededitBufferevents - sent whenever the edit buffer is updated
Figterm computes the current edit buffer from its semantically annotated screen representation on any screen updates.
Each of these events also contains the most recent context of environment variables, working directory, etc.