diff --git a/src/pane/terminal.rs b/src/pane/terminal.rs index 66c23c26..53d8e32d 100644 --- a/src/pane/terminal.rs +++ b/src/pane/terminal.rs @@ -2946,12 +2946,12 @@ fn ghostty_recent_text_for_terminal( terminal: &crate::ghostty::Terminal, lines: usize, ) -> Result { - let Some((start, end, cols)) = ghostty_recent_read_range(terminal, lines)? else { + let Some((start, end, _)) = ghostty_recent_read_range(terminal, lines)? else { return Ok(String::new()); }; let mut rows = Vec::with_capacity(end.saturating_sub(start).saturating_add(1)); for y in start..=end { - rows.push(ghostty_screen_row(terminal, cols, y as u32)?); + rows.push(ghostty_screen_row(terminal, y as u32)?); } trim_trailing_blank_rows(&mut rows); Ok(recent_text_from_rows(&rows, lines)) @@ -3013,10 +3013,7 @@ fn ghostty_recent_read_range( .min(total_rows.saturating_sub(1)); let mut last_content_row = None; for row in (viewport_start..total_rows).rev() { - if !ghostty_screen_row(terminal, cols, row as u32)? - .trim() - .is_empty() - { + if !ghostty_screen_row(terminal, row as u32)?.trim().is_empty() { last_content_row = Some(row); break; } @@ -3056,12 +3053,15 @@ fn ghostty_extract_selection( fn ghostty_screen_row( terminal: &crate::ghostty::Terminal, - cols: u16, y: u32, ) -> Result { let mut line = String::new(); - for x in 0..cols { - let (wide, graphemes) = terminal.screen_cell(x, y)?; + // Resolve the scrollback page once per row rather than once per column. + for crate::ghostty::ScreenTextCell { wide, graphemes } in terminal + .screen_text_rows_range(y as usize, y as usize + 1)? + .into_iter() + .flat_map(|row| row.cells) + { if wide == crate::ghostty::CellWide::SpacerTail { continue; } @@ -5807,6 +5807,20 @@ mod tests { assert_eq!(pane.detection_text(), "日本語テスト ABC 123\n"); } + #[test] + fn recent_rows_preserve_combining_text_and_hide_image_placeholders() { + let (tx, _rx) = mpsc::channel(4); + let mut terminal = crate::ghostty::Terminal::new(40, 3, 1024 * 1024).unwrap(); + terminal.write("old\r\n".repeat(100).as_bytes()); + terminal.write("界 e\u{301} \u{10eeee} tail ".as_bytes()); + let pane = GhosttyPaneTerminal::new(terminal, tx).unwrap(); + assert_eq!(pane.recent_text(1), "界 e\u{301} tail\n"); + let detection = pane.detection_text(); + assert_eq!(detection, "old\nold\n界 e\u{301} tail\n"); + pane.set_scroll_offset_from_bottom(100); + assert_eq!(pane.detection_text(), detection); + } + #[test] fn visible_ansi_preserves_cell_style_sequences() { let (tx, _rx) = mpsc::channel(4);