aboutsummaryrefslogtreecommitdiff
path: root/src/pane.rs
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-09-19 14:47:18 +0300
committerGitHub <noreply@github.com>2026-09-19 14:47:18 +0300
commitcc7c696cd0772e466da403eb4e3f00f4e8d8ae1e (patch)
treed8325f7f2fb1ac53613ae56e8215042e6d497dd9 /src/pane.rs
parent3f2a6e743f67bc947cfa06be25df106d00b9ee11 (diff)
feat: add configurable pane screen and scrollback clearing (#4383)
Diffstat (limited to 'src/pane.rs')
-rw-r--r--src/pane.rs64
1 files changed, 64 insertions, 0 deletions
diff --git a/src/pane.rs b/src/pane.rs
index 29cbad2a..3e1c52a4 100644
--- a/src/pane.rs
+++ b/src/pane.rs
@@ -3081,6 +3081,20 @@ impl PaneRuntime {
self.compression.wake();
}
+ pub fn clear_screen(&self) -> Result<(), String> {
+ let guard = match self.content_write_lock.lock() {
+ Ok(guard) => guard,
+ Err(poisoned) => poisoned.into_inner(),
+ };
+ self.content_seq.fetch_add(1, Ordering::AcqRel);
+ let result = self.terminal.clear_screen();
+ self.content_seq.fetch_add(1, Ordering::Release);
+ drop(guard);
+ self.compression.wake();
+ mark_detection_content_changed(&self.detection_content_seq);
+ result
+ }
+
/// Reset scroll to live view (offset = 0).
pub fn scroll_reset(&self) {
self.terminal.scroll_reset();
@@ -3669,6 +3683,56 @@ mod tests {
use super::*;
#[tokio::test]
+ async fn clear_pane_preserves_wrapped_input_and_unfinished_vt_sequence() {
+ let (runtime, mut rx) = PaneRuntime::test_with_channel_and_scrollback_bytes(
+ 10,
+ 5,
+ 100_000,
+ b"old\r\nold\r\nold\r\nold\r\nold\r\n\x1b[32m$ abcdefghijklmnop\x1b[1A\x1b[4G\x1b[",
+ 4,
+ );
+ let before = runtime.content_seq();
+ runtime.scroll_up(1);
+ runtime.clear_screen().unwrap();
+ let snapshot = runtime.collect_dirty_patch_snapshot(10, 5).unwrap();
+ assert!(snapshot.content_revision > before);
+ assert!(!matches!(snapshot.patch, TerminalDirtyPatchOutcome::Clean));
+ let metrics = runtime.scroll_metrics().unwrap();
+ assert_eq!(metrics.max_offset_from_bottom, 0);
+ assert_eq!(metrics.offset_from_bottom, 0);
+ let text = runtime.recent_unwrapped_text_snapshot(100).text;
+ assert!(text.contains("$ abcdefghijklmnop"), "{text:?}");
+ assert!(!text.contains("old"), "{text:?}");
+ runtime.test_process_pty_bytes(b"5 q");
+ assert!(!runtime.visible_text().contains("5 q"));
+ assert!(rx.try_recv().is_err(), "clear must not send child input");
+ }
+
+ #[tokio::test]
+ async fn clear_pane_preserves_alternate_screen_and_primary_history() {
+ let runtime = PaneRuntime::test_with_scrollback_bytes(
+ 20,
+ 4,
+ 100_000,
+ b"one\r\ntwo\r\nthree\r\nfour\r\nfive\x1b[?1049halt app",
+ );
+ let before = runtime.visible_text();
+ runtime.clear_screen().unwrap();
+ assert_eq!(runtime.visible_text(), before);
+ runtime.test_process_pty_bytes(b"\x1b[?1049l");
+ assert!(runtime
+ .recent_unwrapped_text_snapshot(100)
+ .text
+ .contains("one"));
+ runtime.clear_screen().unwrap();
+ assert!(!runtime
+ .recent_unwrapped_text_snapshot(100)
+ .text
+ .contains("one"));
+ assert!(runtime.visible_text().contains("five"));
+ }
+
+ #[tokio::test]
async fn dirty_patch_snapshot_keeps_clean_metadata_and_terminal_fallback() {
let (runtime, _rx) = PaneRuntime::test_with_channel(20, 4);
runtime