use crate::{
rope::{
chunk::Chunk,
length_summary::{LengthExtendedGraphemes, LengthNewlines, LengthSummary},
},
utils::Utils,
};
use num::traits::SaturatingSub;
pub struct ExtendedGraphemeIter<'c> {
chunk: &'c Chunk,
chunk_offset: LengthSummary,
}
impl<'c> ExtendedGraphemeIter<'c> {
const INITIAL_CHUNK_OFFSET: LengthSummary = LengthSummary::ZERO;
#[must_use]
pub const fn new(chunk: &'c Chunk) -> Self {
let chunk_offset = Self::INITIAL_CHUNK_OFFSET;
Self { chunk, chunk_offset }
}
fn newline_extended_grapheme_offsets_geq(&self) -> &'c [LengthExtendedGraphemes] {
self.chunk
.newline_extended_grapheme_offsets_geq(self.chunk_offset.extended_graphemes)
}
pub fn advance_to_start_of_next_line(&mut self) -> Option<LengthSummary> {
let start_of_next_line_extended_grapheme_offset =
self.newline_extended_grapheme_offsets_geq().first()?.incremented();
if self.chunk.len().extended_graphemes() <= start_of_next_line_extended_grapheme_offset {
return None;
}
let len_extended_graphemes_advanced = self
.chunk_offset
.extended_graphemes_mut()
.translate_to(start_of_next_line_extended_grapheme_offset);
let length_advanced = LengthSummary::new(LengthNewlines::ONE, len_extended_graphemes_advanced);
self.chunk_offset.lines_mut().increment();
length_advanced.some()
}
pub fn advance_to_end_of_chunk(&mut self) -> LengthSummary {
let len_lines_advanced = self.chunk_offset.lines_mut().translate_to(self.chunk.len().lines());
let len_extended_graphemes_advanced = self
.chunk_offset
.extended_graphemes_mut()
.translate_to(self.chunk.len().extended_graphemes());
LengthSummary::new(len_lines_advanced, len_extended_graphemes_advanced)
}
pub fn advance_within_line(&mut self, len_extended_graphemes: usize) -> Result<LengthSummary, LengthSummary> {
if let Some(newline_extended_grapheme_offset) = self.newline_extended_grapheme_offsets_geq().first() {
let start_of_next_line_extended_grapheme_offset = newline_extended_grapheme_offset.incremented();
let len_extended_graphemes_to_start_of_next_line =
start_of_next_line_extended_grapheme_offset.saturating_sub(&self.chunk_offset.extended_graphemes());
let length = if len_extended_graphemes < len_extended_graphemes_to_start_of_next_line {
LengthSummary::new(0, len_extended_graphemes)
} else {
LengthSummary::new(1, len_extended_graphemes_to_start_of_next_line)
};
self.chunk_offset.saturating_add_assign(&length);
length.ok()
} else {
let len_extended_graphemes_to_end_of_chunk = self
.chunk
.len()
.extended_graphemes()
.saturating_sub(&self.chunk_offset.extended_graphemes());
if len_extended_graphemes <= len_extended_graphemes_to_end_of_chunk {
self.chunk_offset
.extended_graphemes_mut()
.saturating_add_assign(&len_extended_graphemes);
len_extended_graphemes.convert::<LengthSummary>().ok()
} else {
self.chunk_offset
.extended_graphemes_mut()
.saturating_add_assign(&len_extended_graphemes_to_end_of_chunk);
len_extended_graphemes_to_end_of_chunk.convert::<LengthSummary>().err()
}
}
}
}
impl<'r> Iterator for ExtendedGraphemeIter<'r> {
type Item = &'r str;
fn next(&mut self) -> Option<Self::Item> {
let extended_grapheme_offset = self.chunk_offset.extended_graphemes().convert::<usize>();
let extended_grapheme = self
.chunk
.extended_grapheme_byte_index_intervals()
.get(extended_grapheme_offset)?
.clone()
.map_range(usize::from)
.index_into(self.chunk.as_str());
self.chunk_offset.extended_graphemes_mut().increment();
if extended_grapheme.is_newline() {
self.chunk_offset.lines_mut().increment();
}
extended_grapheme.some()
}
}