diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-09 22:07:33 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-09 22:07:52 +0300 |
| commit | 3a9888cd564dc45d23a7bdd0dba29a6ff1ef48a1 (patch) | |
| tree | 19a36f33ee5813220f70d7df7805c71c3b21f591 /src/pane | |
| parent | c6d8d8351e67a183efda3aae4dc0986e9b1e9a48 (diff) | |
feat: add ansi pane read format
Diffstat (limited to 'src/pane')
| -rw-r--r-- | src/pane/terminal.rs | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/src/pane/terminal.rs b/src/pane/terminal.rs index fcda5fa6..80f83915 100644 --- a/src/pane/terminal.rs +++ b/src/pane/terminal.rs @@ -133,6 +133,10 @@ impl PaneTerminal { self.ghostty.visible_text() } + pub fn visible_ansi(&self) -> String { + self.ghostty.visible_ansi() + } + pub fn detection_text(&self) -> String { self.ghostty.detection_text() } @@ -141,10 +145,18 @@ impl PaneTerminal { self.ghostty.recent_text(lines) } + pub fn recent_ansi(&self, lines: usize) -> String { + self.ghostty.recent_ansi(lines) + } + pub fn recent_unwrapped_text(&self, lines: usize) -> String { self.ghostty.recent_unwrapped_text(lines) } + pub fn recent_unwrapped_ansi(&self, lines: usize) -> String { + self.ghostty.recent_unwrapped_ansi(lines) + } + pub fn extract_selection(&self, selection: &crate::selection::Selection) -> Option<String> { self.ghostty.extract_selection(selection) } @@ -546,6 +558,14 @@ impl GhosttyPaneTerminal { .unwrap_or_default() } + pub fn visible_ansi(&self) -> String { + self.core + .lock() + .ok() + .and_then(|core| ghostty_visible_ansi(&core).ok()) + .unwrap_or_default() + } + pub fn detection_text(&self) -> String { self.core .lock() @@ -562,6 +582,14 @@ impl GhosttyPaneTerminal { .unwrap_or_default() } + pub fn recent_ansi(&self, lines: usize) -> String { + self.core + .lock() + .ok() + .and_then(|core| ghostty_recent_ansi(&core, lines, false).ok()) + .unwrap_or_default() + } + pub fn recent_unwrapped_text(&self, lines: usize) -> String { self.core .lock() @@ -570,6 +598,14 @@ impl GhosttyPaneTerminal { .unwrap_or_default() } + pub fn recent_unwrapped_ansi(&self, lines: usize) -> String { + self.core + .lock() + .ok() + .and_then(|core| ghostty_recent_ansi(&core, lines, true).ok()) + .unwrap_or_default() + } + pub fn extract_selection(&self, selection: &crate::selection::Selection) -> Option<String> { self.core .lock() @@ -726,6 +762,19 @@ fn ghostty_visible_text(core: &mut GhosttyPaneCore) -> Result<String, crate::gho Ok(lines_to_text(lines)) } +fn ghostty_visible_ansi(core: &GhosttyPaneCore) -> Result<String, crate::ghostty::Error> { + let rows = core.terminal.rows()?; + let cols = core.terminal.cols()?; + if rows == 0 || cols == 0 { + return Ok(String::new()); + } + core.terminal.read_ansi_viewport( + (0, 0), + (cols.saturating_sub(1), u32::from(rows.saturating_sub(1))), + false, + ) +} + fn ghostty_detection_text(core: &GhosttyPaneCore) -> Result<String, crate::ghostty::Error> { let lines = core .terminal @@ -766,6 +815,22 @@ fn ghostty_recent_text_unwrapped( .read_text_screen((0, start), (cols.saturating_sub(1), end), false) } +fn ghostty_recent_ansi( + core: &GhosttyPaneCore, + lines: usize, + unwrap: bool, +) -> Result<String, crate::ghostty::Error> { + let total_rows = core.terminal.total_rows()?; + let cols = core.terminal.cols()?; + if total_rows == 0 || cols == 0 { + return Ok(String::new()); + } + let start = total_rows.saturating_sub(lines) as u32; + let end = (total_rows.saturating_sub(1)) as u32; + core.terminal + .read_ansi_screen((0, start), (cols.saturating_sub(1), end), false, unwrap) +} + fn ghostty_extract_selection( core: &mut GhosttyPaneCore, selection: &crate::selection::Selection, @@ -1457,6 +1522,32 @@ mod tests { } #[test] + fn visible_ansi_preserves_cell_style_sequences() { + let (tx, _rx) = mpsc::channel(4); + let mut terminal = crate::ghostty::Terminal::new(20, 3, 100).unwrap(); + terminal.write(b"\x1b[31;1mred\x1b[0m plain"); + let pane = GhosttyPaneTerminal::new(terminal, tx).unwrap(); + + let ansi = pane.visible_ansi(); + assert!(ansi.contains("red")); + assert!(ansi.contains("plain")); + assert!(ansi.contains("\x1b[")); + } + + #[test] + fn recent_ansi_can_read_styled_scrollback() { + let (tx, _rx) = mpsc::channel(4); + let mut terminal = crate::ghostty::Terminal::new(20, 3, 100).unwrap(); + terminal.write(b"\x1b[34mblue\x1b[0m\r\nline2\r\nline3\r\nline4"); + let pane = GhosttyPaneTerminal::new(terminal, tx).unwrap(); + + let ansi = pane.recent_ansi(4); + assert!(ansi.contains("blue")); + assert!(ansi.contains("line4")); + assert!(ansi.contains("\x1b[")); + } + + #[test] fn resize_reflow_keeps_scrolled_viewport_and_bottom_detection_sane() { let (tx, _rx) = mpsc::channel(4); let mut terminal = crate::ghostty::Terminal::new(12, 4, 10_000).unwrap(); |
