| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
|
| 14 |
|
| 15 |
|
| 16 |
|
| 17 |
|
| 18 |
use crate::{Cell, Grid}; |
| 19 |
|
| 20 |
impl Grid { |
| 21 |
|
| 22 |
|
| 23 |
|
| 24 |
|
| 25 |
|
| 26 |
pub fn abs_rows(&self) -> usize { |
| 27 |
self.abs_base() + self.rows() as usize |
| 28 |
} |
| 29 |
|
| 30 |
|
| 31 |
fn abs_base(&self) -> usize { |
| 32 |
if self.on_alt { 0 } else { self.history.len() } |
| 33 |
} |
| 34 |
|
| 35 |
|
| 36 |
|
| 37 |
|
| 38 |
|
| 39 |
pub fn abs_row_of_view(&self, r: u16) -> usize { |
| 40 |
|
| 41 |
|
| 42 |
self.abs_base() + r as usize - self.view_offset() as usize |
| 43 |
} |
| 44 |
|
| 45 |
|
| 46 |
|
| 47 |
|
| 48 |
|
| 49 |
|
| 50 |
pub fn text_range(&self, start: usize, end: usize) -> String { |
| 51 |
let end = end.min(self.abs_rows()); |
| 52 |
let mut out = String::new(); |
| 53 |
for r in start..end { |
| 54 |
let wrapped = self.abs_row_wrapped(r) && r + 1 < end; |
| 55 |
let cells = self.abs_row(r); |
| 56 |
if wrapped { |
| 57 |
|
| 58 |
|
| 59 |
|
| 60 |
self.push_row_text(cells, &mut out); |
| 61 |
} else { |
| 62 |
let mut line = String::new(); |
| 63 |
self.push_row_text(cells, &mut line); |
| 64 |
out.push_str(line.trim_end()); |
| 65 |
out.push('\n'); |
| 66 |
} |
| 67 |
} |
| 68 |
out |
| 69 |
} |
| 70 |
|
| 71 |
|
| 72 |
fn abs_row(&self, r: usize) -> &[Cell] { |
| 73 |
match r.checked_sub(self.abs_base()) { |
| 74 |
Some(live) => { |
| 75 |
let start = self.row_start(live as u16); |
| 76 |
&self.active_cells()[start..start + self.cols() as usize] |
| 77 |
} |
| 78 |
None => &self.history[r].cells, |
| 79 |
} |
| 80 |
} |
| 81 |
|
| 82 |
|
| 83 |
fn abs_row_wrapped(&self, r: usize) -> bool { |
| 84 |
match r.checked_sub(self.abs_base()) { |
| 85 |
Some(live) => { |
| 86 |
let phys = self.phys_row(live as u16) as usize; |
| 87 |
let flags = if self.on_alt { |
| 88 |
&self.alt_wrapped |
| 89 |
} else { |
| 90 |
&self.main_wrapped |
| 91 |
}; |
| 92 |
flags.get(phys).copied().unwrap_or(false) |
| 93 |
} |
| 94 |
None => self.history[r].wrapped, |
| 95 |
} |
| 96 |
} |
| 97 |
} |
| 98 |
|
| 99 |
impl Grid { |
| 100 |
|
| 101 |
|
| 102 |
|
| 103 |
|
| 104 |
|
| 105 |
|
| 106 |
fn push_row_text(&self, cells: &[Cell], out: &mut String) { |
| 107 |
for cell in cells.iter().filter(|c| !c.is_spacer()) { |
| 108 |
self.push_cell_text(cell, out); |
| 109 |
} |
| 110 |
} |
| 111 |
} |
| 112 |
|
| 113 |
#[cfg(test)] |
| 114 |
mod tests { |
| 115 |
use super::*; |
| 116 |
use shop_vt::Parser; |
| 117 |
|
| 118 |
|
| 119 |
|
| 120 |
fn grid_fed(cols: u16, rows: u16, bytes: &str) -> Grid { |
| 121 |
let mut grid = Grid::new(cols, rows); |
| 122 |
let mut parser = Parser::new(); |
| 123 |
parser.advance(&mut grid, bytes.as_bytes()); |
| 124 |
grid |
| 125 |
} |
| 126 |
|
| 127 |
fn all(g: &Grid) -> String { |
| 128 |
g.text_range(0, g.abs_rows()) |
| 129 |
} |
| 130 |
|
| 131 |
#[test] |
| 132 |
fn the_live_screen_comes_out_line_by_line() { |
| 133 |
let g = grid_fed(20, 3, "one\r\ntwo\r\nthree"); |
| 134 |
assert_eq!(all(&g), "one\ntwo\nthree\n"); |
| 135 |
} |
| 136 |
|
| 137 |
#[test] |
| 138 |
fn padding_is_trimmed_but_blank_rows_survive() { |
| 139 |
let g = grid_fed(20, 3, "top\r\n\r\nbottom"); |
| 140 |
assert_eq!(all(&g), "top\n\nbottom\n"); |
| 141 |
} |
| 142 |
|
| 143 |
#[test] |
| 144 |
fn a_wrapped_line_comes_out_as_one_line() { |
| 145 |
let g = grid_fed(6, 3, "abcdefghij"); |
| 146 |
assert_eq!(g.text_range(0, 2), "abcdefghij\n"); |
| 147 |
} |
| 148 |
|
| 149 |
#[test] |
| 150 |
fn a_row_that_filled_exactly_still_breaks() { |
| 151 |
|
| 152 |
|
| 153 |
let g = grid_fed(6, 3, "abcdef\r\nghij"); |
| 154 |
assert_eq!(g.text_range(0, 2), "abcdef\nghij\n"); |
| 155 |
} |
| 156 |
|
| 157 |
#[test] |
| 158 |
fn a_space_straddling_the_wrap_survives() { |
| 159 |
|
| 160 |
let g = grid_fed(6, 3, "ab cd"); |
| 161 |
assert_eq!(g.text_range(0, 2), "ab cd\n"); |
| 162 |
} |
| 163 |
|
| 164 |
#[test] |
| 165 |
fn three_rows_of_one_wrapped_line_come_out_as_one() { |
| 166 |
let g = grid_fed(4, 4, "0123456789ab"); |
| 167 |
assert_eq!(g.text_range(0, 3), "0123456789ab\n"); |
| 168 |
} |
| 169 |
|
| 170 |
#[test] |
| 171 |
fn the_blank_rows_under_the_output_emit_as_blank_lines() { |
| 172 |
|
| 173 |
|
| 174 |
let g = grid_fed(20, 4, "one\r\ntwo"); |
| 175 |
assert_eq!(all(&g), "one\ntwo\n\n\n"); |
| 176 |
} |
| 177 |
|
| 178 |
#[test] |
| 179 |
fn scrollback_comes_out_ahead_of_the_screen() { |
| 180 |
|
| 181 |
let g = grid_fed(20, 3, "one\r\ntwo\r\nthree\r\nfour\r\nfive\r\nsix"); |
| 182 |
assert_eq!(g.history_len(), 3); |
| 183 |
assert_eq!(g.abs_rows(), 6); |
| 184 |
assert_eq!(all(&g), "one\ntwo\nthree\nfour\nfive\nsix\n"); |
| 185 |
} |
| 186 |
|
| 187 |
#[test] |
| 188 |
fn a_range_takes_the_rows_it_names() { |
| 189 |
let g = grid_fed(20, 3, "one\r\ntwo\r\nthree\r\nfour\r\nfive\r\nsix"); |
| 190 |
assert_eq!(g.text_range(2, 4), "three\nfour\n"); |
| 191 |
assert_eq!(g.text_range(5, 99), "six\n", "an over-long end clamps"); |
| 192 |
assert_eq!(g.text_range(6, 6), ""); |
| 193 |
} |
| 194 |
|
| 195 |
#[test] |
| 196 |
fn a_range_ending_mid_wrap_does_not_run_on() { |
| 197 |
|
| 198 |
|
| 199 |
let g = grid_fed(6, 3, "abcdefghij"); |
| 200 |
assert_eq!(g.text_range(0, 1), "abcdef\n"); |
| 201 |
} |
| 202 |
|
| 203 |
#[test] |
| 204 |
fn the_visible_screen_is_a_range_like_any_other() { |
| 205 |
let mut g = grid_fed(20, 3, "one\r\ntwo\r\nthree\r\nfour\r\nfive\r\nsix"); |
| 206 |
let visible = |g: &Grid| { |
| 207 |
let top = g.abs_row_of_view(0); |
| 208 |
g.text_range(top, top + g.rows() as usize) |
| 209 |
}; |
| 210 |
assert_eq!(visible(&g), "four\nfive\nsix\n"); |
| 211 |
assert!(g.scroll_view_up(2)); |
| 212 |
assert_eq!(visible(&g), "two\nthree\nfour\n"); |
| 213 |
} |
| 214 |
|
| 215 |
#[test] |
| 216 |
fn the_alt_screen_emits_only_itself() { |
| 217 |
|
| 218 |
let g = grid_fed( |
| 219 |
20, |
| 220 |
3, |
| 221 |
"main1\r\nmain2\r\nmain3\r\nmain4\x1b[?1049h\x1b[Halt1\r\nalt2", |
| 222 |
); |
| 223 |
assert_eq!(g.abs_rows(), 3, "history is unreachable from alt"); |
| 224 |
assert_eq!(all(&g), "alt1\nalt2\n\n"); |
| 225 |
} |
| 226 |
} |
| 227 |
|