Refactor line reordering from callback to iterator

This commit is contained in:
Laurenz 2021-09-27 22:36:07 +02:00
parent ed0c804017
commit f1ab290572

View File

@ -1,6 +1,7 @@
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::rc::Rc; use std::rc::Rc;
use itertools::Either;
use unicode_bidi::{BidiInfo, Level}; use unicode_bidi::{BidiInfo, Level};
use xi_unicode::LineBreakIterator; use xi_unicode::LineBreakIterator;
@ -437,7 +438,7 @@ impl<'a> LineLayout<'a> {
let mut offset = Length::zero(); let mut offset = Length::zero();
let mut ruler = Align::Start; let mut ruler = Align::Start;
self.reordered(ctx, |ctx, item| { for item in self.reordered() {
let mut position = |frame: &Frame, align| { let mut position = |frame: &Frame, align| {
// FIXME: Ruler alignment for RTL. // FIXME: Ruler alignment for RTL.
ruler = ruler.max(align); ruler = ruler.max(align);
@ -468,50 +469,44 @@ impl<'a> LineLayout<'a> {
output.push_frame(pos, frame); output.push_frame(pos, frame);
} }
} }
}); }
output output
} }
/// Iterate through the line's items in visual order. /// Iterate through the line's items in visual order.
fn reordered<F>(&self, ctx: &LayoutContext, mut f: F) fn reordered(&self) -> impl Iterator<Item = &ParItem<'a>> {
where
F: FnMut(&LayoutContext, &ParItem<'a>),
{
// The bidi crate doesn't like empty lines. // The bidi crate doesn't like empty lines.
if self.line.is_empty() { let (levels, runs) = if !self.line.is_empty() {
return; // Find the paragraph that contains the line.
} let para = self
.bidi
.paragraphs
.iter()
.find(|para| para.range.contains(&self.line.start))
.unwrap();
// Find the paragraph that contains the line. // Compute the reordered ranges in visual order (left to right).
let para = self self.bidi.visual_runs(para, self.line.clone())
.bidi } else {
.paragraphs <_>::default()
.iter() };
.find(|para| para.range.contains(&self.line.start))
.unwrap();
// Compute the reordered ranges in visual order (left to right). runs.into_iter()
let (levels, runs) = self.bidi.visual_runs(para, self.line.clone()); .flat_map(move |run| {
let first_idx = self.find(run.start).unwrap();
let last_idx = self.find(run.end - 1).unwrap();
let range = first_idx ..= last_idx;
// Find the items for each run. // Provide the items forwards or backwards depending on the run's
for run in runs { // direction.
let first_idx = self.find(run.start).unwrap(); if levels[run.start].is_ltr() {
let last_idx = self.find(run.end - 1).unwrap(); Either::Left(range)
let range = first_idx ..= last_idx; } else {
Either::Right(range.rev())
// Provide the items forwards or backwards depending on the run's
// direction.
if levels[run.start].is_ltr() {
for item in range {
f(ctx, self.get(item).unwrap());
} }
} else { })
for item in range.rev() { .map(move |idx| self.get(idx).unwrap())
f(ctx, self.get(item).unwrap());
}
}
}
} }
/// Find the index of the item whose range contains the `text_offset`. /// Find the index of the item whose range contains the `text_offset`.