Ports ZIG/src/terminal/Terminal.zig - printing with grapheme clustering and Unicode widths, wraparound, insert mode, charsets, cursor movement, scroll regions and left/right margins, erase/insert/delete, tabs, modes, alternate screens, save/restore cursor, DECALN, full reset, palette, mouse/focus/paste flags, kitty keyboard, titles/pwd, semantic prompts, hyperlinks, protected mode, synchronized output and resize. stream_readonly.rs implements ghostty_vt::stream::Handler, applying all state-changing actions to a Terminal and ignoring queries; it is the base for termio's side-effecting handler. StringMap uses the onig crate for Oniguruma-compatible regexes (needed by Ghostty's URL regex). Not yet wired: kitty graphics (hooks and dirty flags are in place where Zig calls into it, storage is being ported separately) and the tmux control-mode viewer. Test Plan: - cargo test -p ghostty-terminal (849 passed, 5 ignored; all 372 Terminal and 33 stream_readonly tests ported; ignored ones are blocked on the PageList parity pass)
673 lines
19 KiB
Rust
673 lines
19 KiB
Rust
use super::*;
|
|
use crate::page::{ContentTag, SemanticContent, SemanticPrompt as RowPrompt};
|
|
use ghostty_vt::{
|
|
ansi::ProtectedMode,
|
|
color::Rgb,
|
|
csi::{EraseDisplay, EraseLine, TabClear},
|
|
osc::semantic_prompt::{Action as PA, Command},
|
|
sgr::{Attribute as A, Underline},
|
|
};
|
|
|
|
fn pr(t: &mut Terminal, s: &str) {
|
|
for c in s.chars() {
|
|
t.print(u32::from(c)).unwrap();
|
|
}
|
|
}
|
|
|
|
fn red() -> Rgb {
|
|
Rgb {
|
|
r: 0xFF,
|
|
g: 0,
|
|
b: 0,
|
|
}
|
|
}
|
|
|
|
fn assert_bg_red(t: &Terminal, xs: std::ops::Range<u16>, y: u32) {
|
|
for x in xs {
|
|
let c = cell_at(t, pt_active(x, y));
|
|
assert_eq!(c.content_tag(), ContentTag::BgColorRgb);
|
|
assert_eq!(c.color_rgb(), red());
|
|
}
|
|
}
|
|
|
|
fn abc_def_ghi(t: &mut Terminal) {
|
|
pr(t, "ABC");
|
|
t.carriage_return();
|
|
t.linefeed().unwrap();
|
|
pr(t, "DEF");
|
|
t.carriage_return();
|
|
t.linefeed().unwrap();
|
|
pr(t, "GHI");
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_resets_pending_wrap() {
|
|
let mut t = init(5, 5);
|
|
pr(&mut t, "ABCDE");
|
|
assert!(t.screen().cursor.pending_wrap);
|
|
t.erase_line(EraseLine::Right, false);
|
|
assert!(!t.screen().cursor.pending_wrap);
|
|
t.print(u32::from('B')).unwrap();
|
|
assert_eq!("ABCDB", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_resets_wrap() {
|
|
let mut t = init(5, 5);
|
|
pr(&mut t, "ABCDE123");
|
|
assert!(row_at(&t, pt_active(0, 0)).wrap());
|
|
t.set_cursor_pos(1, 1);
|
|
t.erase_line(EraseLine::Right, false);
|
|
assert!(!row_at(&t, pt_active(0, 0)).wrap());
|
|
t.print(u32::from('X')).unwrap();
|
|
assert_eq!("X\n123", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_right_preserves_background_sgr() {
|
|
let mut t = init(5, 5);
|
|
pr(&mut t, "ABCDE");
|
|
t.set_cursor_pos(1, 2);
|
|
t.set_attribute(A::DirectColorBg(red())).unwrap();
|
|
t.erase_line(EraseLine::Right, false);
|
|
assert_eq!("A", plain(&t));
|
|
assert_bg_red(&t, 1..5, 0);
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_right_wide_character() {
|
|
let mut t = init(10, 5);
|
|
pr(&mut t, "AB橋DE");
|
|
t.set_cursor_pos(1, 4);
|
|
t.clear_dirty();
|
|
t.erase_line(EraseLine::Right, false);
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert_eq!("AB", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_right_protected_attributes_respected_with_iso() {
|
|
let mut t = init(5, 5);
|
|
t.set_protected_mode(ProtectedMode::Iso);
|
|
pr(&mut t, "ABC");
|
|
t.set_cursor_pos(1, 1);
|
|
t.clear_dirty();
|
|
t.erase_line(EraseLine::Right, false);
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert_eq!("ABC", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_right_protected_attributes_ignored_with_dec_most_recent() {
|
|
let mut t = init(5, 5);
|
|
t.set_protected_mode(ProtectedMode::Iso);
|
|
pr(&mut t, "ABC");
|
|
t.set_protected_mode(ProtectedMode::Dec);
|
|
t.set_protected_mode(ProtectedMode::Off);
|
|
t.set_cursor_pos(1, 2);
|
|
t.clear_dirty();
|
|
t.erase_line(EraseLine::Right, false);
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert_eq!("A", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_right_protected_attributes_ignored_with_dec_set() {
|
|
let mut t = init(5, 5);
|
|
t.set_protected_mode(ProtectedMode::Dec);
|
|
pr(&mut t, "ABC");
|
|
t.set_cursor_pos(1, 2);
|
|
t.clear_dirty();
|
|
t.erase_line(EraseLine::Right, false);
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert_eq!("A", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_right_protected_requested() {
|
|
let mut t = init(10, 5);
|
|
pr(&mut t, "12345678");
|
|
let y = usize::from(t.screen().cursor.y);
|
|
t.set_cursor_pos(y + 1, 6);
|
|
t.set_protected_mode(ProtectedMode::Dec);
|
|
t.print(u32::from('X')).unwrap();
|
|
let y = usize::from(t.screen().cursor.y);
|
|
t.set_cursor_pos(y + 1, 4);
|
|
t.clear_dirty();
|
|
t.erase_line(EraseLine::Right, true);
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert_eq!("123 X", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_simple_erase_left() {
|
|
let mut t = init(5, 5);
|
|
pr(&mut t, "ABCDE");
|
|
t.set_cursor_pos(1, 3);
|
|
t.clear_dirty();
|
|
t.erase_line(EraseLine::Left, false);
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert_eq!(" DE", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_left_resets_wrap() {
|
|
let mut t = init(5, 5);
|
|
pr(&mut t, "ABCDE");
|
|
assert!(t.screen().cursor.pending_wrap);
|
|
t.clear_dirty();
|
|
t.erase_line(EraseLine::Left, false);
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert!(!t.screen().cursor.pending_wrap);
|
|
t.print(u32::from('B')).unwrap();
|
|
assert_eq!(" B", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_left_preserves_background_sgr() {
|
|
let mut t = init(5, 5);
|
|
pr(&mut t, "ABCDE");
|
|
t.set_cursor_pos(1, 2);
|
|
t.set_attribute(A::DirectColorBg(red())).unwrap();
|
|
t.erase_line(EraseLine::Left, false);
|
|
assert_eq!(" CDE", plain(&t));
|
|
assert_bg_red(&t, 0..2, 0);
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_left_wide_character() {
|
|
let mut t = init(10, 5);
|
|
pr(&mut t, "AB橋DE");
|
|
t.set_cursor_pos(1, 3);
|
|
t.clear_dirty();
|
|
t.erase_line(EraseLine::Left, false);
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert_eq!(" DE", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_left_protected_attributes_respected_with_iso() {
|
|
let mut t = init(5, 5);
|
|
t.set_protected_mode(ProtectedMode::Iso);
|
|
pr(&mut t, "ABC");
|
|
t.set_cursor_pos(1, 1);
|
|
t.clear_dirty();
|
|
t.erase_line(EraseLine::Left, false);
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert_eq!("ABC", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_left_protected_attributes_ignored_with_dec_most_recent() {
|
|
let mut t = init(5, 5);
|
|
t.set_protected_mode(ProtectedMode::Iso);
|
|
pr(&mut t, "ABC");
|
|
t.set_protected_mode(ProtectedMode::Dec);
|
|
t.set_protected_mode(ProtectedMode::Off);
|
|
t.set_cursor_pos(1, 2);
|
|
t.clear_dirty();
|
|
t.erase_line(EraseLine::Left, false);
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert_eq!(" C", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_left_protected_attributes_ignored_with_dec_set() {
|
|
let mut t = init(5, 5);
|
|
t.set_protected_mode(ProtectedMode::Dec);
|
|
pr(&mut t, "ABC");
|
|
t.set_cursor_pos(1, 2);
|
|
t.clear_dirty();
|
|
t.erase_line(EraseLine::Left, false);
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert_eq!(" C", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_left_protected_requested() {
|
|
let mut t = init(10, 5);
|
|
pr(&mut t, "123456789");
|
|
let y = usize::from(t.screen().cursor.y);
|
|
t.set_cursor_pos(y + 1, 6);
|
|
t.set_protected_mode(ProtectedMode::Dec);
|
|
t.print(u32::from('X')).unwrap();
|
|
let y = usize::from(t.screen().cursor.y);
|
|
t.set_cursor_pos(y + 1, 8);
|
|
t.clear_dirty();
|
|
t.erase_line(EraseLine::Left, true);
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert_eq!(" X 9", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_complete_preserves_background_sgr() {
|
|
let mut t = init(5, 5);
|
|
pr(&mut t, "ABCDE");
|
|
t.set_cursor_pos(1, 2);
|
|
t.set_attribute(A::DirectColorBg(red())).unwrap();
|
|
t.erase_line(EraseLine::Complete, false);
|
|
assert_eq!("", plain(&t));
|
|
assert_bg_red(&t, 0..5, 0);
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_complete_protected_attributes_respected_with_iso() {
|
|
let mut t = init(5, 5);
|
|
t.set_protected_mode(ProtectedMode::Iso);
|
|
pr(&mut t, "ABC");
|
|
t.set_cursor_pos(1, 1);
|
|
t.clear_dirty();
|
|
t.erase_line(EraseLine::Complete, false);
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert_eq!("ABC", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_complete_protected_attributes_ignored_with_dec_most_recent() {
|
|
let mut t = init(5, 5);
|
|
t.set_protected_mode(ProtectedMode::Iso);
|
|
pr(&mut t, "ABC");
|
|
t.set_protected_mode(ProtectedMode::Dec);
|
|
t.set_protected_mode(ProtectedMode::Off);
|
|
t.set_cursor_pos(1, 2);
|
|
t.clear_dirty();
|
|
t.erase_line(EraseLine::Complete, false);
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert_eq!("", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_complete_protected_attributes_ignored_with_dec_set() {
|
|
let mut t = init(5, 5);
|
|
t.set_protected_mode(ProtectedMode::Dec);
|
|
pr(&mut t, "ABC");
|
|
t.set_cursor_pos(1, 2);
|
|
t.clear_dirty();
|
|
t.erase_line(EraseLine::Complete, false);
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert_eq!("", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_line_complete_protected_requested() {
|
|
let mut t = init(10, 5);
|
|
pr(&mut t, "123456789");
|
|
let y = usize::from(t.screen().cursor.y);
|
|
t.set_cursor_pos(y + 1, 6);
|
|
t.set_protected_mode(ProtectedMode::Dec);
|
|
t.print(u32::from('X')).unwrap();
|
|
let y = usize::from(t.screen().cursor.y);
|
|
t.set_cursor_pos(y + 1, 8);
|
|
t.clear_dirty();
|
|
t.erase_line(EraseLine::Complete, true);
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert_eq!(" X", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_tab_clear_single() {
|
|
let mut t = init(30, 5);
|
|
t.horizontal_tab();
|
|
t.tab_clear(TabClear::Current);
|
|
assert!(!t.is_dirty(pt_active(0, 0)));
|
|
t.set_cursor_pos(1, 1);
|
|
t.horizontal_tab();
|
|
assert_eq!(16, t.screen().cursor.x);
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_tab_clear_all() {
|
|
let mut t = init(30, 5);
|
|
t.tab_clear(TabClear::All);
|
|
assert!(!t.is_dirty(pt_active(0, 0)));
|
|
t.set_cursor_pos(1, 1);
|
|
t.horizontal_tab();
|
|
assert_eq!(29, t.screen().cursor.x);
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_print_repeat_simple() {
|
|
let mut t = init(5, 5);
|
|
t.print_string("A").unwrap();
|
|
t.print_repeat(1).unwrap();
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert_eq!("AA", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_print_repeat_wrap() {
|
|
let mut t = init(5, 5);
|
|
t.print_string(" A").unwrap();
|
|
t.print_repeat(1).unwrap();
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert_eq!(" A\nA", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_print_repeat_no_previous_character() {
|
|
let mut t = init(5, 5);
|
|
t.print_repeat(1).unwrap();
|
|
assert!(!t.is_dirty(pt_active(0, 0)));
|
|
assert_eq!("", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_print_attributes() {
|
|
let mut t = init(5, 5);
|
|
let rgb = |r, g, b| Rgb { r, g, b };
|
|
|
|
{
|
|
t.set_attribute(A::DirectColorFg(rgb(1, 2, 3))).unwrap();
|
|
assert_eq!("0;38:2::1:2:3", t.print_attributes());
|
|
t.set_attribute(A::Unset).unwrap();
|
|
}
|
|
{
|
|
t.set_attribute(A::Bold).unwrap();
|
|
t.set_attribute(A::DirectColorBg(rgb(1, 2, 3))).unwrap();
|
|
assert_eq!("0;1;48:2::1:2:3", t.print_attributes());
|
|
t.set_attribute(A::Unset).unwrap();
|
|
}
|
|
{
|
|
t.set_attribute(A::Bold).unwrap();
|
|
t.set_attribute(A::Faint).unwrap();
|
|
t.set_attribute(A::Italic).unwrap();
|
|
t.set_attribute(A::Underline(Underline::Single)).unwrap();
|
|
t.set_attribute(A::Blink).unwrap();
|
|
t.set_attribute(A::Inverse).unwrap();
|
|
t.set_attribute(A::Invisible).unwrap();
|
|
t.set_attribute(A::Strikethrough).unwrap();
|
|
t.set_attribute(A::DirectColorFg(rgb(100, 200, 255)))
|
|
.unwrap();
|
|
t.set_attribute(A::DirectColorBg(rgb(101, 102, 103)))
|
|
.unwrap();
|
|
assert_eq!(
|
|
"0;1;2;3;4;5;7;8;9;38:2::100:200:255;48:2::101:102:103",
|
|
t.print_attributes()
|
|
);
|
|
t.set_attribute(A::Unset).unwrap();
|
|
}
|
|
{
|
|
t.set_attribute(A::Underline(Underline::Single)).unwrap();
|
|
assert_eq!("0;4", t.print_attributes());
|
|
t.set_attribute(A::Unset).unwrap();
|
|
}
|
|
assert_eq!("0", t.print_attributes());
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_simple_erase_below() {
|
|
let mut t = init(5, 5);
|
|
abc_def_ghi(&mut t);
|
|
t.set_cursor_pos(2, 2);
|
|
t.clear_dirty();
|
|
t.erase_display(EraseDisplay::Below, false);
|
|
assert!(!t.is_dirty(pt_active(0, 0)));
|
|
assert!(t.is_dirty(pt_active(0, 1)));
|
|
assert!(t.is_dirty(pt_active(0, 2)));
|
|
assert_eq!("ABC\nD", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_erase_below_preserves_sgr_bg() {
|
|
let mut t = init(5, 5);
|
|
abc_def_ghi(&mut t);
|
|
t.set_cursor_pos(2, 2);
|
|
t.set_attribute(A::DirectColorBg(red())).unwrap();
|
|
t.erase_display(EraseDisplay::Below, false);
|
|
assert_eq!("ABC\nD", plain(&t));
|
|
assert_bg_red(&t, 1..5, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_below_split_multi_cell() {
|
|
let mut t = init(5, 5);
|
|
t.print_string("AB橋C").unwrap();
|
|
t.carriage_return();
|
|
t.linefeed().unwrap();
|
|
t.print_string("DE橋F").unwrap();
|
|
t.carriage_return();
|
|
t.linefeed().unwrap();
|
|
t.print_string("GH橋I").unwrap();
|
|
t.set_cursor_pos(2, 4);
|
|
t.erase_display(EraseDisplay::Below, false);
|
|
assert_eq!("AB橋C\nDE", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_below_protected_attributes_respected_with_iso() {
|
|
let mut t = init(5, 5);
|
|
t.set_protected_mode(ProtectedMode::Iso);
|
|
abc_def_ghi(&mut t);
|
|
t.set_cursor_pos(2, 2);
|
|
t.erase_display(EraseDisplay::Below, false);
|
|
assert_eq!("ABC\nDEF\nGHI", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_below_protected_attributes_ignored_with_dec_most_recent() {
|
|
let mut t = init(5, 5);
|
|
t.set_protected_mode(ProtectedMode::Iso);
|
|
abc_def_ghi(&mut t);
|
|
t.set_protected_mode(ProtectedMode::Dec);
|
|
t.set_protected_mode(ProtectedMode::Off);
|
|
t.set_cursor_pos(2, 2);
|
|
t.erase_display(EraseDisplay::Below, false);
|
|
assert_eq!("ABC\nD", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_below_protected_attributes_ignored_with_dec_set() {
|
|
let mut t = init(5, 5);
|
|
t.set_protected_mode(ProtectedMode::Dec);
|
|
abc_def_ghi(&mut t);
|
|
t.set_cursor_pos(2, 2);
|
|
t.erase_display(EraseDisplay::Below, false);
|
|
assert_eq!("ABC\nD", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_below_protected_attributes_respected_with_force() {
|
|
let mut t = init(5, 5);
|
|
t.set_protected_mode(ProtectedMode::Dec);
|
|
abc_def_ghi(&mut t);
|
|
t.set_cursor_pos(2, 2);
|
|
t.erase_display(EraseDisplay::Below, true);
|
|
assert_eq!("ABC\nDEF\nGHI", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_simple_erase_above() {
|
|
let mut t = init(5, 5);
|
|
abc_def_ghi(&mut t);
|
|
t.set_cursor_pos(2, 2);
|
|
t.clear_dirty();
|
|
t.erase_display(EraseDisplay::Above, false);
|
|
assert!(t.is_dirty(pt_active(0, 0)));
|
|
assert!(t.is_dirty(pt_active(0, 1)));
|
|
assert!(!t.is_dirty(pt_active(0, 2)));
|
|
assert_eq!("\n F\nGHI", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_erase_above_preserves_sgr_bg() {
|
|
let mut t = init(5, 5);
|
|
abc_def_ghi(&mut t);
|
|
t.set_cursor_pos(2, 2);
|
|
t.set_attribute(A::DirectColorBg(red())).unwrap();
|
|
t.erase_display(EraseDisplay::Above, false);
|
|
assert_eq!("\n F\nGHI", plain(&t));
|
|
assert_bg_red(&t, 0..2, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_above_split_multi_cell() {
|
|
let mut t = init(5, 5);
|
|
t.print_string("AB橋C").unwrap();
|
|
t.carriage_return();
|
|
t.linefeed().unwrap();
|
|
t.print_string("DE橋F").unwrap();
|
|
t.carriage_return();
|
|
t.linefeed().unwrap();
|
|
t.print_string("GH橋I").unwrap();
|
|
t.set_cursor_pos(2, 3);
|
|
t.erase_display(EraseDisplay::Above, false);
|
|
assert_eq!("\n F\nGH橋I", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_above_protected_attributes_respected_with_iso() {
|
|
let mut t = init(5, 5);
|
|
t.set_protected_mode(ProtectedMode::Iso);
|
|
abc_def_ghi(&mut t);
|
|
t.set_cursor_pos(2, 2);
|
|
t.erase_display(EraseDisplay::Above, false);
|
|
assert_eq!("ABC\nDEF\nGHI", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_above_protected_attributes_ignored_with_dec_most_recent() {
|
|
let mut t = init(5, 5);
|
|
t.set_protected_mode(ProtectedMode::Iso);
|
|
abc_def_ghi(&mut t);
|
|
t.set_protected_mode(ProtectedMode::Dec);
|
|
t.set_protected_mode(ProtectedMode::Off);
|
|
t.set_cursor_pos(2, 2);
|
|
t.erase_display(EraseDisplay::Above, false);
|
|
assert_eq!("\n F\nGHI", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_above_protected_attributes_ignored_with_dec_set() {
|
|
let mut t = init(5, 5);
|
|
t.set_protected_mode(ProtectedMode::Dec);
|
|
abc_def_ghi(&mut t);
|
|
t.set_cursor_pos(2, 2);
|
|
t.erase_display(EraseDisplay::Above, false);
|
|
assert_eq!("\n F\nGHI", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_above_protected_attributes_respected_with_force() {
|
|
let mut t = init(5, 5);
|
|
t.set_protected_mode(ProtectedMode::Dec);
|
|
abc_def_ghi(&mut t);
|
|
t.set_cursor_pos(2, 2);
|
|
t.erase_display(EraseDisplay::Above, true);
|
|
assert_eq!("ABC\nDEF\nGHI", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_protected_complete() {
|
|
let mut t = init(10, 5);
|
|
t.print(u32::from('A')).unwrap();
|
|
t.carriage_return();
|
|
t.linefeed().unwrap();
|
|
pr(&mut t, "123456789");
|
|
let y = usize::from(t.screen().cursor.y);
|
|
t.set_cursor_pos(y + 1, 6);
|
|
t.set_protected_mode(ProtectedMode::Dec);
|
|
t.print(u32::from('X')).unwrap();
|
|
let y = usize::from(t.screen().cursor.y);
|
|
t.set_cursor_pos(y + 1, 4);
|
|
t.clear_dirty();
|
|
t.erase_display(EraseDisplay::Complete, true);
|
|
for y in 0..u32::from(t.rows) {
|
|
assert!(t.is_dirty(pt_active(0, y)));
|
|
}
|
|
assert_eq!("\n X", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_protected_below() {
|
|
let mut t = init(10, 5);
|
|
t.print(u32::from('A')).unwrap();
|
|
t.carriage_return();
|
|
t.linefeed().unwrap();
|
|
pr(&mut t, "123456789");
|
|
let y = usize::from(t.screen().cursor.y);
|
|
t.set_cursor_pos(y + 1, 6);
|
|
t.set_protected_mode(ProtectedMode::Dec);
|
|
t.print(u32::from('X')).unwrap();
|
|
let y = usize::from(t.screen().cursor.y);
|
|
t.set_cursor_pos(y + 1, 4);
|
|
t.erase_display(EraseDisplay::Below, true);
|
|
assert_eq!("A\n123 X", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_scroll_complete() {
|
|
let mut t = init(10, 5);
|
|
t.print(u32::from('A')).unwrap();
|
|
t.carriage_return();
|
|
t.linefeed().unwrap();
|
|
t.erase_display(EraseDisplay::ScrollComplete, false);
|
|
assert_eq!("", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_protected_above() {
|
|
let mut t = init(10, 3);
|
|
t.print(u32::from('A')).unwrap();
|
|
t.carriage_return();
|
|
t.linefeed().unwrap();
|
|
pr(&mut t, "123456789");
|
|
let y = usize::from(t.screen().cursor.y);
|
|
t.set_cursor_pos(y + 1, 6);
|
|
t.set_protected_mode(ProtectedMode::Dec);
|
|
t.print(u32::from('X')).unwrap();
|
|
let y = usize::from(t.screen().cursor.y);
|
|
t.set_cursor_pos(y + 1, 8);
|
|
t.erase_display(EraseDisplay::Above, true);
|
|
assert_eq!("\n X 9", plain(&t));
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_erase_display_complete_preserves_cursor() {
|
|
let mut t = init(5, 5);
|
|
|
|
// Set our cursor
|
|
t.set_attribute(A::Bold).unwrap();
|
|
t.print_string("AAAA").unwrap();
|
|
assert_ne!(style::DEFAULT_ID, t.screen().cursor.style_id);
|
|
|
|
// Erasing the display may detect that our style is no longer in use
|
|
// and prune our style, which we don't want because its still our
|
|
// active cursor.
|
|
t.erase_display(EraseDisplay::Complete, false);
|
|
assert_ne!(style::DEFAULT_ID, t.screen().cursor.style_id);
|
|
}
|
|
|
|
#[test]
|
|
fn terminal_semantic_prompt() {
|
|
let mut t = init(10, 5);
|
|
|
|
// Prompt
|
|
t.semantic_prompt(&Command::init(PA::FreshLineNewPrompt))
|
|
.unwrap();
|
|
pr(&mut t, "hello");
|
|
|
|
assert_eq!(0, t.screen().cursor.y);
|
|
assert_eq!(5, t.screen().cursor.x);
|
|
{
|
|
let pt = pt_active(t.screen().cursor.x - 1, u32::from(t.screen().cursor.y));
|
|
assert_eq!(SemanticContent::Prompt, cell_at(&t, pt).semantic_content());
|
|
assert_eq!(RowPrompt::Prompt, row_at(&t, pt).semantic_prompt());
|
|
}
|
|
|
|
// Start input but end it on EOL
|
|
t.semantic_prompt(&Command::init(PA::EndPromptStartInputTerminateEol))
|
|
.unwrap();
|
|
t.carriage_return();
|
|
t.linefeed().unwrap();
|
|
|
|
// Write some output
|
|
assert_eq!(1, t.screen().cursor.y);
|
|
assert_eq!(0, t.screen().cursor.x);
|
|
pr(&mut t, "world");
|
|
{
|
|
let pt = pt_active(t.screen().cursor.x - 1, u32::from(t.screen().cursor.y));
|
|
assert_eq!(SemanticContent::Output, cell_at(&t, pt).semantic_content());
|
|
assert_eq!(RowPrompt::None, row_at(&t, pt).semantic_prompt());
|
|
}
|
|
}
|