#[cfg(doc)]
use crate::Screen;
#[derive(Debug)]
pub enum BasicFormattedCaptureRange<'a> {
Full(
&'a mut BasicFormattedCaptureState,
),
Visible,
}
#[derive(Debug, Default, Clone)]
pub struct BasicFormattedCaptureState {
attrs: crate::attrs::Attrs,
newline_pending: bool,
}
impl BasicFormattedCaptureState {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
pub struct RowContents<'a>(pub(crate) &'a crate::row::Row);
impl RowContents<'_> {
pub fn write_formatted_basic(
&self,
writer: &mut impl alloc::fmt::Write,
state: &mut BasicFormattedCaptureState,
) -> alloc::fmt::Result {
if state.newline_pending {
writer.write_char('\n')?;
}
self.0
.write_contents_formatted_basic(writer, &mut state.attrs)?;
state.newline_pending = !self.0.wrapped();
Ok(())
}
}
pub struct BasicFormattedToPlain<'a> {
indexed: BasicFormattedToPlainIndexed<'a>,
}
impl<'a> Iterator for BasicFormattedToPlain<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
self.indexed.next().map(|(_i, s)| s)
}
}
impl core::iter::FusedIterator for BasicFormattedToPlain<'_> {}
pub struct BasicFormattedToPlainIndexed<'a> {
capture: &'a str,
index: usize,
}
impl<'a> Iterator for BasicFormattedToPlainIndexed<'a> {
type Item = (usize, &'a str);
fn next(&mut self) -> Option<Self::Item> {
while !self.capture.is_empty() {
let index = self.index;
let Some((before, after)) = self.capture.split_once('\x1b')
else {
self.index += self.capture.len();
return Some((index, core::mem::take(&mut self.capture)));
};
self.index += before.len() + 1;
self.capture =
if let Some((esc_body, after_esc)) = after.split_once('m') {
self.index += esc_body.len() + 1;
after_esc
} else {
self.index += after.len();
""
};
if !before.is_empty() {
return Some((index, before));
}
}
None
}
}
impl core::iter::FusedIterator for BasicFormattedToPlainIndexed<'_> {}
#[must_use]
pub fn basic_formatted_to_plain(capture: &str) -> BasicFormattedToPlain<'_> {
BasicFormattedToPlain {
indexed: basic_formatted_to_plain_indexed(capture),
}
}
#[must_use]
pub fn basic_formatted_to_plain_indexed(
capture: &str,
) -> BasicFormattedToPlainIndexed<'_> {
BasicFormattedToPlainIndexed { capture, index: 0 }
}
pub struct BasicFormattedRows<'a> {
capture: Option<&'a str>,
width: u16,
}
impl<'a> Iterator for BasicFormattedRows<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
use unicode_width::UnicodeWidthChar as _;
let capture = self.capture?;
let mut width = 0_u16;
let mut chars = capture.char_indices();
while let Some((i, c)) = chars.next() {
if c == '\x1b' {
while chars.next().is_some_and(|(_, c)| c != 'm') {}
continue;
}
if c == '\n' {
let (row, rest) = capture.split_at(i);
self.capture = Some(&rest[c.len_utf8()..]);
return Some(row);
}
let char_width = c.width().map_or(1, |w| {
u16::try_from(w).unwrap_or(u16::MAX).clamp(0, 2)
});
if char_width == 0 {
continue;
}
let old_width = width;
if let Some(new_width) = width.checked_add(char_width) {
width = new_width;
if new_width <= self.width {
continue;
}
}
if old_width == 0 {
continue;
}
let (before, after) = capture.split_at(i);
self.capture = Some(after);
return Some(before);
}
self.capture.take()
}
}
impl core::iter::FusedIterator for BasicFormattedRows<'_> {}
#[must_use]
pub fn basic_formatted_rows(
capture: &str,
screen_width: u16,
) -> BasicFormattedRows<'_> {
BasicFormattedRows {
capture: Some(capture),
width: screen_width,
}
}