feat(vt): port the stream layer between parser and handlers

Ports ZIG/src/terminal/stream.zig: the Stream feeds bytes through the
UTF-8 decoder and parser and dispatches every CSI/ESC/OSC/DCS/APC and
C0/C1 control to a Handler as a high-level Action (all 95 Zig
variants). Zig's comptime handler interface becomes a Handler trait with
an associated error type, a required vt() and an optional vt_raw() hook.

Performance: next_slice() processes 4 KiB chunks, finds ESC with memchr
and decodes printable runs in bulk (scalar port of ZIG/src/simd's
index_of / utf8_decode_until_control_seq), plus the CSI parameter fast
path.

Terminals must construct the stream with Stream::new_alloc so the OSC
parser accepts allocating commands (colors, OSC 133), as in Zig.

Minor differences: after a stray UTF-8 byte the rest of the chunk
returns to the fast path, and a spurious Zig warning after CSI 2/5 W is
dropped.

Test Plan:
- cargo test -p ghostty-vt (448 passed; 37/38 stream tests ported,
  the skipped one only exercises the C ABI)
This commit is contained in:
ddidderr committed 2026-10-11 21:59:15 +02:00
1 parent e03432241b
commit 47b479dcfb
7 files changed
+2635 -6

No files matched your search

+20 -6
View File
@@ -157,6 +157,20 @@ pub struct Esc {
}
impl Esc {
/// Build an ESC from slices (extra intermediates beyond the maximum are
/// dropped). Used by the stream to synthesize C1 controls.
pub fn new(intermediates: &[u8], final_: u8) -> Self {
let mut e = Esc {
intermediates_buf: [0; MAX_INTERMEDIATE],
intermediates_len: 0,
final_,
};
let n = intermediates.len().min(MAX_INTERMEDIATE);
e.intermediates_buf[..n].copy_from_slice(&intermediates[..n]);
e.intermediates_len = n as u8;
e
}
pub fn intermediates(&self) -> &[u8] {
&self.intermediates_buf[..self.intermediates_len as usize]
}
@@ -252,12 +266,12 @@ pub struct Parser {
intermediates: [u8; MAX_INTERMEDIATE],
intermediates_idx: u8,
/// Param tracking, building
params: [u16; MAX_PARAMS],
params_sep: SepList,
params_idx: u8,
param_acc: u16,
param_acc_idx: u8,
/// Param tracking, building (crate-visible for the stream's CSI fast path)
pub(crate) params: [u16; MAX_PARAMS],
pub(crate) params_sep: SepList,
pub(crate) params_idx: u8,
pub(crate) param_acc: u16,
pub(crate) param_acc_idx: u8,
/// Parser for OSC sequences
pub osc_parser: osc::Parser,