Files
galaxy/crates/galaxy_terminal/src/model/grid/row.rs
T

332 lines
8.5 KiB
Rust

// The code in this file is adapted from the alacritty_terminal crate under the
// Apache license; see: crates/warp_terminal/src/model/LICENSE-ALACRITTY.
//! Defines the Row type which makes up lines in the grid.
use std::cmp::{max, min};
use std::ops::{Index, IndexMut, Range, RangeFrom, RangeFull, RangeTo, RangeToInclusive};
use std::{ptr, slice};
use serde::{Deserialize, Serialize};
use crate::model::grid::cell::{Cell, ResetDiscriminant};
/// A row in the grid.
#[derive(Serialize, Deserialize, Default, Clone, Debug)]
pub struct Row {
inner: Vec<Cell>,
/// Maximum number of occupied entries.
///
/// This is the upper bound on the number of elements in the row, which have been modified
/// since the last reset. All cells after this point are guaranteed to be equal.
///
/// TODO(visibility): This should be changed to `pub(crate)` when possible.
pub occ: usize,
}
impl PartialEq for Row {
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
impl Row {
/// Create a new terminal row.
pub fn new(usizes: usize) -> Row {
debug_assert!(usizes >= 1);
let mut inner: Vec<Cell> = Vec::with_capacity(usizes);
// This is a slightly optimized version of `std::vec::Vec::resize`.
unsafe {
let mut ptr = inner.as_mut_ptr();
for _ in 1..usizes {
ptr::write(ptr, Cell::default());
ptr = ptr.offset(1);
}
ptr::write(ptr, Cell::default());
inner.set_len(usizes);
}
Row { inner, occ: 0 }
}
/// Increase the number of usizes in the row.
#[inline]
pub fn grow(&mut self, cols: usize) {
if self.inner.len() >= cols {
return;
}
self.inner.resize_with(cols, Cell::default);
}
/// Reduce the number of usizes in the row.
///
/// This will return all non-empty cells that were removed.
pub fn shrink(&mut self, cols: usize) -> Option<Vec<Cell>> {
if self.inner.len() <= cols {
return None;
}
// Split off cells for a new row.
let mut new_row = self.inner.split_off(cols);
let index = new_row
.iter()
// NOTE: We do NOT want to treat the "end of prompt" cell as empty in this case - we
// want to preserve the marker in the Cell's `extra` field and carry it over for the row shrinking
// in the context of a resize.
.rposition(|c| !c.is_empty() || c.is_end_of_prompt())
.map(|i| i + 1)
.unwrap_or(0);
new_row.truncate(index);
self.occ = min(self.occ, cols);
if new_row.is_empty() {
None
} else {
Some(new_row)
}
}
/// Truncate row to desired number of columns.
pub fn truncate(&mut self, cols: usize) {
self.inner.truncate(cols);
}
/// Reset all cells in the row to the `template` cell.
#[inline]
pub fn reset(&mut self, template: &Cell) {
debug_assert!(!self.inner.is_empty());
// Mark all cells as dirty if template cell changed.
let len = self.inner.len();
if self.inner[len - 1].discriminant() != template.discriminant() {
self.occ = len;
}
// Reset every dirty cell in the row.
for item in &mut self.inner[0..self.occ] {
item.reset(template);
}
self.occ = 0;
}
pub fn get(&self, index: usize) -> Option<&Cell> {
if index < self.len() {
Some(&self[index])
} else {
None
}
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut Cell> {
if index < self.len() {
Some(&mut self[index])
} else {
None
}
}
pub fn estimated_heap_usage_bytes(&self) -> usize {
self.inner.capacity() * std::mem::size_of::<Cell>()
}
pub fn estimated_memory_usage_bytes(&self) -> usize {
// size of struct on the stack
std::mem::size_of::<Self>()
// size of heap-allocated data in self.inner
+ self.estimated_heap_usage_bytes()
}
}
#[allow(clippy::len_without_is_empty)]
impl Row {
#[inline]
pub fn from_vec(vec: Vec<Cell>, occ: usize) -> Row {
Row { inner: vec, occ }
}
#[inline]
pub fn len(&self) -> usize {
self.inner.len()
}
#[inline]
pub fn last(&self) -> Option<&Cell> {
self.inner.last()
}
#[inline]
pub fn last_mut(&mut self) -> Option<&mut Cell> {
self.occ = self.inner.len();
self.inner.last_mut()
}
#[inline]
pub fn append(&mut self, vec: &mut Vec<Cell>) {
self.occ += vec.len();
self.inner.append(vec);
}
#[inline]
pub fn append_front(&mut self, mut vec: Vec<Cell>) {
self.occ += vec.len();
vec.append(&mut self.inner);
self.inner = vec;
}
/// Check if all cells in the row are empty.
#[inline]
pub fn is_clear(&self) -> bool {
self.inner.iter().all(Cell::is_empty)
}
/// Returns `true` if no cells in the row contain an end of prompt marker
/// and `false` otherwise.
#[inline]
pub fn has_no_end_of_prompt_marker(&self) -> bool {
self.inner.iter().all(|cell| !Cell::is_end_of_prompt(cell))
}
#[inline]
pub fn front_split_off(&mut self, at: usize) -> Vec<Cell> {
self.occ = self.occ.saturating_sub(at);
let mut split = self.inner.split_off(at);
std::mem::swap(&mut split, &mut self.inner);
split
}
/// Returns the set of cells that have been dirtied since the row was last
/// reset.
///
/// This is guaranteed to return all cells that contain content that should
/// be rendered, but may also return some additional cells after the last
/// actually-relevant cell.
///
/// This returns a slice instead of an [`Iterator`] as a small performance
/// optimization.
pub(crate) fn dirty_cells(&self) -> &[Cell] {
&self.inner[0..self.occ]
}
}
impl<'a> IntoIterator for &'a mut Row {
type Item = &'a mut Cell;
type IntoIter = slice::IterMut<'a, Cell>;
#[inline]
fn into_iter(self) -> slice::IterMut<'a, Cell> {
self.occ = self.len();
self.inner.iter_mut()
}
}
impl Index<usize> for Row {
type Output = Cell;
#[inline]
fn index(&self, index: usize) -> &Cell {
&self.inner[index]
}
}
impl IndexMut<usize> for Row {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut Cell {
self.occ = max(self.occ, index + 1);
&mut self.inner[index]
}
}
impl Index<Range<usize>> for Row {
type Output = [Cell];
#[inline]
fn index(&self, index: Range<usize>) -> &[Cell] {
&self.inner[(index.start)..(index.end)]
}
}
impl IndexMut<Range<usize>> for Row {
#[inline]
fn index_mut(&mut self, index: Range<usize>) -> &mut [Cell] {
self.occ = max(self.occ, index.end);
&mut self.inner[(index.start)..(index.end)]
}
}
impl Index<RangeTo<usize>> for Row {
type Output = [Cell];
#[inline]
fn index(&self, index: RangeTo<usize>) -> &[Cell] {
&self.inner[..(index.end)]
}
}
impl IndexMut<RangeTo<usize>> for Row {
#[inline]
fn index_mut(&mut self, index: RangeTo<usize>) -> &mut [Cell] {
self.occ = max(self.occ, index.end);
&mut self.inner[..(index.end)]
}
}
impl Index<RangeFrom<usize>> for Row {
type Output = [Cell];
#[inline]
fn index(&self, index: RangeFrom<usize>) -> &[Cell] {
&self.inner[(index.start)..]
}
}
impl IndexMut<RangeFrom<usize>> for Row {
#[inline]
fn index_mut(&mut self, index: RangeFrom<usize>) -> &mut [Cell] {
self.occ = self.len();
&mut self.inner[(index.start)..]
}
}
impl Index<RangeFull> for Row {
type Output = [Cell];
#[inline]
fn index(&self, _: RangeFull) -> &[Cell] {
&self.inner[..]
}
}
impl IndexMut<RangeFull> for Row {
#[inline]
fn index_mut(&mut self, _: RangeFull) -> &mut [Cell] {
self.occ = self.len();
&mut self.inner[..]
}
}
impl Index<RangeToInclusive<usize>> for Row {
type Output = [Cell];
#[inline]
fn index(&self, index: RangeToInclusive<usize>) -> &[Cell] {
&self.inner[..=(index.end)]
}
}
impl IndexMut<RangeToInclusive<usize>> for Row {
#[inline]
fn index_mut(&mut self, index: RangeToInclusive<usize>) -> &mut [Cell] {
self.occ = max(self.occ, index.end);
&mut self.inner[..=(index.end)]
}
}