mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
Remove attribute type from spans::LinesIterator
This commit is contained in:
parent
c220cc679a
commit
25e65a87e8
@ -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();
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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,14 +84,11 @@ impl<'a, T> Iterator for LinesIterator<'a, T> {
|
|||||||
Chunk {
|
Chunk {
|
||||||
width,
|
width,
|
||||||
segments: vec![
|
segments: vec![
|
||||||
SegmentWithText {
|
Segment {
|
||||||
text: g,
|
width,
|
||||||
seg: Segment {
|
span_id: seg.span_id,
|
||||||
width,
|
start,
|
||||||
span_id: seg.seg.span_id,
|
end,
|
||||||
start,
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
hard_stop: false,
|
hard_stop: 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();
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)]
|
||||||
|
@ -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[..],
|
||||||
|
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user