Type safety for logical indices in line layout (#6848)
This commit is contained in:
parent
64f0c12d0a
commit
a880ac8bbf
|
|
@ -18,25 +18,6 @@ const EN_DASH: char = '–';
|
||||||
const EM_DASH: char = '—';
|
const EM_DASH: char = '—';
|
||||||
const LINE_SEPARATOR: char = '\u{2028}'; // We use LS to distinguish justified breaks.
|
const LINE_SEPARATOR: char = '\u{2028}'; // We use LS to distinguish justified breaks.
|
||||||
|
|
||||||
// We use indices to remember the logical (as opposed to visual) order of items.
|
|
||||||
// During line building, the items are stored in visual (BiDi-reordered) order.
|
|
||||||
// When committing to a line and building its frame, we sort by logical index.
|
|
||||||
//
|
|
||||||
// - Special layout-generated items have custom indices that ensure correct
|
|
||||||
// ordering w.r.t. to each other and normal elements, listed below.
|
|
||||||
// - Normal items have their position in `p.items` plus the number of special
|
|
||||||
// reserved prefix indices.
|
|
||||||
//
|
|
||||||
// Logical indices must be unique within a line because we use an unstable sort.
|
|
||||||
const START_HYPHEN_IDX: usize = 0;
|
|
||||||
const fn logical_item_idx(i: usize) -> usize {
|
|
||||||
// This won't overflow because the `idx` comes from a vector which is
|
|
||||||
// limited to `isize::MAX` elements.
|
|
||||||
i + 1
|
|
||||||
}
|
|
||||||
const FALLBACK_TEXT_IDX: usize = usize::MAX - 1;
|
|
||||||
const END_HYPHEN_IDX: usize = usize::MAX;
|
|
||||||
|
|
||||||
/// A layouted line, consisting of a sequence of layouted inline items that are
|
/// A layouted line, consisting of a sequence of layouted inline items that are
|
||||||
/// mostly borrowed from the preparation phase. This type enables you to measure
|
/// mostly borrowed from the preparation phase. This type enables you to measure
|
||||||
/// the size of a line in a range before committing to building the line's
|
/// the size of a line in a range before committing to building the line's
|
||||||
|
|
@ -180,7 +161,7 @@ pub fn line<'a>(
|
||||||
&& let Some(hyphen) =
|
&& let Some(hyphen) =
|
||||||
ShapedText::hyphen(engine, p.config.fallback, base, trim, false)
|
ShapedText::hyphen(engine, p.config.fallback, base, trim, false)
|
||||||
{
|
{
|
||||||
items.push(Item::Text(hyphen), START_HYPHEN_IDX);
|
items.push(Item::Text(hyphen), LogicalIndex::START_HYPHEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
collect_items(&mut items, engine, p, range, trim);
|
collect_items(&mut items, engine, p, range, trim);
|
||||||
|
|
@ -191,7 +172,7 @@ pub fn line<'a>(
|
||||||
&& let Some(hyphen) =
|
&& let Some(hyphen) =
|
||||||
ShapedText::hyphen(engine, p.config.fallback, base, trim, true)
|
ShapedText::hyphen(engine, p.config.fallback, base, trim, true)
|
||||||
{
|
{
|
||||||
items.push(Item::Text(hyphen), END_HYPHEN_IDX);
|
items.push(Item::Text(hyphen), LogicalIndex::END_HYPHEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that there is no weak spacing at the start and end of the line.
|
// Ensure that there is no weak spacing at the start and end of the line.
|
||||||
|
|
@ -236,7 +217,7 @@ fn collect_items<'a>(
|
||||||
if !items.iter().any(|item| matches!(item, Item::Text(_)))
|
if !items.iter().any(|item| matches!(item, Item::Text(_)))
|
||||||
&& let Some(fallback) = fallback
|
&& let Some(fallback) = fallback
|
||||||
{
|
{
|
||||||
items.push(fallback, FALLBACK_TEXT_IDX);
|
items.push(fallback, LogicalIndex::FALLBACK_TEXT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -302,7 +283,7 @@ fn collect_range<'a>(
|
||||||
fallback: &mut Option<ItemEntry<'a>>,
|
fallback: &mut Option<ItemEntry<'a>>,
|
||||||
) {
|
) {
|
||||||
for (i, (subrange, item)) in p.slice(range.clone()) {
|
for (i, (subrange, item)) in p.slice(range.clone()) {
|
||||||
let idx = logical_item_idx(i);
|
let idx = LogicalIndex::from_item_index(i);
|
||||||
|
|
||||||
// All non-text items are just kept, they can't be split.
|
// All non-text items are just kept, they can't be split.
|
||||||
let Item::Text(shaped) = item else {
|
let Item::Text(shaped) = item else {
|
||||||
|
|
@ -538,7 +519,7 @@ pub fn commit(
|
||||||
// Build the frames and determine the height and baseline.
|
// Build the frames and determine the height and baseline.
|
||||||
let mut frames = vec![];
|
let mut frames = vec![];
|
||||||
for &(idx, ref item) in line.items.indexed_iter() {
|
for &(idx, ref item) in line.items.indexed_iter() {
|
||||||
let mut push = |offset: &mut Abs, frame: Frame, idx: usize| {
|
let mut push = |offset: &mut Abs, frame: Frame, idx: LogicalIndex| {
|
||||||
let width = frame.width();
|
let width = frame.width();
|
||||||
top.set_max(frame.baseline());
|
top.set_max(frame.baseline());
|
||||||
bottom.set_max(frame.size().y - frame.baseline());
|
bottom.set_max(frame.size().y - frame.baseline());
|
||||||
|
|
@ -670,7 +651,7 @@ fn overhang(c: char) -> f64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A collection of owned or borrowed inline items.
|
/// A collection of owned or borrowed inline items.
|
||||||
pub struct Items<'a>(Vec<(usize, ItemEntry<'a>)>);
|
pub struct Items<'a>(Vec<(LogicalIndex, ItemEntry<'a>)>);
|
||||||
|
|
||||||
impl<'a> Items<'a> {
|
impl<'a> Items<'a> {
|
||||||
/// Create empty items.
|
/// Create empty items.
|
||||||
|
|
@ -679,7 +660,7 @@ impl<'a> Items<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push a new item.
|
/// Push a new item.
|
||||||
pub fn push(&mut self, entry: impl Into<ItemEntry<'a>>, idx: usize) {
|
pub fn push(&mut self, entry: impl Into<ItemEntry<'a>>, idx: LogicalIndex) {
|
||||||
self.0.push((idx, entry.into()));
|
self.0.push((idx, entry.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -695,7 +676,7 @@ impl<'a> Items<'a> {
|
||||||
/// provide the indices in visual order!
|
/// provide the indices in visual order!
|
||||||
pub fn indexed_iter(
|
pub fn indexed_iter(
|
||||||
&self,
|
&self,
|
||||||
) -> impl DoubleEndedIterator<Item = &(usize, ItemEntry<'a>)> {
|
) -> impl DoubleEndedIterator<Item = &(LogicalIndex, ItemEntry<'a>)> {
|
||||||
self.0.iter()
|
self.0.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -726,7 +707,7 @@ impl<'a> Items<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Deref for Items<'a> {
|
impl<'a> Deref for Items<'a> {
|
||||||
type Target = Vec<(usize, ItemEntry<'a>)>;
|
type Target = Vec<(LogicalIndex, ItemEntry<'a>)>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
|
|
@ -745,6 +726,34 @@ impl Debug for Items<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We use indices to remember the logical (as opposed to visual) order of
|
||||||
|
/// items. During line building, the items are stored in visual (BiDi-reordered)
|
||||||
|
/// order. When committing to a line and building its frame, we sort by logical
|
||||||
|
/// index.
|
||||||
|
///
|
||||||
|
/// - Special layout-generated items have custom indices that ensure correct
|
||||||
|
/// ordering w.r.t. to each other and normal elements, listed below.
|
||||||
|
/// - Normal items have their position in `p.items` plus the number of special
|
||||||
|
/// reserved prefix indices.
|
||||||
|
///
|
||||||
|
/// Logical indices must be unique within a line because we use an unstable
|
||||||
|
/// sort.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
|
pub struct LogicalIndex(usize);
|
||||||
|
|
||||||
|
impl LogicalIndex {
|
||||||
|
const START_HYPHEN: Self = Self(0);
|
||||||
|
const FALLBACK_TEXT: Self = Self(usize::MAX - 1);
|
||||||
|
const END_HYPHEN: Self = Self(usize::MAX);
|
||||||
|
|
||||||
|
/// Create a logical index from the index of an item in the [`p.items`](Preparation::items).
|
||||||
|
const fn from_item_index(i: usize) -> Self {
|
||||||
|
// This won't overflow because the `idx` comes from a vector which is
|
||||||
|
// limited to `isize::MAX` elements.
|
||||||
|
Self(i + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A reference to or a boxed item.
|
/// A reference to or a boxed item.
|
||||||
///
|
///
|
||||||
/// This is conceptually similar to a [`Cow<'a, Item<'a>>`][std::borrow::Cow],
|
/// This is conceptually similar to a [`Cow<'a, Item<'a>>`][std::borrow::Cow],
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue