Skip to main content

Term

Struct Term 

Source
pub struct Term { /* private fields */ }
Expand description

Owns the authoritative screen state and applies VT actions to it.

Implementations§

Source§

impl Term

Source

pub fn search(&self, query: &str) -> Vec<Match>

Literal search over the whole buffer ([scrollback ++ screen]), returning every non-overlapping match top-to-bottom in absolute coordinates. Matches cross soft-wrapped rows (one logical line) and skip wide-char spacers. Smart-case: a query with no uppercase matches case-insensitively.

Source

pub fn search_with(&self, query: &str, opts: SearchOptions) -> Vec<Match>

Search with explicit SearchOptions — regex, whole-word, and a case-sensitivity override on top of the literal + smart-case search (#314). Same coordinates, soft-wrap join, spacer skip, and grapheme-mark inclusion (#304) as search.

Source

pub fn search_scroll_to(&mut self, m: &Match)

Scroll the viewport so a match’s start line is visible (placed at the top when it sits in history; the live view when it is already on screen).

Source

pub fn match_spans(&self, m: &Match) -> Vec<SelectionSpan>

Project a match onto the current viewport as inclusive-column spans, one per visible row (off-screen parts dropped) — for the renderer to highlight, like selection_range.

Both column ends are bounded here, and the left half is not an accident of symmetry (#678). A Match’s columns are consumer-supplied by design: Term::set_active_search_match documents taking one the caller assembled outside the engine’s own result set (the past-cap path, #436), and Match’s fields are public. So the usual guarantee — “the engine found it, therefore it is in range” — does not hold on this path, and only the index form (Term::set_active_search_highlight) keeps it by construction.

Left unbounded, a start column past the last one made right >= left fail on the match’s own row and dropped it, so a multi-row match lost its first row while the rest painted — the shape that reads as “the highlight is fine” at a glance, and the reason this was not a visible defect for as long as it existed.

Bounded here rather than at the three storing intakes. #671 is the sibling but not the same shape: it did not touch selection_range, whose left is still unbounded — it clamped selection’s producer (Term::viewport_to_abs), which made the read-site asymmetry unreachable. Search has no producer to clamp, because the coordinate is the consumer’s, which is exactly why the same asymmetry stayed live here. right is already bounded in this expression, so the bound restores a symmetry rather than adding a rule, and the write side is three intakes, one taking a whole Vec. The bound is the row’s extent, which is the grid width — both row producers resize every row to grid.cols() — so a short line does not shrink a match reaching past its text.

The references split 1–1 on the guard, and clamping is the chosen side, not the obvious one. alacritty clamps a column unconditionally (Point::grid_clamp, run on both endpoints before any per-type arithmetic); xterm hides — its decoration renderer carries a commented arm for precisely this input (“exceeded the container width, so hide”), which is justerm’s old outcome. What breaks the tie is that the old outcome was neither: it dropped one row and painted the rest. The cost of clamping is recorded with it in reference-facts.md — on a grid ending in a wide glyph the clamped column can be the pair’s trailing spacer, so a span can cover half a glyph (the #454 class), which hiding would not have produced.

Source

pub fn set_search_highlights(&mut self, matches: Vec<Match>)

Set the search highlights to paint (#108). The consumer owns the Vec<Match> (it drives next/prev); handing it back here lets frame() project the highlights onto the viewport. An empty vec clears them.

Source

pub fn set_active_search_highlight(&mut self, index: Option<usize>)

Designate which member of the held highlight set is the active match (#428) — the one the consumer’s next/prev navigation currently points at. frame() projects it into overlay.active_match (it also stays in overlay.matches; the renderer’s ranking resolves the overlap, #424). None or an out-of-range index projects nothing; the designation resets whenever a new set is passed to set_search_highlights. The index resolves to its span at call time (#436) — both designation APIs converge on one stored representation.

Source

pub fn set_active_search_match(&mut self, m: Option<Match>)

Designate the active match by its absolute span (#436), independent of the held highlight set — the past-cap path: a backend that caps its hand-over (the documented 1000, xterm’s highlightLimit) can still give the current match its active emphasis, exactly as xterm creates the active decoration from the found result outside the capped list. The span projects through the same viewport math as any match (wrap-aware); it need not be a member of the held set, so past the cap the match paints the ACTIVE colour only (no plain highlight underneath). None clears. Same lifecycle as the index form: reset on every set_search_highlights hand-over and on any coordinate-shifting invalidation.

Source§

impl Term

Source

pub fn selection_begin( &mut self, row: usize, col: usize, side: Side, ty: SelectionType, )

Begin a selection of ty at viewport (row, col), side.

Source

pub fn selection_extend(&mut self, row: usize, col: usize, side: Side)

Extend the live selection’s focus to viewport (row, col), side.

Source

pub fn selection_clear(&mut self)

Clear the selection.

Source

pub fn selection_range(&self) -> Vec<SelectionSpan>

The selection projected onto the current viewport: one inclusive-column span per visible row. Rows scrolled off-screen (above or below) are dropped. Empty when nothing is selected. See SelectionSpan.

Source

pub fn selection_text(&self) -> Option<String>

The selected text (for copy), or None when nothing is selected.

Source

pub fn accessible_text(&self) -> String

The whole buffer as one text document (#150): scrollback + screen assembled into logical lines (soft-wrap joined, wide-spacers skipped, trailing blanks trimmed at the logical end) — the accessible-view a screen reader reads as a document, distinct from the viewport row tree (#119). Reuses the selection extraction (extract_lines) over the full range. On the alt screen only the alt buffer is shown — its “scrollback” is the primary buffer’s, not this app’s — mirroring viewport_logical_lines’ alt floor.

Source§

impl Term

Source

pub fn add_marker(&mut self, row: usize) -> MarkerId

Register a decoration marker at viewport row, returning its stable id (#118). The row is resolved to an absolute buffer line (like a selection anchor), so the marker tracks that content through scroll/eviction/reflow.

Source

pub fn command_marks(&self) -> Vec<(MarkerId, usize, MarkerKind)>

The OSC 133 command-boundary marks in buffer order — (id, absolute line, kind) (#158). Plain decoration markers (#118) are excluded. The consumer pairs prompt/command/finished marks and drives navigation/announce policy (#160); core only parses and anchors them.

Instantaneous, deliberately (#742). The lines are undated and move on both of marker_index’s axes, so the contract is re-ask, not rebase — see Engine::command_marks for the derivation, which is the docs.rs surface a consumer actually reads. Two properties keep that honest and are facts about this function rather than preferences: the scope below is constant, so a re-ask always answers and an empty answer can only mean disposal; and the lines are primary even on alt, so they are not in the active buffer’s space.

Changing either — routing this through markers(), or filtering by anything buffer-dependent — invalidates the contract above, not just this line.

Source

pub fn command_lines(&self) -> Vec<CommandLine>

The executed shell commands recovered from OSC-133 marks, in buffer order (#166) — the data behind screen-reader command navigation. Each CommandLine pairs a CommandStart(B) with the following OutputStart(C) to extract the typed command (the prompt before B and the output after C excluded via the captured columns, VSCode extractCommandLine parity), and attaches the trailing CommandFinished(D) exit. A command still being typed (B with no C yet) is not navigable — its text has no bound — so it is omitted until output starts.

The answer is instantaneous, and its lines are document lines into Term::accessible_text on the primary screen — the contract a caller reads is on Engine::command_lines, pinned by tests/command_lines_document.rs (#743, ADR-0029 D6). Note the omission above is why absence here means gone or not yet complete rather than the flat “disposed” that holds for Self::command_marks; both are absences a re-ask resolves, which is what D3.2 needs of them.

Source

pub fn remove_marker(&mut self, id: MarkerId)

Remove a marker by id (#118). Disposing it fires MarkerDisposed so the consumer’s cleanup is one path whether the marker left by eviction or by this explicit call (xterm’s dispose() likewise always fires onDispose). A no-op for an unknown/already-disposed id.

Source

pub fn marker_index(&self) -> MarkerIndex

Every live marker of the active buffer, with the basis that keeps the answer usable (#490). The pull half of the marker surface: a consumer asks once and rebases per frame rather than being handed every marker in every frame.

Ordering is the engine’s own, which is the precedence a consumer joins decorations by (#458/#461) — the same reason marker_positions does not sort.

Source§

impl Term

Source

pub fn viewport_logical_lines(&self) -> Vec<LogicalLine>

The viewport’s logical lines (#113/ADR-0017): each line’s text plus a per-char map to its viewport (row, col). Wide-char spacers are skipped and trailing blanks trimmed (so the text is 1:1 with cells). Empty rows are dropped. The cell-aware assembly the consumer can’t do in frame mode.

Source§

impl Term

Source

pub fn track_point(&mut self, line: usize, col: usize) -> TrackedId

Track absolute buffer (line, col), returning a stable id (#691). The engine keeps the position on the content that is there now, through eviction, region scrolls and reflow, for as long as that line is in the buffer; Self::tracked_point reads it back and answers None once the line has left it.

A line, not the characters on it, and the distinction is load-bearing. Erasing or overwriting the cells under a tracked point leaves it Some — measured. That is deliberate rather than the marker defect one file over (#750): a tracked point is a positional reference whose only consumer asks “which occurrence was I on” and resolves by nearest position, so a point over rewritten content is still a serviceable answer, where a command mark asserts that a command happened there. The sentence above said “that content” and read as the stronger promise.

The coordinate is absolute, not a viewport row, because the positions worth tracking are off-screen ones — a search match in scrollback is the case this exists for, and add_marker’s viewport intake structurally cannot name it.

Out of range is bounded, not rejected, and bounded at the read rather than here: the engine owns no producer for this coordinate — it is the consumer’s, like a Match — which is the second branch of ADR-0026 D2, the same one match_spans takes.

Source

pub fn tracked_point(&self, id: TrackedId) -> Option<(usize, usize)>

Where the point registered as id sits now, or None if it has left the buffer (or the id was never issued / already released).

Bounded here, both ends, per ADR-0026 D2/D3: the line into the range of the buffer the point belongs to, and the column to the grid width rather than the line’s text (D4). The column’s domain is [0, cols] like a marker’s: one past the last cell is a legal bound, which is what a caller pairing this with text extraction needs.

Only the ACTIVE buffer’s points resolve. A point registered on the other screen answers None until that screen is active again — because the number this returns cannot carry its own frame: the primary grid and the alt grid occupy the same absolute indices [scrollback.len(), scrollback.len() + rows), so a primary grid row and an alt row are the same integer naming different content, and no floor or ceiling can separate them. Measured: a point on primary line 4 and the alt screen’s second row both read 4.

Returning the stored number regardless was the first attempt, and it hands a consumer a plausible coordinate for the wrong screen with nothing to detect it by — the public surface has no frame tag. That is this module’s own stated failure mode arriving through the read. The sibling routes the same way for the same reason (markers()), and neither reference can have the problem: xterm’s markers hang off a Buffer, ghostty’s pins off a per-screen PageList, so a cross-screen read is unconstructible there rather than merely wrong.

So None covers three cases a caller does not need to distinguish — the content left the buffer, the id was released or never issued, or the point belongs to the screen that is not up. All three mean do not move anything on account of this point, which is the only question the one caller shape asks.

Source

pub fn untrack_point(&mut self, id: TrackedId)

Release id. A no-op for an unknown or already-released id.

Not optional housekeeping: the engine cannot know when a holder is done with a position, so without this the registry only ever grows.

Source§

impl Term

Source

pub fn new(cols: usize, rows: usize) -> Self

Source

pub fn with_scrollback( cols: usize, rows: usize, scrollback_limit: usize, ) -> Self

Source

pub fn damage(&self) -> TermDamage

What changed since the last reset_damage() — line ranges, each with a changed column span. See ADR-0003.

Source

pub fn reset_damage(&mut self)

Clear accumulated damage. The consumer calls this after applying a frame (the ack); the next damage() reflects only changes since.

Source

pub fn mark_fully_damaged(&mut self)

Mark the whole screen damaged (alt switch / clear / flood, and a consumer reattach that needs a full re-sync — see crate::Engine::mark_fully_damaged).

Source

pub fn scroll_delta(&self) -> Option<ScrollOp>

The first-class scroll recorded since the last reset_damage, if any. Suppressed while scrolled up — a content scroll must not shift the frozen viewport.

The count is capped at the region’s own height (#661). Shifting a region by more than its height moves every source row outside it, so the surplus names nothing a consumer can act on — while it does overflow the wire’s i16 and turn an up-scroll into a down-scroll: measured, a single 32 770-byte feed() of newlines, no slow consumer required. Both references that state a quantity at their own scroll sites clamp it to the same bound (alacritty term/mod.rs:773, ghostty Terminal.zig:2703).

The cap is here, on the read, and not on the accumulator in record_scroll: a region that scrolls far and comes back then still reports its true small net, instead of one walked down from a saturated value.

A second, crate-internal bound backs it up, and it is representational rather than semantic: MAX_ROWS is u16::MAX while the wire field is i16, so a region can legally be taller than any count that field can hold. In that corner the magnitude truncates. What it never does is wrap — a wrapped count arrives with the opposite sign and the consumer shifts the wrong way, which is the whole of #661.

Source

pub fn frame(&self) -> Frame

Build a serializable Frame from the current damage + grid + grapheme pool (#6). Full ships every row; Partial ships the damaged spans. The global side-table is remapped to frame-local indices — the engine pool is append-only and leaky, so a frame carries only the clusters its cells reference, renumbered, with each cell’s extra rewritten to the local id.

Source

pub fn set_word_separators(&mut self, separators: &str)

Number of lines currently held in scrollback history. Replace the word-boundary set used by Word (semantic) selection — the policy half of selection_begin(.., SelectionType::Word), injected per ADR-0017 (core owns the buffer walk, the consumer owns which characters separate words). The default is DEFAULT_WORD_SEPARATORS.

' ' is forced into whatever you pass, and that is not a convenience. A blank cell packs ' ', so the space terminates the walk at the end of a row’s written text and backstops the wide-pair rule: without it, double-clicking next to a wide separator starts the highlight on that separator’s trailing spacer, bisecting the glyph, and the walk then runs to the row’s end through the padding. Enforcing it here rather than in the walk is ghostty’s shape — it prepends its own blank codepoint to every parsed set at the config intake (config/Config.zig, “Always include null as first boundary”), so selectWord never has to.

A consequence worth knowing before you narrow the set: this predicate is the only thing bounding the walk, so a set that omits the separators actually present in the buffer makes one double-click walk the whole soft-wrap run (see #206).

Source

pub fn word_separators(&self) -> &str

The word-boundary set currently in force — what was passed to Term::set_word_separators plus the forced ' ', or DEFAULT_WORD_SEPARATORS if it was never called.

Source

pub fn scrollback_len(&self) -> usize

Source

pub fn synchronized_output(&self) -> bool

Whether the app has an open synchronized-output block (DEC ?2026, #73).

Source

pub fn color_scheme_updates(&self) -> bool

Whether the app enabled color-scheme-update notifications (DEC ?2031, #85).

Source

pub fn grapheme_clustering(&self) -> bool

Whether the app enabled grapheme-cluster mode (DEC ?2027, #295): emoji ZWJ / skin-tone / flag / VS16 sequences are clustered into one cell. OFF (default) is per-char, wcwidth-compat.

Source

pub fn win32_input_mode(&self) -> bool

Whether the app enabled win32-input-mode (DEC ?9001, #86). The engine does not encode the raw key-records itself (a non-goal); a ConPTY consumer reads this to decide whether to emit them.

Source

pub fn report_color_scheme(&mut self, dark: bool)

Queue a color-scheme report (CSI ? 997 ; 1 n dark / ; 2 n light) on the reply channel. The consumer calls this to answer a ColorSchemeQuery event or, when its scheme changes and color_scheme_updates() is set, to send the unsolicited notification. The engine never stores or interprets the scheme (#85).

Source

pub fn report_clipboard( &mut self, target: ClipboardTarget, text: &str, terminator: Terminator, )

Answer an OSC 52 TermEvent::QueryClipboard (#828): base64-encode the consumer’s text into the OSC 52 reply envelope, ST-terminated.

The consumer hands the target back rather than the engine remembering which one was asked about — the same shape as Term::report_palette_color, which takes its index back for the same reason. alacritty is the alternative: its query captures the target and the terminator in a closure the consumer later calls (alacritty_terminal/src/term/mod.rs:1740), which is one more piece of hidden state and one more question (“what if replies interleave?”) bought for nothing the consumer does not already hold.

Answering is optional, and that is the security property. The engine holds no clipboard, so a query it is never asked to answer reveals nothing; a consumer refuses a read simply by not calling this, whatever it does about writes.

The selector round-trips. c / p / s in, the same one out, which is why ClipboardTarget keeps p and s apart: every reference echoes the field the application wrote — xterm the recognised list (misc.c:3384), alacritty the raw byte (…/term/mod.rs:1744), ghostty its three locations (src/Surface.zig:5954) — and it is the one field a client can pair a reply on. The single exception is an empty field, answered naming c, which is what alacritty also sends once vte has defaulted it: the reply says what the engine understood, and there is no selector to echo.

The reply echoes the terminator the query arrived with, like every other reply this crate queues — settled for the whole channel rather than for this sequence, which is where #828 left it (#836).

Source

pub fn report_palette_color( &mut self, index: u8, spec: &str, terminator: Terminator, )

Answer an OSC 4 palette query (#122): wrap the consumer-supplied spec for index in the OSC 4 reply envelope.

The reply echoes the terminator the query arrived with, which the consumer takes off the Query… event and hands back here (#836): the spec says a terminal “uses the same terminator used in a query” (ctlseqs.txt:2020), and the engine cannot choose on the consumer’s behalf because only the parser ever saw which byte arrived.

Source

pub fn report_foreground(&mut self, spec: &str, terminator: Terminator)

Answer an OSC 10 foreground query (#122): wrap the consumer-supplied spec in the OSC 10 reply envelope.

The reply echoes the terminator the query arrived with, which the consumer takes off the Query… event and hands back here (#836): the spec says a terminal “uses the same terminator used in a query” (ctlseqs.txt:2020), and the engine cannot choose on the consumer’s behalf because only the parser ever saw which byte arrived.

Source

pub fn report_background(&mut self, spec: &str, terminator: Terminator)

Answer an OSC 11 background query (#122): wrap the consumer-supplied spec (it knows its palette) in the OSC 11 reply envelope. The engine formats the envelope only — it never knows the colour.

The reply echoes the terminator the query arrived with, which the consumer takes off the Query… event and hands back here (#836): the spec says a terminal “uses the same terminator used in a query” (ctlseqs.txt:2020), and the engine cannot choose on the consumer’s behalf because only the parser ever saw which byte arrived.

Source

pub fn report_cursor_color(&mut self, spec: &str, terminator: Terminator)

Answer an OSC 12 cursor-colour query (#832): the same envelope one slot over, terminated like its siblings. The consumer supplies the spec — it owns the palette, and the engine never learns the colour.

The reply echoes the terminator the query arrived with, which the consumer takes off the Query… event and hands back here (#836): the spec says a terminal “uses the same terminator used in a query” (ctlseqs.txt:2020), and the engine cannot choose on the consumer’s behalf because only the parser ever saw which byte arrived.

Source

pub fn viewport_line(&self, i: usize) -> &[Cell]

The cells of visible row i (0..rows) at the current scroll position. The viewport windows into [history.. ; screen..]: rows above scrollback.len() come from history, the rest from the live screen.

Source

pub fn scroll_up(&mut self, n: usize)

Scroll the viewport up by n lines into history (clamped to the oldest).

Source

pub fn scroll_down(&mut self, n: usize)

Scroll the viewport down by n lines toward the live screen.

Source

pub fn scroll_to_bottom(&mut self)

Jump the viewport back to the live screen (follow the bottom).

Source

pub fn resize(&mut self, cols: usize, rows: usize)

Resize the screen to cols x rows. Rows dropped off the top (on shrink) enter scrollback. Column reflow of soft-wrapped lines is layered on top separately (#7). The whole screen is damaged.

cols is widened to MIN_COLUMNS — a narrower screen cannot hold a width-2 glyph, so it is clamped rather than represented (#547).

Source

pub fn grid(&self) -> &Grid

Source

pub fn cursor(&self) -> &Cursor

Source

pub fn bracketed_paste(&self) -> bool

Whether bracketed-paste mode (DEC ?2004) is enabled. The input encoder (#11) reads this to decide whether to wrap pasted text in markers.

Source

pub fn encode_key(&self, ev: KeyEvent) -> Option<Vec<u8>>

Encode a key event to bytes using the active cursor-key mode (DECCKM) and the kitty keyboard-protocol flags (encode_key consults both).

Source

pub fn encode_mouse(&self, ev: MouseEvent) -> Option<Vec<u8>>

Encode a mouse event using the active tracking mode + encoding. None when reporting is off or the event is filtered by the mode.

Source

pub fn encode_paste(&self, text: &str) -> Vec<u8>

Encode pasted text, wrapping it in bracketed-paste markers when ?2004 is on.

Source

pub fn encode_focus(&self, focused: bool) -> Option<Vec<u8>>

Encode a focus change (CSI I/CSI O), or None when focus reporting (?1004) is off.

Source

pub fn drain_events(&mut self) -> Vec<TermEvent>

Take the consumer events queued since the last drain, emptying the queue.

Source

pub fn drain_replies(&mut self) -> Vec<u8>

Take the reply bytes queued since the last drain (DA/DSR/DECRQM answers), emptying the buffer. The consumer writes them back to the PTY.

Trait Implementations§

Source§

impl Perform for Term

Source§

fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool)

OSC dispatch (#12 event surface): title (0/2), cwd (7). OSC 8 hyperlink is per-cell state, handled in its own slice (#26), not here.

Source§

fn print(&mut self, c: char)

Draw a character to the screen and update states.
Source§

fn execute(&mut self, byte: u8)

Execute a C0 or C1 control function.
Source§

fn csi_dispatch( &mut self, params: &Params, intermediates: &[u8], _ignore: bool, action: char, )

A final character has arrived for a CSI sequence Read more
Source§

fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, byte: u8)

The final character of an escape sequence has arrived. Read more
Source§

fn hook( &mut self, _params: &Params, _intermediates: &[u8], _ignore: bool, _action: char, )

Invoked when a final character arrives in first part of device control string. Read more
Source§

fn put(&mut self, _byte: u8)

Pass bytes as part of a device control string to the handle chosen in hook. C0 controls will also be passed to the handler.
Source§

fn unhook(&mut self)

Called when a device control string is terminated. Read more
Source§

fn terminated(&self) -> bool

Whether the parser should terminate prematurely. Read more

Auto Trait Implementations§

§

impl Freeze for Term

§

impl RefUnwindSafe for Term

§

impl Send for Term

§

impl Sync for Term

§

impl Unpin for Term

§

impl UnsafeUnpin for Term

§

impl UnwindSafe for Term

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.