Remove attribute type from spans::LinesIterator

This commit is contained in:
Alexandre Bury 2018-02-16 16:05:15 -08:00
parent c220cc679a
commit 25e65a87e8
8 changed files with 101 additions and 88 deletions

View File

@ -1,39 +1,37 @@
use super::segment::SegmentWithText; use super::segment::Segment;
/// Non-splittable piece of text. /// Non-splittable piece of text.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Chunk<'a> { pub struct Chunk {
pub width: usize, pub width: usize,
pub segments: Vec<SegmentWithText<'a>>, pub segments: Vec<Segment>,
pub hard_stop: bool, pub hard_stop: bool,
pub ends_with_space: bool, pub ends_with_space: bool,
} }
impl<'a> Chunk<'a> { impl Chunk {
/// Remove some text from the front. /// Remove some text from the front.
/// ///
/// We're given the length (number of bytes) and the width. /// We're given the length (number of bytes) and the width.
pub fn remove_front(&mut self, mut to_remove: ChunkPart) { pub fn remove_front(&mut self, mut to_remove: ChunkPart) {
// Remove something from each segment until we've removed enough. // Remove something from each segment until we've removed enough.
for segment in &mut self.segments { for segment in &mut self.segments {
if to_remove.length <= segment.seg.end - segment.seg.start { if to_remove.length <= segment.end - segment.start {
// This segment is bigger than what we need to remove // This segment is bigger than what we need to remove
// So just trim the prefix and stop there. // So just trim the prefix and stop there.
segment.seg.start += to_remove.length; segment.start += to_remove.length;
segment.seg.width -= to_remove.width; segment.width -= to_remove.width;
segment.text = &segment.text[to_remove.length..];
self.width -= to_remove.width; self.width -= to_remove.width;
break; break;
} else { } else {
// This segment is too small, so it'll disapear entirely. // This segment is too small, so it'll disapear entirely.
to_remove.length -= segment.seg.end - segment.seg.start; to_remove.length -= segment.end - segment.start;
to_remove.width -= segment.seg.width; to_remove.width -= segment.width;
self.width -= segment.seg.width; self.width -= segment.width;
// Empty this segment // Empty this segment
segment.seg.start = segment.seg.end; segment.start = segment.end;
segment.seg.width = 0; segment.width = 0;
segment.text = "";
} }
} }
} }
@ -59,11 +57,11 @@ impl<'a> Chunk<'a> {
// If yes, just drop it. // If yes, just drop it.
let last_empty = { let last_empty = {
let last = self.segments.last_mut().unwrap(); let last = self.segments.last_mut().unwrap();
last.seg.end -= 1; last.end -= 1;
if self.ends_with_space { if self.ends_with_space {
last.seg.width -= 1; last.width -= 1;
} }
last.seg.start == last.seg.end last.start == last.end
}; };
if last_empty { if last_empty {
self.segments.pop().unwrap(); self.segments.pop().unwrap();

View File

@ -1,18 +1,16 @@
use super::chunk::Chunk; use super::chunk::Chunk;
use super::segment::{Segment, SegmentWithText}; use super::segment::{Segment};
use std::rc::Rc;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use utils::span::SpannedString; use utils::span::SpannedText;
use xi_unicode::LineBreakLeafIter; use xi_unicode::LineBreakLeafIter;
/// Iterator that returns non-breakable chunks of text. /// Iterator that returns non-breakable chunks of text.
/// ///
/// Works accross spans of text. /// Works accross spans of text.
pub struct ChunkIterator<'a, T> pub struct ChunkIterator<S> {
where
T: 'a,
{
/// Input that we want to chunk. /// Input that we want to chunk.
source: &'a SpannedString<T>, source: Rc<S>,
/// ID of the span we are processing. /// ID of the span we are processing.
current_span: usize, current_span: usize,
@ -21,12 +19,9 @@ where
offset: usize, offset: usize,
} }
impl<'a, T> ChunkIterator<'a, T> impl<S> ChunkIterator<S> {
where
T: 'a,
{
/// Creates a new ChunkIterator on the given styled string. /// Creates a new ChunkIterator on the given styled string.
pub fn new(source: &'a SpannedString<T>) -> Self { pub fn new(source: Rc<S>) -> Self {
ChunkIterator { ChunkIterator {
source, source,
current_span: 0, current_span: 0,
@ -39,30 +34,30 @@ where
/// ///
/// These chunks may go accross spans (a single word may be broken into more /// These chunks may go accross spans (a single word may be broken into more
/// than one span, for instance if parts of it are marked up differently). /// than one span, for instance if parts of it are marked up differently).
impl<'a, T> Iterator for ChunkIterator<'a, T> impl<S> Iterator for ChunkIterator<S>
where where
T: 'a, S: SpannedText,
{ {
type Item = Chunk<'a>; type Item = Chunk;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if self.current_span >= self.source.spans_raw().len() { if self.current_span >= self.source.spans().len() {
return None; return None;
} }
// Protect agains empty spans // Protect agains empty spans
if self.source.spans_raw()[self.current_span].is_empty() { if self.source.spans()[self.current_span].as_ref().is_empty() {
self.current_span += 1; self.current_span += 1;
return self.next(); return self.next();
} }
let mut span = &self.source.spans_raw()[self.current_span]; let mut span = self.source.spans()[self.current_span].as_ref();
let mut span_text = span.content.resolve(self.source.source()); let mut span_text = span.resolve(self.source.source());
let mut total_width = 0; let mut total_width = 0;
// We'll use an iterator from xi-unicode to detect possible breaks. // We'll use an iterator from xi-unicode to detect possible breaks.
let text = span.content.resolve(self.source.source()); let text = span.resolve(self.source.source());
let mut iter = LineBreakLeafIter::new(text, self.offset); let mut iter = LineBreakLeafIter::new(text, self.offset);
// We'll accumulate segments from spans. // We'll accumulate segments from spans.
@ -89,9 +84,8 @@ where
let (width, ends_with_space) = if pos == 0 { let (width, ends_with_space) = if pos == 0 {
// If pos = 0, we had a span before. // If pos = 0, we had a span before.
let prev_span = let prev_span =
&self.source.spans_raw()[self.current_span - 1]; self.source.spans()[self.current_span - 1].as_ref();
let prev_text = let prev_text = prev_span.resolve(self.source.source());
prev_span.content.resolve(self.source.source());
(0, prev_text.ends_with(' ')) (0, prev_text.ends_with(' '))
} else { } else {
// We actually got something. // We actually got something.
@ -108,14 +102,11 @@ where
if pos != 0 { if pos != 0 {
// If pos != 0, we got an actual segment of a span. // If pos != 0, we got an actual segment of a span.
total_width += width; total_width += width;
segments.push(SegmentWithText { segments.push(Segment {
seg: Segment {
span_id: self.current_span, span_id: self.current_span,
start: self.offset, start: self.offset,
end: pos, end: pos,
width, width,
},
text: &span_text[self.offset..pos],
}); });
} }
@ -125,19 +116,17 @@ where
self.current_span += 1; self.current_span += 1;
// Skip empty spans // Skip empty spans
while let Some(true) = self.source while let Some(true) =
.spans_raw() self.source.spans().get(self.current_span).map(|span| {
.get(self.current_span) span.as_ref().resolve(self.source.source()).is_empty()
.map(|span| {
span.content.resolve(self.source.source()).is_empty()
}) { }) {
self.current_span += 1; self.current_span += 1;
} }
if self.current_span >= self.source.spans_raw().len() { if self.current_span >= self.source.spans().len() {
// If this was the last chunk, return as is! // If this was the last chunk, return as is!
// Well, make sure we don't end with a newline... // Well, make sure we don't end with a newline...
let text = span.content.resolve(self.source.source()); let text = span.resolve(self.source.source());
let hard_stop = hard_stop || text.ends_with('\n'); let hard_stop = hard_stop || text.ends_with('\n');
return Some(Chunk { return Some(Chunk {
@ -148,8 +137,8 @@ where
}); });
} }
span = &self.source.spans_raw()[self.current_span]; span = self.source.spans()[self.current_span].as_ref();
span_text = span.content.resolve(self.source.source()); span_text = span.resolve(self.source.source());
self.offset = 0; self.offset = 0;
continue; continue;
} }

View File

@ -5,18 +5,20 @@ use super::row::Row;
use super::segment::{Segment, SegmentWithText}; use super::segment::{Segment, SegmentWithText};
use super::segment_merge_iterator::SegmentMergeIterator; use super::segment_merge_iterator::SegmentMergeIterator;
use std::iter::Peekable; use std::iter::Peekable;
use std::rc::Rc;
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use utils::span::SpannedString; use utils::span::SpannedText;
/// Generates rows of text in constrainted width. /// Generates rows of text in constrainted width.
/// ///
/// Works on spans of text. /// Works on spans of text.
pub struct LinesIterator<'a, T> pub struct LinesIterator<S>
where where
T: 'a, S: SpannedText,
{ {
iter: Peekable<ChunkIterator<'a, T>>, iter: Peekable<ChunkIterator<S>>,
source: Rc<S>,
/// Available width /// Available width
width: usize, width: usize,
@ -26,18 +28,27 @@ where
chunk_offset: ChunkPart, chunk_offset: ChunkPart,
} }
impl<'a, T> LinesIterator<'a, T> { impl<S> LinesIterator<S>
where
S: SpannedText,
{
/// Creates a new iterator with the given content and width. /// Creates a new iterator with the given content and width.
pub fn new(source: &'a SpannedString<T>, width: usize) -> Self { pub fn new(source: S, width: usize) -> Self {
let source = Rc::new(source);
let chunk_source = source.clone();
LinesIterator { LinesIterator {
iter: ChunkIterator::new(source).peekable(), iter: ChunkIterator::new(chunk_source).peekable(),
source,
width, width,
chunk_offset: ChunkPart::default(), chunk_offset: ChunkPart::default(),
} }
} }
} }
impl<'a, T> Iterator for LinesIterator<'a, T> { impl<S> Iterator for LinesIterator<S>
where
S: SpannedText,
{
type Item = Row; type Item = Row;
fn next(&mut self) -> Option<Row> { fn next(&mut self) -> Option<Row> {
@ -59,9 +70,13 @@ impl<'a, T> Iterator for LinesIterator<'a, T> {
chunk.remove_front(self.chunk_offset); chunk.remove_front(self.chunk_offset);
// Try to fit part of it? // Try to fit part of it?
let graphemes = chunk.segments.iter().flat_map(|seg| { let source = self.source.as_ref();
let mut offset = seg.seg.start; let graphemes = chunk.segments.iter().flat_map(move |seg| {
seg.text.graphemes(true).map(move |g| { let mut offset = seg.start;
let text = seg.resolve_plain(source);
text.graphemes(true).map(move |g| {
let width = g.width(); let width = g.width();
let start = offset; let start = offset;
let end = offset + g.len(); let end = offset + g.len();
@ -69,15 +84,12 @@ impl<'a, T> Iterator for LinesIterator<'a, T> {
Chunk { Chunk {
width, width,
segments: vec![ segments: vec![
SegmentWithText { Segment {
text: g,
seg: Segment {
width, width,
span_id: seg.seg.span_id, span_id: seg.span_id,
start, start,
end, end,
}, },
},
], ],
hard_stop: false, hard_stop: false,
ends_with_space: false, ends_with_space: false,
@ -103,7 +115,7 @@ impl<'a, T> Iterator for LinesIterator<'a, T> {
let length: usize = chunks let length: usize = chunks
.iter() .iter()
.flat_map(|chunk| chunk.segments.iter()) .flat_map(|chunk| chunk.segments.iter())
.map(|segment| segment.text.len()) .map(|segment| segment.end - segment.start)
.sum(); .sum();
self.chunk_offset.width += width; self.chunk_offset.width += width;
@ -120,7 +132,6 @@ impl<'a, T> Iterator for LinesIterator<'a, T> {
chunks chunks
.into_iter() .into_iter()
.flat_map(|chunk| chunk.segments) .flat_map(|chunk| chunk.segments)
.map(|segment| segment.seg)
.filter(|segment| segment.start != segment.end), .filter(|segment| segment.start != segment.end),
).collect(); ).collect();

View File

@ -2,11 +2,11 @@ use super::chunk::{Chunk, ChunkPart};
use std::iter::Peekable; use std::iter::Peekable;
/// Concatenates chunks as long as they fit in the given width. /// Concatenates chunks as long as they fit in the given width.
pub fn prefix<'a, I>( pub fn prefix<I>(
tokens: &mut Peekable<I>, width: usize, offset: &mut ChunkPart tokens: &mut Peekable<I>, width: usize, offset: &mut ChunkPart
) -> Vec<Chunk<'a>> ) -> Vec<Chunk>
where where
I: Iterator<Item = Chunk<'a>>, I: Iterator<Item = Chunk>,
{ {
let mut available = width; let mut available = width;
let mut chunks = Vec::new(); let mut chunks = Vec::new();

View File

@ -1,5 +1,5 @@
use super::Segment; use super::Segment;
use utils::span::{Span, SpannedString}; use utils::span::{Span, SpannedStr};
/// A list of segments representing a row of text /// A list of segments representing a row of text
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -13,11 +13,11 @@ pub struct Row {
impl Row { impl Row {
/// Resolve the row indices into string slices and attributes. /// Resolve the row indices into string slices and attributes.
pub fn resolve<'a, T>( pub fn resolve<'a, T>(
&self, source: &'a SpannedString<T> &self, source: SpannedStr<'a, T>
) -> Vec<Span<'a, T>> { ) -> Vec<Span<'a, T>> {
self.segments self.segments
.iter() .iter()
.map(|seg| seg.resolve(source)) .map(|seg| seg.resolve(source.clone()))
.collect() .collect()
} }
} }

View File

@ -1,4 +1,4 @@
use utils::span::{Span, SpannedString}; use utils::span::{SpannedStr, Span, SpannedText};
/// Refers to a part of a span /// Refers to a part of a span
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -22,7 +22,9 @@ impl Segment {
} }
/// Resolve this segment to a string slice and an attribute. /// Resolve this segment to a string slice and an attribute.
pub fn resolve<'a, T>(&self, source: &'a SpannedString<T>) -> Span<'a, T> { pub fn resolve<'a, T>(
&self, source: SpannedStr<'a, T>
) -> Span<'a, T> {
let span = &source.spans_raw()[self.span_id]; let span = &source.spans_raw()[self.span_id];
let content = span.content.resolve(source.source()); let content = span.content.resolve(source.source());
@ -33,6 +35,19 @@ impl Segment {
attr: &span.attr, attr: &span.attr,
} }
} }
/// Resolves this segment to plain text.
pub fn resolve_plain<'a, S>(&self, source: &'a S) -> &'a str
where
S: SpannedText,
{
let span = &source.spans()[self.span_id];
let content = span.as_ref().resolve(source.source());
let content = &content[self.start..self.end];
content
}
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]

View File

@ -21,7 +21,7 @@ fn test_line_breaks() {
let iter = LinesIterator::new(&input, 17); let iter = LinesIterator::new(&input, 17);
let rows: Vec<_> = iter.map(|row| row.resolve(&input)).collect(); let rows: Vec<_> = iter.map(|row| row.resolve(input.as_spanned_str())).collect();
assert_eq!( assert_eq!(
&rows[..], &rows[..],

View File

@ -461,7 +461,7 @@ impl View for TextView {
let l = row.width; let l = row.width;
let mut x = self.align.h.get_offset(l, printer.size.x); let mut x = self.align.h.get_offset(l, printer.size.x);
for span in row.resolve(&content.content) { for span in row.resolve(content.content.as_spanned_str()) {
printer.with_style(*span.attr, |printer| { printer.with_style(*span.attr, |printer| {
printer.print((x, 0), span.content); printer.print((x, 0), span.content);
x += span.content.width(); x += span.content.width();