166 lines
10 KiB
Markdown
166 lines
10 KiB
Markdown
|
|
## Files
|
|
|
|
### The package
|
|
|
|
| File | Role |
|
|
|---|---|
|
|
| `main.py` | Entry point. Imports every command and listener class so Sublime discovers them, and defines `plugin_loaded()`. |
|
|
| `vterm/terminal.py` | `Terminal`: pty process, emulator ownership, the reader/renderer threads, and everything written back to the app. |
|
|
| `vterm/emulator.py` | `VtScreen` and `VtStream`: pyte subclasses adding the terminal features these agents need. |
|
|
| `vterm/render.py` | Screen → view text, colour overlays, caret, folding, trimming. |
|
|
| `vterm/scheme.py` | `SchemeManager`: generates one theme's colour scheme, the scopes the renderer paints with. `get_scheme_manager` caches one instance per theme name, each terminal frozen to the instance it was opened or rethemed with. |
|
|
| `vterm/themes.py` | The built-in palettes (`default`, `homebrew`, `pro`, `slate`, `red-alert`). |
|
|
| `vterm/commands.py` | The Sublime commands: open, keypress, copy/paste, theme picker, clear, cleanup, close. |
|
|
| `vterm/listeners.py` | `VtermCoreEventListener`: focus, IME capture, copy/paste rerouting, the mousemap context. |
|
|
| `vterm/mouse.py` | Wheel and click handling. |
|
|
| `vterm/keys.py` | Key name + modifiers → escape sequence tables. |
|
|
| `vterm/utils.py` | `settings()`, the `responsive` throttle, the `intermission` pacer, `rev_wcwidth`. |
|
|
|
|
### Sublime-facing files at the repo root
|
|
|
|
| File | Role |
|
|
|---|---|
|
|
| `vterm.sublime-settings` | Defaults for the eleven plugin settings. |
|
|
| `vterm.sublime-commands` | Four palette entries: open, open terminal in project folder, clear screen, select theme. |
|
|
| `vterm.sublime-syntax` | An intentionally empty hidden syntax, so terminal text is never tokenized. |
|
|
| `Default.sublime-keymap` | 245 generated bindings. **Do not edit by hand** — regenerate with `tools/gen_keymap.py`. |
|
|
| `Default (OSX).sublime-mousemap` | Two wheel bindings and the middle button. |
|
|
| `dependencies.json` | Package Control libraries. |
|
|
|
|
Generated at runtime, not in the repo: `Packages/User/vterm/vterm.hidden-color-scheme`.
|
|
|
|
## Classes
|
|
|
|
- **`Terminal`** (`terminal.py`) — one per terminal tab. Owns the pty, the `VtScreen` and
|
|
`VtStream`, and the two threads. Instances live in the class-level registry
|
|
`Terminal._terminals`, keyed by **view id**; `Terminal.from_id(view.id())` is how every
|
|
command and listener finds its terminal, and it is a plain dict lookup, so it is the
|
|
cheapest possible gate to put at the top of a hook.
|
|
- **`TerminalPtyProcess`** (`terminal.py`) — `ptyprocess.PtyProcess` that accepts `str`.
|
|
- **`VtScreen`** (`emulator.py`) — `pyte.Screen` plus the alternate screen, scrollback,
|
|
synchronized output, mouse/focus/paste modes, cursor styles, query replies, OSC
|
|
handling. Mutated only by the renderer thread, so it carries no lock.
|
|
- **`VtStream`** (`emulator.py`) — `pyte.Stream` with a rewritten parser FSM. Stock pyte
|
|
drops what these agents rely on: `>`/`<`/`=` prefixed CSI, the DECRQM `$p` intermediate,
|
|
and OSC codes longer than one digit. Its `feed()` regex-matches complete plain CSI and
|
|
dispatches them directly (params memoized) — pyte resumes the parser coroutine once per
|
|
character, which dominated escape-heavy floods. OSC, charsets, private/prefixed CSI and
|
|
`:` subparams still take the coroutine, byte for byte; a read ending mid-CSI is carried
|
|
into the next one. Parity is enforced by a differential test (`run_fast_path_cases`).
|
|
- **`Cursor`** (`emulator.py`) — replaces pyte's so the default attribute set can be a
|
|
`Char` carrying the extra `linefeed` field.
|
|
- **`SchemeManager`** (`scheme.py`) — module singleton `scheme_manager`. Owns the
|
|
generated scheme file and the mapping from colour combination to scope name.
|
|
- **`VtermRenderCommand`** (`render.py`) — the per-frame logic. Sublime rebuilds every
|
|
`TextCommand` instance whenever any plugin loads, so the view's render state
|
|
(`RenderState`: `colored_lines`, `row_cache`, `key_counter`, the scrollback chunk
|
|
bookkeeping `archived_chunks`, `open_rows`, `open_chunk`, `trimmed`) lives on
|
|
`terminal.render_state`, bound as `self.state` at the top of each `run()`.
|
|
|
|
### Commands
|
|
|
|
| Command | Class | Reached from |
|
|
|---|---|---|
|
|
| `vterm_open` | `VtermOpenCommand` | palette, user keybindings |
|
|
| `vterm_open_terminal_project_folder` | `VtermOpenTerminalProjectFolderCommand` | palette |
|
|
| `vterm_initialize_view` | `VtermInitializeViewCommand` | internal |
|
|
| `vterm_keypress` | `VtermKeypressCommand` | 244 keymap bindings |
|
|
| `vterm_render` | `VtermRenderCommand` | renderer thread |
|
|
| `vterm_show_cursor` | `VtermShowCursorCommand` | internal |
|
|
| `vterm_copy` / `vterm_paste` / `vterm_paste_text` | `commands.py` | `on_text_command`, IME capture |
|
|
| `vterm_paste_selection` | `commands.py` | mousemap, middle button |
|
|
| `vterm_select_theme` | `VtermSelectThemeCommand` | palette |
|
|
| `vterm_clear_screen` | `VtermClearScreenCommand` | palette, `super+shift+k` |
|
|
| `vterm_cleanup` | `VtermCleanupCommand` | renderer, on process exit |
|
|
| `vterm_close` | `VtermCloseCommand` | nothing — available for a user keybinding |
|
|
| `vterm_clear_undo_stack` / `vterm_noop` | `commands.py` | internal |
|
|
| `vterm_scroll` / `vterm_mouse_press` / `vterm_mouse_release` | `mouse.py` | mousemap |
|
|
|
|
One application-wide `EventListener`: `VtermCoreEventListener` (`listeners.py`).
|
|
|
|
|
|
---
|
|
|
|
## Terminal concepts, briefly
|
|
|
|
For anyone who has not worked with terminal protocols before. The ones vterm implements
|
|
are marked ✅.
|
|
|
|
**Control bytes** — single characters below 0x20 with an effect rather than a glyph: CR
|
|
(`\r`) return to column 0, LF (`\n`) next line, BS backspace, HT tab, BEL bell.
|
|
|
|
**Escape sequence** — `ESC` (0x1B) followed by more bytes. Everything below is one.
|
|
|
|
- **CSI** ✅ — Control Sequence Introducer, `ESC [`. The workhorse: `ESC[2J` clear screen,
|
|
`ESC[10;5H` move the cursor. Ends with a *final byte* that names the operation;
|
|
numbers in between are its parameters.
|
|
- **SGR** ✅ — Select Graphic Rendition, `CSI … m`. Sets colour and style: `ESC[1m` bold,
|
|
`ESC[31m` red foreground, `ESC[38;5;208m` a 256-palette colour, `ESC[38;2;r;g;bm`
|
|
truecolor, `ESC[0m` reset. This is what TUIs emit most.
|
|
- **OSC** ✅ — Operating System Command, `ESC ]`, ending at BEL or ST. Carries strings
|
|
rather than numbers: `OSC 0/2` set the window title, `OSC 10/11` query the default
|
|
foreground/background, `OSC 52` reads or writes the system clipboard.
|
|
- **DCS / APC** — Device Control String (`ESC P`) and Application Program Command
|
|
(`ESC _`). Long payloads: sixel images, kitty graphics. vterm parses their framing and
|
|
discards the payload, so an image never leaks into the text.
|
|
- **SS3** ✅ — `ESC O`, a single shifted character. Cursor keys take this form in
|
|
application mode; F1-F4 use it too.
|
|
- **ST** — String Terminator, `ESC \`, which ends OSC/DCS/APC payloads (BEL also ends OSC).
|
|
|
|
**Modes** are switches. DEC *private* modes are set with `CSI ? n h` (DECSET) and cleared
|
|
with `CSI ? n l` (DECRST); **DECRQM** ✅ (`CSI ? n $p`) asks whether one is on, which is
|
|
how an app discovers what the terminal supports.
|
|
|
|
- **1049** ✅ alternate screen — a second, scrollback-less buffer for full-screen apps, so
|
|
leaving vim restores your shell exactly as it was.
|
|
- **2026** ✅ synchronized output — "I am mid-frame, do not draw yet"; what makes a
|
|
fullscreen UI update without tearing.
|
|
- **2004** ✅ bracketed paste — pasted text is wrapped in `ESC[200~`/`ESC[201~` so an app
|
|
can tell it apart from typing and not auto-indent it.
|
|
- **1000 / 1002 / 1003** ✅ mouse reporting — clicks / clicks with drag / all motion.
|
|
- **1006** ✅ SGR mouse encoding — the modern wire form, `ESC[<button;col;rowM`, without
|
|
the 223-column limit of the original.
|
|
- **1004** ✅ focus reporting — `ESC[I` on focus, `ESC[O` on blur.
|
|
- **DECCKM** ✅ — application cursor keys: arrows switch from `CSI A` to `SS3 A`.
|
|
- **DECAWM** ✅ — autowrap at the right margin.
|
|
- **DECSCNM** ✅ — reverse video for the whole screen.
|
|
- **DECSCUSR** ✅ (`CSI n SP q`) — caret shape: block, underline or bar, blinking or not.
|
|
- **DECSTBM** ✅ (`CSI t;b r`) — set the scrolling region to a band of rows.
|
|
- **DECKPAM** — application keypad mode, the numeric keypad's equivalent of DECCKM.
|
|
vterm ignores it.
|
|
- **LNM** ✅ — line feed/new line mode; decides whether Enter sends CR or CR LF.
|
|
|
|
**Queries** — the app asks, the terminal answers *on the input stream*, as if typed:
|
|
|
|
- **DA1 / DA2** ✅ (`CSI c`, `CSI > c`) — Device Attributes: what kind of terminal are you,
|
|
and what version. Claude Code waits on DA1 with no timeout, so failing to answer hangs it.
|
|
- **DSR / CPR** ✅ (`CSI n`, `CSI 6n`) — Device Status Report / Cursor Position Report:
|
|
"are you ok" and "where is the cursor". Private `DSR 996` reports whether the palette is
|
|
light or dark.
|
|
- **XTVERSION** ✅ (`CSI > q`) — the terminal's name and version. Naming ourselves is what
|
|
lets apps enable 2026 on a generic `TERM`.
|
|
- **Kitty keyboard protocol** — a richer key encoding with release events. vterm answers
|
|
the probe (`CSI ? u`) with zero flags, so apps fall back to something else — for pi that
|
|
is modifyOtherKeys, which it then enables unprompted.
|
|
- **modifyOtherKeys** ✅ (`CSI > 4 ; n m`) — xterm's pre-kitty answer to the same problem:
|
|
keys whose modified form has no legacy encoding are sent as `CSI 27 ; <mod> ; <code> ~`.
|
|
While an app has it on, vterm uses that form for modified enter, tab, backspace and space —
|
|
Shift+Enter is `CSI 27;2;13~`, since a bare `ESC CR` is indistinguishable from Alt+Enter.
|
|
Ctrl+letters keep their control bytes, so Ctrl+C stays `\x03`.
|
|
|
|
**Other terms**
|
|
|
|
- **pty** — pseudo-terminal: a kernel device pair. The child thinks its stdin/stdout is a
|
|
terminal; we hold the master end and read what it writes.
|
|
- **TIOCSWINSZ / SIGWINCH** ✅ — telling the pty its new size delivers SIGWINCH to the
|
|
child, which is how a resize reaches the app.
|
|
- **wcwidth** ✅ — how many columns a character occupies: 1 for ASCII, 2 for CJK and most
|
|
emoji, 0 for combining marks. Terminals are grids, so this has to agree with the font
|
|
or the caret drifts.
|
|
- **IME** — Input Method Editor: the OS layer that turns keystrokes into composed text
|
|
(pinyin → 中文, dead keys → accents). It commits text without a matching key event,
|
|
which is why `on_modified` exists.
|
|
- **TERM / COLORTERM** ✅ — environment variables describing the terminal's capabilities.
|
|
Children get `TERM=xterm-256color` and `COLORTERM=truecolor`.
|