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