CHT — Cursor Forward Tabulation (CSI I)
Advance the cursor N tab stops — the parameterized form of pressing Tab.
Byte forms
Every common string-literal form so you can paste-and-search either direction.
\x1b[NI\033[1I\e[1IESC [ N I1b 5b <N> 49Description
Cursor Forward Tabulation. Final byte I (0x49, uppercase i) moves the cursor forward to the next tab stop, repeated N times (default 1). It is the mirror of CBT (CSI Ps Z) and a parameterized form of HT (\t, 0x09). The terminfo cap is cht. Where HT moves a single tab forward, CHT lets you skip ahead by an arbitrary count without emitting N literal \t bytes, which matters in raw mode where \t may be consumed by line discipline.
Spec citation: ECMA-48 §8.3.10 (CHT)
Examples
printf 'a\033[3Ib' # cursor advances 3 tab stops, then writes bimport sys; sys.stdout.write('\x1b[3I')fmt.Print("\x1b[3I")process.stdout.write('\x1b[3I')printf("\x1b[3I");Used in
Real-world tools that emit this sequence — anchors the bytes to commands you've already used.
- vim / neovim `<C-i>` jump-list-forwardvim's `<C-i>` jump-list-forward binding (the inverse of `<C-o>`) is terminologically distinct from Tab but shares the same keycode on most terminals — vim's terminal-mode redraw after `<C-i>` may emit CHT for cursor positioning in the displayed buffer rather than HT. `:set verbose=15` reveals the parameterized form
- less / more horizontal scroll (`<Right>` in `-#` chop mode)less ≥ 633 with `--chop-long-lines` uses `\x1b[<N>I` to scroll the visible window N tab-stops to the right rather than emitting N space-padded redraws — keeps the existing screen content stable and just shifts the viewport
- GNU indent / clang-format tab-aware reformat`indent --tab-size=4` and `clang-format --style='{IndentWidth: 4, UseTab: ForIndentation}'` emit CHT in their terminal-preview output (the `-d`/`--diff` modes) when the diff hunk crosses a custom tab grid set via TBC + HTS — avoids the visual-noise of N literal tab characters in colour-diff output
- ncurses `wtab()` / column-aligned table widgetsncurses' `wtab()` (window-tab-advance) maps to the terminal's `cht` capability — visible in `infocmp xterm-256color | grep cht` (= `\E[%p1%dI`). Used by `nmtui`, `dialog`, `alsamixer` for aligning multi-column field labels without emitting variable-length HT runs
- GNU pr column layout (`pr -t -m -w 80`)GNU `pr -t -m -w 80` (multi-column-merge mode) emits CHT between columns when stdout is a terminal and `LC_ALL` is set such that the tab grid isn't the default 8 — keeps column boundaries crisp without depending on the receiving terminal's HT interpretation
Frequently asked
Short answers to the questions developers actually search for this sequence.
- When should I emit CHT (
\x1b[I) instead of literal\tcharacters? - Three situations. (1) Raw mode without ONLCR / OPOST cooking —
\tmay still pass, but if your TTY discipline is fully raw and counting bytes for a column model, CHT with an explicit count gives you a single deterministic emission instead of N separate HT cells. (2) Skipping multiple stops in one move —\x1b[5Iis one CSI vs. five\tbytes, lower latency over slow serial links and easier to log. (3) Inside DCS / OSC payloads where\twould be interpreted by the parser as data rather than a control. Most TUIs still use\tfor prose output because it's terminfo-portable viaht; CHT (cht) is the parameterized form for cases above. CBT (\x1b[Z, seecsi-cbt) is the symmetric reverse. - Does CHT count tab stops cleared by
\x1b[3g? - No — CHT counts only *currently set* tab stops, so once you've issued TBC clear-all (
\x1b[3g, seecsi-tbc) and not re-set any stops via HTS (\x1bH, seeesc-hts),\x1b[NIis a no-op regardless ofN. The cursor stays put because the count refers to stops the cursor would *land on*, not columns it would *cross*. This is the same family of state-leak bug as\tgoing inert after a clear-all: any code that emits CHT for layout must either re-establish tab stops on entry or query DSR\x1b[6nafter the move to detect that nothing happened. The defensive pattern is the same — pair\x1b[3gwith a fresh HTS layout before yielding control.
Terminal support
- xterm
- yes
- Linux console (fbcon)
- yes
- macOS Terminal.app
- yes
- iTerm2
- yes
- Windows Terminal
- yes
- cmd.exe / ConPTY
- partial
- kitty
- yes
- alacritty
- yes
- WezTerm
- yes
- Ghostty
- yes
- GNOME Terminal
- yes
- Konsole
- yes
- tmux
- yes
- GNU screen
- yes
| xterm | Linux console (fbcon) | macOS Terminal.app | iTerm2 | Windows Terminal | cmd.exe / ConPTY | kitty | alacritty | WezTerm | Ghostty | GNOME Terminal | Konsole | tmux | GNU screen |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| yes | yes | yes | yes | yes | partial | yes | yes | yes | yes | yes | yes | yes | yes |