use crate::term::BufWrite as _;
#[derive(Clone, Debug)]
pub struct Row {
cells: Vec<crate::cell::Cell>,
wrapped: bool,
}
impl Row {
pub fn new(cols: u16) -> Self {
Self {
cells: vec![crate::cell::Cell::default(); usize::from(cols)],
wrapped: false,
}
}
fn cols(&self) -> u16 {
self.cells
.len()
.try_into()
.unwrap()
}
pub fn clear(&mut self, attrs: crate::attrs::Attrs) {
for cell in &mut self.cells {
cell.clear(attrs);
}
self.wrapped = false;
}
fn cells(&self) -> impl Iterator<Item = &crate::cell::Cell> {
self.cells.iter()
}
pub fn get(&self, col: u16) -> Option<&crate::cell::Cell> {
self.cells.get(usize::from(col))
}
pub fn get_mut(&mut self, col: u16) -> Option<&mut crate::cell::Cell> {
self.cells.get_mut(usize::from(col))
}
pub fn insert(&mut self, i: u16, cell: crate::cell::Cell) {
self.cells.insert(usize::from(i), cell);
self.wrapped = false;
}
pub fn remove(&mut self, i: u16) {
self.clear_wide(i);
self.cells.remove(usize::from(i));
self.wrapped = false;
}
pub fn erase(&mut self, i: u16, attrs: crate::attrs::Attrs) {
let wide = self.cells[usize::from(i)].is_wide();
self.clear_wide(i);
self.cells[usize::from(i)].clear(attrs);
if i == self.cols() - if wide { 2 } else { 1 } {
self.wrapped = false;
}
}
pub fn truncate(&mut self, len: u16) {
self.cells.truncate(usize::from(len));
self.wrapped = false;
let last_cell = &mut self.cells[usize::from(len) - 1];
if last_cell.is_wide() {
last_cell.clear(*last_cell.attrs());
}
}
pub fn resize(&mut self, len: u16, cell: crate::cell::Cell) {
self.cells.resize(usize::from(len), cell);
self.wrapped = false;
}
pub fn wrap(&mut self, wrap: bool) {
self.wrapped = wrap;
}
pub fn wrapped(&self) -> bool {
self.wrapped
}
pub fn clear_wide(&mut self, col: u16) {
let cell = &self.cells[usize::from(col)];
let other = if cell.is_wide() {
&mut self.cells[usize::from(col + 1)]
} else if cell.is_wide_continuation() {
&mut self.cells[usize::from(col - 1)]
} else {
return;
};
other.clear(*other.attrs());
}
pub fn write_contents(
&self,
contents: &mut String,
start: u16,
width: u16,
wrapping: bool,
) {
let mut prev_was_wide = false;
let mut prev_col = start;
for (col, cell) in self
.cells()
.enumerate()
.skip(usize::from(start))
.take(usize::from(width))
{
if prev_was_wide {
prev_was_wide = false;
continue;
}
prev_was_wide = cell.is_wide();
let col: u16 = col.try_into().unwrap();
if cell.has_contents() {
for _ in 0..(col - prev_col) {
contents.push(' ');
}
prev_col += col - prev_col;
contents.push_str(&cell.contents());
prev_col += if cell.is_wide() { 2 } else { 1 };
}
}
if prev_col == start && wrapping {
contents.push('\n');
}
}
pub fn write_contents_formatted(
&self,
contents: &mut Vec<u8>,
start: u16,
width: u16,
row: u16,
wrapping: bool,
prev_pos: Option<crate::grid::Pos>,
prev_attrs: Option<crate::attrs::Attrs>,
) -> (crate::grid::Pos, crate::attrs::Attrs) {
let mut prev_was_wide = false;
let default_cell = crate::cell::Cell::default();
let mut prev_pos = prev_pos.unwrap_or_else(|| {
if wrapping {
crate::grid::Pos {
row: row - 1,
col: self.cols(),
}
} else {
crate::grid::Pos { row, col: start }
}
});
let mut prev_attrs = prev_attrs.unwrap_or_default();
let first_cell = &self.cells[usize::from(start)];
if wrapping && first_cell == &default_cell {
let default_attrs = default_cell.attrs();
if &prev_attrs != default_attrs {
default_attrs.write_escape_code_diff(contents, &prev_attrs);
prev_attrs = *default_attrs;
}
contents.push(b' ');
crate::term::Backspace::default().write_buf(contents);
crate::term::EraseChar::new(1).write_buf(contents);
prev_pos = crate::grid::Pos { row, col: 0 };
}
let mut erase: Option<(u16, &crate::attrs::Attrs)> = None;
for (col, cell) in self
.cells()
.enumerate()
.skip(usize::from(start))
.take(usize::from(width))
{
if prev_was_wide {
prev_was_wide = false;
continue;
}
prev_was_wide = cell.is_wide();
let col: u16 = col.try_into().unwrap();
let pos = crate::grid::Pos { row, col };
if let Some((prev_col, attrs)) = erase {
if cell.has_contents() || cell.attrs() != attrs {
let new_pos = crate::grid::Pos { row, col: prev_col };
if wrapping
&& prev_pos.row + 1 == new_pos.row
&& prev_pos.col >= self.cols()
{
if new_pos.col > 0 {
contents.extend(
" ".repeat(usize::from(new_pos.col))
.as_bytes(),
);
} else {
contents.extend(b" ");
crate::term::Backspace::default()
.write_buf(contents);
}
} else {
crate::term::MoveFromTo::new(prev_pos, new_pos)
.write_buf(contents);
}
prev_pos = new_pos;
if &prev_attrs != attrs {
attrs.write_escape_code_diff(contents, &prev_attrs);
prev_attrs = *attrs;
}
crate::term::EraseChar::new(pos.col - prev_col)
.write_buf(contents);
erase = None;
}
}
if cell != &default_cell {
let attrs = cell.attrs();
if cell.has_contents() {
if pos != prev_pos {
if !wrapping
|| prev_pos.row + 1 != pos.row
|| prev_pos.col
< self.cols() - u16::from(cell.is_wide())
|| pos.col != 0
{
crate::term::MoveFromTo::new(prev_pos, pos)
.write_buf(contents);
}
prev_pos = pos;
}
if &prev_attrs != attrs {
attrs.write_escape_code_diff(contents, &prev_attrs);
prev_attrs = *attrs;
}
prev_pos.col += if cell.is_wide() { 2 } else { 1 };
let cell_contents = cell.contents();
contents.extend(cell_contents.as_bytes());
} else if erase.is_none() {
erase = Some((pos.col, attrs));
}
}
}
if let Some((prev_col, attrs)) = erase {
let new_pos = crate::grid::Pos { row, col: prev_col };
if wrapping
&& prev_pos.row + 1 == new_pos.row
&& prev_pos.col >= self.cols()
{
if new_pos.col > 0 {
contents.extend(
" ".repeat(usize::from(new_pos.col)).as_bytes(),
);
} else {
contents.extend(b" ");
crate::term::Backspace::default().write_buf(contents);
}
} else {
crate::term::MoveFromTo::new(prev_pos, new_pos)
.write_buf(contents);
}
prev_pos = new_pos;
if &prev_attrs != attrs {
attrs.write_escape_code_diff(contents, &prev_attrs);
prev_attrs = *attrs;
}
crate::term::ClearRowForward::default().write_buf(contents);
}
(prev_pos, prev_attrs)
}
pub fn write_contents_diff(
&self,
contents: &mut Vec<u8>,
prev: &Self,
start: u16,
width: u16,
row: u16,
wrapping: bool,
prev_wrapping: bool,
mut prev_pos: crate::grid::Pos,
mut prev_attrs: crate::attrs::Attrs,
) -> (crate::grid::Pos, crate::attrs::Attrs) {
let mut prev_was_wide = false;
let first_cell = &self.cells[usize::from(start)];
let prev_first_cell = &prev.cells[usize::from(start)];
if wrapping
&& !prev_wrapping
&& first_cell == prev_first_cell
&& prev_pos.row + 1 == row
&& prev_pos.col
>= self.cols() - u16::from(prev_first_cell.is_wide())
{
let first_cell_attrs = first_cell.attrs();
if &prev_attrs != first_cell_attrs {
first_cell_attrs
.write_escape_code_diff(contents, &prev_attrs);
prev_attrs = *first_cell_attrs;
}
let mut cell_contents = prev_first_cell.contents();
let need_erase = if cell_contents.is_empty() {
cell_contents = " ".to_string();
true
} else {
false
};
contents.extend(cell_contents.as_bytes());
crate::term::Backspace::default().write_buf(contents);
if prev_first_cell.is_wide() {
crate::term::Backspace::default().write_buf(contents);
}
if need_erase {
crate::term::EraseChar::new(1).write_buf(contents);
}
prev_pos = crate::grid::Pos { row, col: 0 };
}
let mut erase: Option<(u16, &crate::attrs::Attrs)> = None;
for (col, (cell, prev_cell)) in self
.cells()
.zip(prev.cells())
.enumerate()
.skip(usize::from(start))
.take(usize::from(width))
{
if prev_was_wide {
prev_was_wide = false;
continue;
}
prev_was_wide = cell.is_wide();
let col: u16 = col.try_into().unwrap();
let pos = crate::grid::Pos { row, col };
if let Some((prev_col, attrs)) = erase {
if cell.has_contents() || cell.attrs() != attrs {
let new_pos = crate::grid::Pos { row, col: prev_col };
if wrapping
&& prev_pos.row + 1 == new_pos.row
&& prev_pos.col >= self.cols()
{
if new_pos.col > 0 {
contents.extend(
" ".repeat(usize::from(new_pos.col))
.as_bytes(),
);
} else {
contents.extend(b" ");
crate::term::Backspace::default()
.write_buf(contents);
}
} else {
crate::term::MoveFromTo::new(prev_pos, new_pos)
.write_buf(contents);
}
prev_pos = new_pos;
if &prev_attrs != attrs {
attrs.write_escape_code_diff(contents, &prev_attrs);
prev_attrs = *attrs;
}
crate::term::EraseChar::new(pos.col - prev_col)
.write_buf(contents);
erase = None;
}
}
if cell != prev_cell {
let attrs = cell.attrs();
if cell.has_contents() {
if pos != prev_pos {
if !wrapping
|| prev_pos.row + 1 != pos.row
|| prev_pos.col
< self.cols() - u16::from(cell.is_wide())
|| pos.col != 0
{
crate::term::MoveFromTo::new(prev_pos, pos)
.write_buf(contents);
}
prev_pos = pos;
}
if &prev_attrs != attrs {
attrs.write_escape_code_diff(contents, &prev_attrs);
prev_attrs = *attrs;
}
prev_pos.col += if cell.is_wide() { 2 } else { 1 };
contents.extend(cell.contents().as_bytes());
} else if erase.is_none() {
erase = Some((pos.col, attrs));
}
}
}
if let Some((prev_col, attrs)) = erase {
let new_pos = crate::grid::Pos { row, col: prev_col };
if wrapping
&& prev_pos.row + 1 == new_pos.row
&& prev_pos.col >= self.cols()
{
if new_pos.col > 0 {
contents.extend(
" ".repeat(usize::from(new_pos.col)).as_bytes(),
);
} else {
contents.extend(b" ");
crate::term::Backspace::default().write_buf(contents);
}
} else {
crate::term::MoveFromTo::new(prev_pos, new_pos)
.write_buf(contents);
}
prev_pos = new_pos;
if &prev_attrs != attrs {
attrs.write_escape_code_diff(contents, &prev_attrs);
prev_attrs = *attrs;
}
crate::term::ClearRowForward::default().write_buf(contents);
}
if (!self.wrapped && prev.wrapped) || (!prev.wrapped && self.wrapped)
{
let end_pos = if self.cells[usize::from(self.cols() - 1)]
.is_wide_continuation()
{
crate::grid::Pos {
row,
col: self.cols() - 2,
}
} else {
crate::grid::Pos {
row,
col: self.cols() - 1,
}
};
crate::term::MoveFromTo::new(prev_pos, end_pos)
.write_buf(contents);
prev_pos = end_pos;
if !self.wrapped {
crate::term::EraseChar::new(1).write_buf(contents);
}
let end_cell = &self.cells[usize::from(end_pos.col)];
if end_cell.has_contents() {
let attrs = end_cell.attrs();
if &prev_attrs != attrs {
attrs.write_escape_code_diff(contents, &prev_attrs);
prev_attrs = *attrs;
}
contents.extend(end_cell.contents().as_bytes());
prev_pos.col += if end_cell.is_wide() { 2 } else { 1 };
}
}
(prev_pos, prev_attrs)
}
}