use super::chunk::Chunk;
use super::segment::Segment;
use crate::utils::span::SpannedText;
use std::rc::Rc;
use unicode_width::UnicodeWidthStr;
use xi_unicode::LineBreakLeafIter;
pub struct ChunkIterator<S> {
source: Rc<S>,
current_span: usize,
offset: usize,
}
impl<S> ChunkIterator<S> {
pub fn new(source: Rc<S>) -> Self {
ChunkIterator {
source,
current_span: 0,
offset: 0,
}
}
}
impl<S> Iterator for ChunkIterator<S>
where
S: SpannedText,
{
type Item = Chunk;
fn next(&mut self) -> Option<Self::Item> {
if self.current_span >= self.source.spans().len() {
return None;
}
if self.source.spans()[self.current_span].as_ref().is_empty() {
self.current_span += 1;
return self.next();
}
let mut span = self.source.spans()[self.current_span].as_ref();
let mut span_text = span.resolve(self.source.source());
let mut total_width = 0;
let mut segments = Vec::new();
let mut iter = LineBreakLeafIter::new(span_text, self.offset);
loop {
let (pos, hard_stop) = iter.next(span_text);
let (width, ends_with_space) = if pos == 0 {
let prev_span =
self.source.spans()[self.current_span - 1].as_ref();
let prev_text = prev_span.resolve(self.source.source());
(0, prev_text.ends_with(' '))
} else {
let text = &span_text[self.offset..pos];
(text.width(), text.ends_with(' '))
};
if pos != 0 {
total_width += width;
segments.push(Segment {
span_id: self.current_span,
start: self.offset,
end: pos,
width,
});
}
if pos == span_text.len() {
self.current_span += 1;
while let Some(true) =
self.source.spans().get(self.current_span).map(|span| {
span.as_ref().resolve(self.source.source()).is_empty()
})
{
self.current_span += 1;
}
if self.current_span >= self.source.spans().len() {
let hard_stop = hard_stop || span_text.ends_with('\n');
return Some(Chunk {
width: total_width,
segments,
hard_stop,
ends_with_space,
});
}
span = self.source.spans()[self.current_span].as_ref();
span_text = span.resolve(self.source.source());
self.offset = 0;
continue;
}
self.offset = pos;
return Some(Chunk {
width: total_width,
segments,
hard_stop,
ends_with_space,
});
}
}
}