Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,39 @@ reads that marker.

## [Unreleased]

### Changed

- The terminal emulator is vt100 0.18.1, up from 0.16.2 (#608), which brings
the screen a test sees closer to xterm's in ways a snapshot can show. A
change of height moves rows as xterm does: a shorter screen drops the rows
below the cursor, then pushes rows off the top into history, and a taller
one pulls the newest back onto the grid. 0.16 cut rows off the bottom, so
after a shrink the line the cursor was on could vanish. A VS16
emoji-presentation sequence (`⚠️`, `❤️`) is two columns wide, as
`unicode-width` and ratatui lay it out; it was one, which shifted the rest
of the row against the application's own layout. HVP (`CSI f`) and HPA
(`` CSI ` ``) move the cursor, which they silently did not; ED 3
(`CSI 3 J`) empties the terminal's history, and termlens keeps the rows it
had already retained, as it does across RIS; IL and DL with the cursor
outside the scroll region do nothing, where they used to edit rows outside
it. None of these is listed by `unsupported()` any more. The MSRV stays
1.85.

### Fixed

- Leaving the alternate screen no longer copies the whole retained history
a second time (#576). The alternate screen's grid has no history of its
own, and its empty one was read as the main screen's history emptied, so
every trip through a full-screen program repeated every scrolled-off row
in `scrollback_text()`, `full_text()` and the styled history.
- Narrowing the terminal through a wide character and then erasing that row
no longer panics the emulator (#577); vt100 0.18 keeps the row consistent.
- REP (`CSI n b`) repeats the last character drawn (#578). The
`xterm-256color` terminfo termlens sets advertises it, and ncurses uses it
for runs of one character outside a UTF-8 locale, so a horizontal line or
a row of `=` drew as a single character. Under insert mode the repeated
characters still overwrite rather than insert, as `docs/LIMITATIONS.md`
says.
- `termlens` no longer panics with exit code 101 (#594). A closed or full
stderr (`2>&1 | true`, `2>/dev/full`) made every diagnostic and the
`inspect` trailer panic; the write is now checked, and a trailer that cannot
Expand Down
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ authors = ["Vyncint Ng <vyncint@icloud.com>"]
[workspace.dependencies]

portable-pty = "0.9"
vt100 = "0.16"
vt100 = "0.18"
thiserror = "2"
unicode-width = "0.2"
# Needle matching folds both sides to NFC. Terminals draw NFC and NFD
Expand Down
8 changes: 8 additions & 0 deletions crates/termlens/src/emu/seq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,11 @@ pub(crate) enum SeqEvent {
/// scrolled before this byte, because the backend's history is about
/// to be rebuilt empty (#391).
HardReset,
/// `ED 3` (`CSI 3 J`) completed: erase the saved lines. vt100 empties
/// the active grid's history for it, so this is RIS's case without the
/// reset: the emulator saves the rows that scrolled before this byte,
/// and the history it reads next starts over.
ScrollbackErased,
/// The application asked the terminal a question.
Query(Query),
/// An inline graphics payload completed. Carried out of the tracker
Expand Down Expand Up @@ -1212,6 +1217,9 @@ impl SeqTracker {
b'Z' if self.csi_param_count <= 1 => {
return SeqEvent::Tabs(TabOp::Back(count));
}
// ED 3, erase the saved lines. vt100 reads the first
// parameter alone, and so does this.
b'J' if ps == 3 => return SeqEvent::ScrollbackErased,
_ => {}
}
}
Expand Down
27 changes: 18 additions & 9 deletions crates/termlens/src/emu/shadow.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! Recovering the three SGR attributes `vt100` drops.
//!
//! `vt100` 0.16's `Attrs` is `{ fgcolor, bgcolor, mode: u8 }` and its SGR
//! dispatch handles only `0 1 2 3 4 7 22 23 24 27` plus the colour params.
//! `vt100`'s `Attrs` (0.16 through 0.18) is `{ fgcolor, bgcolor, mode: u8 }`
//! and its SGR dispatch handles only `0 1 2 3 4 7 22 23 24 27` plus the
//! colour params.
//! `5`/`6` (blink), `8` (conceal) and `9` (strikethrough), and their resets
//! `25`/`28`/`29`, never reach a cell — so three distinct renderings collapse
//! into one value, and a test asserting that a password field is masked
Expand Down Expand Up @@ -94,13 +95,18 @@ pub(super) struct AttrShadow {
}

impl AttrShadow {
/// `scrollback` is zero unless the terminal retains styled history:
/// text-only history needs the shadow for the visible grid alone, while
/// styled history reads the shadow's scrolled rows in lockstep with the
/// primary's (#146).
/// `scrollback` is the primary's. Styled history reads the shadow's
/// scrolled rows in lockstep with the primary's (#146), and even without
/// it the shadow's grid needs the same history: a resize that makes the
/// screen taller pulls rows back out of history onto the grid, and the
/// shadow must get the same rows back or the two grids diverge.
pub(super) fn new(rows: u16, cols: u16, scrollback: usize) -> Self {
Self {
parser: ::vt100::Parser::new(rows, cols, scrollback),
parser: ::vt100::Parser::new(
super::vt100::nz(rows),
super::vt100::nz(cols),
scrollback,
),
state: State::Ground,
pending: Vec::new(),
}
Expand All @@ -113,7 +119,9 @@ impl AttrShadow {
}

pub(super) fn set_size(&mut self, rows: u16, cols: u16) {
self.parser.screen_mut().set_size(rows, cols);
self.parser
.screen_mut()
.set_size(super::vt100::nz(rows), super::vt100::nz(cols));
}

/// The shadow cell at `(row, col)`, whose bold/italic/underline flags are
Expand Down Expand Up @@ -462,7 +470,8 @@ mod tests {
&b"\x1b]0;title\x1b[31mA\x1b[5mB\x1b[0mC"[..],
&b"\x1b[1;2\x1b[31mA"[..],
] {
let mut primary = ::vt100::Parser::new(4, 20, 0);
let mut primary =
::vt100::Parser::new(super::super::vt100::nz(4), super::super::vt100::nz(20), 0);
let mut shadow = AttrShadow::new(4, 20, 0);
primary.process(payload);
shadow.feed(payload);
Expand Down
9 changes: 7 additions & 2 deletions crates/termlens/src/emu/unhandled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,12 @@ mod tests {
use super::*;

fn fed(bytes: &[u8]) -> vt100::Parser<Unhandled> {
let mut parser = vt100::Parser::new_with_callbacks(4, 20, 0, Unhandled::default());
let mut parser = vt100::Parser::new_with_callbacks(
super::super::vt100::nz(4),
super::super::vt100::nz(20),
0,
Unhandled::default(),
);
parser.process(bytes);
parser
}
Expand Down Expand Up @@ -374,7 +379,7 @@ mod tests {
);
assert_eq!(
parser.screen().size(),
(4, 20),
(super::super::vt100::nz(4), super::super::vt100::nz(20)),
"the request is recorded, not honoured"
);
}
Expand Down
Loading
Loading