use super::Screen;
use super::logical_line::{self, LogicalLineOptions};
use crate::cell::Cell;
use crate::grid::{Pos, Size};
use crate::row::Row;
pub(super) fn reflow_primary(screen: &mut Screen, new_size: Size) {
let old_size = screen.grid.size();
if old_size == new_size {
return;
}
let cursor = screen.grid.pos();
let scrollback_count = screen.grid.scrollback_available();
let cursor_global_row = scrollback_count + cursor.row as usize;
let cursor_global_col = cursor.col as usize;
let mut materialized: Vec<MaterializedLine> = Vec::new();
let mut anchor_line_idx: Option<usize> = None;
let mut anchor_offset: usize = 0;
{
let opts = LogicalLineOptions {
include_scrollback: true,
join_wrapped: true,
trim_trailing_blanks: false,
};
let mut consumed_rows: usize = 0;
for span in logical_line::iter_grid(&screen.grid, opts) {
let n_rows = span.rows.len();
let mut cells: Vec<Cell> = Vec::with_capacity(n_rows * usize::from(old_size.cols));
for row in &span.rows {
for c in 0..old_size.cols {
cells.push(row.get(c).cloned().unwrap_or_else(Cell::new));
}
}
if anchor_line_idx.is_none()
&& cursor_global_row >= consumed_rows
&& cursor_global_row < consumed_rows + n_rows
{
let row_in_line = cursor_global_row - consumed_rows;
anchor_line_idx = Some(materialized.len());
anchor_offset = row_in_line * usize::from(old_size.cols) + cursor_global_col;
}
consumed_rows += n_rows;
materialized.push(MaterializedLine { cells });
}
}
while materialized
.last()
.is_some_and(|line| line.cells.iter().all(|c| !c.has_contents()))
&& anchor_line_idx != Some(materialized.len() - 1)
{
materialized.pop();
}
let new_cols = usize::from(new_size.cols);
let mut emitted: Vec<Row> = Vec::new();
let mut new_cursor_global_idx: Option<usize> = None;
let mut new_cursor_col: u16 = 0;
for (line_idx, line) in materialized.iter().enumerate() {
let trimmed_len = line
.cells
.iter()
.rposition(Cell::has_contents)
.map(|p| p + 1)
.unwrap_or(0);
let cursor_offset = if anchor_line_idx == Some(line_idx) {
Some(anchor_offset)
} else {
None
};
let effective_len = match cursor_offset {
Some(off) => trimmed_len.max(off + 1),
None => trimmed_len,
};
let num_rows = if effective_len == 0 {
1
} else {
effective_len.div_ceil(new_cols.max(1))
};
let line_start_idx = emitted.len();
for chunk_idx in 0..num_rows {
let mut new_row = Row::new(new_size.cols);
new_row.mark_dirty();
let chunk_start = chunk_idx * new_cols;
let chunk_end = ((chunk_idx + 1) * new_cols).min(line.cells.len());
for (col_idx, src_idx) in (chunk_start..chunk_end).enumerate() {
if let Some(slot) = new_row.get_mut(col_idx as u16) {
*slot = line.cells[src_idx].clone();
}
}
new_row.wrap(chunk_idx + 1 < num_rows);
emitted.push(new_row);
}
if let Some(off) = cursor_offset {
let chunk_idx = off / new_cols.max(1);
let col_in_chunk = off % new_cols.max(1);
new_cursor_global_idx = Some(line_start_idx + chunk_idx);
new_cursor_col = col_in_chunk as u16;
}
}
let new_drawing_count = usize::from(new_size.rows);
let total_emitted = emitted.len();
let drawing_start = total_emitted.saturating_sub(new_drawing_count);
let mut drawing_rows = emitted.split_off(drawing_start);
let scrollback_extra = emitted;
while drawing_rows.len() < new_drawing_count {
let mut blank = Row::new(new_size.cols);
blank.mark_dirty();
drawing_rows.push(blank);
}
let new_pos = match new_cursor_global_idx {
Some(g) if g >= drawing_start => Pos {
row: (g - drawing_start) as u16,
col: new_cursor_col,
},
_ => Pos { row: 0, col: 0 },
};
screen
.grid
.install_reflowed(drawing_rows, scrollback_extra, new_size, new_pos);
}
struct MaterializedLine {
cells: Vec<Cell>,
}