Use spans::LinesIterator from simple::LinesIterator

This commit is contained in:
Alexandre Bury 2018-02-16 17:04:39 -08:00
parent 0ff08f3a9f
commit 2760e92ce6
2 changed files with 39 additions and 70 deletions

View File

@ -1,16 +1,16 @@
use super::{prefix, Row}; use super::{prefix, Row};
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use utils::lines::spans;
use utils::span::{IndexedSpan, SpannedText};
/// Generates rows of text in constrained width. /// Generates rows of text in constrained width.
/// ///
/// Given a long text and a width constraint, it iterates over /// Given a long text and a width constraint, it iterates over
/// substrings of the text, each within the constraint. /// substrings of the text, each within the constraint.
pub struct LinesIterator<'a> { pub struct LinesIterator<'a> {
/// Content to iterate on. iter: spans::LinesIterator<DummySpannedText<'a>>,
content: &'a str,
/// Current offset in the content.
offset: usize,
/// Available width. Don't output lines wider than that. /// Available width. Don't output lines wider than that.
width: usize, width: usize,
@ -19,16 +19,42 @@ pub struct LinesIterator<'a> {
show_spaces: bool, show_spaces: bool,
} }
struct DummySpannedText<'a> {
content: &'a str,
attrs: Vec<IndexedSpan<()>>,
}
impl<'a> DummySpannedText<'a> {
fn new(content: &'a str) -> Self {
let attrs = vec![IndexedSpan::simple(content, ())];
DummySpannedText { content, attrs }
}
}
impl<'a> SpannedText for DummySpannedText<'a> {
type S = IndexedSpan<()>;
fn source(&self) -> &str {
self.content
}
fn spans(&self) -> &[IndexedSpan<()>] {
&self.attrs
}
}
impl<'a> LinesIterator<'a> { impl<'a> LinesIterator<'a> {
/// Returns a new `LinesIterator` on `content`. /// Returns a new `LinesIterator` on `content`.
/// ///
/// Yields rows of `width` cells or less. /// Yields rows of `width` cells or less.
pub fn new(content: &'a str, width: usize) -> Self { pub fn new(content: &'a str, width: usize) -> Self {
let iter =
spans::LinesIterator::new(DummySpannedText::new(content), width);
let show_spaces = false;
LinesIterator { LinesIterator {
content, iter,
width, width,
offset: 0, show_spaces,
show_spaces: false,
} }
} }
@ -46,70 +72,13 @@ impl<'a> Iterator for LinesIterator<'a> {
type Item = Row; type Item = Row;
fn next(&mut self) -> Option<Row> { fn next(&mut self) -> Option<Row> {
if self.offset >= self.content.len() { let row = self.iter.next()?;
// This is the end.
return None;
}
// We start at the current offset. let start = row.segments.first()?.start;
let start = self.offset; let end = row.segments.last()?.end;
let content = &self.content[start..];
// Find the ideal line, in an infinitely wide world. let width = row.width;
// We'll make a line larger than that.
let next = content.find('\n').unwrap_or_else(|| content.len());
let content = &content[..next];
let allowed_width = if self.show_spaces { Some(Row { start, end, width })
// Remove 1 from the available space, if possible.
self.width.saturating_sub(1)
} else {
self.width
};
let line_width = content.width();
if line_width <= allowed_width {
// We found a newline before the allowed limit.
// Break early.
// Advance the cursor to after the newline.
self.offset += next + 1;
return Some(Row {
start: start,
end: start + next,
width: line_width,
});
}
// First attempt: only break on spaces.
let prefix_length =
match prefix(content.split(' '), allowed_width, " ").length {
// If this fail, fallback: only break on graphemes.
// There's no whitespace to skip there.
// And don't reserve the white space anymore.
0 => prefix(content.graphemes(true), self.width, "").length,
other => {
// If it works, advance the cursor by 1
// to jump the whitespace.
// We don't want to add 1 to `prefix_length` though, it
// would include the whitespace in the row.
self.offset += 1;
other
}
};
if prefix_length == 0 {
// This mean we can't even get a single char?
// Sucks. Let's bail.
return None;
}
// Advance the offset to the end of the line.
self.offset += prefix_length;
Some(Row {
start: start,
end: start + prefix_length,
width: self.width,
})
} }
} }

View File

@ -31,7 +31,7 @@ impl Row {
where where
S: AsRef<IndexedCow>, S: AsRef<IndexedCow>,
{ {
let (start, _) = self.segments.get(0)?.source_indices(spans)?; let (start, _) = self.segments.first()?.source_indices(spans)?;
let (_, end) = self.segments.last()?.source_indices(spans)?; let (_, end) = self.segments.last()?.source_indices(spans)?;
Some((start, end)) Some((start, end))