Pure location assignment (#4352)
@ -3,7 +3,7 @@ use ecow::{eco_vec, EcoString, EcoVec};
|
|||||||
use typst::engine::{Engine, Route};
|
use typst::engine::{Engine, Route};
|
||||||
use typst::eval::{Tracer, Vm};
|
use typst::eval::{Tracer, Vm};
|
||||||
use typst::foundations::{Context, Label, Scopes, Styles, Value};
|
use typst::foundations::{Context, Label, Scopes, Styles, Value};
|
||||||
use typst::introspection::{Introspector, Locator};
|
use typst::introspection::Introspector;
|
||||||
use typst::model::{BibliographyElem, Document};
|
use typst::model::{BibliographyElem, Document};
|
||||||
use typst::syntax::{ast, LinkedNode, Span, SyntaxKind};
|
use typst::syntax::{ast, LinkedNode, Span, SyntaxKind};
|
||||||
use typst::World;
|
use typst::World;
|
||||||
@ -58,14 +58,12 @@ pub fn analyze_import(world: &dyn World, source: &LinkedNode) -> Option<Value> {
|
|||||||
return Some(source);
|
return Some(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut locator = Locator::default();
|
|
||||||
let introspector = Introspector::default();
|
let introspector = Introspector::default();
|
||||||
let mut tracer = Tracer::new();
|
let mut tracer = Tracer::new();
|
||||||
let engine = Engine {
|
let engine = Engine {
|
||||||
world: world.track(),
|
world: world.track(),
|
||||||
route: Route::default(),
|
route: Route::default(),
|
||||||
introspector: introspector.track(),
|
introspector: introspector.track(),
|
||||||
locator: &mut locator,
|
|
||||||
tracer: tracer.track_mut(),
|
tracer: tracer.track_mut(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -59,6 +59,8 @@ enum EventKind {
|
|||||||
/// Enable the timer.
|
/// Enable the timer.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn enable() {
|
pub fn enable() {
|
||||||
|
// We only need atomicity and no synchronization of other
|
||||||
|
// operations, so `Relaxed` is fine.
|
||||||
ENABLED.store(true, Relaxed);
|
ENABLED.store(true, Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,9 @@ use std::any::Any;
|
|||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use portable_atomic::{AtomicU128, Ordering};
|
use portable_atomic::AtomicU128;
|
||||||
use siphasher::sip128::{Hasher128, SipHasher13};
|
use siphasher::sip128::{Hasher128, SipHasher13};
|
||||||
|
|
||||||
/// A wrapper type with lazily-computed hash.
|
/// A wrapper type with lazily-computed hash.
|
||||||
@ -70,7 +71,9 @@ impl<T: ?Sized> LazyHash<T> {
|
|||||||
/// Get the hash, returns zero if not computed yet.
|
/// Get the hash, returns zero if not computed yet.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn load_hash(&self) -> u128 {
|
fn load_hash(&self) -> u128 {
|
||||||
self.hash.load(Ordering::SeqCst)
|
// We only need atomicity and no synchronization of other operations, so
|
||||||
|
// `Relaxed` is fine.
|
||||||
|
self.hash.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,20 +81,18 @@ impl<T: Hash + ?Sized + 'static> LazyHash<T> {
|
|||||||
/// Get the hash or compute it if not set yet.
|
/// Get the hash or compute it if not set yet.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn load_or_compute_hash(&self) -> u128 {
|
fn load_or_compute_hash(&self) -> u128 {
|
||||||
let hash = self.load_hash();
|
let mut hash = self.load_hash();
|
||||||
if hash == 0 {
|
if hash == 0 {
|
||||||
let hashed = hash_item(&self.value);
|
hash = hash_item(&self.value);
|
||||||
self.hash.store(hashed, Ordering::SeqCst);
|
self.hash.store(hash, Ordering::Relaxed);
|
||||||
hashed
|
|
||||||
} else {
|
|
||||||
hash
|
|
||||||
}
|
}
|
||||||
|
hash
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset the hash to zero.
|
/// Reset the hash to zero.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn reset_hash(&mut self) {
|
fn reset_hash(&mut self) {
|
||||||
// Because we have a mutable reference, we can skip the atomic
|
// Because we have a mutable reference, we can skip the atomic.
|
||||||
*self.hash.get_mut() = 0;
|
*self.hash.get_mut() = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -366,13 +366,12 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> At<T> for Result<T, HintedString> {
|
impl<T> At<T> for HintedStrResult<T> {
|
||||||
fn at(self, span: Span) -> SourceResult<T> {
|
fn at(self, span: Span) -> SourceResult<T> {
|
||||||
self.map_err(|err| {
|
self.map_err(|err| {
|
||||||
let mut components = err.0.into_iter();
|
let mut components = err.0.into_iter();
|
||||||
let message = components.next().unwrap();
|
let message = components.next().unwrap();
|
||||||
let diag = SourceDiagnostic::error(span, message).with_hints(components);
|
let diag = SourceDiagnostic::error(span, message).with_hints(components);
|
||||||
|
|
||||||
eco_vec![diag]
|
eco_vec![diag]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use comemo::{Track, Tracked, TrackedMut, Validate};
|
|||||||
|
|
||||||
use crate::diag::SourceResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::eval::Tracer;
|
use crate::eval::Tracer;
|
||||||
use crate::introspection::{Introspector, Locator};
|
use crate::introspection::Introspector;
|
||||||
use crate::syntax::FileId;
|
use crate::syntax::FileId;
|
||||||
use crate::World;
|
use crate::World;
|
||||||
|
|
||||||
@ -19,8 +19,6 @@ pub struct Engine<'a> {
|
|||||||
/// The route the engine took during compilation. This is used to detect
|
/// The route the engine took during compilation. This is used to detect
|
||||||
/// cyclic imports and excessive nesting.
|
/// cyclic imports and excessive nesting.
|
||||||
pub route: Route<'a>,
|
pub route: Route<'a>,
|
||||||
/// Provides stable identities to elements.
|
|
||||||
pub locator: &'a mut Locator<'a>,
|
|
||||||
/// The tracer for inspection of the values an expression produces.
|
/// The tracer for inspection of the values an expression produces.
|
||||||
pub tracer: TrackedMut<'a, Tracer>,
|
pub tracer: TrackedMut<'a, Tracer>,
|
||||||
}
|
}
|
||||||
@ -148,6 +146,8 @@ impl<'a> Route<'a> {
|
|||||||
|
|
||||||
/// Whether the route's depth is less than or equal to the given depth.
|
/// Whether the route's depth is less than or equal to the given depth.
|
||||||
pub fn within(&self, depth: usize) -> bool {
|
pub fn within(&self, depth: usize) -> bool {
|
||||||
|
// We only need atomicity and no synchronization of other operations, so
|
||||||
|
// `Relaxed` is fine.
|
||||||
use Ordering::Relaxed;
|
use Ordering::Relaxed;
|
||||||
|
|
||||||
let upper = self.upper.load(Relaxed);
|
let upper = self.upper.load(Relaxed);
|
||||||
@ -183,8 +183,6 @@ impl Clone for Route<'_> {
|
|||||||
outer: self.outer,
|
outer: self.outer,
|
||||||
id: self.id,
|
id: self.id,
|
||||||
len: self.len,
|
len: self.len,
|
||||||
// The ordering doesn't really matter since it's the upper bound
|
|
||||||
// is only an optimization.
|
|
||||||
upper: AtomicUsize::new(self.upper.load(Ordering::Relaxed)),
|
upper: AtomicUsize::new(self.upper.load(Ordering::Relaxed)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use crate::foundations::{
|
|||||||
call_method_mut, is_mutating_method, Arg, Args, Bytes, Capturer, Closure, Content,
|
call_method_mut, is_mutating_method, Arg, Args, Bytes, Capturer, Closure, Content,
|
||||||
Context, Func, IntoValue, NativeElement, Scope, Scopes, Value,
|
Context, Func, IntoValue, NativeElement, Scope, Scopes, Value,
|
||||||
};
|
};
|
||||||
use crate::introspection::{Introspector, Locator};
|
use crate::introspection::Introspector;
|
||||||
use crate::math::{Accent, AccentElem, LrElem};
|
use crate::math::{Accent, AccentElem, LrElem};
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
use crate::syntax::ast::{self, AstNode};
|
use crate::syntax::ast::{self, AstNode};
|
||||||
@ -276,7 +276,6 @@ pub(crate) fn call_closure(
|
|||||||
world: Tracked<dyn World + '_>,
|
world: Tracked<dyn World + '_>,
|
||||||
introspector: Tracked<Introspector>,
|
introspector: Tracked<Introspector>,
|
||||||
route: Tracked<Route>,
|
route: Tracked<Route>,
|
||||||
locator: Tracked<Locator>,
|
|
||||||
tracer: TrackedMut<Tracer>,
|
tracer: TrackedMut<Tracer>,
|
||||||
context: Tracked<Context>,
|
context: Tracked<Context>,
|
||||||
mut args: Args,
|
mut args: Args,
|
||||||
@ -292,12 +291,10 @@ pub(crate) fn call_closure(
|
|||||||
scopes.top = closure.captured.clone();
|
scopes.top = closure.captured.clone();
|
||||||
|
|
||||||
// Prepare the engine.
|
// Prepare the engine.
|
||||||
let mut locator = Locator::chained(locator);
|
|
||||||
let engine = Engine {
|
let engine = Engine {
|
||||||
world,
|
world,
|
||||||
introspector,
|
introspector,
|
||||||
route: Route::extend(route),
|
route: Route::extend(route),
|
||||||
locator: &mut locator,
|
|
||||||
tracer,
|
tracer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ use comemo::{Track, Tracked, TrackedMut};
|
|||||||
use crate::diag::{bail, SourceResult};
|
use crate::diag::{bail, SourceResult};
|
||||||
use crate::engine::{Engine, Route};
|
use crate::engine::{Engine, Route};
|
||||||
use crate::foundations::{Cast, Context, Module, NativeElement, Scope, Scopes, Value};
|
use crate::foundations::{Cast, Context, Module, NativeElement, Scope, Scopes, Value};
|
||||||
use crate::introspection::{Introspector, Locator};
|
use crate::introspection::Introspector;
|
||||||
use crate::math::EquationElem;
|
use crate::math::EquationElem;
|
||||||
use crate::syntax::{ast, parse, parse_code, parse_math, Source, Span};
|
use crate::syntax::{ast, parse, parse_code, parse_math, Source, Span};
|
||||||
use crate::World;
|
use crate::World;
|
||||||
@ -49,13 +49,11 @@ pub fn eval(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the engine.
|
// Prepare the engine.
|
||||||
let mut locator = Locator::new();
|
|
||||||
let introspector = Introspector::default();
|
let introspector = Introspector::default();
|
||||||
let engine = Engine {
|
let engine = Engine {
|
||||||
world,
|
world,
|
||||||
route: Route::extend(route).with_id(id),
|
route: Route::extend(route).with_id(id),
|
||||||
introspector: introspector.track(),
|
introspector: introspector.track(),
|
||||||
locator: &mut locator,
|
|
||||||
tracer,
|
tracer,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -118,13 +116,11 @@ pub fn eval_string(
|
|||||||
|
|
||||||
// Prepare the engine.
|
// Prepare the engine.
|
||||||
let mut tracer = Tracer::new();
|
let mut tracer = Tracer::new();
|
||||||
let mut locator = Locator::new();
|
|
||||||
let introspector = Introspector::default();
|
let introspector = Introspector::default();
|
||||||
let engine = Engine {
|
let engine = Engine {
|
||||||
world,
|
world,
|
||||||
introspector: introspector.track(),
|
introspector: introspector.track(),
|
||||||
route: Route::default(),
|
route: Route::default(),
|
||||||
locator: &mut locator,
|
|
||||||
tracer: tracer.track_mut(),
|
tracer: tracer.track_mut(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ use crate::foundations::{
|
|||||||
NativeElement, Recipe, RecipeIndex, Repr, Selector, Str, Style, StyleChain, Styles,
|
NativeElement, Recipe, RecipeIndex, Repr, Selector, Str, Style, StyleChain, Styles,
|
||||||
Value,
|
Value,
|
||||||
};
|
};
|
||||||
use crate::introspection::{Location, TagElem};
|
use crate::introspection::Location;
|
||||||
use crate::layout::{AlignElem, Alignment, Axes, Length, MoveElem, PadElem, Rel, Sides};
|
use crate::layout::{AlignElem, Alignment, Axes, Length, MoveElem, PadElem, Rel, Sides};
|
||||||
use crate::model::{Destination, EmphElem, LinkElem, StrongElem};
|
use crate::model::{Destination, EmphElem, LinkElem, StrongElem};
|
||||||
use crate::realize::{Behave, Behaviour};
|
use crate::realize::{Behave, Behaviour};
|
||||||
@ -494,7 +494,7 @@ impl Content {
|
|||||||
pub fn backlinked(self, loc: Location) -> Self {
|
pub fn backlinked(self, loc: Location) -> Self {
|
||||||
let mut backlink = Content::empty().spanned(self.span());
|
let mut backlink = Content::empty().spanned(self.span());
|
||||||
backlink.set_location(loc);
|
backlink.set_location(loc);
|
||||||
TagElem::packed(backlink) + self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set alignments for this content.
|
/// Set alignments for this content.
|
||||||
|
@ -298,7 +298,6 @@ impl Func {
|
|||||||
engine.world,
|
engine.world,
|
||||||
engine.introspector,
|
engine.introspector,
|
||||||
engine.route.track(),
|
engine.route.track(),
|
||||||
engine.locator.track(),
|
|
||||||
TrackedMut::reborrow_mut(&mut engine.tracer),
|
TrackedMut::reborrow_mut(&mut engine.tracer),
|
||||||
context,
|
context,
|
||||||
args,
|
args,
|
||||||
|
@ -13,7 +13,7 @@ use crate::foundations::{
|
|||||||
Element, Func, IntoValue, Label, LocatableSelector, NativeElement, Packed, Repr,
|
Element, Func, IntoValue, Label, LocatableSelector, NativeElement, Packed, Repr,
|
||||||
Selector, Show, Smart, Str, StyleChain, Value,
|
Selector, Show, Smart, Str, StyleChain, Value,
|
||||||
};
|
};
|
||||||
use crate::introspection::{Introspector, Locatable, Location, Locator};
|
use crate::introspection::{Introspector, Locatable, Location};
|
||||||
use crate::layout::{Frame, FrameItem, PageElem};
|
use crate::layout::{Frame, FrameItem, PageElem};
|
||||||
use crate::math::EquationElem;
|
use crate::math::EquationElem;
|
||||||
use crate::model::{FigureElem, HeadingElem, Numbering, NumberingPattern};
|
use crate::model::{FigureElem, HeadingElem, Numbering, NumberingPattern};
|
||||||
@ -282,7 +282,6 @@ impl Counter {
|
|||||||
engine.world,
|
engine.world,
|
||||||
engine.introspector,
|
engine.introspector,
|
||||||
engine.route.track(),
|
engine.route.track(),
|
||||||
engine.locator.track(),
|
|
||||||
TrackedMut::reborrow_mut(&mut engine.tracer),
|
TrackedMut::reborrow_mut(&mut engine.tracer),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -294,15 +293,12 @@ impl Counter {
|
|||||||
world: Tracked<dyn World + '_>,
|
world: Tracked<dyn World + '_>,
|
||||||
introspector: Tracked<Introspector>,
|
introspector: Tracked<Introspector>,
|
||||||
route: Tracked<Route>,
|
route: Tracked<Route>,
|
||||||
locator: Tracked<Locator>,
|
|
||||||
tracer: TrackedMut<Tracer>,
|
tracer: TrackedMut<Tracer>,
|
||||||
) -> SourceResult<EcoVec<(CounterState, NonZeroUsize)>> {
|
) -> SourceResult<EcoVec<(CounterState, NonZeroUsize)>> {
|
||||||
let mut locator = Locator::chained(locator);
|
|
||||||
let mut engine = Engine {
|
let mut engine = Engine {
|
||||||
world,
|
world,
|
||||||
introspector,
|
introspector,
|
||||||
route: Route::extend(route).unnested(),
|
route: Route::extend(route).unnested(),
|
||||||
locator: &mut locator,
|
|
||||||
tracer,
|
tracer,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -815,8 +811,8 @@ impl ManualPageCounter {
|
|||||||
for (_, item) in page.items() {
|
for (_, item) in page.items() {
|
||||||
match item {
|
match item {
|
||||||
FrameItem::Group(group) => self.visit(engine, &group.frame)?,
|
FrameItem::Group(group) => self.visit(engine, &group.frame)?,
|
||||||
FrameItem::Tag(elem) => {
|
FrameItem::Tag(tag) => {
|
||||||
let Some(elem) = elem.to_packed::<CounterUpdateElem>() else {
|
let Some(elem) = tag.elem.to_packed::<CounterUpdateElem>() else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if *elem.key() == CounterKey::Page {
|
if *elem.key() == CounterKey::Page {
|
||||||
|
@ -16,7 +16,7 @@ use crate::model::Numbering;
|
|||||||
use crate::utils::NonZeroExt;
|
use crate::utils::NonZeroExt;
|
||||||
|
|
||||||
/// Can be queried for elements and their positions.
|
/// Can be queried for elements and their positions.
|
||||||
#[derive(Clone)]
|
#[derive(Default, Clone)]
|
||||||
pub struct Introspector {
|
pub struct Introspector {
|
||||||
/// The number of pages in the document.
|
/// The number of pages in the document.
|
||||||
pages: usize,
|
pages: usize,
|
||||||
@ -25,6 +25,9 @@ pub struct Introspector {
|
|||||||
/// Maps labels to their indices in the element list. We use a smallvec such
|
/// Maps labels to their indices in the element list. We use a smallvec such
|
||||||
/// that if the label is unique, we don't need to allocate.
|
/// that if the label is unique, we don't need to allocate.
|
||||||
labels: HashMap<Label, SmallVec<[usize; 1]>>,
|
labels: HashMap<Label, SmallVec<[usize; 1]>>,
|
||||||
|
/// Maps from element keys to the locations of all elements that had this
|
||||||
|
/// key. Used for introspector-assisted location assignment.
|
||||||
|
keys: HashMap<u128, SmallVec<[Location; 1]>>,
|
||||||
/// The page numberings, indexed by page number minus 1.
|
/// The page numberings, indexed by page number minus 1.
|
||||||
page_numberings: Vec<Option<Numbering>>,
|
page_numberings: Vec<Option<Numbering>>,
|
||||||
/// Caches queries done on the introspector. This is important because
|
/// Caches queries done on the introspector. This is important because
|
||||||
@ -41,6 +44,7 @@ impl Introspector {
|
|||||||
self.pages = pages.len();
|
self.pages = pages.len();
|
||||||
self.elems.clear();
|
self.elems.clear();
|
||||||
self.labels.clear();
|
self.labels.clear();
|
||||||
|
self.keys.clear();
|
||||||
self.page_numberings.clear();
|
self.page_numberings.clear();
|
||||||
self.queries.clear();
|
self.queries.clear();
|
||||||
|
|
||||||
@ -61,18 +65,21 @@ impl Introspector {
|
|||||||
.pre_concat(group.transform);
|
.pre_concat(group.transform);
|
||||||
self.extract(&group.frame, page, ts);
|
self.extract(&group.frame, page, ts);
|
||||||
}
|
}
|
||||||
FrameItem::Tag(elem)
|
FrameItem::Tag(tag)
|
||||||
if !self.elems.contains_key(&elem.location().unwrap()) =>
|
if !self.elems.contains_key(&tag.elem.location().unwrap()) =>
|
||||||
{
|
{
|
||||||
let pos = pos.transform(ts);
|
let pos = pos.transform(ts);
|
||||||
let ret = self.elems.insert(
|
let loc = tag.elem.location().unwrap();
|
||||||
elem.location().unwrap(),
|
let ret = self
|
||||||
(elem.clone(), Position { page, point: pos }),
|
.elems
|
||||||
);
|
.insert(loc, (tag.elem.clone(), Position { page, point: pos }));
|
||||||
assert!(ret.is_none(), "duplicate locations");
|
assert!(ret.is_none(), "duplicate locations");
|
||||||
|
|
||||||
|
// Build the key map.
|
||||||
|
self.keys.entry(tag.key).or_default().push(loc);
|
||||||
|
|
||||||
// Build the label cache.
|
// Build the label cache.
|
||||||
if let Some(label) = elem.label() {
|
if let Some(label) = tag.elem.label() {
|
||||||
self.labels.entry(label).or_default().push(self.elems.len() - 1);
|
self.labels.entry(label).or_default().push(self.elems.len() - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,21 +93,24 @@ impl Introspector {
|
|||||||
self.elems.values().map(|(c, _)| c)
|
self.elems.values().map(|(c, _)| c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Perform a binary search for `elem` among the `list`.
|
||||||
|
fn binary_search(&self, list: &[Content], elem: &Content) -> Result<usize, usize> {
|
||||||
|
list.binary_search_by_key(&self.elem_index(elem), |elem| self.elem_index(elem))
|
||||||
|
}
|
||||||
|
|
||||||
/// Get an element by its location.
|
/// Get an element by its location.
|
||||||
fn get(&self, location: &Location) -> Option<&Content> {
|
fn get(&self, location: &Location) -> Option<&Content> {
|
||||||
self.elems.get(location).map(|(elem, _)| elem)
|
self.elems.get(location).map(|(elem, _)| elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the index of this element among all.
|
/// Get the index of this element among all.
|
||||||
fn index(&self, elem: &Content) -> usize {
|
fn elem_index(&self, elem: &Content) -> usize {
|
||||||
self.elems
|
self.loc_index(&elem.location().unwrap())
|
||||||
.get_index_of(&elem.location().unwrap())
|
|
||||||
.unwrap_or(usize::MAX)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform a binary search for `elem` among the `list`.
|
/// Get the index of the element with this location among all.
|
||||||
fn binary_search(&self, list: &[Content], elem: &Content) -> Result<usize, usize> {
|
fn loc_index(&self, location: &Location) -> usize {
|
||||||
list.binary_search_by_key(&self.index(elem), |elem| self.index(elem))
|
self.elems.get_index_of(location).unwrap_or(usize::MAX)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +193,7 @@ impl Introspector {
|
|||||||
Selector::Or(selectors) => selectors
|
Selector::Or(selectors) => selectors
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|sel| self.query(sel))
|
.flat_map(|sel| self.query(sel))
|
||||||
.map(|elem| self.index(&elem))
|
.map(|elem| self.elem_index(&elem))
|
||||||
.collect::<BTreeSet<usize>>()
|
.collect::<BTreeSet<usize>>()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|index| self.elems[index].0.clone())
|
.map(|index| self.elems[index].0.clone())
|
||||||
@ -283,17 +293,20 @@ impl Introspector {
|
|||||||
.map(|&(_, pos)| pos)
|
.map(|&(_, pos)| pos)
|
||||||
.unwrap_or(Position { page: NonZeroUsize::ONE, point: Point::zero() })
|
.unwrap_or(Position { page: NonZeroUsize::ONE, point: Point::zero() })
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Introspector {
|
/// Try to find a location for an element with the given `key` hash
|
||||||
fn default() -> Self {
|
/// that is closest after the `anchor`.
|
||||||
Self {
|
///
|
||||||
pages: 0,
|
/// This is used for introspector-assisted location assignment during
|
||||||
elems: IndexMap::new(),
|
/// measurement. See the "Dealing with Measurement" section of the
|
||||||
labels: HashMap::new(),
|
/// [`Locator`](crate::introspection::Locator) docs for more details.
|
||||||
page_numberings: vec![],
|
pub fn locator(&self, key: u128, anchor: Location) -> Option<Location> {
|
||||||
queries: QueryCache::default(),
|
let anchor = self.loc_index(&anchor);
|
||||||
}
|
self.keys
|
||||||
|
.get(&key)?
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.min_by_key(|loc| self.loc_index(loc).wrapping_sub(anchor))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
|
|
||||||
use ecow::EcoString;
|
use ecow::EcoString;
|
||||||
@ -21,26 +22,27 @@ use crate::model::Numbering;
|
|||||||
/// elements, but you will find only those that have an explicit label attached
|
/// elements, but you will find only those that have an explicit label attached
|
||||||
/// to them. This limitation will be resolved in the future.
|
/// to them. This limitation will be resolved in the future.
|
||||||
#[ty(scope)]
|
#[ty(scope)]
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct Location {
|
pub struct Location(u128);
|
||||||
/// The hash of the element.
|
|
||||||
pub hash: u128,
|
|
||||||
/// An unique number among elements with the same hash. This is the reason
|
|
||||||
/// we need a `Locator` everywhere.
|
|
||||||
pub disambiguator: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Location {
|
impl Location {
|
||||||
|
/// Create a new location from a unique hash.
|
||||||
|
pub fn new(hash: u128) -> Self {
|
||||||
|
Self(hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract the raw hash.
|
||||||
|
pub fn hash(self) -> u128 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
/// Produces a well-known variant of this location.
|
/// Produces a well-known variant of this location.
|
||||||
///
|
///
|
||||||
/// This is a synthetic location created from another one and is used, for
|
/// This is a synthetic location created from another one and is used, for
|
||||||
/// example, in bibliography management to create individual linkable
|
/// example, in bibliography management to create individual linkable
|
||||||
/// locations for reference entries from the bibliography's location.
|
/// locations for reference entries from the bibliography's location.
|
||||||
pub fn variant(self, n: usize) -> Self {
|
pub fn variant(self, n: usize) -> Self {
|
||||||
Self {
|
Self(crate::utils::hash128(&(self.0, n)))
|
||||||
hash: crate::utils::hash128(&(self.hash, n)),
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,6 +93,12 @@ impl Location {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for Location {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "Location({})", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Repr for Location {
|
impl Repr for Location {
|
||||||
fn repr(&self) -> EcoString {
|
fn repr(&self) -> EcoString {
|
||||||
"..".into()
|
"..".into()
|
||||||
|
@ -1,117 +1,350 @@
|
|||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use comemo::{Track, Tracked, Validate};
|
use comemo::{Tracked, Validate};
|
||||||
|
|
||||||
use crate::introspection::Location;
|
use crate::introspection::{Introspector, Location};
|
||||||
use crate::layout::{Frame, FrameItem};
|
|
||||||
|
|
||||||
/// Provides locations for elements in the document.
|
/// Provides locations for elements in the document.
|
||||||
///
|
///
|
||||||
/// A [`Location`] consists of an element's hash plus a disambiguator. Just the
|
/// A [`Location`] is a unique ID for an element generated during realization.
|
||||||
/// hash is not enough because we can have multiple equal elements with the same
|
|
||||||
/// hash (not a hash collision, just equal elements!). Between these, we
|
|
||||||
/// disambiguate with an increasing number. In principle, the disambiguator
|
|
||||||
/// could just be counted up. However, counting is an impure operation and as
|
|
||||||
/// such we can't count across a memoization boundary. [^1]
|
|
||||||
///
|
///
|
||||||
/// Instead, we only mutate within a single "layout run" and combine the results
|
/// # How to use this
|
||||||
/// with disambiguators from an outer tracked locator. Thus, the locators form a
|
/// The same content may yield different results when laid out in different
|
||||||
/// "tracked chain". When a layout run ends, its mutations are discarded and, on
|
/// parts of the document. To reflect this, every layout operation receives a
|
||||||
/// the other side of the memoization boundary, we
|
/// locator and every layout operation requires a locator. In code:
|
||||||
/// [reconstruct](Self::visit_frame) them from the resulting [frames](Frame).
|
|
||||||
///
|
///
|
||||||
/// [^1]: Well, we could with [`TrackedMut`](comemo::TrackedMut), but the
|
/// - all layouters receive an owned `Locator`
|
||||||
/// overhead is quite high, especially since we need to save & undo the counting
|
/// - all layout functions take an owned `Locator`
|
||||||
/// when only measuring.
|
///
|
||||||
#[derive(Default, Clone)]
|
/// When a layouter only requires a single sublayout call, it can simply pass on
|
||||||
|
/// its locator. When a layouter needs to call multiple sublayouters, we need to
|
||||||
|
/// make an explicit decision:
|
||||||
|
///
|
||||||
|
/// - Split: When we're layouting multiple distinct children (or other pieces of
|
||||||
|
/// content), we need to split up the locator with [`Locator::split`]. This
|
||||||
|
/// allows us to produce multiple new `Locator`s for the sublayouts. When we
|
||||||
|
/// split the locator, each sublocator will be a distinct entity and using it
|
||||||
|
/// to e.g. layout the same piece of figure content will yield distinctly
|
||||||
|
/// numbered figures.
|
||||||
|
///
|
||||||
|
/// - Relayout: When we're layouting the same content multiple times (e.g. when
|
||||||
|
/// measuring something), we can call [`Locator::relayout`] to use the same
|
||||||
|
/// locator multiple times. This indicates to the compiler that it's actually
|
||||||
|
/// the same content. Using it to e.g. layout the same piece of figure content
|
||||||
|
/// will yield the same figure number both times. Typically, when we layout
|
||||||
|
/// something multiple times using `relayout`, only one of the outputs
|
||||||
|
/// actually ends up in the document, while the other outputs are only used
|
||||||
|
/// for measurement and then discarded.
|
||||||
|
///
|
||||||
|
/// The `Locator` intentionally does not implement `Copy` and `Clone` so that it
|
||||||
|
/// can only be used once. This ensures that whenever we are layouting multiple
|
||||||
|
/// things, we make an explicit decision whether we want to split or relayout.
|
||||||
|
///
|
||||||
|
/// # How it works
|
||||||
|
/// There are two primary considerations for the assignment of locations:
|
||||||
|
///
|
||||||
|
/// 1. Locations should match up over multiple layout iterations, so that
|
||||||
|
/// elements can be identified as being the same: That's the whole point of
|
||||||
|
/// them.
|
||||||
|
///
|
||||||
|
/// 2. Locations should be as stable as possible across document edits, so that
|
||||||
|
/// incremental compilation is effective.
|
||||||
|
///
|
||||||
|
/// 3. We want to assign them with as little long-lived state as possible to
|
||||||
|
/// enable parallelization of the layout process.
|
||||||
|
///
|
||||||
|
/// Let's look at a few different assignment strategies to get a feeling for
|
||||||
|
/// these requirements:
|
||||||
|
///
|
||||||
|
/// - A very simple way to generate unique IDs would be to just increase a
|
||||||
|
/// counter for each element. In this setup, (1) is somewhat satisfied: In
|
||||||
|
/// principle, the counter will line up across iterations, but things start to
|
||||||
|
/// break down once we generate content dependant on introspection since the
|
||||||
|
/// IDs generated for that new content will shift the IDs for all following
|
||||||
|
/// elements in the document. (2) is not satisfied since an edit in the middle
|
||||||
|
/// of the document shifts all later IDs. (3) is obviously not satisfied.
|
||||||
|
/// Conclusion: Not great.
|
||||||
|
///
|
||||||
|
/// - To make things more robust, we can incorporate some stable knowledge about
|
||||||
|
/// the element into the ID. For this, we can use the element's span since it
|
||||||
|
/// is already mostly unique: Elements resulting from different source code
|
||||||
|
/// locations are guaranteed to have different spans. However, we can also
|
||||||
|
/// have multiple distinct elements generated from the same source location:
|
||||||
|
/// e.g. `#for _ in range(5) { figure(..) }`. To handle this case, we can then
|
||||||
|
/// disambiguate elements with the same span with an increasing counter. In
|
||||||
|
/// this setup, (1) is mostly satisfied: Unless we do stuff like generating
|
||||||
|
/// colliding counter updates dependant on introspection, things will line up.
|
||||||
|
/// (2) is also reasonably well satisfied, as typical edits will only affect
|
||||||
|
/// the single element at the currently edited span. Only if we edit inside of
|
||||||
|
/// a function, loop, or similar construct, we will affect multiple elements.
|
||||||
|
/// (3) is still a problem though, since we count up.
|
||||||
|
///
|
||||||
|
/// - What's left is to get rid of the mutable state. Note that layout is a
|
||||||
|
/// recursive process and has a tree-shaped execution graph. Thus, we can try
|
||||||
|
/// to determine an element's ID based on the path of execution taken in this
|
||||||
|
/// graph. Something like "3rd element in layer 1, 7th element in layer 2,
|
||||||
|
/// ..". This is basically the first approach, but on a per-layer basis. Thus,
|
||||||
|
/// we can again apply our trick from the second approach, and use the span +
|
||||||
|
/// disambiguation strategy on a per-layer basis: "1st element with span X in
|
||||||
|
/// layer 1, 3rd element with span Y in layer 2". The chance for a collision
|
||||||
|
/// is now pretty low and our state is wholly local to each level. So, if we
|
||||||
|
/// want to parallelize layout within a layer, we can generate the IDs for
|
||||||
|
/// that layer upfront and then start forking out. The final remaining
|
||||||
|
/// question is how we can compactly encode this information: For this, as
|
||||||
|
/// always, we use hashing! We incorporate the ID information from each layer
|
||||||
|
/// into a single hash and thanks to the collision resistence of 128-bit
|
||||||
|
/// SipHash, we get almost guaranteed unique locations. We don't even store
|
||||||
|
/// the full layer information at all, but rather hash _hierarchically:_ Let
|
||||||
|
/// `k_x` be our local per-layer ID for layer `x` and `h_x` be the full
|
||||||
|
/// combined hash for layer `x`. We compute `h_n = hash(h_(n-1), k_n)`.
|
||||||
|
///
|
||||||
|
/// So that's what's going on conceptually in this type. For efficient
|
||||||
|
/// memoization, we do all of this in a tracked fashion, such that we only
|
||||||
|
/// observe the hash for all the layers above us, if we actually need to
|
||||||
|
/// generate a [`Location`]. Thus, if we have a piece of content that does not
|
||||||
|
/// contain any locatable elements, we can cache its layout even if it occurs in
|
||||||
|
/// different places.
|
||||||
|
///
|
||||||
|
/// # Dealing with measurement
|
||||||
|
/// As explained above, any kind of measurement the compiler performs requires a
|
||||||
|
/// locator that matches the one used during real layout. This ensures that the
|
||||||
|
/// locations assigned during measurement match up exactly with the locations of
|
||||||
|
/// real document elements. Without this guarantee, many introspection-driven
|
||||||
|
/// features (like counters, state, and citations) don't work correctly (since
|
||||||
|
/// they perform queries dependant on concrete locations).
|
||||||
|
///
|
||||||
|
/// This is all fine and good, but things get really tricky when the _user_
|
||||||
|
/// measures such introspecting content since the user isn't kindly managing
|
||||||
|
/// locators for us. Our standard `Locator` workflow assigns locations that
|
||||||
|
/// depend a lot on the exact placement in the hierarchy of elements. For this
|
||||||
|
/// reason, something that is measured, but then placed into something like a
|
||||||
|
/// grid will get a location influenced by the grid. Without a locator, we can't
|
||||||
|
/// make the connection between the measured content and the real content, so we
|
||||||
|
/// can't ensure that the locations match up.
|
||||||
|
///
|
||||||
|
/// One possible way to deal with this is to force the user to uniquely identify
|
||||||
|
/// content before being measured after all. This would mean that the user needs
|
||||||
|
/// to come up with an identifier that is unique within the surrounding context
|
||||||
|
/// block and attach it to the content in some way. However, after careful
|
||||||
|
/// consideration, I have concluded that this is simply too big of an ask from
|
||||||
|
/// users: Understanding why this is even necessary is pretty complicated and
|
||||||
|
/// how to best come up with a unique ID is even more so.
|
||||||
|
///
|
||||||
|
/// For this reason, I chose an alternative best-effort approach: The locator
|
||||||
|
/// has a custom "measurement mode" (entered through [`LocatorLink::measure`]),
|
||||||
|
/// in which it does its best to assign locations that match up. Specifically,
|
||||||
|
/// it uses the key hashes of the individual locatable elements in the measured
|
||||||
|
/// content (which may not be unique if content is reused) and combines them
|
||||||
|
/// with the context's location to find the most likely matching real element.
|
||||||
|
/// This approach works correctly almost all of the time (especially for
|
||||||
|
/// "normal" hand-written content where the key hashes rarely collide, as
|
||||||
|
/// opposed to code-heavy things where they do).
|
||||||
|
///
|
||||||
|
/// Support for enhancing this with user-provided uniqueness can still be added
|
||||||
|
/// in the future. It will most likely anyway be added simply because it's
|
||||||
|
/// automatically included when we add a way to "freeze" content for things like
|
||||||
|
/// slidehows. But it will be opt-in because it's just too much complication.
|
||||||
pub struct Locator<'a> {
|
pub struct Locator<'a> {
|
||||||
/// Maps from a hash to the maximum number we've seen for this hash. This
|
/// A local hash that incorporates all layers since the last memoization
|
||||||
/// number becomes the `disambiguator`.
|
/// boundary.
|
||||||
hashes: RefCell<HashMap<u128, usize>>,
|
local: u128,
|
||||||
/// An outer `Locator`, from which we can get disambiguator for hashes
|
/// A pointer to an outer cached locator, which contributes the information
|
||||||
/// outside of the current "layout run".
|
/// for all the layers beyond the memoization boundary on-demand.
|
||||||
///
|
outer: Option<&'a LocatorLink<'a>>,
|
||||||
/// We need to override the constraint's lifetime here so that `Tracked` is
|
|
||||||
/// covariant over the constraint. If it becomes invariant, we're in for a
|
|
||||||
/// world of lifetime pain.
|
|
||||||
outer: Option<Tracked<'a, Self, <Locator<'static> as Validate>::Constraint>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Locator<'a> {
|
impl<'a> Locator<'a> {
|
||||||
/// Create a new locator.
|
/// Create a new root-level locator.
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new chained locator.
|
|
||||||
pub fn chained(outer: Tracked<'a, Self>) -> Self {
|
|
||||||
Self { outer: Some(outer), ..Default::default() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start tracking this locator.
|
|
||||||
///
|
///
|
||||||
/// In comparison to [`Track::track`], this method skips this chain link
|
/// Should typically only be created at the document level, though there
|
||||||
/// if it does not contribute anything.
|
/// are a few places where we use it as well that just don't support
|
||||||
pub fn track(&self) -> Tracked<'_, Self> {
|
/// introspection (e.g. drawable patterns).
|
||||||
match self.outer {
|
pub fn root() -> Self {
|
||||||
Some(outer) if self.hashes.borrow().is_empty() => outer,
|
Self { local: 0, outer: None }
|
||||||
_ => Track::track(self),
|
}
|
||||||
|
|
||||||
|
/// Creates a new synthetic locator.
|
||||||
|
///
|
||||||
|
/// This can be used to create a new dependant layout based on an element.
|
||||||
|
/// This is used for layouting footnote entries based on the location
|
||||||
|
/// of the associated footnote.
|
||||||
|
pub fn synthesize(location: Location) -> Self {
|
||||||
|
Self { local: location.hash(), outer: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new locator that points to the given link.
|
||||||
|
pub fn link(link: &'a LocatorLink<'a>) -> Self {
|
||||||
|
Self { local: 0, outer: Some(link) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Produce a stable identifier for this call site.
|
impl<'a> Locator<'a> {
|
||||||
pub fn locate(&mut self, hash: u128) -> Location {
|
/// Returns a type that can be used to generate `Locator`s for multiple
|
||||||
// Get the current disambiguator for this hash.
|
/// child elements. See the type-level docs for more details.
|
||||||
let disambiguator = self.disambiguator_impl(hash);
|
pub fn split(self) -> SplitLocator<'a> {
|
||||||
|
SplitLocator {
|
||||||
// Bump the next disambiguator up by one.
|
local: self.local,
|
||||||
self.hashes.get_mut().insert(hash, disambiguator + 1);
|
outer: self.outer,
|
||||||
|
disambiguators: HashMap::new(),
|
||||||
// Create the location in its default variant.
|
|
||||||
Location { hash, disambiguator }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Advance past a frame.
|
|
||||||
pub fn visit_frame(&mut self, frame: &Frame) {
|
|
||||||
for (_, item) in frame.items() {
|
|
||||||
match item {
|
|
||||||
FrameItem::Group(group) => self.visit_frame(&group.frame),
|
|
||||||
FrameItem::Tag(elem) => {
|
|
||||||
let hashes = self.hashes.get_mut();
|
|
||||||
let loc = elem.location().unwrap();
|
|
||||||
let entry = hashes.entry(loc.hash).or_default();
|
|
||||||
|
|
||||||
// Next disambiguator needs to be at least one larger than
|
|
||||||
// the maximum we've seen so far.
|
|
||||||
*entry = (*entry).max(loc.disambiguator + 1);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Advance past a number of frames.
|
|
||||||
pub fn visit_frames<'b>(&mut self, frames: impl IntoIterator<Item = &'b Frame>) {
|
|
||||||
for frame in frames {
|
|
||||||
self.visit_frame(frame);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The current disambiguator for the given hash.
|
/// Creates a copy of this locator for measurement or relayout of the same
|
||||||
fn disambiguator_impl(&self, hash: u128) -> usize {
|
/// content. See the type-level docs for more details.
|
||||||
*self
|
///
|
||||||
.hashes
|
/// This is effectively just `Clone`, but the `Locator` doesn't implement
|
||||||
.borrow_mut()
|
/// `Clone` to make this operation explicit.
|
||||||
.entry(hash)
|
pub fn relayout(&self) -> Self {
|
||||||
.or_insert_with(|| self.outer.map_or(0, |outer| outer.disambiguator(hash)))
|
Self { local: self.local, outer: self.outer }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[comemo::track]
|
#[comemo::track]
|
||||||
impl<'a> Locator<'a> {
|
impl<'a> Locator<'a> {
|
||||||
/// The current disambiguator for the hash.
|
/// Resolves the locator based on its local and the outer information.
|
||||||
fn disambiguator(&self, hash: u128) -> usize {
|
fn resolve(&self) -> Resolved {
|
||||||
self.disambiguator_impl(hash)
|
match self.outer {
|
||||||
|
None => Resolved::Hash(self.local),
|
||||||
|
Some(outer) => match outer.resolve() {
|
||||||
|
Resolved::Hash(outer) => {
|
||||||
|
Resolved::Hash(crate::utils::hash128(&(self.local, outer)))
|
||||||
|
}
|
||||||
|
Resolved::Measure(anchor) => Resolved::Measure(anchor),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Locator<'_> {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "Locator({:?})", self.resolve())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The fully resolved value of a locator.
|
||||||
|
#[derive(Debug, Copy, Clone, Hash)]
|
||||||
|
enum Resolved {
|
||||||
|
/// The full hash, incorporating the local and all outer information.
|
||||||
|
Hash(u128),
|
||||||
|
/// Indicates that the locator is in measurement mode, with the given anchor
|
||||||
|
/// location.
|
||||||
|
Measure(Location),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A type that generates unique sublocators.
|
||||||
|
pub struct SplitLocator<'a> {
|
||||||
|
/// A local hash that incorporates all layers since the last memoization
|
||||||
|
/// boundary.
|
||||||
|
local: u128,
|
||||||
|
/// A pointer to an outer cached locator, which contributes the information
|
||||||
|
/// for all the layers beyond the memoization boundary on-demand.
|
||||||
|
outer: Option<&'a LocatorLink<'a>>,
|
||||||
|
/// Simply counts up the number of times we've seen each local hash.
|
||||||
|
disambiguators: HashMap<u128, usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SplitLocator<'a> {
|
||||||
|
/// Produces a sublocator for a subtree keyed by `key`. The keys do *not*
|
||||||
|
/// need to be unique among the `next()` calls on this split locator. (They
|
||||||
|
/// can even all be `&()`.)
|
||||||
|
///
|
||||||
|
/// However, stable & mostly unique keys lead to more stable locations
|
||||||
|
/// throughout edits, improving incremental compilation performance.
|
||||||
|
///
|
||||||
|
/// A common choice for a key is the span of the content that will be
|
||||||
|
/// layouted with this locator.
|
||||||
|
pub fn next<K: Hash>(&mut self, key: &K) -> Locator<'a> {
|
||||||
|
self.next_inner(crate::utils::hash128(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produces a sublocator for a subtree.
|
||||||
|
pub fn next_inner(&mut self, key: u128) -> Locator<'a> {
|
||||||
|
// Produce a locator disambiguator, for elements with the same key
|
||||||
|
// within this `SplitLocator`.
|
||||||
|
let disambiguator = {
|
||||||
|
let slot = self.disambiguators.entry(key).or_default();
|
||||||
|
std::mem::replace(slot, *slot + 1)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Combine the key, disambiguator and local hash into a sub-local hash.
|
||||||
|
// The outer information is not yet merged into this, it is added
|
||||||
|
// on-demand in `Locator::resolve`.
|
||||||
|
let local = crate::utils::hash128(&(key, disambiguator, self.local));
|
||||||
|
|
||||||
|
Locator { outer: self.outer, local }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produces a unique location for an element.
|
||||||
|
pub fn next_location(
|
||||||
|
&mut self,
|
||||||
|
introspector: Tracked<Introspector>,
|
||||||
|
key: u128,
|
||||||
|
) -> Location {
|
||||||
|
match self.next_inner(key).resolve() {
|
||||||
|
Resolved::Hash(hash) => Location::new(hash),
|
||||||
|
Resolved::Measure(anchor) => {
|
||||||
|
// If we aren't able to find a matching element in the document,
|
||||||
|
// default to the anchor, so that it's at least remotely in
|
||||||
|
// the right area (so that counters can be resolved).
|
||||||
|
introspector.locator(key, anchor).unwrap_or(anchor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A locator can be linked to this type to only access information across the
|
||||||
|
/// memoization boundary on-demand, improving the cache hit chance.
|
||||||
|
pub struct LocatorLink<'a> {
|
||||||
|
/// The link itself.
|
||||||
|
kind: LinkKind<'a>,
|
||||||
|
/// The cached resolved link.
|
||||||
|
resolved: OnceLock<Resolved>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The different kinds of locator links.
|
||||||
|
enum LinkKind<'a> {
|
||||||
|
/// An outer `Locator`, which we can resolved if necessary.
|
||||||
|
///
|
||||||
|
/// We need to override the constraint's lifetime here so that `Tracked` is
|
||||||
|
/// covariant over the constraint. If it becomes invariant, we're in for a
|
||||||
|
/// world of lifetime pain.
|
||||||
|
Outer(Tracked<'a, Locator<'a>, <Locator<'static> as Validate>::Constraint>),
|
||||||
|
/// A link which indicates that we are in measurement mode.
|
||||||
|
Measure(Location),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LocatorLink<'a> {
|
||||||
|
/// Create a locator link.
|
||||||
|
pub fn new(outer: Tracked<'a, Locator<'a>>) -> Self {
|
||||||
|
LocatorLink {
|
||||||
|
kind: LinkKind::Outer(outer),
|
||||||
|
resolved: OnceLock::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a link that puts any linked downstream locator into measurement
|
||||||
|
/// mode.
|
||||||
|
///
|
||||||
|
/// Read the "Dealing with measurement" section of the [`Locator`] docs for
|
||||||
|
/// more details.
|
||||||
|
pub fn measure(anchor: Location) -> Self {
|
||||||
|
LocatorLink {
|
||||||
|
kind: LinkKind::Measure(anchor),
|
||||||
|
resolved: OnceLock::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve the link.
|
||||||
|
///
|
||||||
|
/// The result is cached in this link, so that we don't traverse the link
|
||||||
|
/// chain over and over again.
|
||||||
|
fn resolve(&self) -> Resolved {
|
||||||
|
*self.resolved.get_or_init(|| match self.kind {
|
||||||
|
LinkKind::Outer(outer) => outer.resolve(),
|
||||||
|
LinkKind::Measure(anchor) => Resolved::Measure(anchor),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,8 @@ pub use self::metadata::*;
|
|||||||
pub use self::query_::*;
|
pub use self::query_::*;
|
||||||
pub use self::state::*;
|
pub use self::state::*;
|
||||||
|
|
||||||
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
|
||||||
use crate::diag::{bail, SourceResult};
|
use crate::diag::{bail, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
@ -56,7 +58,7 @@ pub fn define(global: &mut Scope) {
|
|||||||
global.define_func::<locate>();
|
global.define_func::<locate>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Holds a locatable element that was realized.
|
/// Holds a tag for a locatable element that was realized.
|
||||||
///
|
///
|
||||||
/// The `TagElem` is handled by all layouters. The held element becomes
|
/// The `TagElem` is handled by all layouters. The held element becomes
|
||||||
/// available for introspection in the next compiler iteration.
|
/// available for introspection in the next compiler iteration.
|
||||||
@ -65,14 +67,13 @@ pub struct TagElem {
|
|||||||
/// The introspectible element.
|
/// The introspectible element.
|
||||||
#[required]
|
#[required]
|
||||||
#[internal]
|
#[internal]
|
||||||
pub elem: Content,
|
pub tag: Tag,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TagElem {
|
impl TagElem {
|
||||||
/// Create a packed tag element.
|
/// Create a packed tag element.
|
||||||
pub fn packed(elem: Content) -> Content {
|
pub fn packed(tag: Tag) -> Content {
|
||||||
let span = elem.span();
|
let mut content = Self::new(tag).pack();
|
||||||
let mut content = Self::new(elem).pack().spanned(span);
|
|
||||||
// We can skip preparation for the `TagElem`.
|
// We can skip preparation for the `TagElem`.
|
||||||
content.mark_prepared();
|
content.mark_prepared();
|
||||||
content
|
content
|
||||||
@ -92,3 +93,29 @@ impl Behave for Packed<TagElem> {
|
|||||||
Behaviour::Invisible
|
Behaviour::Invisible
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Holds a locatable element that was realized.
|
||||||
|
#[derive(Clone, PartialEq, Hash)]
|
||||||
|
pub struct Tag {
|
||||||
|
/// The introspectible element.
|
||||||
|
pub elem: Content,
|
||||||
|
/// The element's key hash, which forms the base of its location (but is
|
||||||
|
/// locally disambiguated and combined with outer hashes).
|
||||||
|
///
|
||||||
|
/// We need to retain this for introspector-assisted location assignment
|
||||||
|
/// during measurement.
|
||||||
|
pub(crate) key: u128,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tag {
|
||||||
|
/// Create a tag from an element and its key hash.
|
||||||
|
pub fn new(elem: Content, key: u128) -> Self {
|
||||||
|
Self { elem, key }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Tag {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "Tag({:?})", self.elem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,7 +9,7 @@ use crate::foundations::{
|
|||||||
LocatableSelector, NativeElement, Packed, Repr, Selector, Show, Str, StyleChain,
|
LocatableSelector, NativeElement, Packed, Repr, Selector, Show, Str, StyleChain,
|
||||||
Value,
|
Value,
|
||||||
};
|
};
|
||||||
use crate::introspection::{Introspector, Locatable, Location, Locator};
|
use crate::introspection::{Introspector, Locatable, Location};
|
||||||
use crate::syntax::Span;
|
use crate::syntax::Span;
|
||||||
use crate::World;
|
use crate::World;
|
||||||
|
|
||||||
@ -215,7 +215,6 @@ impl State {
|
|||||||
engine.world,
|
engine.world,
|
||||||
engine.introspector,
|
engine.introspector,
|
||||||
engine.route.track(),
|
engine.route.track(),
|
||||||
engine.locator.track(),
|
|
||||||
TrackedMut::reborrow_mut(&mut engine.tracer),
|
TrackedMut::reborrow_mut(&mut engine.tracer),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -227,15 +226,12 @@ impl State {
|
|||||||
world: Tracked<dyn World + '_>,
|
world: Tracked<dyn World + '_>,
|
||||||
introspector: Tracked<Introspector>,
|
introspector: Tracked<Introspector>,
|
||||||
route: Tracked<Route>,
|
route: Tracked<Route>,
|
||||||
locator: Tracked<Locator>,
|
|
||||||
tracer: TrackedMut<Tracer>,
|
tracer: TrackedMut<Tracer>,
|
||||||
) -> SourceResult<EcoVec<Value>> {
|
) -> SourceResult<EcoVec<Value>> {
|
||||||
let mut locator = Locator::chained(locator);
|
|
||||||
let mut engine = Engine {
|
let mut engine = Engine {
|
||||||
world,
|
world,
|
||||||
introspector,
|
introspector,
|
||||||
route: Route::extend(route).unnested(),
|
route: Route::extend(route).unnested(),
|
||||||
locator: &mut locator,
|
|
||||||
tracer,
|
tracer,
|
||||||
};
|
};
|
||||||
let mut state = self.init.clone();
|
let mut state = self.init.clone();
|
||||||
|
@ -3,6 +3,7 @@ use std::num::NonZeroUsize;
|
|||||||
use crate::diag::SourceResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{elem, Content, NativeElement, Packed, Show, StyleChain};
|
use crate::foundations::{elem, Content, NativeElement, Packed, Show, StyleChain};
|
||||||
|
use crate::introspection::Locator;
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, Axes, BlockElem, Dir, Fragment, Frame, Length, Point, Ratio, Regions, Rel, Size,
|
Abs, Axes, BlockElem, Dir, Fragment, Frame, Length, Point, Ratio, Regions, Rel, Size,
|
||||||
};
|
};
|
||||||
@ -62,7 +63,8 @@ impl Show for Packed<ColumnsElem> {
|
|||||||
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(BlockElem::multi_layouter(self.clone(), layout_columns)
|
Ok(BlockElem::multi_layouter(self.clone(), layout_columns)
|
||||||
.with_rootable(true)
|
.with_rootable(true)
|
||||||
.pack())
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,6 +73,7 @@ impl Show for Packed<ColumnsElem> {
|
|||||||
fn layout_columns(
|
fn layout_columns(
|
||||||
elem: &Packed<ColumnsElem>,
|
elem: &Packed<ColumnsElem>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -79,7 +82,7 @@ fn layout_columns(
|
|||||||
// Separating the infinite space into infinite columns does not make
|
// Separating the infinite space into infinite columns does not make
|
||||||
// much sense.
|
// much sense.
|
||||||
if !regions.size.x.is_finite() {
|
if !regions.size.x.is_finite() {
|
||||||
return body.layout(engine, styles, regions);
|
return body.layout(engine, locator, styles, regions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the width of the gutter and each column.
|
// Determine the width of the gutter and each column.
|
||||||
@ -104,7 +107,7 @@ fn layout_columns(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Layout the children.
|
// Layout the children.
|
||||||
let mut frames = body.layout(engine, styles, pod)?.into_iter();
|
let mut frames = body.layout(engine, locator, styles, pod)?.into_iter();
|
||||||
let mut finished = vec![];
|
let mut finished = vec![];
|
||||||
|
|
||||||
let dir = TextElem::dir_in(styles);
|
let dir = TextElem::dir_in(styles);
|
||||||
|
@ -7,6 +7,7 @@ use crate::foundations::{
|
|||||||
cast, elem, Args, AutoValue, Construct, Content, NativeElement, Packed, Resolve,
|
cast, elem, Args, AutoValue, Construct, Content, NativeElement, Packed, Resolve,
|
||||||
Smart, StyleChain, Value,
|
Smart, StyleChain, Value,
|
||||||
};
|
};
|
||||||
|
use crate::introspection::Locator;
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, Axes, Corners, Em, Fr, Fragment, Frame, FrameKind, Length, Region, Regions, Rel,
|
Abs, Axes, Corners, Em, Fr, Fragment, Frame, FrameKind, Length, Region, Regions, Rel,
|
||||||
Sides, Size, Spacing, VElem,
|
Sides, Size, Spacing, VElem,
|
||||||
@ -120,6 +121,7 @@ impl Packed<BoxElem> {
|
|||||||
pub fn layout(
|
pub fn layout(
|
||||||
&self,
|
&self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
region: Size,
|
region: Size,
|
||||||
) -> SourceResult<Frame> {
|
) -> SourceResult<Frame> {
|
||||||
@ -140,7 +142,7 @@ impl Packed<BoxElem> {
|
|||||||
// If we have a child, layout it into the body. Boxes are boundaries
|
// If we have a child, layout it into the body. Boxes are boundaries
|
||||||
// for gradient relativeness, so we set the `FrameKind` to `Hard`.
|
// for gradient relativeness, so we set the `FrameKind` to `Hard`.
|
||||||
Some(body) => body
|
Some(body) => body
|
||||||
.layout(engine, styles, pod.into_regions())?
|
.layout(engine, locator, styles, pod.into_regions())?
|
||||||
.into_frame()
|
.into_frame()
|
||||||
.with_kind(FrameKind::Hard),
|
.with_kind(FrameKind::Hard),
|
||||||
};
|
};
|
||||||
@ -251,6 +253,7 @@ impl InlineElem {
|
|||||||
callback: fn(
|
callback: fn(
|
||||||
content: &Packed<T>,
|
content: &Packed<T>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
region: Size,
|
region: Size,
|
||||||
) -> SourceResult<Vec<InlineItem>>,
|
) -> SourceResult<Vec<InlineItem>>,
|
||||||
@ -264,10 +267,11 @@ impl Packed<InlineElem> {
|
|||||||
pub fn layout(
|
pub fn layout(
|
||||||
&self,
|
&self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
region: Size,
|
region: Size,
|
||||||
) -> SourceResult<Vec<InlineItem>> {
|
) -> SourceResult<Vec<InlineItem>> {
|
||||||
self.body().call(engine, styles, region)
|
self.body().call(engine, locator, styles, region)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,6 +464,7 @@ impl BlockElem {
|
|||||||
f: fn(
|
f: fn(
|
||||||
content: &Packed<T>,
|
content: &Packed<T>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> SourceResult<Frame>,
|
) -> SourceResult<Frame>,
|
||||||
@ -477,6 +482,7 @@ impl BlockElem {
|
|||||||
f: fn(
|
f: fn(
|
||||||
content: &Packed<T>,
|
content: &Packed<T>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment>,
|
) -> SourceResult<Fragment>,
|
||||||
@ -493,6 +499,7 @@ impl Packed<BlockElem> {
|
|||||||
pub fn layout(
|
pub fn layout(
|
||||||
&self,
|
&self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -530,7 +537,8 @@ impl Packed<BlockElem> {
|
|||||||
|
|
||||||
// If we have content as our body, just layout it.
|
// If we have content as our body, just layout it.
|
||||||
Some(BlockChild::Content(body)) => {
|
Some(BlockChild::Content(body)) => {
|
||||||
let mut fragment = body.measure(engine, styles, pod)?;
|
let mut fragment =
|
||||||
|
body.layout(engine, locator.relayout(), styles, pod)?;
|
||||||
|
|
||||||
// If the body is automatically sized and produced more than one
|
// If the body is automatically sized and produced more than one
|
||||||
// fragment, ensure that the width was consistent across all
|
// fragment, ensure that the width was consistent across all
|
||||||
@ -551,11 +559,7 @@ impl Packed<BlockElem> {
|
|||||||
expand: Axes::new(true, pod.expand.y),
|
expand: Axes::new(true, pod.expand.y),
|
||||||
..pod
|
..pod
|
||||||
};
|
};
|
||||||
fragment = body.layout(engine, styles, pod)?;
|
fragment = body.layout(engine, locator, styles, pod)?;
|
||||||
} else {
|
|
||||||
// Apply the side effect to turn the `measure` into a
|
|
||||||
// `layout`.
|
|
||||||
engine.locator.visit_frames(&fragment);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment
|
fragment
|
||||||
@ -565,7 +569,7 @@ impl Packed<BlockElem> {
|
|||||||
// base region, give it that.
|
// base region, give it that.
|
||||||
Some(BlockChild::SingleLayouter(callback)) => {
|
Some(BlockChild::SingleLayouter(callback)) => {
|
||||||
let pod = Region::new(pod.base(), pod.expand);
|
let pod = Region::new(pod.base(), pod.expand);
|
||||||
callback.call(engine, styles, pod).map(Fragment::frame)?
|
callback.call(engine, locator, styles, pod).map(Fragment::frame)?
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have a child that wants to layout with full region access,
|
// If we have a child that wants to layout with full region access,
|
||||||
@ -577,7 +581,7 @@ impl Packed<BlockElem> {
|
|||||||
Some(BlockChild::MultiLayouter(callback)) => {
|
Some(BlockChild::MultiLayouter(callback)) => {
|
||||||
let expand = (pod.expand | regions.expand) & pod.size.map(Abs::is_finite);
|
let expand = (pod.expand | regions.expand) & pod.size.map(Abs::is_finite);
|
||||||
let pod = Regions { expand, ..pod };
|
let pod = Regions { expand, ..pod };
|
||||||
callback.call(engine, styles, pod)?
|
callback.call(engine, locator, styles, pod)?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -927,6 +931,7 @@ mod callbacks {
|
|||||||
callback! {
|
callback! {
|
||||||
InlineCallback = (
|
InlineCallback = (
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
region: Size,
|
region: Size,
|
||||||
) -> SourceResult<Vec<InlineItem>>
|
) -> SourceResult<Vec<InlineItem>>
|
||||||
@ -935,6 +940,7 @@ mod callbacks {
|
|||||||
callback! {
|
callback! {
|
||||||
BlockSingleCallback = (
|
BlockSingleCallback = (
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> SourceResult<Frame>
|
) -> SourceResult<Frame>
|
||||||
@ -943,6 +949,7 @@ mod callbacks {
|
|||||||
callback! {
|
callback! {
|
||||||
BlockMultiCallback = (
|
BlockMultiCallback = (
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment>
|
) -> SourceResult<Fragment>
|
||||||
|
@ -11,7 +11,7 @@ use crate::engine::Engine;
|
|||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
elem, Args, Construct, Content, NativeElement, Packed, Resolve, Smart, StyleChain,
|
elem, Args, Construct, Content, NativeElement, Packed, Resolve, Smart, StyleChain,
|
||||||
};
|
};
|
||||||
use crate::introspection::TagElem;
|
use crate::introspection::{Locator, SplitLocator, Tag, TagElem};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, AlignElem, Axes, BlockElem, ColbreakElem, FixedAlignment, FlushElem, Fr,
|
Abs, AlignElem, Axes, BlockElem, ColbreakElem, FixedAlignment, FlushElem, Fr,
|
||||||
Fragment, Frame, FrameItem, PlaceElem, Point, Regions, Rel, Size, Spacing, VElem,
|
Fragment, Frame, FrameItem, PlaceElem, Point, Regions, Rel, Size, Spacing, VElem,
|
||||||
@ -43,6 +43,7 @@ impl Packed<FlowElem> {
|
|||||||
pub fn layout(
|
pub fn layout(
|
||||||
&self,
|
&self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -66,7 +67,7 @@ impl Packed<FlowElem> {
|
|||||||
alone = child.is::<BlockElem>();
|
alone = child.is::<BlockElem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut layouter = FlowLayouter::new(regions, styles, alone);
|
let mut layouter = FlowLayouter::new(locator, styles, regions, alone);
|
||||||
for (child, styles) in self.children().chain(&styles) {
|
for (child, styles) in self.children().chain(&styles) {
|
||||||
if let Some(elem) = child.to_packed::<TagElem>() {
|
if let Some(elem) = child.to_packed::<TagElem>() {
|
||||||
layouter.layout_tag(elem);
|
layouter.layout_tag(elem);
|
||||||
@ -105,10 +106,12 @@ impl Debug for FlowElem {
|
|||||||
struct FlowLayouter<'a> {
|
struct FlowLayouter<'a> {
|
||||||
/// Whether this is the root flow.
|
/// Whether this is the root flow.
|
||||||
root: bool,
|
root: bool,
|
||||||
/// The regions to layout children into.
|
/// Provides unique locations to the flow's children.
|
||||||
regions: Regions<'a>,
|
locator: SplitLocator<'a>,
|
||||||
/// The shared styles.
|
/// The shared styles.
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
|
/// The regions to layout children into.
|
||||||
|
regions: Regions<'a>,
|
||||||
/// Whether the flow should expand to fill the region.
|
/// Whether the flow should expand to fill the region.
|
||||||
expand: Axes<bool>,
|
expand: Axes<bool>,
|
||||||
/// The initial size of `regions.size` that was available before we started
|
/// The initial size of `regions.size` that was available before we started
|
||||||
@ -121,7 +124,7 @@ struct FlowLayouter<'a> {
|
|||||||
/// Spacing and layouted blocks for the current region.
|
/// Spacing and layouted blocks for the current region.
|
||||||
items: Vec<FlowItem>,
|
items: Vec<FlowItem>,
|
||||||
/// A queue of tags that will be attached to the next frame.
|
/// A queue of tags that will be attached to the next frame.
|
||||||
pending_tags: Vec<Content>,
|
pending_tags: Vec<Tag>,
|
||||||
/// A queue of floating elements.
|
/// A queue of floating elements.
|
||||||
pending_floats: Vec<FlowItem>,
|
pending_floats: Vec<FlowItem>,
|
||||||
/// Whether we have any footnotes in the current region.
|
/// Whether we have any footnotes in the current region.
|
||||||
@ -192,7 +195,12 @@ impl FlowItem {
|
|||||||
|
|
||||||
impl<'a> FlowLayouter<'a> {
|
impl<'a> FlowLayouter<'a> {
|
||||||
/// Create a new flow layouter.
|
/// Create a new flow layouter.
|
||||||
fn new(mut regions: Regions<'a>, styles: StyleChain<'a>, alone: bool) -> Self {
|
fn new(
|
||||||
|
locator: Locator<'a>,
|
||||||
|
styles: StyleChain<'a>,
|
||||||
|
mut regions: Regions<'a>,
|
||||||
|
alone: bool,
|
||||||
|
) -> Self {
|
||||||
let expand = regions.expand;
|
let expand = regions.expand;
|
||||||
let root = std::mem::replace(&mut regions.root, false);
|
let root = std::mem::replace(&mut regions.root, false);
|
||||||
|
|
||||||
@ -204,8 +212,9 @@ impl<'a> FlowLayouter<'a> {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
root,
|
root,
|
||||||
regions,
|
locator: locator.split(),
|
||||||
styles,
|
styles,
|
||||||
|
regions,
|
||||||
expand,
|
expand,
|
||||||
initial: regions.size,
|
initial: regions.size,
|
||||||
last_was_par: false,
|
last_was_par: false,
|
||||||
@ -223,8 +232,8 @@ impl<'a> FlowLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Place explicit metadata into the flow.
|
/// Place explicit metadata into the flow.
|
||||||
fn layout_tag(&mut self, tag: &Packed<TagElem>) {
|
fn layout_tag(&mut self, elem: &Packed<TagElem>) {
|
||||||
self.pending_tags.push(tag.elem.clone());
|
self.pending_tags.push(elem.tag.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout vertical spacing.
|
/// Layout vertical spacing.
|
||||||
@ -259,6 +268,7 @@ impl<'a> FlowLayouter<'a> {
|
|||||||
let lines = par
|
let lines = par
|
||||||
.layout(
|
.layout(
|
||||||
engine,
|
engine,
|
||||||
|
self.locator.next(&par.span()),
|
||||||
styles,
|
styles,
|
||||||
consecutive,
|
consecutive,
|
||||||
self.regions.base(),
|
self.regions.base(),
|
||||||
@ -330,7 +340,12 @@ impl<'a> FlowLayouter<'a> {
|
|||||||
|
|
||||||
// Layout the block itself.
|
// Layout the block itself.
|
||||||
let sticky = block.sticky(styles);
|
let sticky = block.sticky(styles);
|
||||||
let fragment = block.layout(engine, styles, self.regions)?;
|
let fragment = block.layout(
|
||||||
|
engine,
|
||||||
|
self.locator.next(&block.span()),
|
||||||
|
styles,
|
||||||
|
self.regions,
|
||||||
|
)?;
|
||||||
|
|
||||||
// How to align the block.
|
// How to align the block.
|
||||||
let align = AlignElem::alignment_in(styles).resolve(styles);
|
let align = AlignElem::alignment_in(styles).resolve(styles);
|
||||||
@ -378,7 +393,14 @@ impl<'a> FlowLayouter<'a> {
|
|||||||
align.x().unwrap_or_default().resolve(styles)
|
align.x().unwrap_or_default().resolve(styles)
|
||||||
});
|
});
|
||||||
let y_align = alignment.map(|align| align.y().map(|y| y.resolve(styles)));
|
let y_align = alignment.map(|align| align.y().map(|y| y.resolve(styles)));
|
||||||
let mut frame = placed.layout(engine, styles, self.regions.base())?.into_frame();
|
let mut frame = placed
|
||||||
|
.layout(
|
||||||
|
engine,
|
||||||
|
self.locator.next(&placed.span()),
|
||||||
|
styles,
|
||||||
|
self.regions.base(),
|
||||||
|
)?
|
||||||
|
.into_frame();
|
||||||
frame.post_process(styles);
|
frame.post_process(styles);
|
||||||
let item = FlowItem::Placed { frame, x_align, y_align, delta, float, clearance };
|
let item = FlowItem::Placed { frame, x_align, y_align, delta, float, clearance };
|
||||||
self.layout_item(engine, item)
|
self.layout_item(engine, item)
|
||||||
@ -390,7 +412,7 @@ impl<'a> FlowLayouter<'a> {
|
|||||||
frame.prepend_multiple(
|
frame.prepend_multiple(
|
||||||
self.pending_tags
|
self.pending_tags
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.map(|elem| (Point::zero(), FrameItem::Tag(elem))),
|
.map(|tag| (Point::zero(), FrameItem::Tag(tag))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -631,7 +653,7 @@ impl<'a> FlowLayouter<'a> {
|
|||||||
if force && !self.pending_tags.is_empty() {
|
if force && !self.pending_tags.is_empty() {
|
||||||
let pos = Point::with_y(offset);
|
let pos = Point::with_y(offset);
|
||||||
output.push_multiple(
|
output.push_multiple(
|
||||||
self.pending_tags.drain(..).map(|elem| (pos, FrameItem::Tag(elem))),
|
self.pending_tags.drain(..).map(|tag| (pos, FrameItem::Tag(tag))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -718,7 +740,6 @@ impl FlowLayouter<'_> {
|
|||||||
let prev_items_len = self.items.len();
|
let prev_items_len = self.items.len();
|
||||||
let prev_size = self.regions.size;
|
let prev_size = self.regions.size;
|
||||||
let prev_has_footnotes = self.has_footnotes;
|
let prev_has_footnotes = self.has_footnotes;
|
||||||
let prev_locator = engine.locator.clone();
|
|
||||||
|
|
||||||
// Process footnotes one at a time.
|
// Process footnotes one at a time.
|
||||||
let mut k = 0;
|
let mut k = 0;
|
||||||
@ -735,7 +756,12 @@ impl FlowLayouter<'_> {
|
|||||||
self.regions.size.y -= self.footnote_config.gap;
|
self.regions.size.y -= self.footnote_config.gap;
|
||||||
let frames = FootnoteEntry::new(notes[k].clone())
|
let frames = FootnoteEntry::new(notes[k].clone())
|
||||||
.pack()
|
.pack()
|
||||||
.layout(engine, self.styles, self.regions.with_root(false))?
|
.layout(
|
||||||
|
engine,
|
||||||
|
Locator::synthesize(notes[k].location().unwrap()),
|
||||||
|
self.styles,
|
||||||
|
self.regions.with_root(false),
|
||||||
|
)?
|
||||||
.into_frames();
|
.into_frames();
|
||||||
|
|
||||||
// If the entries didn't fit, abort (to keep footnote and entry
|
// If the entries didn't fit, abort (to keep footnote and entry
|
||||||
@ -749,7 +775,6 @@ impl FlowLayouter<'_> {
|
|||||||
self.items.truncate(prev_items_len);
|
self.items.truncate(prev_items_len);
|
||||||
self.regions.size = prev_size;
|
self.regions.size = prev_size;
|
||||||
self.has_footnotes = prev_has_footnotes;
|
self.has_footnotes = prev_has_footnotes;
|
||||||
*engine.locator = prev_locator;
|
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -784,7 +809,10 @@ impl FlowLayouter<'_> {
|
|||||||
let pod = Regions::one(self.regions.base(), expand);
|
let pod = Regions::one(self.regions.base(), expand);
|
||||||
let separator = &self.footnote_config.separator;
|
let separator = &self.footnote_config.separator;
|
||||||
|
|
||||||
let mut frame = separator.layout(engine, self.styles, pod)?.into_frame();
|
// FIXME: Shouldn't use `root()` here.
|
||||||
|
let mut frame = separator
|
||||||
|
.layout(engine, Locator::root(), self.styles, pod)?
|
||||||
|
.into_frame();
|
||||||
frame.size_mut().y += self.footnote_config.clearance;
|
frame.size_mut().y += self.footnote_config.clearance;
|
||||||
frame.translate(Point::with_y(self.footnote_config.clearance));
|
frame.translate(Point::with_y(self.footnote_config.clearance));
|
||||||
|
|
||||||
@ -801,10 +829,10 @@ fn find_footnotes(notes: &mut Vec<Packed<FootnoteElem>>, frame: &Frame) {
|
|||||||
for (_, item) in frame.items() {
|
for (_, item) in frame.items() {
|
||||||
match item {
|
match item {
|
||||||
FrameItem::Group(group) => find_footnotes(notes, &group.frame),
|
FrameItem::Group(group) => find_footnotes(notes, &group.frame),
|
||||||
FrameItem::Tag(elem)
|
FrameItem::Tag(tag)
|
||||||
if !notes.iter().any(|note| note.location() == elem.location()) =>
|
if !notes.iter().any(|note| note.location() == tag.elem.location()) =>
|
||||||
{
|
{
|
||||||
let Some(footnote) = elem.to_packed::<FootnoteElem>() else {
|
let Some(footnote) = tag.elem.to_packed::<FootnoteElem>() else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
notes.push(footnote.clone());
|
notes.push(footnote.clone());
|
||||||
|
@ -6,7 +6,8 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::foundations::{cast, dict, Content, Dict, StyleChain, Value};
|
use crate::foundations::{cast, dict, Dict, StyleChain, Value};
|
||||||
|
use crate::introspection::Tag;
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, Axes, Corners, FixedAlignment, HideElem, Length, Point, Rel, Sides, Size,
|
Abs, Axes, Corners, FixedAlignment, HideElem, Length, Point, Rel, Sides, Size,
|
||||||
Transform,
|
Transform,
|
||||||
@ -521,8 +522,9 @@ pub enum FrameItem {
|
|||||||
Image(Image, Size, Span),
|
Image(Image, Size, Span),
|
||||||
/// An internal or external link to a destination.
|
/// An internal or external link to a destination.
|
||||||
Link(Destination, Size),
|
Link(Destination, Size),
|
||||||
/// An introspectable element that produced something within this frame.
|
/// An introspectable element that produced something within this frame
|
||||||
Tag(Content),
|
/// alongside its key.
|
||||||
|
Tag(Tag),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for FrameItem {
|
impl Debug for FrameItem {
|
||||||
@ -533,7 +535,7 @@ impl Debug for FrameItem {
|
|||||||
Self::Shape(shape, _) => write!(f, "{shape:?}"),
|
Self::Shape(shape, _) => write!(f, "{shape:?}"),
|
||||||
Self::Image(image, _, _) => write!(f, "{image:?}"),
|
Self::Image(image, _, _) => write!(f, "{image:?}"),
|
||||||
Self::Link(dest, _) => write!(f, "Link({dest:?})"),
|
Self::Link(dest, _) => write!(f, "Link({dest:?})"),
|
||||||
Self::Tag(elem) => write!(f, "Tag({elem:?})"),
|
Self::Tag(tag) => write!(f, "{tag:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,10 @@ use crate::foundations::{
|
|||||||
Array, CastInfo, Content, Context, Fold, FromValue, Func, IntoValue, Reflect,
|
Array, CastInfo, Content, Context, Fold, FromValue, Func, IntoValue, Reflect,
|
||||||
Resolve, Smart, StyleChain, Value,
|
Resolve, Smart, StyleChain, Value,
|
||||||
};
|
};
|
||||||
use crate::layout::{Abs, Alignment, Axes, Length, LinePosition, Rel, Sides, Sizing};
|
use crate::introspection::Locator;
|
||||||
|
use crate::layout::{
|
||||||
|
Abs, Alignment, Axes, Fragment, Length, LinePosition, Regions, Rel, Sides, Sizing,
|
||||||
|
};
|
||||||
use crate::syntax::Span;
|
use crate::syntax::Span;
|
||||||
use crate::utils::NonZeroExt;
|
use crate::utils::NonZeroExt;
|
||||||
use crate::visualize::{Paint, Stroke};
|
use crate::visualize::{Paint, Stroke};
|
||||||
@ -155,10 +158,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a cell in CellGrid, to be laid out by GridLayouter.
|
/// Represents a cell in CellGrid, to be laid out by GridLayouter.
|
||||||
#[derive(Clone)]
|
pub struct Cell<'a> {
|
||||||
pub struct Cell {
|
|
||||||
/// The cell's body.
|
/// The cell's body.
|
||||||
pub body: Content,
|
pub body: Content,
|
||||||
|
/// The cell's locator.
|
||||||
|
pub locator: Locator<'a>,
|
||||||
/// The cell's fill.
|
/// The cell's fill.
|
||||||
pub fill: Option<Paint>,
|
pub fill: Option<Paint>,
|
||||||
/// The amount of columns spanned by the cell.
|
/// The amount of columns spanned by the cell.
|
||||||
@ -184,11 +188,12 @@ pub struct Cell {
|
|||||||
pub breakable: bool,
|
pub breakable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Content> for Cell {
|
impl<'a> Cell<'a> {
|
||||||
/// Create a simple cell given its body.
|
/// Create a simple cell given its body and its locator.
|
||||||
fn from(body: Content) -> Self {
|
pub fn new(body: Content, locator: Locator<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
body,
|
body,
|
||||||
|
locator,
|
||||||
fill: None,
|
fill: None,
|
||||||
colspan: NonZeroUsize::ONE,
|
colspan: NonZeroUsize::ONE,
|
||||||
rowspan: NonZeroUsize::ONE,
|
rowspan: NonZeroUsize::ONE,
|
||||||
@ -197,13 +202,32 @@ impl From<Content> for Cell {
|
|||||||
breakable: true,
|
breakable: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Layout the cell into the given regions.
|
||||||
|
///
|
||||||
|
/// The `disambiguator` indicates which instance of this cell this should be
|
||||||
|
/// layouted as. For normal cells, it is always `0`, but for headers and
|
||||||
|
/// footers, it indicates the index of the header/footer among all. See the
|
||||||
|
/// [`Locator`] docs for more details on the concepts behind this.
|
||||||
|
pub fn layout(
|
||||||
|
&self,
|
||||||
|
engine: &mut Engine,
|
||||||
|
disambiguator: usize,
|
||||||
|
styles: StyleChain,
|
||||||
|
regions: Regions,
|
||||||
|
) -> SourceResult<Fragment> {
|
||||||
|
let mut locator = self.locator.relayout();
|
||||||
|
if disambiguator > 0 {
|
||||||
|
locator = locator.split().next_inner(disambiguator as u128);
|
||||||
|
}
|
||||||
|
self.body.layout(engine, locator, styles, regions)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A grid entry.
|
/// A grid entry.
|
||||||
#[derive(Clone)]
|
pub(super) enum Entry<'a> {
|
||||||
pub(super) enum Entry {
|
|
||||||
/// An entry which holds a cell.
|
/// An entry which holds a cell.
|
||||||
Cell(Cell),
|
Cell(Cell<'a>),
|
||||||
/// An entry which is merged with another cell.
|
/// An entry which is merged with another cell.
|
||||||
Merged {
|
Merged {
|
||||||
/// The index of the cell this entry is merged with.
|
/// The index of the cell this entry is merged with.
|
||||||
@ -211,9 +235,9 @@ pub(super) enum Entry {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entry {
|
impl<'a> Entry<'a> {
|
||||||
/// Obtains the cell inside this entry, if this is not a merged cell.
|
/// Obtains the cell inside this entry, if this is not a merged cell.
|
||||||
fn as_cell(&self) -> Option<&Cell> {
|
fn as_cell(&self) -> Option<&Cell<'a>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Cell(cell) => Some(cell),
|
Self::Cell(cell) => Some(cell),
|
||||||
Self::Merged { .. } => None,
|
Self::Merged { .. } => None,
|
||||||
@ -269,7 +293,7 @@ pub trait ResolvableCell {
|
|||||||
/// the `breakable` field.
|
/// the `breakable` field.
|
||||||
/// Returns a final Cell.
|
/// Returns a final Cell.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn resolve_cell(
|
fn resolve_cell<'a>(
|
||||||
self,
|
self,
|
||||||
x: usize,
|
x: usize,
|
||||||
y: usize,
|
y: usize,
|
||||||
@ -278,8 +302,9 @@ pub trait ResolvableCell {
|
|||||||
inset: Sides<Option<Rel<Length>>>,
|
inset: Sides<Option<Rel<Length>>>,
|
||||||
stroke: Sides<Option<Option<Arc<Stroke<Abs>>>>>,
|
stroke: Sides<Option<Option<Arc<Stroke<Abs>>>>>,
|
||||||
breakable: bool,
|
breakable: bool,
|
||||||
|
locator: Locator<'a>,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Cell;
|
) -> Cell<'a>;
|
||||||
|
|
||||||
/// Returns this cell's column override.
|
/// Returns this cell's column override.
|
||||||
fn x(&self, styles: StyleChain) -> Smart<usize>;
|
fn x(&self, styles: StyleChain) -> Smart<usize>;
|
||||||
@ -298,9 +323,9 @@ pub trait ResolvableCell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A grid of cells, including the columns, rows, and cell data.
|
/// A grid of cells, including the columns, rows, and cell data.
|
||||||
pub struct CellGrid {
|
pub struct CellGrid<'a> {
|
||||||
/// The grid cells.
|
/// The grid cells.
|
||||||
pub(super) entries: Vec<Entry>,
|
pub(super) entries: Vec<Entry<'a>>,
|
||||||
/// The column tracks including gutter tracks.
|
/// The column tracks including gutter tracks.
|
||||||
pub(super) cols: Vec<Sizing>,
|
pub(super) cols: Vec<Sizing>,
|
||||||
/// The row tracks including gutter tracks.
|
/// The row tracks including gutter tracks.
|
||||||
@ -321,12 +346,12 @@ pub struct CellGrid {
|
|||||||
pub(super) has_gutter: bool,
|
pub(super) has_gutter: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CellGrid {
|
impl<'a> CellGrid<'a> {
|
||||||
/// Generates the cell grid, given the tracks and cells.
|
/// Generates the cell grid, given the tracks and cells.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
tracks: Axes<&[Sizing]>,
|
tracks: Axes<&[Sizing]>,
|
||||||
gutter: Axes<&[Sizing]>,
|
gutter: Axes<&[Sizing]>,
|
||||||
cells: impl IntoIterator<Item = Cell>,
|
cells: impl IntoIterator<Item = Cell<'a>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let entries = cells.into_iter().map(Entry::Cell).collect();
|
let entries = cells.into_iter().map(Entry::Cell).collect();
|
||||||
Self::new_internal(tracks, gutter, vec![], vec![], None, None, entries)
|
Self::new_internal(tracks, gutter, vec![], vec![], None, None, entries)
|
||||||
@ -342,6 +367,7 @@ impl CellGrid {
|
|||||||
pub fn resolve<T, C, I>(
|
pub fn resolve<T, C, I>(
|
||||||
tracks: Axes<&[Sizing]>,
|
tracks: Axes<&[Sizing]>,
|
||||||
gutter: Axes<&[Sizing]>,
|
gutter: Axes<&[Sizing]>,
|
||||||
|
locator: Locator<'a>,
|
||||||
children: C,
|
children: C,
|
||||||
fill: &Celled<Option<Paint>>,
|
fill: &Celled<Option<Paint>>,
|
||||||
align: &Celled<Smart<Alignment>>,
|
align: &Celled<Smart<Alignment>>,
|
||||||
@ -357,6 +383,8 @@ impl CellGrid {
|
|||||||
C: IntoIterator<Item = ResolvableGridChild<T, I>>,
|
C: IntoIterator<Item = ResolvableGridChild<T, I>>,
|
||||||
C::IntoIter: ExactSizeIterator,
|
C::IntoIter: ExactSizeIterator,
|
||||||
{
|
{
|
||||||
|
let mut locator = locator.split();
|
||||||
|
|
||||||
// Number of content columns: Always at least one.
|
// Number of content columns: Always at least one.
|
||||||
let c = tracks.x.len().max(1);
|
let c = tracks.x.len().max(1);
|
||||||
|
|
||||||
@ -660,6 +688,7 @@ impl CellGrid {
|
|||||||
inset.resolve(engine, styles, x, y)?,
|
inset.resolve(engine, styles, x, y)?,
|
||||||
stroke.resolve(engine, styles, x, y)?,
|
stroke.resolve(engine, styles, x, y)?,
|
||||||
resolve_breakable(y, rowspan),
|
resolve_breakable(y, rowspan),
|
||||||
|
locator.next(&cell_span),
|
||||||
styles,
|
styles,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -687,7 +716,7 @@ impl CellGrid {
|
|||||||
// (they can be overridden later); however, if no cells
|
// (they can be overridden later); however, if no cells
|
||||||
// occupy them as we finish building the grid, then such
|
// occupy them as we finish building the grid, then such
|
||||||
// positions will be replaced by empty cells.
|
// positions will be replaced by empty cells.
|
||||||
resolved_cells.resize(new_len, None);
|
resolved_cells.resize_with(new_len, || None);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The vector is large enough to contain the cell, so we can
|
// The vector is large enough to contain the cell, so we can
|
||||||
@ -921,6 +950,7 @@ impl CellGrid {
|
|||||||
inset.resolve(engine, styles, x, y)?,
|
inset.resolve(engine, styles, x, y)?,
|
||||||
stroke.resolve(engine, styles, x, y)?,
|
stroke.resolve(engine, styles, x, y)?,
|
||||||
resolve_breakable(y, 1),
|
resolve_breakable(y, 1),
|
||||||
|
locator.next(&()),
|
||||||
styles,
|
styles,
|
||||||
);
|
);
|
||||||
Ok(Entry::Cell(new_cell))
|
Ok(Entry::Cell(new_cell))
|
||||||
@ -1101,7 +1131,7 @@ impl CellGrid {
|
|||||||
hlines: Vec<Vec<Line>>,
|
hlines: Vec<Vec<Line>>,
|
||||||
header: Option<Repeatable<Header>>,
|
header: Option<Repeatable<Header>>,
|
||||||
footer: Option<Repeatable<Footer>>,
|
footer: Option<Repeatable<Footer>>,
|
||||||
entries: Vec<Entry>,
|
entries: Vec<Entry<'a>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut cols = vec![];
|
let mut cols = vec![];
|
||||||
let mut rows = vec![];
|
let mut rows = vec![];
|
||||||
@ -1163,7 +1193,7 @@ impl CellGrid {
|
|||||||
///
|
///
|
||||||
/// Returns `None` if it's a gutter cell.
|
/// Returns `None` if it's a gutter cell.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub(super) fn entry(&self, x: usize, y: usize) -> Option<&Entry> {
|
pub(super) fn entry(&self, x: usize, y: usize) -> Option<&Entry<'a>> {
|
||||||
assert!(x < self.cols.len());
|
assert!(x < self.cols.len());
|
||||||
assert!(y < self.rows.len());
|
assert!(y < self.rows.len());
|
||||||
|
|
||||||
@ -1185,7 +1215,7 @@ impl CellGrid {
|
|||||||
///
|
///
|
||||||
/// Returns `None` if it's a gutter cell or merged position.
|
/// Returns `None` if it's a gutter cell or merged position.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub(super) fn cell(&self, x: usize, y: usize) -> Option<&Cell> {
|
pub(super) fn cell(&self, x: usize, y: usize) -> Option<&Cell<'a>> {
|
||||||
self.entry(x, y).and_then(Entry::as_cell)
|
self.entry(x, y).and_then(Entry::as_cell)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ use crate::visualize::Geometry;
|
|||||||
/// Performs grid layout.
|
/// Performs grid layout.
|
||||||
pub struct GridLayouter<'a> {
|
pub struct GridLayouter<'a> {
|
||||||
/// The grid of cells.
|
/// The grid of cells.
|
||||||
pub(super) grid: &'a CellGrid,
|
pub(super) grid: &'a CellGrid<'a>,
|
||||||
/// The regions to layout children into.
|
/// The regions to layout children into.
|
||||||
pub(super) regions: Regions<'a>,
|
pub(super) regions: Regions<'a>,
|
||||||
/// The inherited styles.
|
/// The inherited styles.
|
||||||
@ -78,8 +78,8 @@ pub(super) enum Row {
|
|||||||
/// where this row is laid out, and it can only be false when a row uses
|
/// where this row is laid out, and it can only be false when a row uses
|
||||||
/// `layout_multi_row`, which in turn is only used by breakable auto rows.
|
/// `layout_multi_row`, which in turn is only used by breakable auto rows.
|
||||||
Frame(Frame, usize, bool),
|
Frame(Frame, usize, bool),
|
||||||
/// Fractional row with y index.
|
/// Fractional row with y index and disambiguator.
|
||||||
Fr(Fr, usize),
|
Fr(Fr, usize, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Row {
|
impl Row {
|
||||||
@ -87,7 +87,7 @@ impl Row {
|
|||||||
fn index(&self) -> usize {
|
fn index(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Self::Frame(_, y, _) => *y,
|
Self::Frame(_, y, _) => *y,
|
||||||
Self::Fr(_, y) => *y,
|
Self::Fr(_, y, _) => *y,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -97,7 +97,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
///
|
///
|
||||||
/// This prepares grid layout by unifying content and gutter tracks.
|
/// This prepares grid layout by unifying content and gutter tracks.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
grid: &'a CellGrid,
|
grid: &'a CellGrid<'a>,
|
||||||
regions: Regions<'a>,
|
regions: Regions<'a>,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
span: Span,
|
span: Span,
|
||||||
@ -133,7 +133,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
if let Some(Repeatable::Repeated(footer)) = &self.grid.footer {
|
if let Some(Repeatable::Repeated(footer)) = &self.grid.footer {
|
||||||
// Ensure rows in the first region will be aware of the possible
|
// Ensure rows in the first region will be aware of the possible
|
||||||
// presence of the footer.
|
// presence of the footer.
|
||||||
self.prepare_footer(footer, engine)?;
|
self.prepare_footer(footer, engine, 0)?;
|
||||||
if matches!(self.grid.header, None | Some(Repeatable::NotRepeated(_))) {
|
if matches!(self.grid.header, None | Some(Repeatable::NotRepeated(_))) {
|
||||||
// No repeatable header, so we won't subtract it later.
|
// No repeatable header, so we won't subtract it later.
|
||||||
self.regions.size.y -= self.footer_height;
|
self.regions.size.y -= self.footer_height;
|
||||||
@ -144,7 +144,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
if let Some(Repeatable::Repeated(header)) = &self.grid.header {
|
if let Some(Repeatable::Repeated(header)) = &self.grid.header {
|
||||||
if y < header.end {
|
if y < header.end {
|
||||||
if y == 0 {
|
if y == 0 {
|
||||||
self.layout_header(header, engine)?;
|
self.layout_header(header, engine, 0)?;
|
||||||
self.regions.size.y -= self.footer_height;
|
self.regions.size.y -= self.footer_height;
|
||||||
}
|
}
|
||||||
// Skip header rows during normal layout.
|
// Skip header rows during normal layout.
|
||||||
@ -155,16 +155,16 @@ impl<'a> GridLayouter<'a> {
|
|||||||
if let Some(Repeatable::Repeated(footer)) = &self.grid.footer {
|
if let Some(Repeatable::Repeated(footer)) = &self.grid.footer {
|
||||||
if y >= footer.start {
|
if y >= footer.start {
|
||||||
if y == footer.start {
|
if y == footer.start {
|
||||||
self.layout_footer(footer, engine)?;
|
self.layout_footer(footer, engine, self.finished.len())?;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.layout_row(y, engine)?;
|
self.layout_row(y, engine, 0)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.finish_region(engine)?;
|
self.finish_region(engine, true)?;
|
||||||
|
|
||||||
// Layout any missing rowspans.
|
// Layout any missing rowspans.
|
||||||
// There are only two possibilities for rowspans not yet laid out
|
// There are only two possibilities for rowspans not yet laid out
|
||||||
@ -189,27 +189,30 @@ impl<'a> GridLayouter<'a> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
y: usize,
|
y: usize,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
disambiguator: usize,
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
// Skip to next region if current one is full, but only for content
|
// Skip to next region if current one is full, but only for content
|
||||||
// rows, not for gutter rows, and only if we aren't laying out an
|
// rows, not for gutter rows, and only if we aren't laying out an
|
||||||
// unbreakable group of rows.
|
// unbreakable group of rows.
|
||||||
let is_content_row = !self.grid.is_gutter_track(y);
|
let is_content_row = !self.grid.is_gutter_track(y);
|
||||||
if self.unbreakable_rows_left == 0 && self.regions.is_full() && is_content_row {
|
if self.unbreakable_rows_left == 0 && self.regions.is_full() && is_content_row {
|
||||||
self.finish_region(engine)?;
|
self.finish_region(engine, false)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_content_row {
|
if is_content_row {
|
||||||
// Gutter rows have no rowspans or possibly unbreakable cells.
|
// Gutter rows have no rowspans or possibly unbreakable cells.
|
||||||
self.check_for_rowspans(y);
|
self.check_for_rowspans(disambiguator, y);
|
||||||
self.check_for_unbreakable_rows(y, engine)?;
|
self.check_for_unbreakable_rows(y, engine)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't layout gutter rows at the top of a region.
|
// Don't layout gutter rows at the top of a region.
|
||||||
if is_content_row || !self.lrows.is_empty() {
|
if is_content_row || !self.lrows.is_empty() {
|
||||||
match self.grid.rows[y] {
|
match self.grid.rows[y] {
|
||||||
Sizing::Auto => self.layout_auto_row(engine, y)?,
|
Sizing::Auto => self.layout_auto_row(engine, disambiguator, y)?,
|
||||||
Sizing::Rel(v) => self.layout_relative_row(engine, v, y)?,
|
Sizing::Rel(v) => {
|
||||||
Sizing::Fr(v) => self.lrows.push(Row::Fr(v, y)),
|
self.layout_relative_row(engine, disambiguator, v, y)?
|
||||||
|
}
|
||||||
|
Sizing::Fr(v) => self.lrows.push(Row::Fr(v, y, disambiguator)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -841,7 +844,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
|
|
||||||
let size = Size::new(available, height);
|
let size = Size::new(available, height);
|
||||||
let pod = Regions::one(size, Axes::splat(false));
|
let pod = Regions::one(size, Axes::splat(false));
|
||||||
let frame = cell.body.measure(engine, self.styles, pod)?.into_frame();
|
let frame = cell.layout(engine, 0, self.styles, pod)?.into_frame();
|
||||||
resolved.set_max(frame.width() - already_covered_width);
|
resolved.set_max(frame.width() - already_covered_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -901,11 +904,17 @@ impl<'a> GridLayouter<'a> {
|
|||||||
|
|
||||||
/// Layout a row with automatic height. Such a row may break across multiple
|
/// Layout a row with automatic height. Such a row may break across multiple
|
||||||
/// regions.
|
/// regions.
|
||||||
fn layout_auto_row(&mut self, engine: &mut Engine, y: usize) -> SourceResult<()> {
|
fn layout_auto_row(
|
||||||
|
&mut self,
|
||||||
|
engine: &mut Engine,
|
||||||
|
disambiguator: usize,
|
||||||
|
y: usize,
|
||||||
|
) -> SourceResult<()> {
|
||||||
// Determine the size for each region of the row. If the first region
|
// Determine the size for each region of the row. If the first region
|
||||||
// ends up empty for some column, skip the region and remeasure.
|
// ends up empty for some column, skip the region and remeasure.
|
||||||
let mut resolved = match self.measure_auto_row(
|
let mut resolved = match self.measure_auto_row(
|
||||||
engine,
|
engine,
|
||||||
|
disambiguator,
|
||||||
y,
|
y,
|
||||||
true,
|
true,
|
||||||
self.unbreakable_rows_left,
|
self.unbreakable_rows_left,
|
||||||
@ -913,8 +922,15 @@ impl<'a> GridLayouter<'a> {
|
|||||||
)? {
|
)? {
|
||||||
Some(resolved) => resolved,
|
Some(resolved) => resolved,
|
||||||
None => {
|
None => {
|
||||||
self.finish_region(engine)?;
|
self.finish_region(engine, false)?;
|
||||||
self.measure_auto_row(engine, y, false, self.unbreakable_rows_left, None)?
|
self.measure_auto_row(
|
||||||
|
engine,
|
||||||
|
disambiguator,
|
||||||
|
y,
|
||||||
|
false,
|
||||||
|
self.unbreakable_rows_left,
|
||||||
|
None,
|
||||||
|
)?
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -926,7 +942,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
|
|
||||||
// Layout into a single region.
|
// Layout into a single region.
|
||||||
if let &[first] = resolved.as_slice() {
|
if let &[first] = resolved.as_slice() {
|
||||||
let frame = self.layout_single_row(engine, first, y)?;
|
let frame = self.layout_single_row(engine, disambiguator, first, y)?;
|
||||||
self.push_row(frame, y, true);
|
self.push_row(frame, y, true);
|
||||||
|
|
||||||
if self
|
if self
|
||||||
@ -966,12 +982,12 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Layout into multiple regions.
|
// Layout into multiple regions.
|
||||||
let fragment = self.layout_multi_row(engine, &resolved, y)?;
|
let fragment = self.layout_multi_row(engine, disambiguator, &resolved, y)?;
|
||||||
let len = fragment.len();
|
let len = fragment.len();
|
||||||
for (i, frame) in fragment.into_iter().enumerate() {
|
for (i, frame) in fragment.into_iter().enumerate() {
|
||||||
self.push_row(frame, y, i + 1 == len);
|
self.push_row(frame, y, i + 1 == len);
|
||||||
if i + 1 < len {
|
if i + 1 < len {
|
||||||
self.finish_region(engine)?;
|
self.finish_region(engine, false)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -989,6 +1005,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
pub(super) fn measure_auto_row(
|
pub(super) fn measure_auto_row(
|
||||||
&self,
|
&self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
disambiguator: usize,
|
||||||
y: usize,
|
y: usize,
|
||||||
can_skip: bool,
|
can_skip: bool,
|
||||||
unbreakable_rows_left: usize,
|
unbreakable_rows_left: usize,
|
||||||
@ -1069,7 +1086,8 @@ impl<'a> GridLayouter<'a> {
|
|||||||
pod
|
pod
|
||||||
};
|
};
|
||||||
|
|
||||||
let frames = cell.body.measure(engine, self.styles, pod)?.into_frames();
|
let frames =
|
||||||
|
cell.layout(engine, disambiguator, self.styles, pod)?.into_frames();
|
||||||
|
|
||||||
// Skip the first region if one cell in it is empty. Then,
|
// Skip the first region if one cell in it is empty. Then,
|
||||||
// remeasure.
|
// remeasure.
|
||||||
@ -1145,6 +1163,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
&pending_rowspans,
|
&pending_rowspans,
|
||||||
unbreakable_rows_left,
|
unbreakable_rows_left,
|
||||||
row_group_data,
|
row_group_data,
|
||||||
|
disambiguator,
|
||||||
engine,
|
engine,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@ -1159,11 +1178,12 @@ impl<'a> GridLayouter<'a> {
|
|||||||
fn layout_relative_row(
|
fn layout_relative_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
disambiguator: usize,
|
||||||
v: Rel<Length>,
|
v: Rel<Length>,
|
||||||
y: usize,
|
y: usize,
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
let resolved = v.resolve(self.styles).relative_to(self.regions.base().y);
|
let resolved = v.resolve(self.styles).relative_to(self.regions.base().y);
|
||||||
let frame = self.layout_single_row(engine, resolved, y)?;
|
let frame = self.layout_single_row(engine, disambiguator, resolved, y)?;
|
||||||
|
|
||||||
if self
|
if self
|
||||||
.grid
|
.grid
|
||||||
@ -1185,7 +1205,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
&& !self.regions.size.y.fits(height)
|
&& !self.regions.size.y.fits(height)
|
||||||
&& !in_last_with_offset(self.regions, self.header_height + self.footer_height)
|
&& !in_last_with_offset(self.regions, self.header_height + self.footer_height)
|
||||||
{
|
{
|
||||||
self.finish_region(engine)?;
|
self.finish_region(engine, false)?;
|
||||||
|
|
||||||
// Don't skip multiple regions for gutter and don't push a row.
|
// Don't skip multiple regions for gutter and don't push a row.
|
||||||
if self.grid.is_gutter_track(y) {
|
if self.grid.is_gutter_track(y) {
|
||||||
@ -1202,6 +1222,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
fn layout_single_row(
|
fn layout_single_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
disambiguator: usize,
|
||||||
height: Abs,
|
height: Abs,
|
||||||
y: usize,
|
y: usize,
|
||||||
) -> SourceResult<Frame> {
|
) -> SourceResult<Frame> {
|
||||||
@ -1232,7 +1253,9 @@ impl<'a> GridLayouter<'a> {
|
|||||||
// rows.
|
// rows.
|
||||||
pod.full = self.regions.full;
|
pod.full = self.regions.full;
|
||||||
}
|
}
|
||||||
let frame = cell.body.layout(engine, self.styles, pod)?.into_frame();
|
let frame = cell
|
||||||
|
.layout(engine, disambiguator, self.styles, pod)?
|
||||||
|
.into_frame();
|
||||||
let mut pos = pos;
|
let mut pos = pos;
|
||||||
if self.is_rtl {
|
if self.is_rtl {
|
||||||
// In the grid, cell colspans expand to the right,
|
// In the grid, cell colspans expand to the right,
|
||||||
@ -1261,6 +1284,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
fn layout_multi_row(
|
fn layout_multi_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
disambiguator: usize,
|
||||||
heights: &[Abs],
|
heights: &[Abs],
|
||||||
y: usize,
|
y: usize,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -1286,7 +1310,8 @@ impl<'a> GridLayouter<'a> {
|
|||||||
pod.size.x = width;
|
pod.size.x = width;
|
||||||
|
|
||||||
// Push the layouted frames into the individual output frames.
|
// Push the layouted frames into the individual output frames.
|
||||||
let fragment = cell.body.layout(engine, self.styles, pod)?;
|
let fragment =
|
||||||
|
cell.layout(engine, disambiguator, self.styles, pod)?;
|
||||||
for (output, frame) in outputs.iter_mut().zip(fragment) {
|
for (output, frame) in outputs.iter_mut().zip(fragment) {
|
||||||
let mut pos = pos;
|
let mut pos = pos;
|
||||||
if self.is_rtl {
|
if self.is_rtl {
|
||||||
@ -1314,7 +1339,11 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Finish rows for one region.
|
/// Finish rows for one region.
|
||||||
pub(super) fn finish_region(&mut self, engine: &mut Engine) -> SourceResult<()> {
|
pub(super) fn finish_region(
|
||||||
|
&mut self,
|
||||||
|
engine: &mut Engine,
|
||||||
|
last: bool,
|
||||||
|
) -> SourceResult<()> {
|
||||||
if self
|
if self
|
||||||
.lrows
|
.lrows
|
||||||
.last()
|
.last()
|
||||||
@ -1369,7 +1398,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
&& self.lrows.iter().all(|row| row.index() < footer.start)
|
&& self.lrows.iter().all(|row| row.index() < footer.start)
|
||||||
{
|
{
|
||||||
laid_out_footer_start = Some(footer.start);
|
laid_out_footer_start = Some(footer.start);
|
||||||
self.layout_footer(footer, engine)?;
|
self.layout_footer(footer, engine, self.finished.len())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1379,7 +1408,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
for row in &self.lrows {
|
for row in &self.lrows {
|
||||||
match row {
|
match row {
|
||||||
Row::Frame(frame, _, _) => used += frame.height(),
|
Row::Frame(frame, _, _) => used += frame.height(),
|
||||||
Row::Fr(v, _) => fr += *v,
|
Row::Fr(v, _, _) => fr += *v,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1400,10 +1429,10 @@ impl<'a> GridLayouter<'a> {
|
|||||||
for row in std::mem::take(&mut self.lrows) {
|
for row in std::mem::take(&mut self.lrows) {
|
||||||
let (frame, y, is_last) = match row {
|
let (frame, y, is_last) = match row {
|
||||||
Row::Frame(frame, y, is_last) => (frame, y, is_last),
|
Row::Frame(frame, y, is_last) => (frame, y, is_last),
|
||||||
Row::Fr(v, y) => {
|
Row::Fr(v, y, disambiguator) => {
|
||||||
let remaining = self.regions.full - used;
|
let remaining = self.regions.full - used;
|
||||||
let height = v.share(fr, remaining);
|
let height = v.share(fr, remaining);
|
||||||
(self.layout_single_row(engine, height, y)?, y, true)
|
(self.layout_single_row(engine, disambiguator, height, y)?, y, true)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1499,17 +1528,20 @@ impl<'a> GridLayouter<'a> {
|
|||||||
|
|
||||||
self.finish_region_internal(output, rrows);
|
self.finish_region_internal(output, rrows);
|
||||||
|
|
||||||
|
if !last {
|
||||||
|
let disambiguator = self.finished.len();
|
||||||
if let Some(Repeatable::Repeated(footer)) = &self.grid.footer {
|
if let Some(Repeatable::Repeated(footer)) = &self.grid.footer {
|
||||||
self.prepare_footer(footer, engine)?;
|
self.prepare_footer(footer, engine, disambiguator)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(Repeatable::Repeated(header)) = &self.grid.header {
|
if let Some(Repeatable::Repeated(header)) = &self.grid.header {
|
||||||
// Add a header to the new region.
|
// Add a header to the new region.
|
||||||
self.layout_header(header, engine)?;
|
self.layout_header(header, engine, disambiguator)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure rows don't try to overrun the footer.
|
// Ensure rows don't try to overrun the footer.
|
||||||
self.regions.size.y -= self.footer_height;
|
self.regions.size.y -= self.footer_height;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -602,12 +602,14 @@ mod test {
|
|||||||
use super::super::cells::Entry;
|
use super::super::cells::Entry;
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::foundations::Content;
|
use crate::foundations::Content;
|
||||||
|
use crate::introspection::Locator;
|
||||||
use crate::layout::{Axes, Cell, Sides, Sizing};
|
use crate::layout::{Axes, Cell, Sides, Sizing};
|
||||||
use crate::utils::NonZeroExt;
|
use crate::utils::NonZeroExt;
|
||||||
|
|
||||||
fn sample_cell() -> Cell {
|
fn sample_cell() -> Cell<'static> {
|
||||||
Cell {
|
Cell {
|
||||||
body: Content::default(),
|
body: Content::default(),
|
||||||
|
locator: Locator::root(),
|
||||||
fill: None,
|
fill: None,
|
||||||
colspan: NonZeroUsize::ONE,
|
colspan: NonZeroUsize::ONE,
|
||||||
rowspan: NonZeroUsize::ONE,
|
rowspan: NonZeroUsize::ONE,
|
||||||
@ -617,9 +619,10 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cell_with_colspan_rowspan(colspan: usize, rowspan: usize) -> Cell {
|
fn cell_with_colspan_rowspan(colspan: usize, rowspan: usize) -> Cell<'static> {
|
||||||
Cell {
|
Cell {
|
||||||
body: Content::default(),
|
body: Content::default(),
|
||||||
|
locator: Locator::root(),
|
||||||
fill: None,
|
fill: None,
|
||||||
colspan: NonZeroUsize::try_from(colspan).unwrap(),
|
colspan: NonZeroUsize::try_from(colspan).unwrap(),
|
||||||
rowspan: NonZeroUsize::try_from(rowspan).unwrap(),
|
rowspan: NonZeroUsize::try_from(rowspan).unwrap(),
|
||||||
@ -629,7 +632,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample_grid_for_vlines(gutters: bool) -> CellGrid {
|
fn sample_grid_for_vlines(gutters: bool) -> CellGrid<'static> {
|
||||||
const COLS: usize = 4;
|
const COLS: usize = 4;
|
||||||
const ROWS: usize = 6;
|
const ROWS: usize = 6;
|
||||||
let entries = vec![
|
let entries = vec![
|
||||||
@ -1152,7 +1155,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample_grid_for_hlines(gutters: bool) -> CellGrid {
|
fn sample_grid_for_hlines(gutters: bool) -> CellGrid<'static> {
|
||||||
const COLS: usize = 4;
|
const COLS: usize = 4;
|
||||||
const ROWS: usize = 9;
|
const ROWS: usize = 9;
|
||||||
let entries = vec![
|
let entries = vec![
|
||||||
|
@ -22,6 +22,7 @@ use crate::foundations::{
|
|||||||
cast, elem, scope, Array, Content, Fold, NativeElement, Packed, Show, Smart,
|
cast, elem, scope, Array, Content, Fold, NativeElement, Packed, Show, Smart,
|
||||||
StyleChain, Value,
|
StyleChain, Value,
|
||||||
};
|
};
|
||||||
|
use crate::introspection::Locator;
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, Alignment, Axes, BlockElem, Dir, Fragment, Length, OuterHAlignment,
|
Abs, Alignment, Axes, BlockElem, Dir, Fragment, Length, OuterHAlignment,
|
||||||
OuterVAlignment, Regions, Rel, Sides, Sizing,
|
OuterVAlignment, Regions, Rel, Sides, Sizing,
|
||||||
@ -338,7 +339,9 @@ impl GridElem {
|
|||||||
|
|
||||||
impl Show for Packed<GridElem> {
|
impl Show for Packed<GridElem> {
|
||||||
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(BlockElem::multi_layouter(self.clone(), layout_grid).pack())
|
Ok(BlockElem::multi_layouter(self.clone(), layout_grid)
|
||||||
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,6 +350,7 @@ impl Show for Packed<GridElem> {
|
|||||||
fn layout_grid(
|
fn layout_grid(
|
||||||
elem: &Packed<GridElem>,
|
elem: &Packed<GridElem>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -380,6 +384,7 @@ fn layout_grid(
|
|||||||
let grid = CellGrid::resolve(
|
let grid = CellGrid::resolve(
|
||||||
tracks,
|
tracks,
|
||||||
gutter,
|
gutter,
|
||||||
|
locator,
|
||||||
children,
|
children,
|
||||||
fill,
|
fill,
|
||||||
align,
|
align,
|
||||||
@ -854,7 +859,7 @@ impl Default for Packed<GridCell> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ResolvableCell for Packed<GridCell> {
|
impl ResolvableCell for Packed<GridCell> {
|
||||||
fn resolve_cell(
|
fn resolve_cell<'a>(
|
||||||
mut self,
|
mut self,
|
||||||
x: usize,
|
x: usize,
|
||||||
y: usize,
|
y: usize,
|
||||||
@ -863,8 +868,9 @@ impl ResolvableCell for Packed<GridCell> {
|
|||||||
inset: Sides<Option<Rel<Length>>>,
|
inset: Sides<Option<Rel<Length>>>,
|
||||||
stroke: Sides<Option<Option<Arc<Stroke<Abs>>>>>,
|
stroke: Sides<Option<Option<Arc<Stroke<Abs>>>>>,
|
||||||
breakable: bool,
|
breakable: bool,
|
||||||
|
locator: Locator<'a>,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Cell {
|
) -> Cell<'a> {
|
||||||
let cell = &mut *self;
|
let cell = &mut *self;
|
||||||
let colspan = cell.colspan(styles);
|
let colspan = cell.colspan(styles);
|
||||||
let rowspan = cell.rowspan(styles);
|
let rowspan = cell.rowspan(styles);
|
||||||
@ -916,6 +922,7 @@ impl ResolvableCell for Packed<GridCell> {
|
|||||||
cell.push_breakable(Smart::Custom(breakable));
|
cell.push_breakable(Smart::Custom(breakable));
|
||||||
Cell {
|
Cell {
|
||||||
body: self.pack(),
|
body: self.pack(),
|
||||||
|
locator,
|
||||||
fill,
|
fill,
|
||||||
colspan,
|
colspan,
|
||||||
rowspan,
|
rowspan,
|
||||||
|
@ -50,8 +50,10 @@ impl<'a> GridLayouter<'a> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
header: &Header,
|
header: &Header,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
disambiguator: usize,
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
let header_rows = self.simulate_header(header, &self.regions, engine)?;
|
let header_rows =
|
||||||
|
self.simulate_header(header, &self.regions, engine, disambiguator)?;
|
||||||
let mut skipped_region = false;
|
let mut skipped_region = false;
|
||||||
while self.unbreakable_rows_left == 0
|
while self.unbreakable_rows_left == 0
|
||||||
&& !self.regions.size.y.fits(header_rows.height + self.footer_height)
|
&& !self.regions.size.y.fits(header_rows.height + self.footer_height)
|
||||||
@ -71,8 +73,9 @@ impl<'a> GridLayouter<'a> {
|
|||||||
if skipped_region {
|
if skipped_region {
|
||||||
// Simulate the footer again; the region's 'full' might have
|
// Simulate the footer again; the region's 'full' might have
|
||||||
// changed.
|
// changed.
|
||||||
self.footer_height =
|
self.footer_height = self
|
||||||
self.simulate_footer(footer, &self.regions, engine)?.height;
|
.simulate_footer(footer, &self.regions, engine, disambiguator)?
|
||||||
|
.height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +84,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
// within 'layout_row'.
|
// within 'layout_row'.
|
||||||
self.unbreakable_rows_left += header.end;
|
self.unbreakable_rows_left += header.end;
|
||||||
for y in 0..header.end {
|
for y in 0..header.end {
|
||||||
self.layout_row(y, engine)?;
|
self.layout_row(y, engine, disambiguator)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -92,16 +95,20 @@ impl<'a> GridLayouter<'a> {
|
|||||||
header: &Header,
|
header: &Header,
|
||||||
regions: &Regions<'_>,
|
regions: &Regions<'_>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
disambiguator: usize,
|
||||||
) -> SourceResult<UnbreakableRowGroup> {
|
) -> SourceResult<UnbreakableRowGroup> {
|
||||||
// Note that we assume the invariant that any rowspan in a header is
|
// Note that we assume the invariant that any rowspan in a header is
|
||||||
// fully contained within that header. Therefore, there won't be any
|
// fully contained within that header. Therefore, there won't be any
|
||||||
// unbreakable rowspans exceeding the header's rows, and we can safely
|
// unbreakable rowspans exceeding the header's rows, and we can safely
|
||||||
// assume that the amount of unbreakable rows following the first row
|
// assume that the amount of unbreakable rows following the first row
|
||||||
// in the header will be precisely the rows in the header.
|
// in the header will be precisely the rows in the header.
|
||||||
let header_row_group =
|
self.simulate_unbreakable_row_group(
|
||||||
self.simulate_unbreakable_row_group(0, Some(header.end), regions, engine)?;
|
0,
|
||||||
|
Some(header.end),
|
||||||
Ok(header_row_group)
|
regions,
|
||||||
|
engine,
|
||||||
|
disambiguator,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates `self.footer_height` by simulating the footer, and skips to fitting region.
|
/// Updates `self.footer_height` by simulating the footer, and skips to fitting region.
|
||||||
@ -109,8 +116,11 @@ impl<'a> GridLayouter<'a> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
footer: &Footer,
|
footer: &Footer,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
disambiguator: usize,
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
let footer_height = self.simulate_footer(footer, &self.regions, engine)?.height;
|
let footer_height = self
|
||||||
|
.simulate_footer(footer, &self.regions, engine, disambiguator)?
|
||||||
|
.height;
|
||||||
let mut skipped_region = false;
|
let mut skipped_region = false;
|
||||||
while self.unbreakable_rows_left == 0
|
while self.unbreakable_rows_left == 0
|
||||||
&& !self.regions.size.y.fits(footer_height)
|
&& !self.regions.size.y.fits(footer_height)
|
||||||
@ -125,7 +135,8 @@ impl<'a> GridLayouter<'a> {
|
|||||||
self.footer_height = if skipped_region {
|
self.footer_height = if skipped_region {
|
||||||
// Simulate the footer again; the region's 'full' might have
|
// Simulate the footer again; the region's 'full' might have
|
||||||
// changed.
|
// changed.
|
||||||
self.simulate_footer(footer, &self.regions, engine)?.height
|
self.simulate_footer(footer, &self.regions, engine, disambiguator)?
|
||||||
|
.height
|
||||||
} else {
|
} else {
|
||||||
footer_height
|
footer_height
|
||||||
};
|
};
|
||||||
@ -139,6 +150,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
footer: &Footer,
|
footer: &Footer,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
disambiguator: usize,
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
// Ensure footer rows have their own height available.
|
// Ensure footer rows have their own height available.
|
||||||
// Won't change much as we're creating an unbreakable row group
|
// Won't change much as we're creating an unbreakable row group
|
||||||
@ -148,7 +160,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
let footer_len = self.grid.rows.len() - footer.start;
|
let footer_len = self.grid.rows.len() - footer.start;
|
||||||
self.unbreakable_rows_left += footer_len;
|
self.unbreakable_rows_left += footer_len;
|
||||||
for y in footer.start..self.grid.rows.len() {
|
for y in footer.start..self.grid.rows.len() {
|
||||||
self.layout_row(y, engine)?;
|
self.layout_row(y, engine, disambiguator)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -160,19 +172,19 @@ impl<'a> GridLayouter<'a> {
|
|||||||
footer: &Footer,
|
footer: &Footer,
|
||||||
regions: &Regions<'_>,
|
regions: &Regions<'_>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
disambiguator: usize,
|
||||||
) -> SourceResult<UnbreakableRowGroup> {
|
) -> SourceResult<UnbreakableRowGroup> {
|
||||||
// Note that we assume the invariant that any rowspan in a footer is
|
// Note that we assume the invariant that any rowspan in a footer is
|
||||||
// fully contained within that footer. Therefore, there won't be any
|
// fully contained within that footer. Therefore, there won't be any
|
||||||
// unbreakable rowspans exceeding the footer's rows, and we can safely
|
// unbreakable rowspans exceeding the footer's rows, and we can safely
|
||||||
// assume that the amount of unbreakable rows following the first row
|
// assume that the amount of unbreakable rows following the first row
|
||||||
// in the footer will be precisely the rows in the footer.
|
// in the footer will be precisely the rows in the footer.
|
||||||
let footer_row_group = self.simulate_unbreakable_row_group(
|
self.simulate_unbreakable_row_group(
|
||||||
footer.start,
|
footer.start,
|
||||||
Some(self.grid.rows.len() - footer.start),
|
Some(self.grid.rows.len() - footer.start),
|
||||||
regions,
|
regions,
|
||||||
engine,
|
engine,
|
||||||
)?;
|
disambiguator,
|
||||||
|
)
|
||||||
Ok(footer_row_group)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,8 @@ pub(super) struct Rowspan {
|
|||||||
pub(super) x: usize,
|
pub(super) x: usize,
|
||||||
/// First row of this rowspan.
|
/// First row of this rowspan.
|
||||||
pub(super) y: usize,
|
pub(super) y: usize,
|
||||||
|
/// The disambiguator for laying out the cells.
|
||||||
|
pub(super) disambiguator: usize,
|
||||||
/// Amount of rows spanned by the cell at (x, y).
|
/// Amount of rows spanned by the cell at (x, y).
|
||||||
pub(super) rowspan: usize,
|
pub(super) rowspan: usize,
|
||||||
/// Whether all rows of the rowspan are part of an unbreakable row group.
|
/// Whether all rows of the rowspan are part of an unbreakable row group.
|
||||||
@ -100,6 +102,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
let Rowspan {
|
let Rowspan {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
|
disambiguator,
|
||||||
rowspan,
|
rowspan,
|
||||||
is_effectively_unbreakable,
|
is_effectively_unbreakable,
|
||||||
dx,
|
dx,
|
||||||
@ -136,7 +139,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Push the layouted frames directly into the finished frames.
|
// Push the layouted frames directly into the finished frames.
|
||||||
let fragment = cell.body.layout(engine, self.styles, pod)?;
|
let fragment = cell.layout(engine, disambiguator, self.styles, pod)?;
|
||||||
let (current_region, current_rrows) = current_region_data.unzip();
|
let (current_region, current_rrows) = current_region_data.unzip();
|
||||||
for ((i, finished), frame) in self
|
for ((i, finished), frame) in self
|
||||||
.finished
|
.finished
|
||||||
@ -179,7 +182,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
|
|
||||||
/// Checks if a row contains the beginning of one or more rowspan cells.
|
/// Checks if a row contains the beginning of one or more rowspan cells.
|
||||||
/// If so, adds them to the rowspans vector.
|
/// If so, adds them to the rowspans vector.
|
||||||
pub(super) fn check_for_rowspans(&mut self, y: usize) {
|
pub(super) fn check_for_rowspans(&mut self, disambiguator: usize, y: usize) {
|
||||||
// We will compute the horizontal offset of each rowspan in advance.
|
// We will compute the horizontal offset of each rowspan in advance.
|
||||||
// For that reason, we must reverse the column order when using RTL.
|
// For that reason, we must reverse the column order when using RTL.
|
||||||
let offsets = points(self.rcols.iter().copied().rev_if(self.is_rtl));
|
let offsets = points(self.rcols.iter().copied().rev_if(self.is_rtl));
|
||||||
@ -193,6 +196,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
self.rowspans.push(Rowspan {
|
self.rowspans.push(Rowspan {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
|
disambiguator,
|
||||||
rowspan,
|
rowspan,
|
||||||
// The field below will be updated in
|
// The field below will be updated in
|
||||||
// 'check_for_unbreakable_rows'.
|
// 'check_for_unbreakable_rows'.
|
||||||
@ -241,6 +245,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
amount_unbreakable_rows,
|
amount_unbreakable_rows,
|
||||||
&self.regions,
|
&self.regions,
|
||||||
engine,
|
engine,
|
||||||
|
0,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Skip to fitting region.
|
// Skip to fitting region.
|
||||||
@ -250,7 +255,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
self.header_height + self.footer_height,
|
self.header_height + self.footer_height,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
self.finish_region(engine)?;
|
self.finish_region(engine, false)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update unbreakable rows left.
|
// Update unbreakable rows left.
|
||||||
@ -291,6 +296,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
amount_unbreakable_rows: Option<usize>,
|
amount_unbreakable_rows: Option<usize>,
|
||||||
regions: &Regions<'_>,
|
regions: &Regions<'_>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
disambiguator: usize,
|
||||||
) -> SourceResult<UnbreakableRowGroup> {
|
) -> SourceResult<UnbreakableRowGroup> {
|
||||||
let mut row_group = UnbreakableRowGroup::default();
|
let mut row_group = UnbreakableRowGroup::default();
|
||||||
let mut unbreakable_rows_left = amount_unbreakable_rows.unwrap_or(0);
|
let mut unbreakable_rows_left = amount_unbreakable_rows.unwrap_or(0);
|
||||||
@ -319,6 +325,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
Sizing::Auto => self
|
Sizing::Auto => self
|
||||||
.measure_auto_row(
|
.measure_auto_row(
|
||||||
engine,
|
engine,
|
||||||
|
disambiguator,
|
||||||
y,
|
y,
|
||||||
false,
|
false,
|
||||||
unbreakable_rows_left,
|
unbreakable_rows_left,
|
||||||
@ -657,6 +664,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
/// auto row will have to expand, given the current sizes of the auto row
|
/// auto row will have to expand, given the current sizes of the auto row
|
||||||
/// in each region and the pending rowspans' data (parent Y, rowspan amount
|
/// in each region and the pending rowspans' data (parent Y, rowspan amount
|
||||||
/// and vector of requested sizes).
|
/// and vector of requested sizes).
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(super) fn simulate_and_measure_rowspans_in_auto_row(
|
pub(super) fn simulate_and_measure_rowspans_in_auto_row(
|
||||||
&self,
|
&self,
|
||||||
y: usize,
|
y: usize,
|
||||||
@ -664,6 +672,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
pending_rowspans: &[(usize, usize, Vec<Abs>)],
|
pending_rowspans: &[(usize, usize, Vec<Abs>)],
|
||||||
unbreakable_rows_left: usize,
|
unbreakable_rows_left: usize,
|
||||||
row_group_data: Option<&UnbreakableRowGroup>,
|
row_group_data: Option<&UnbreakableRowGroup>,
|
||||||
|
mut disambiguator: usize,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
// To begin our simulation, we have to unify the sizes demanded by
|
// To begin our simulation, we have to unify the sizes demanded by
|
||||||
@ -726,6 +735,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
// expand) because we popped the last resolved size from the
|
// expand) because we popped the last resolved size from the
|
||||||
// resolved vector, above.
|
// resolved vector, above.
|
||||||
simulated_regions.next();
|
simulated_regions.next();
|
||||||
|
disambiguator += 1;
|
||||||
|
|
||||||
// Subtract the initial header and footer height, since that's the
|
// Subtract the initial header and footer height, since that's the
|
||||||
// height we used when subtracting from the region backlog's
|
// height we used when subtracting from the region backlog's
|
||||||
@ -749,6 +759,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
engine,
|
engine,
|
||||||
last_resolved_size,
|
last_resolved_size,
|
||||||
unbreakable_rows_left,
|
unbreakable_rows_left,
|
||||||
|
disambiguator,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if !simulations_stabilized {
|
if !simulations_stabilized {
|
||||||
@ -839,6 +850,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
last_resolved_size: Option<Abs>,
|
last_resolved_size: Option<Abs>,
|
||||||
unbreakable_rows_left: usize,
|
unbreakable_rows_left: usize,
|
||||||
|
mut disambiguator: usize,
|
||||||
) -> SourceResult<bool> {
|
) -> SourceResult<bool> {
|
||||||
// The max amount this row can expand will be the total size requested
|
// The max amount this row can expand will be the total size requested
|
||||||
// by rowspans which was not yet resolved. It is worth noting that,
|
// by rowspans which was not yet resolved. It is worth noting that,
|
||||||
@ -861,6 +873,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
// of the requested rowspan height, we give up.
|
// of the requested rowspan height, we give up.
|
||||||
for _attempt in 0..5 {
|
for _attempt in 0..5 {
|
||||||
let rowspan_simulator = RowspanSimulator::new(
|
let rowspan_simulator = RowspanSimulator::new(
|
||||||
|
disambiguator,
|
||||||
simulated_regions,
|
simulated_regions,
|
||||||
self.header_height,
|
self.header_height,
|
||||||
self.footer_height,
|
self.footer_height,
|
||||||
@ -947,6 +960,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
extra_amount_to_grow -= simulated_regions.size.y.max(Abs::zero());
|
extra_amount_to_grow -= simulated_regions.size.y.max(Abs::zero());
|
||||||
simulated_regions.next();
|
simulated_regions.next();
|
||||||
simulated_regions.size.y -= self.header_height + self.footer_height;
|
simulated_regions.size.y -= self.header_height + self.footer_height;
|
||||||
|
disambiguator += 1;
|
||||||
}
|
}
|
||||||
simulated_regions.size.y -= extra_amount_to_grow;
|
simulated_regions.size.y -= extra_amount_to_grow;
|
||||||
}
|
}
|
||||||
@ -958,6 +972,8 @@ impl<'a> GridLayouter<'a> {
|
|||||||
|
|
||||||
/// Auxiliary structure holding state during rowspan simulation.
|
/// Auxiliary structure holding state during rowspan simulation.
|
||||||
struct RowspanSimulator<'a> {
|
struct RowspanSimulator<'a> {
|
||||||
|
/// The number of finished regions.
|
||||||
|
finished: usize,
|
||||||
/// The state of regions during the simulation.
|
/// The state of regions during the simulation.
|
||||||
regions: Regions<'a>,
|
regions: Regions<'a>,
|
||||||
/// The height of the header in the currently simulated region.
|
/// The height of the header in the currently simulated region.
|
||||||
@ -974,8 +990,14 @@ struct RowspanSimulator<'a> {
|
|||||||
impl<'a> RowspanSimulator<'a> {
|
impl<'a> RowspanSimulator<'a> {
|
||||||
/// Creates new rowspan simulation state with the given regions and initial
|
/// Creates new rowspan simulation state with the given regions and initial
|
||||||
/// header and footer heights. Other fields should always start as zero.
|
/// header and footer heights. Other fields should always start as zero.
|
||||||
fn new(regions: Regions<'a>, header_height: Abs, footer_height: Abs) -> Self {
|
fn new(
|
||||||
|
finished: usize,
|
||||||
|
regions: Regions<'a>,
|
||||||
|
header_height: Abs,
|
||||||
|
footer_height: Abs,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
finished,
|
||||||
regions,
|
regions,
|
||||||
header_height,
|
header_height,
|
||||||
footer_height,
|
footer_height,
|
||||||
@ -1024,6 +1046,7 @@ impl<'a> RowspanSimulator<'a> {
|
|||||||
None,
|
None,
|
||||||
&self.regions,
|
&self.regions,
|
||||||
engine,
|
engine,
|
||||||
|
0,
|
||||||
)?;
|
)?;
|
||||||
while !self.regions.size.y.fits(row_group.height)
|
while !self.regions.size.y.fits(row_group.height)
|
||||||
&& !in_last_with_offset(
|
&& !in_last_with_offset(
|
||||||
@ -1099,16 +1122,21 @@ impl<'a> RowspanSimulator<'a> {
|
|||||||
// backlog to consider the initial header and footer heights; however,
|
// backlog to consider the initial header and footer heights; however,
|
||||||
// our simulation checks what happens AFTER the auto row, so we can
|
// our simulation checks what happens AFTER the auto row, so we can
|
||||||
// just use the original backlog from `self.regions`.
|
// just use the original backlog from `self.regions`.
|
||||||
|
let disambiguator = self.finished;
|
||||||
let header_height =
|
let header_height =
|
||||||
if let Some(Repeatable::Repeated(header)) = &layouter.grid.header {
|
if let Some(Repeatable::Repeated(header)) = &layouter.grid.header {
|
||||||
layouter.simulate_header(header, &self.regions, engine)?.height
|
layouter
|
||||||
|
.simulate_header(header, &self.regions, engine, disambiguator)?
|
||||||
|
.height
|
||||||
} else {
|
} else {
|
||||||
Abs::zero()
|
Abs::zero()
|
||||||
};
|
};
|
||||||
|
|
||||||
let footer_height =
|
let footer_height =
|
||||||
if let Some(Repeatable::Repeated(footer)) = &layouter.grid.footer {
|
if let Some(Repeatable::Repeated(footer)) = &layouter.grid.footer {
|
||||||
layouter.simulate_footer(footer, &self.regions, engine)?.height
|
layouter
|
||||||
|
.simulate_footer(footer, &self.regions, engine, disambiguator)?
|
||||||
|
.height
|
||||||
} else {
|
} else {
|
||||||
Abs::zero()
|
Abs::zero()
|
||||||
};
|
};
|
||||||
@ -1120,6 +1148,7 @@ impl<'a> RowspanSimulator<'a> {
|
|||||||
&& !self.regions.in_last()
|
&& !self.regions.in_last()
|
||||||
{
|
{
|
||||||
self.regions.next();
|
self.regions.next();
|
||||||
|
self.finished += 1;
|
||||||
skipped_region = true;
|
skipped_region = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1127,7 +1156,9 @@ impl<'a> RowspanSimulator<'a> {
|
|||||||
self.header_height = if skipped_region {
|
self.header_height = if skipped_region {
|
||||||
// Simulate headers again, at the new region, as
|
// Simulate headers again, at the new region, as
|
||||||
// the full region height may change.
|
// the full region height may change.
|
||||||
layouter.simulate_header(header, &self.regions, engine)?.height
|
layouter
|
||||||
|
.simulate_header(header, &self.regions, engine, disambiguator)?
|
||||||
|
.height
|
||||||
} else {
|
} else {
|
||||||
header_height
|
header_height
|
||||||
};
|
};
|
||||||
@ -1137,7 +1168,9 @@ impl<'a> RowspanSimulator<'a> {
|
|||||||
self.footer_height = if skipped_region {
|
self.footer_height = if skipped_region {
|
||||||
// Simulate footers again, at the new region, as
|
// Simulate footers again, at the new region, as
|
||||||
// the full region height may change.
|
// the full region height may change.
|
||||||
layouter.simulate_footer(footer, &self.regions, engine)?.height
|
layouter
|
||||||
|
.simulate_footer(footer, &self.regions, engine, disambiguator)?
|
||||||
|
.height
|
||||||
} else {
|
} else {
|
||||||
footer_height
|
footer_height
|
||||||
};
|
};
|
||||||
@ -1162,6 +1195,7 @@ impl<'a> RowspanSimulator<'a> {
|
|||||||
self.total_spanned_height -= self.latest_spanned_gutter_height;
|
self.total_spanned_height -= self.latest_spanned_gutter_height;
|
||||||
self.latest_spanned_gutter_height = Abs::zero();
|
self.latest_spanned_gutter_height = Abs::zero();
|
||||||
self.regions.next();
|
self.regions.next();
|
||||||
|
self.finished += 1;
|
||||||
|
|
||||||
self.simulate_header_footer_layout(layouter, engine)
|
self.simulate_header_footer_layout(layouter, engine)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
mod linebreak;
|
mod linebreak;
|
||||||
mod shaping;
|
mod shaping;
|
||||||
|
|
||||||
use comemo::{Tracked, TrackedMut};
|
use comemo::{Track, Tracked, TrackedMut};
|
||||||
use unicode_bidi::{BidiInfo, Level as BidiLevel};
|
use unicode_bidi::{BidiInfo, Level as BidiLevel};
|
||||||
use unicode_script::{Script, UnicodeScript};
|
use unicode_script::{Script, UnicodeScript};
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ use crate::diag::{bail, SourceResult};
|
|||||||
use crate::engine::{Engine, Route};
|
use crate::engine::{Engine, Route};
|
||||||
use crate::eval::Tracer;
|
use crate::eval::Tracer;
|
||||||
use crate::foundations::{Packed, Resolve, Smart, StyleChain};
|
use crate::foundations::{Packed, Resolve, Smart, StyleChain};
|
||||||
use crate::introspection::{Introspector, Locator, TagElem};
|
use crate::introspection::{Introspector, Locator, LocatorLink, Tag, TagElem};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, AlignElem, BoxElem, Dir, Em, FixedAlignment, Fr, Fragment, Frame, FrameItem,
|
Abs, AlignElem, BoxElem, Dir, Em, FixedAlignment, Fr, Fragment, Frame, FrameItem,
|
||||||
HElem, InlineElem, InlineItem, Point, Size, Sizing, Spacing,
|
HElem, InlineElem, InlineItem, Point, Size, Sizing, Spacing,
|
||||||
@ -33,6 +33,7 @@ use crate::World;
|
|||||||
pub(crate) fn layout_inline(
|
pub(crate) fn layout_inline(
|
||||||
children: &StyleVec,
|
children: &StyleVec,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
consecutive: bool,
|
consecutive: bool,
|
||||||
region: Size,
|
region: Size,
|
||||||
@ -45,25 +46,25 @@ pub(crate) fn layout_inline(
|
|||||||
world: Tracked<dyn World + '_>,
|
world: Tracked<dyn World + '_>,
|
||||||
introspector: Tracked<Introspector>,
|
introspector: Tracked<Introspector>,
|
||||||
route: Tracked<Route>,
|
route: Tracked<Route>,
|
||||||
locator: Tracked<Locator>,
|
|
||||||
tracer: TrackedMut<Tracer>,
|
tracer: TrackedMut<Tracer>,
|
||||||
|
locator: Tracked<Locator>,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
consecutive: bool,
|
consecutive: bool,
|
||||||
region: Size,
|
region: Size,
|
||||||
expand: bool,
|
expand: bool,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
let mut locator = Locator::chained(locator);
|
let link = LocatorLink::new(locator);
|
||||||
|
let locator = Locator::link(&link);
|
||||||
let mut engine = Engine {
|
let mut engine = Engine {
|
||||||
world,
|
world,
|
||||||
introspector,
|
introspector,
|
||||||
route: Route::extend(route),
|
route: Route::extend(route),
|
||||||
locator: &mut locator,
|
|
||||||
tracer,
|
tracer,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Collect all text into one string for BiDi analysis.
|
// Collect all text into one string for BiDi analysis.
|
||||||
let (text, segments, spans) =
|
let (text, segments, spans) =
|
||||||
collect(children, &mut engine, &styles, region, consecutive)?;
|
collect(children, &mut engine, locator, &styles, region, consecutive)?;
|
||||||
|
|
||||||
// Perform BiDi analysis and then prepare paragraph layout by building a
|
// Perform BiDi analysis and then prepare paragraph layout by building a
|
||||||
// representation on which we can do line breaking without layouting
|
// representation on which we can do line breaking without layouting
|
||||||
@ -78,21 +79,18 @@ pub(crate) fn layout_inline(
|
|||||||
finalize(&mut engine, &p, &lines, region, expand, shrink)
|
finalize(&mut engine, &p, &lines, region, expand, shrink)
|
||||||
}
|
}
|
||||||
|
|
||||||
let fragment = cached(
|
cached(
|
||||||
children,
|
children,
|
||||||
engine.world,
|
engine.world,
|
||||||
engine.introspector,
|
engine.introspector,
|
||||||
engine.route.track(),
|
engine.route.track(),
|
||||||
engine.locator.track(),
|
|
||||||
TrackedMut::reborrow_mut(&mut engine.tracer),
|
TrackedMut::reborrow_mut(&mut engine.tracer),
|
||||||
|
locator.track(),
|
||||||
styles,
|
styles,
|
||||||
consecutive,
|
consecutive,
|
||||||
region,
|
region,
|
||||||
expand,
|
expand,
|
||||||
)?;
|
)
|
||||||
|
|
||||||
engine.locator.visit_frames(&fragment);
|
|
||||||
Ok(fragment)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Range of a substring of text.
|
/// Range of a substring of text.
|
||||||
@ -223,11 +221,11 @@ enum Item<'a> {
|
|||||||
/// Absolute spacing between other items, and whether it is weak.
|
/// Absolute spacing between other items, and whether it is weak.
|
||||||
Absolute(Abs, bool),
|
Absolute(Abs, bool),
|
||||||
/// Fractional spacing between other items.
|
/// Fractional spacing between other items.
|
||||||
Fractional(Fr, Option<(&'a Packed<BoxElem>, StyleChain<'a>)>),
|
Fractional(Fr, Option<(&'a Packed<BoxElem>, Locator<'a>, StyleChain<'a>)>),
|
||||||
/// Layouted inline-level content.
|
/// Layouted inline-level content.
|
||||||
Frame(Frame, StyleChain<'a>),
|
Frame(Frame, StyleChain<'a>),
|
||||||
/// A tag.
|
/// A tag.
|
||||||
Tag(&'a Packed<TagElem>),
|
Tag(&'a Tag),
|
||||||
/// An item that is invisible and needs to be skipped, e.g. a Unicode
|
/// An item that is invisible and needs to be skipped, e.g. a Unicode
|
||||||
/// isolate.
|
/// isolate.
|
||||||
Skip(&'static str),
|
Skip(&'static str),
|
||||||
@ -431,12 +429,14 @@ impl<'a> Line<'a> {
|
|||||||
fn collect<'a>(
|
fn collect<'a>(
|
||||||
children: &'a StyleVec,
|
children: &'a StyleVec,
|
||||||
engine: &mut Engine<'_>,
|
engine: &mut Engine<'_>,
|
||||||
|
locator: Locator<'a>,
|
||||||
styles: &'a StyleChain<'a>,
|
styles: &'a StyleChain<'a>,
|
||||||
region: Size,
|
region: Size,
|
||||||
consecutive: bool,
|
consecutive: bool,
|
||||||
) -> SourceResult<(String, Vec<Segment<'a>>, SpanMapper)> {
|
) -> SourceResult<(String, Vec<Segment<'a>>, SpanMapper)> {
|
||||||
let mut collector = Collector::new(2 + children.len());
|
let mut collector = Collector::new(2 + children.len());
|
||||||
let mut iter = children.chain(styles).peekable();
|
let mut iter = children.chain(styles).peekable();
|
||||||
|
let mut locator = locator.split();
|
||||||
|
|
||||||
let first_line_indent = ParElem::first_line_indent_in(*styles);
|
let first_line_indent = ParElem::first_line_indent_in(*styles);
|
||||||
if !first_line_indent.is_zero()
|
if !first_line_indent.is_zero()
|
||||||
@ -535,7 +535,7 @@ fn collect<'a>(
|
|||||||
} else if let Some(elem) = child.to_packed::<InlineElem>() {
|
} else if let Some(elem) = child.to_packed::<InlineElem>() {
|
||||||
collector.push_item(Item::Skip(LTR_ISOLATE));
|
collector.push_item(Item::Skip(LTR_ISOLATE));
|
||||||
|
|
||||||
for item in elem.layout(engine, styles, region)? {
|
for item in elem.layout(engine, locator.next(&elem.span()), styles, region)? {
|
||||||
match item {
|
match item {
|
||||||
InlineItem::Space(space, weak) => {
|
InlineItem::Space(space, weak) => {
|
||||||
collector.push_item(Item::Absolute(space, weak));
|
collector.push_item(Item::Absolute(space, weak));
|
||||||
@ -548,14 +548,15 @@ fn collect<'a>(
|
|||||||
|
|
||||||
collector.push_item(Item::Skip(POP_ISOLATE));
|
collector.push_item(Item::Skip(POP_ISOLATE));
|
||||||
} else if let Some(elem) = child.to_packed::<BoxElem>() {
|
} else if let Some(elem) = child.to_packed::<BoxElem>() {
|
||||||
|
let loc = locator.next(&elem.span());
|
||||||
if let Sizing::Fr(v) = elem.width(styles) {
|
if let Sizing::Fr(v) = elem.width(styles) {
|
||||||
collector.push_item(Item::Fractional(v, Some((elem, styles))));
|
collector.push_item(Item::Fractional(v, Some((elem, loc, styles))));
|
||||||
} else {
|
} else {
|
||||||
let frame = elem.layout(engine, styles, region)?;
|
let frame = elem.layout(engine, loc, styles, region)?;
|
||||||
collector.push_item(Item::Frame(frame, styles));
|
collector.push_item(Item::Frame(frame, styles));
|
||||||
}
|
}
|
||||||
} else if let Some(elem) = child.to_packed::<TagElem>() {
|
} else if let Some(elem) = child.to_packed::<TagElem>() {
|
||||||
collector.push_item(Item::Tag(elem));
|
collector.push_item(Item::Tag(&elem.tag));
|
||||||
} else {
|
} else {
|
||||||
bail!(child.span(), "unexpected paragraph child");
|
bail!(child.span(), "unexpected paragraph child");
|
||||||
};
|
};
|
||||||
@ -1408,9 +1409,10 @@ fn commit(
|
|||||||
}
|
}
|
||||||
Item::Fractional(v, elem) => {
|
Item::Fractional(v, elem) => {
|
||||||
let amount = v.share(fr, remaining);
|
let amount = v.share(fr, remaining);
|
||||||
if let Some((elem, styles)) = elem {
|
if let Some((elem, loc, styles)) = elem {
|
||||||
let region = Size::new(amount, full);
|
let region = Size::new(amount, full);
|
||||||
let mut frame = elem.layout(engine, *styles, region)?;
|
let mut frame =
|
||||||
|
elem.layout(engine, loc.relayout(), *styles, region)?;
|
||||||
frame.post_process(*styles);
|
frame.post_process(*styles);
|
||||||
frame.translate(Point::with_y(TextElem::baseline_in(*styles)));
|
frame.translate(Point::with_y(TextElem::baseline_in(*styles)));
|
||||||
push(&mut offset, frame);
|
push(&mut offset, frame);
|
||||||
@ -1432,7 +1434,7 @@ fn commit(
|
|||||||
}
|
}
|
||||||
Item::Tag(tag) => {
|
Item::Tag(tag) => {
|
||||||
let mut frame = Frame::soft(Size::zero());
|
let mut frame = Frame::soft(Size::zero());
|
||||||
frame.push(Point::zero(), FrameItem::Tag(tag.elem.clone()));
|
frame.push(Point::zero(), FrameItem::Tag((*tag).clone()));
|
||||||
frames.push((offset, frame));
|
frames.push((offset, frame));
|
||||||
}
|
}
|
||||||
Item::Skip(_) => {}
|
Item::Skip(_) => {}
|
||||||
|
@ -76,7 +76,9 @@ struct LayoutElem {
|
|||||||
|
|
||||||
impl Show for Packed<LayoutElem> {
|
impl Show for Packed<LayoutElem> {
|
||||||
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(BlockElem::multi_layouter(self.clone(), |elem, engine, styles, regions| {
|
Ok(BlockElem::multi_layouter(
|
||||||
|
self.clone(),
|
||||||
|
|elem, engine, locator, styles, regions| {
|
||||||
// Gets the current region's base size, which will be the size of the
|
// Gets the current region's base size, which will be the size of the
|
||||||
// outer container, or of the page if there is no such container.
|
// outer container, or of the page if there is no such container.
|
||||||
let Size { x, y } = regions.base();
|
let Size { x, y } = regions.base();
|
||||||
@ -84,10 +86,16 @@ impl Show for Packed<LayoutElem> {
|
|||||||
let context = Context::new(Some(loc), Some(styles));
|
let context = Context::new(Some(loc), Some(styles));
|
||||||
let result = elem
|
let result = elem
|
||||||
.func()
|
.func()
|
||||||
.call(engine, context.track(), [dict! { "width" => x, "height" => y }])?
|
.call(
|
||||||
|
engine,
|
||||||
|
context.track(),
|
||||||
|
[dict! { "width" => x, "height" => y }],
|
||||||
|
)?
|
||||||
.display();
|
.display();
|
||||||
result.layout(engine, styles, regions)
|
result.layout(engine, locator, styles, regions)
|
||||||
})
|
},
|
||||||
.pack())
|
)
|
||||||
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ use crate::engine::Engine;
|
|||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
dict, func, Content, Context, Dict, Resolve, Smart, StyleChain, Styles,
|
dict, func, Content, Context, Dict, Resolve, Smart, StyleChain, Styles,
|
||||||
};
|
};
|
||||||
|
use crate::introspection::{Locator, LocatorLink};
|
||||||
use crate::layout::{Abs, Axes, Length, Regions, Size};
|
use crate::layout::{Abs, Axes, Length, Regions, Size};
|
||||||
use crate::syntax::Span;
|
use crate::syntax::Span;
|
||||||
|
|
||||||
@ -85,13 +86,24 @@ pub fn measure(
|
|||||||
None => context.styles().at(span)?,
|
None => context.styles().at(span)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let available = Axes::new(
|
// Create a pod region with the available space.
|
||||||
|
let pod = Regions::one(
|
||||||
|
Axes::new(
|
||||||
width.resolve(styles).unwrap_or(Abs::inf()),
|
width.resolve(styles).unwrap_or(Abs::inf()),
|
||||||
height.resolve(styles).unwrap_or(Abs::inf()),
|
height.resolve(styles).unwrap_or(Abs::inf()),
|
||||||
|
),
|
||||||
|
Axes::splat(false),
|
||||||
);
|
);
|
||||||
|
|
||||||
let pod = Regions::one(available, Axes::splat(false));
|
// We put the locator into a special "measurement mode" to ensure that
|
||||||
let frame = content.measure(engine, styles, pod)?.into_frame();
|
// introspection-driven features within the content continue to work. Read
|
||||||
|
// the "Dealing with measurement" section of the [`Locator`] docs for more
|
||||||
|
// details.
|
||||||
|
let here = context.location().at(span)?;
|
||||||
|
let link = LocatorLink::measure(here);
|
||||||
|
let locator = Locator::link(&link);
|
||||||
|
|
||||||
|
let frame = content.layout(engine, locator, styles, pod)?.into_frame();
|
||||||
let Size { x, y } = frame.size();
|
let Size { x, y } = frame.size();
|
||||||
Ok(dict! { "width" => x, "height" => y })
|
Ok(dict! { "width" => x, "height" => y })
|
||||||
}
|
}
|
||||||
|
@ -69,13 +69,13 @@ pub use self::transform::*;
|
|||||||
|
|
||||||
pub(crate) use self::inline::*;
|
pub(crate) use self::inline::*;
|
||||||
|
|
||||||
use comemo::{Tracked, TrackedMut};
|
use comemo::{Track, Tracked, TrackedMut};
|
||||||
|
|
||||||
use crate::diag::{bail, SourceResult};
|
use crate::diag::{bail, SourceResult};
|
||||||
use crate::engine::{Engine, Route};
|
use crate::engine::{Engine, Route};
|
||||||
use crate::eval::Tracer;
|
use crate::eval::Tracer;
|
||||||
use crate::foundations::{category, Category, Content, Scope, StyleChain};
|
use crate::foundations::{category, Category, Content, Scope, StyleChain};
|
||||||
use crate::introspection::{Introspector, Locator};
|
use crate::introspection::{Introspector, Locator, LocatorLink};
|
||||||
use crate::model::Document;
|
use crate::model::Document;
|
||||||
use crate::realize::{realize_doc, realize_flow, Arenas};
|
use crate::realize::{realize_doc, realize_flow, Arenas};
|
||||||
use crate::World;
|
use crate::World;
|
||||||
@ -138,21 +138,20 @@ impl Content {
|
|||||||
world: Tracked<dyn World + '_>,
|
world: Tracked<dyn World + '_>,
|
||||||
introspector: Tracked<Introspector>,
|
introspector: Tracked<Introspector>,
|
||||||
route: Tracked<Route>,
|
route: Tracked<Route>,
|
||||||
locator: Tracked<Locator>,
|
|
||||||
tracer: TrackedMut<Tracer>,
|
tracer: TrackedMut<Tracer>,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<Document> {
|
) -> SourceResult<Document> {
|
||||||
let mut locator = Locator::chained(locator);
|
let mut locator = Locator::root().split();
|
||||||
let mut engine = Engine {
|
let mut engine = Engine {
|
||||||
world,
|
world,
|
||||||
introspector,
|
introspector,
|
||||||
route: Route::extend(route).unnested(),
|
route: Route::extend(route).unnested(),
|
||||||
locator: &mut locator,
|
|
||||||
tracer,
|
tracer,
|
||||||
};
|
};
|
||||||
let arenas = Arenas::default();
|
let arenas = Arenas::default();
|
||||||
let (document, styles) = realize_doc(&mut engine, &arenas, content, styles)?;
|
let (document, styles) =
|
||||||
document.layout(&mut engine, styles)
|
realize_doc(&mut engine, locator.next(&()), &arenas, content, styles)?;
|
||||||
|
document.layout(&mut engine, locator.next(&()), styles)
|
||||||
}
|
}
|
||||||
|
|
||||||
cached(
|
cached(
|
||||||
@ -160,7 +159,6 @@ impl Content {
|
|||||||
engine.world,
|
engine.world,
|
||||||
engine.introspector,
|
engine.introspector,
|
||||||
engine.route.track(),
|
engine.route.track(),
|
||||||
engine.locator.track(),
|
|
||||||
TrackedMut::reborrow_mut(&mut engine.tracer),
|
TrackedMut::reborrow_mut(&mut engine.tracer),
|
||||||
styles,
|
styles,
|
||||||
)
|
)
|
||||||
@ -170,22 +168,7 @@ impl Content {
|
|||||||
pub fn layout(
|
pub fn layout(
|
||||||
&self,
|
&self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
styles: StyleChain,
|
locator: Locator,
|
||||||
regions: Regions,
|
|
||||||
) -> SourceResult<Fragment> {
|
|
||||||
let fragment = self.measure(engine, styles, regions)?;
|
|
||||||
engine.locator.visit_frames(&fragment);
|
|
||||||
Ok(fragment)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Layout without side effects.
|
|
||||||
///
|
|
||||||
/// For the results to be valid, the element must either be layouted again
|
|
||||||
/// or the measurement must be confirmed through a call to
|
|
||||||
/// `engine.locator.visit_frames(&fragment)`.
|
|
||||||
pub fn measure(
|
|
||||||
&self,
|
|
||||||
engine: &mut Engine,
|
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -196,17 +179,17 @@ impl Content {
|
|||||||
world: Tracked<dyn World + '_>,
|
world: Tracked<dyn World + '_>,
|
||||||
introspector: Tracked<Introspector>,
|
introspector: Tracked<Introspector>,
|
||||||
route: Tracked<Route>,
|
route: Tracked<Route>,
|
||||||
locator: Tracked<Locator>,
|
|
||||||
tracer: TrackedMut<Tracer>,
|
tracer: TrackedMut<Tracer>,
|
||||||
|
locator: Tracked<Locator>,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
let mut locator = Locator::chained(locator);
|
let link = LocatorLink::new(locator);
|
||||||
|
let locator = Locator::link(&link);
|
||||||
let mut engine = Engine {
|
let mut engine = Engine {
|
||||||
world,
|
world,
|
||||||
introspector,
|
introspector,
|
||||||
route: Route::extend(route),
|
route: Route::extend(route),
|
||||||
locator: &mut locator,
|
|
||||||
tracer,
|
tracer,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -219,14 +202,16 @@ impl Content {
|
|||||||
|
|
||||||
// If we are in a `PageElem`, this might already be a realized flow.
|
// If we are in a `PageElem`, this might already be a realized flow.
|
||||||
if let Some(flow) = content.to_packed::<FlowElem>() {
|
if let Some(flow) = content.to_packed::<FlowElem>() {
|
||||||
return flow.layout(&mut engine, styles, regions);
|
return flow.layout(&mut engine, locator, styles, regions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Layout the content by first turning it into a `FlowElem` and then
|
// Layout the content by first turning it into a `FlowElem` and then
|
||||||
// layouting that.
|
// layouting that.
|
||||||
|
let mut locator = locator.split();
|
||||||
let arenas = Arenas::default();
|
let arenas = Arenas::default();
|
||||||
let (flow, styles) = realize_flow(&mut engine, &arenas, content, styles)?;
|
let (flow, styles) =
|
||||||
flow.layout(&mut engine, styles, regions)
|
realize_flow(&mut engine, locator.next(&()), &arenas, content, styles)?;
|
||||||
|
flow.layout(&mut engine, locator.next(&()), styles, regions)
|
||||||
}
|
}
|
||||||
|
|
||||||
cached(
|
cached(
|
||||||
@ -234,8 +219,8 @@ impl Content {
|
|||||||
engine.world,
|
engine.world,
|
||||||
engine.introspector,
|
engine.introspector,
|
||||||
engine.route.track(),
|
engine.route.track(),
|
||||||
engine.locator.track(),
|
|
||||||
TrackedMut::reborrow_mut(&mut engine.tracer),
|
TrackedMut::reborrow_mut(&mut engine.tracer),
|
||||||
|
locator.track(),
|
||||||
styles,
|
styles,
|
||||||
regions,
|
regions,
|
||||||
)
|
)
|
||||||
|
@ -3,6 +3,7 @@ use crate::engine::Engine;
|
|||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
elem, Content, NativeElement, Packed, Resolve, Show, StyleChain,
|
elem, Content, NativeElement, Packed, Resolve, Show, StyleChain,
|
||||||
};
|
};
|
||||||
|
use crate::introspection::Locator;
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, BlockElem, Fragment, Frame, Length, Point, Regions, Rel, Sides, Size,
|
Abs, BlockElem, Fragment, Frame, Length, Point, Regions, Rel, Sides, Size,
|
||||||
};
|
};
|
||||||
@ -64,7 +65,9 @@ pub struct PadElem {
|
|||||||
|
|
||||||
impl Show for Packed<PadElem> {
|
impl Show for Packed<PadElem> {
|
||||||
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(BlockElem::multi_layouter(self.clone(), layout_pad).pack())
|
Ok(BlockElem::multi_layouter(self.clone(), layout_pad)
|
||||||
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +76,7 @@ impl Show for Packed<PadElem> {
|
|||||||
fn layout_pad(
|
fn layout_pad(
|
||||||
elem: &Packed<PadElem>,
|
elem: &Packed<PadElem>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -87,7 +91,7 @@ fn layout_pad(
|
|||||||
let pod = regions.map(&mut backlog, |size| shrink(size, &padding));
|
let pod = regions.map(&mut backlog, |size| shrink(size, &padding));
|
||||||
|
|
||||||
// Layout child into padded regions.
|
// Layout child into padded regions.
|
||||||
let mut fragment = elem.body().layout(engine, styles, pod)?;
|
let mut fragment = elem.body().layout(engine, locator, styles, pod)?;
|
||||||
|
|
||||||
for frame in &mut fragment {
|
for frame in &mut fragment {
|
||||||
grow(frame, &padding);
|
grow(frame, &padding);
|
||||||
|
@ -12,7 +12,9 @@ use crate::foundations::{
|
|||||||
cast, elem, AutoValue, Cast, Content, Context, Dict, Fold, Func, NativeElement,
|
cast, elem, AutoValue, Cast, Content, Context, Dict, Fold, Func, NativeElement,
|
||||||
Packed, Resolve, Smart, StyleChain, Value,
|
Packed, Resolve, Smart, StyleChain, Value,
|
||||||
};
|
};
|
||||||
use crate::introspection::{Counter, CounterDisplayElem, CounterKey, ManualPageCounter};
|
use crate::introspection::{
|
||||||
|
Counter, CounterDisplayElem, CounterKey, Locator, ManualPageCounter,
|
||||||
|
};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, AlignElem, Alignment, Axes, ColumnsElem, Dir, Frame, HAlignment, Length,
|
Abs, AlignElem, Alignment, Axes, ColumnsElem, Dir, Frame, HAlignment, Length,
|
||||||
OuterVAlignment, Point, Ratio, Regions, Rel, Sides, Size, SpecificAlignment,
|
OuterVAlignment, Point, Ratio, Regions, Rel, Sides, Size, SpecificAlignment,
|
||||||
@ -349,10 +351,13 @@ impl Packed<PageElem> {
|
|||||||
pub fn layout(
|
pub fn layout(
|
||||||
&self,
|
&self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
page_counter: &mut ManualPageCounter,
|
page_counter: &mut ManualPageCounter,
|
||||||
extend_to: Option<Parity>,
|
extend_to: Option<Parity>,
|
||||||
) -> SourceResult<Vec<Page>> {
|
) -> SourceResult<Vec<Page>> {
|
||||||
|
let mut locator = locator.split();
|
||||||
|
|
||||||
// When one of the lengths is infinite the page fits its content along
|
// When one of the lengths is infinite the page fits its content along
|
||||||
// that axis.
|
// that axis.
|
||||||
let width = self.width(styles).unwrap_or(Abs::inf());
|
let width = self.width(styles).unwrap_or(Abs::inf());
|
||||||
@ -400,7 +405,9 @@ impl Packed<PageElem> {
|
|||||||
regions.root = true;
|
regions.root = true;
|
||||||
|
|
||||||
// Layout the child.
|
// Layout the child.
|
||||||
let mut frames = child.layout(engine, styles, regions)?.into_frames();
|
let mut frames = child
|
||||||
|
.layout(engine, locator.next(&self.span()), styles, regions)?
|
||||||
|
.into_frames();
|
||||||
|
|
||||||
// Align the child to the pagebreak's parity.
|
// Align the child to the pagebreak's parity.
|
||||||
// Check for page count after adding the pending frames
|
// Check for page count after adding the pending frames
|
||||||
@ -504,7 +511,7 @@ impl Packed<PageElem> {
|
|||||||
let sub = content
|
let sub = content
|
||||||
.clone()
|
.clone()
|
||||||
.styled(AlignElem::set_alignment(align))
|
.styled(AlignElem::set_alignment(align))
|
||||||
.layout(engine, styles, pod)?
|
.layout(engine, locator.next(&content.span()), styles, pod)?
|
||||||
.into_frame();
|
.into_frame();
|
||||||
|
|
||||||
if ptr::eq(marginal, header) || ptr::eq(marginal, background) {
|
if ptr::eq(marginal, header) || ptr::eq(marginal, background) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::diag::{bail, At, Hint, SourceResult};
|
use crate::diag::{bail, At, Hint, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{elem, scope, Content, Packed, Smart, StyleChain, Unlabellable};
|
use crate::foundations::{elem, scope, Content, Packed, Smart, StyleChain, Unlabellable};
|
||||||
|
use crate::introspection::Locator;
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Alignment, Axes, Em, Fragment, Length, Regions, Rel, Size, VAlignment,
|
Alignment, Axes, Em, Fragment, Length, Regions, Rel, Size, VAlignment,
|
||||||
};
|
};
|
||||||
@ -108,6 +109,7 @@ impl Packed<PlaceElem> {
|
|||||||
pub fn layout(
|
pub fn layout(
|
||||||
&self,
|
&self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
base: Size,
|
base: Size,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -134,7 +136,7 @@ impl Packed<PlaceElem> {
|
|||||||
.aligned(alignment.unwrap_or_else(|| Alignment::CENTER));
|
.aligned(alignment.unwrap_or_else(|| Alignment::CENTER));
|
||||||
|
|
||||||
let pod = Regions::one(base, Axes::splat(false));
|
let pod = Regions::one(base, Axes::splat(false));
|
||||||
let frame = child.layout(engine, styles, pod)?.into_frame();
|
let frame = child.layout(engine, locator, styles, pod)?.into_frame();
|
||||||
Ok(Fragment::frame(frame))
|
Ok(Fragment::frame(frame))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ use crate::engine::Engine;
|
|||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
elem, Content, NativeElement, Packed, Resolve, Show, StyleChain,
|
elem, Content, NativeElement, Packed, Resolve, Show, StyleChain,
|
||||||
};
|
};
|
||||||
|
use crate::introspection::Locator;
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, AlignElem, Axes, BlockElem, Fragment, Frame, Point, Regions, Size,
|
Abs, AlignElem, Axes, BlockElem, Fragment, Frame, Point, Regions, Size,
|
||||||
};
|
};
|
||||||
@ -38,7 +39,9 @@ pub struct RepeatElem {
|
|||||||
|
|
||||||
impl Show for Packed<RepeatElem> {
|
impl Show for Packed<RepeatElem> {
|
||||||
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(BlockElem::multi_layouter(self.clone(), layout_repeat).pack())
|
Ok(BlockElem::multi_layouter(self.clone(), layout_repeat)
|
||||||
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,11 +50,13 @@ impl Show for Packed<RepeatElem> {
|
|||||||
fn layout_repeat(
|
fn layout_repeat(
|
||||||
elem: &Packed<RepeatElem>,
|
elem: &Packed<RepeatElem>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
let pod = Regions::one(regions.size, Axes::new(false, false));
|
let pod = Regions::one(regions.size, Axes::new(false, false));
|
||||||
let piece = elem.body().layout(engine, styles, pod)?.into_frame();
|
let piece = elem.body().layout(engine, locator, styles, pod)?.into_frame();
|
||||||
|
|
||||||
let align = AlignElem::alignment_in(styles).resolve(styles);
|
let align = AlignElem::alignment_in(styles).resolve(styles);
|
||||||
|
|
||||||
let fill = regions.size.x;
|
let fill = regions.size.x;
|
||||||
|
@ -6,6 +6,7 @@ use crate::engine::Engine;
|
|||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
cast, elem, Content, NativeElement, Packed, Resolve, Show, StyleChain, StyledElem,
|
cast, elem, Content, NativeElement, Packed, Resolve, Show, StyleChain, StyledElem,
|
||||||
};
|
};
|
||||||
|
use crate::introspection::{Locator, SplitLocator};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, AlignElem, Axes, Axis, BlockElem, Dir, FixedAlignment, Fr, Fragment, Frame,
|
Abs, AlignElem, Axes, Axis, BlockElem, Dir, FixedAlignment, Fr, Fragment, Frame,
|
||||||
HElem, Point, Regions, Size, Spacing, VElem,
|
HElem, Point, Regions, Size, Spacing, VElem,
|
||||||
@ -56,7 +57,9 @@ pub struct StackElem {
|
|||||||
|
|
||||||
impl Show for Packed<StackElem> {
|
impl Show for Packed<StackElem> {
|
||||||
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(BlockElem::multi_layouter(self.clone(), layout_stack).pack())
|
Ok(BlockElem::multi_layouter(self.clone(), layout_stack)
|
||||||
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,10 +96,13 @@ cast! {
|
|||||||
fn layout_stack(
|
fn layout_stack(
|
||||||
elem: &Packed<StackElem>,
|
elem: &Packed<StackElem>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
let mut layouter = StackLayouter::new(elem.span(), elem.dir(styles), regions, styles);
|
let mut layouter =
|
||||||
|
StackLayouter::new(elem.span(), elem.dir(styles), locator, styles, regions);
|
||||||
|
|
||||||
let axis = layouter.dir.axis();
|
let axis = layouter.dir.axis();
|
||||||
|
|
||||||
// Spacing to insert before the next block.
|
// Spacing to insert before the next block.
|
||||||
@ -145,10 +151,12 @@ struct StackLayouter<'a> {
|
|||||||
dir: Dir,
|
dir: Dir,
|
||||||
/// The axis of the stacking direction.
|
/// The axis of the stacking direction.
|
||||||
axis: Axis,
|
axis: Axis,
|
||||||
/// The regions to layout children into.
|
/// Provides unique locations to the stack's children.
|
||||||
regions: Regions<'a>,
|
locator: SplitLocator<'a>,
|
||||||
/// The inherited styles.
|
/// The inherited styles.
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
|
/// The regions to layout children into.
|
||||||
|
regions: Regions<'a>,
|
||||||
/// Whether the stack itself should expand to fill the region.
|
/// Whether the stack itself should expand to fill the region.
|
||||||
expand: Axes<bool>,
|
expand: Axes<bool>,
|
||||||
/// The initial size of the current region before we started subtracting.
|
/// The initial size of the current region before we started subtracting.
|
||||||
@ -179,8 +187,9 @@ impl<'a> StackLayouter<'a> {
|
|||||||
fn new(
|
fn new(
|
||||||
span: Span,
|
span: Span,
|
||||||
dir: Dir,
|
dir: Dir,
|
||||||
mut regions: Regions<'a>,
|
locator: Locator<'a>,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
|
mut regions: Regions<'a>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let axis = dir.axis();
|
let axis = dir.axis();
|
||||||
let expand = regions.expand;
|
let expand = regions.expand;
|
||||||
@ -192,8 +201,9 @@ impl<'a> StackLayouter<'a> {
|
|||||||
span,
|
span,
|
||||||
dir,
|
dir,
|
||||||
axis,
|
axis,
|
||||||
regions,
|
locator: locator.split(),
|
||||||
styles,
|
styles,
|
||||||
|
regions,
|
||||||
expand,
|
expand,
|
||||||
initial: regions.size,
|
initial: regions.size,
|
||||||
used: GenericSize::zero(),
|
used: GenericSize::zero(),
|
||||||
@ -247,7 +257,13 @@ impl<'a> StackLayouter<'a> {
|
|||||||
}
|
}
|
||||||
.resolve(styles);
|
.resolve(styles);
|
||||||
|
|
||||||
let fragment = block.layout(engine, styles, self.regions)?;
|
let fragment = block.layout(
|
||||||
|
engine,
|
||||||
|
self.locator.next(&block.span()),
|
||||||
|
styles,
|
||||||
|
self.regions,
|
||||||
|
)?;
|
||||||
|
|
||||||
let len = fragment.len();
|
let len = fragment.len();
|
||||||
for (i, frame) in fragment.into_iter().enumerate() {
|
for (i, frame) in fragment.into_iter().enumerate() {
|
||||||
// Grow our size, shrink the region and save the frame for later.
|
// Grow our size, shrink the region and save the frame for later.
|
||||||
|
@ -3,6 +3,7 @@ use crate::engine::Engine;
|
|||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
elem, Content, NativeElement, Packed, Resolve, Show, StyleChain,
|
elem, Content, NativeElement, Packed, Resolve, Show, StyleChain,
|
||||||
};
|
};
|
||||||
|
use crate::introspection::Locator;
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, Alignment, Angle, Axes, BlockElem, FixedAlignment, Frame, HAlignment, Length,
|
Abs, Alignment, Angle, Axes, BlockElem, FixedAlignment, Frame, HAlignment, Length,
|
||||||
Point, Ratio, Region, Regions, Rel, Size, VAlignment,
|
Point, Ratio, Region, Regions, Rel, Size, VAlignment,
|
||||||
@ -41,7 +42,9 @@ pub struct MoveElem {
|
|||||||
|
|
||||||
impl Show for Packed<MoveElem> {
|
impl Show for Packed<MoveElem> {
|
||||||
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(BlockElem::single_layouter(self.clone(), layout_move).pack())
|
Ok(BlockElem::single_layouter(self.clone(), layout_move)
|
||||||
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,12 +53,13 @@ impl Show for Packed<MoveElem> {
|
|||||||
fn layout_move(
|
fn layout_move(
|
||||||
elem: &Packed<MoveElem>,
|
elem: &Packed<MoveElem>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> SourceResult<Frame> {
|
) -> SourceResult<Frame> {
|
||||||
let mut frame = elem
|
let mut frame = elem
|
||||||
.body()
|
.body()
|
||||||
.layout(engine, styles, region.into_regions())?
|
.layout(engine, locator, styles, region.into_regions())?
|
||||||
.into_frame();
|
.into_frame();
|
||||||
let delta = Axes::new(elem.dx(styles), elem.dy(styles)).resolve(styles);
|
let delta = Axes::new(elem.dx(styles), elem.dy(styles)).resolve(styles);
|
||||||
let delta = delta.zip_map(region.size, Rel::relative_to);
|
let delta = delta.zip_map(region.size, Rel::relative_to);
|
||||||
@ -126,7 +130,9 @@ pub struct RotateElem {
|
|||||||
|
|
||||||
impl Show for Packed<RotateElem> {
|
impl Show for Packed<RotateElem> {
|
||||||
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(BlockElem::single_layouter(self.clone(), layout_rotate).pack())
|
Ok(BlockElem::single_layouter(self.clone(), layout_rotate)
|
||||||
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,6 +141,7 @@ impl Show for Packed<RotateElem> {
|
|||||||
fn layout_rotate(
|
fn layout_rotate(
|
||||||
elem: &Packed<RotateElem>,
|
elem: &Packed<RotateElem>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> SourceResult<Frame> {
|
) -> SourceResult<Frame> {
|
||||||
@ -151,6 +158,7 @@ fn layout_rotate(
|
|||||||
|
|
||||||
measure_and_layout(
|
measure_and_layout(
|
||||||
engine,
|
engine,
|
||||||
|
locator,
|
||||||
region,
|
region,
|
||||||
size,
|
size,
|
||||||
styles,
|
styles,
|
||||||
@ -219,7 +227,9 @@ pub struct ScaleElem {
|
|||||||
|
|
||||||
impl Show for Packed<ScaleElem> {
|
impl Show for Packed<ScaleElem> {
|
||||||
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(BlockElem::single_layouter(self.clone(), layout_scale).pack())
|
Ok(BlockElem::single_layouter(self.clone(), layout_scale)
|
||||||
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,6 +238,7 @@ impl Show for Packed<ScaleElem> {
|
|||||||
fn layout_scale(
|
fn layout_scale(
|
||||||
elem: &Packed<ScaleElem>,
|
elem: &Packed<ScaleElem>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> SourceResult<Frame> {
|
) -> SourceResult<Frame> {
|
||||||
@ -240,6 +251,7 @@ fn layout_scale(
|
|||||||
|
|
||||||
measure_and_layout(
|
measure_and_layout(
|
||||||
engine,
|
engine,
|
||||||
|
locator,
|
||||||
region,
|
region,
|
||||||
size,
|
size,
|
||||||
styles,
|
styles,
|
||||||
@ -379,6 +391,7 @@ impl Default for Transform {
|
|||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn measure_and_layout(
|
fn measure_and_layout(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
region: Region,
|
region: Region,
|
||||||
size: Size,
|
size: Size,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
@ -390,11 +403,11 @@ fn measure_and_layout(
|
|||||||
if reflow {
|
if reflow {
|
||||||
// Measure the size of the body.
|
// Measure the size of the body.
|
||||||
let pod = Regions::one(size, Axes::splat(false));
|
let pod = Regions::one(size, Axes::splat(false));
|
||||||
let frame = body.measure(engine, styles, pod)?.into_frame();
|
let frame = body.layout(engine, locator.relayout(), styles, pod)?.into_frame();
|
||||||
|
|
||||||
// Actually perform the layout.
|
// Actually perform the layout.
|
||||||
let pod = Regions::one(frame.size(), Axes::splat(true));
|
let pod = Regions::one(frame.size(), Axes::splat(true));
|
||||||
let mut frame = body.layout(engine, styles, pod)?.into_frame();
|
let mut frame = body.layout(engine, locator, styles, pod)?.into_frame();
|
||||||
let Axes { x, y } = align.zip_map(frame.size(), FixedAlignment::position);
|
let Axes { x, y } = align.zip_map(frame.size(), FixedAlignment::position);
|
||||||
|
|
||||||
// Compute the transform.
|
// Compute the transform.
|
||||||
@ -410,7 +423,9 @@ fn measure_and_layout(
|
|||||||
Ok(frame)
|
Ok(frame)
|
||||||
} else {
|
} else {
|
||||||
// Layout the body.
|
// Layout the body.
|
||||||
let mut frame = body.layout(engine, styles, region.into_regions())?.into_frame();
|
let mut frame = body
|
||||||
|
.layout(engine, locator, styles, region.into_regions())?
|
||||||
|
.into_frame();
|
||||||
let Axes { x, y } = align.zip_map(frame.size(), FixedAlignment::position);
|
let Axes { x, y } = align.zip_map(frame.size(), FixedAlignment::position);
|
||||||
|
|
||||||
// Compute the transform.
|
// Compute the transform.
|
||||||
|
@ -69,7 +69,7 @@ use crate::eval::Tracer;
|
|||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
Array, Bytes, Content, Datetime, Dict, Module, Scope, StyleChain, Styles, Value,
|
Array, Bytes, Content, Datetime, Dict, Module, Scope, StyleChain, Styles, Value,
|
||||||
};
|
};
|
||||||
use crate::introspection::{Introspector, Locator};
|
use crate::introspection::Introspector;
|
||||||
use crate::layout::{Alignment, Dir};
|
use crate::layout::{Alignment, Dir};
|
||||||
use crate::model::Document;
|
use crate::model::Document;
|
||||||
use crate::syntax::package::PackageSpec;
|
use crate::syntax::package::PackageSpec;
|
||||||
@ -129,12 +129,10 @@ fn typeset(
|
|||||||
tracer.delayed();
|
tracer.delayed();
|
||||||
|
|
||||||
let constraint = <Introspector as Validate>::Constraint::new();
|
let constraint = <Introspector as Validate>::Constraint::new();
|
||||||
let mut locator = Locator::new();
|
|
||||||
let mut engine = Engine {
|
let mut engine = Engine {
|
||||||
world,
|
world,
|
||||||
route: Route::default(),
|
route: Route::default(),
|
||||||
tracer: tracer.track_mut(),
|
tracer: tracer.track_mut(),
|
||||||
locator: &mut locator,
|
|
||||||
introspector: document.introspector.track_with(&constraint),
|
introspector: document.introspector.track_with(&constraint),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ use unicode_segmentation::UnicodeSegmentation;
|
|||||||
use crate::diag::SourceResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{Content, Packed, StyleChain};
|
use crate::foundations::{Content, Packed, StyleChain};
|
||||||
|
use crate::introspection::{Locator, SplitLocator};
|
||||||
use crate::layout::{Abs, Axes, BoxElem, Em, Frame, Regions, Size};
|
use crate::layout::{Abs, Axes, BoxElem, Em, Frame, Regions, Size};
|
||||||
use crate::math::{
|
use crate::math::{
|
||||||
scaled_font_size, styled_char, EquationElem, FrameFragment, GlyphFragment,
|
scaled_font_size, styled_char, EquationElem, FrameFragment, GlyphFragment,
|
||||||
@ -49,6 +50,7 @@ macro_rules! percent {
|
|||||||
pub struct MathContext<'a, 'b, 'v> {
|
pub struct MathContext<'a, 'b, 'v> {
|
||||||
// External.
|
// External.
|
||||||
pub engine: &'v mut Engine<'b>,
|
pub engine: &'v mut Engine<'b>,
|
||||||
|
pub locator: SplitLocator<'v>,
|
||||||
pub regions: Regions<'static>,
|
pub regions: Regions<'static>,
|
||||||
// Font-related.
|
// Font-related.
|
||||||
pub font: &'a Font,
|
pub font: &'a Font,
|
||||||
@ -65,6 +67,7 @@ pub struct MathContext<'a, 'b, 'v> {
|
|||||||
impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
engine: &'v mut Engine<'b>,
|
engine: &'v mut Engine<'b>,
|
||||||
|
locator: Locator<'v>,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
base: Size,
|
base: Size,
|
||||||
font: &'a Font,
|
font: &'a Font,
|
||||||
@ -103,6 +106,7 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
engine,
|
engine,
|
||||||
|
locator: locator.split(),
|
||||||
regions: Regions::one(base, Axes::splat(false)),
|
regions: Regions::one(base, Axes::splat(false)),
|
||||||
font,
|
font,
|
||||||
ttf: font.ttf(),
|
ttf: font.ttf(),
|
||||||
@ -174,7 +178,12 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
|||||||
) -> SourceResult<Frame> {
|
) -> SourceResult<Frame> {
|
||||||
let local =
|
let local =
|
||||||
TextElem::set_size(TextSize(scaled_font_size(self, styles).into())).wrap();
|
TextElem::set_size(TextSize(scaled_font_size(self, styles).into())).wrap();
|
||||||
boxed.layout(self.engine, styles.chain(&local), self.regions.base())
|
boxed.layout(
|
||||||
|
self.engine,
|
||||||
|
self.locator.next(&boxed.span()),
|
||||||
|
styles.chain(&local),
|
||||||
|
self.regions.base(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout the given [`Content`] into a [`Frame`].
|
/// Layout the given [`Content`] into a [`Frame`].
|
||||||
@ -186,7 +195,12 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
|||||||
let local =
|
let local =
|
||||||
TextElem::set_size(TextSize(scaled_font_size(self, styles).into())).wrap();
|
TextElem::set_size(TextSize(scaled_font_size(self, styles).into())).wrap();
|
||||||
Ok(content
|
Ok(content
|
||||||
.layout(self.engine, styles.chain(&local), self.regions)?
|
.layout(
|
||||||
|
self.engine,
|
||||||
|
self.locator.next(&content.span()),
|
||||||
|
styles.chain(&local),
|
||||||
|
self.regions,
|
||||||
|
)?
|
||||||
.into_frame())
|
.into_frame())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,7 +304,14 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
|||||||
let par = ParElem::new(StyleVec::wrap(eco_vec![text]));
|
let par = ParElem::new(StyleVec::wrap(eco_vec![text]));
|
||||||
let frame = Packed::new(par)
|
let frame = Packed::new(par)
|
||||||
.spanned(span)
|
.spanned(span)
|
||||||
.layout(self.engine, styles, false, Size::splat(Abs::inf()), false)?
|
.layout(
|
||||||
|
self.engine,
|
||||||
|
self.locator.next(&span),
|
||||||
|
styles,
|
||||||
|
false,
|
||||||
|
Size::splat(Abs::inf()),
|
||||||
|
false,
|
||||||
|
)?
|
||||||
.into_frame();
|
.into_frame();
|
||||||
|
|
||||||
Ok(FrameFragment::new(self, styles, frame)
|
Ok(FrameFragment::new(self, styles, frame)
|
||||||
|
@ -8,7 +8,7 @@ use crate::foundations::{
|
|||||||
elem, Content, NativeElement, Packed, Resolve, Show, ShowSet, Smart, StyleChain,
|
elem, Content, NativeElement, Packed, Resolve, Show, ShowSet, Smart, StyleChain,
|
||||||
Styles, Synthesize,
|
Styles, Synthesize,
|
||||||
};
|
};
|
||||||
use crate::introspection::{Count, Counter, CounterUpdate, Locatable};
|
use crate::introspection::{Count, Counter, CounterUpdate, Locatable, Locator};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, AlignElem, Alignment, Axes, BlockElem, Em, FixedAlignment, Fragment, Frame,
|
Abs, AlignElem, Alignment, Axes, BlockElem, Em, FixedAlignment, Fragment, Frame,
|
||||||
InlineElem, InlineItem, OuterHAlignment, Point, Regions, Size, SpecificAlignment,
|
InlineElem, InlineItem, OuterHAlignment, Point, Regions, Size, SpecificAlignment,
|
||||||
@ -166,9 +166,13 @@ impl Synthesize for Packed<EquationElem> {
|
|||||||
impl Show for Packed<EquationElem> {
|
impl Show for Packed<EquationElem> {
|
||||||
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||||
if self.block(styles) {
|
if self.block(styles) {
|
||||||
Ok(BlockElem::multi_layouter(self.clone(), layout_equation_block).pack())
|
Ok(BlockElem::multi_layouter(self.clone(), layout_equation_block)
|
||||||
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
} else {
|
} else {
|
||||||
Ok(InlineElem::layouter(self.clone(), layout_equation_inline).pack())
|
Ok(InlineElem::layouter(self.clone(), layout_equation_inline)
|
||||||
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -265,7 +269,8 @@ impl LayoutMath for Packed<EquationElem> {
|
|||||||
#[typst_macros::time(span = elem.span())]
|
#[typst_macros::time(span = elem.span())]
|
||||||
fn layout_equation_inline(
|
fn layout_equation_inline(
|
||||||
elem: &Packed<EquationElem>,
|
elem: &Packed<EquationElem>,
|
||||||
engine: &mut Engine<'_>,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
region: Size,
|
region: Size,
|
||||||
) -> SourceResult<Vec<InlineItem>> {
|
) -> SourceResult<Vec<InlineItem>> {
|
||||||
@ -273,7 +278,7 @@ fn layout_equation_inline(
|
|||||||
|
|
||||||
let font = find_math_font(engine, styles, elem.span())?;
|
let font = find_math_font(engine, styles, elem.span())?;
|
||||||
|
|
||||||
let mut ctx = MathContext::new(engine, styles, region, &font);
|
let mut ctx = MathContext::new(engine, locator, styles, region, &font);
|
||||||
let run = ctx.layout_into_run(elem, styles)?;
|
let run = ctx.layout_into_run(elem, styles)?;
|
||||||
|
|
||||||
let mut items = if run.row_count() == 1 {
|
let mut items = if run.row_count() == 1 {
|
||||||
@ -311,6 +316,7 @@ fn layout_equation_inline(
|
|||||||
fn layout_equation_block(
|
fn layout_equation_block(
|
||||||
elem: &Packed<EquationElem>,
|
elem: &Packed<EquationElem>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -319,7 +325,9 @@ fn layout_equation_block(
|
|||||||
let span = elem.span();
|
let span = elem.span();
|
||||||
let font = find_math_font(engine, styles, span)?;
|
let font = find_math_font(engine, styles, span)?;
|
||||||
|
|
||||||
let mut ctx = MathContext::new(engine, styles, regions.base(), &font);
|
let mut locator = locator.split();
|
||||||
|
let mut ctx =
|
||||||
|
MathContext::new(engine, locator.next(&()), styles, regions.base(), &font);
|
||||||
let full_equation_builder = ctx
|
let full_equation_builder = ctx
|
||||||
.layout_into_run(elem, styles)?
|
.layout_into_run(elem, styles)?
|
||||||
.multiline_frame_builder(&ctx, styles);
|
.multiline_frame_builder(&ctx, styles);
|
||||||
@ -395,7 +403,7 @@ fn layout_equation_block(
|
|||||||
let number = Counter::of(EquationElem::elem())
|
let number = Counter::of(EquationElem::elem())
|
||||||
.display_at_loc(engine, elem.location().unwrap(), styles, numbering)?
|
.display_at_loc(engine, elem.location().unwrap(), styles, numbering)?
|
||||||
.spanned(span)
|
.spanned(span)
|
||||||
.layout(engine, styles, pod)?
|
.layout(engine, locator.next(&()), styles, pod)?
|
||||||
.into_frame();
|
.into_frame();
|
||||||
|
|
||||||
static NUMBER_GUTTER: Em = Em::new(0.5);
|
static NUMBER_GUTTER: Em = Em::new(0.5);
|
||||||
|
@ -232,7 +232,7 @@ impl LayoutMath for Content {
|
|||||||
return elem.layout_math(ctx, styles);
|
return elem.layout_math(ctx, styles);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(realized) = process(ctx.engine, self, styles)? {
|
if let Some(realized) = process(ctx.engine, &mut ctx.locator, self, styles)? {
|
||||||
return realized.layout_math(ctx, styles);
|
return realized.layout_math(ctx, styles);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,9 +296,9 @@ impl LayoutMath for Content {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(tag) = self.to_packed::<TagElem>() {
|
if let Some(elem) = self.to_packed::<TagElem>() {
|
||||||
let mut frame = Frame::soft(Size::zero());
|
let mut frame = Frame::soft(Size::zero());
|
||||||
frame.push(Point::zero(), FrameItem::Tag(tag.elem.clone()));
|
frame.push(Point::zero(), FrameItem::Tag(elem.tag.clone()));
|
||||||
ctx.push(FrameFragment::new(ctx, styles, frame));
|
ctx.push(FrameFragment::new(ctx, styles, frame));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::diag::{bail, At, SourceResult};
|
use crate::diag::{error, At, HintedString, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
cast, elem, Cast, Content, Label, Packed, Show, Smart, StyleChain, Synthesize,
|
cast, elem, Cast, Content, Label, Packed, Show, Smart, StyleChain, Synthesize,
|
||||||
@ -159,6 +159,17 @@ impl Show for Packed<CiteGroup> {
|
|||||||
.citations
|
.citations
|
||||||
.get(&location)
|
.get(&location)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_else(|| bail!(span, "failed to format citation (this is a bug)"))
|
.ok_or_else(failed_to_format_citation)
|
||||||
|
.at(span)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The error message when a citation wasn't found in the pre-formatted list.
|
||||||
|
#[cold]
|
||||||
|
fn failed_to_format_citation() -> HintedString {
|
||||||
|
error!(
|
||||||
|
"cannot format citation in isolation";
|
||||||
|
hint: "check whether this citation is measured \
|
||||||
|
without being inserted into the document"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -6,7 +6,7 @@ use crate::foundations::{
|
|||||||
cast, elem, Args, Array, Construct, Content, Datetime, Packed, Smart, StyleChain,
|
cast, elem, Args, Array, Construct, Content, Datetime, Packed, Smart, StyleChain,
|
||||||
Value,
|
Value,
|
||||||
};
|
};
|
||||||
use crate::introspection::{Introspector, ManualPageCounter};
|
use crate::introspection::{Introspector, Locator, ManualPageCounter};
|
||||||
use crate::layout::{Page, PageElem};
|
use crate::layout::{Page, PageElem};
|
||||||
use crate::realize::StyleVec;
|
use crate::realize::StyleVec;
|
||||||
|
|
||||||
@ -76,6 +76,7 @@ impl Packed<DocumentElem> {
|
|||||||
pub fn layout(
|
pub fn layout(
|
||||||
&self,
|
&self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<Document> {
|
) -> SourceResult<Document> {
|
||||||
let mut pages = Vec::with_capacity(self.children().len());
|
let mut pages = Vec::with_capacity(self.children().len());
|
||||||
@ -83,13 +84,20 @@ impl Packed<DocumentElem> {
|
|||||||
|
|
||||||
let children = self.children();
|
let children = self.children();
|
||||||
let mut iter = children.chain(&styles).peekable();
|
let mut iter = children.chain(&styles).peekable();
|
||||||
|
let mut locator = locator.split();
|
||||||
|
|
||||||
while let Some((child, styles)) = iter.next() {
|
while let Some((child, styles)) = iter.next() {
|
||||||
if let Some(page) = child.to_packed::<PageElem>() {
|
if let Some(page) = child.to_packed::<PageElem>() {
|
||||||
let extend_to = iter
|
let extend_to = iter
|
||||||
.peek()
|
.peek()
|
||||||
.and_then(|(next, _)| *next.to_packed::<PageElem>()?.clear_to()?);
|
.and_then(|(next, _)| *next.to_packed::<PageElem>()?.clear_to()?);
|
||||||
let run = page.layout(engine, styles, &mut page_counter, extend_to)?;
|
let run = page.layout(
|
||||||
|
engine,
|
||||||
|
locator.next(&page.span()),
|
||||||
|
styles,
|
||||||
|
&mut page_counter,
|
||||||
|
extend_to,
|
||||||
|
)?;
|
||||||
pages.extend(run);
|
pages.extend(run);
|
||||||
} else {
|
} else {
|
||||||
bail!(child.span(), "unexpected document child");
|
bail!(child.span(), "unexpected document child");
|
||||||
|
@ -9,6 +9,7 @@ use crate::foundations::{
|
|||||||
cast, elem, scope, Array, Content, Context, NativeElement, Packed, Show, Smart,
|
cast, elem, scope, Array, Content, Context, NativeElement, Packed, Show, Smart,
|
||||||
StyleChain, Styles,
|
StyleChain, Styles,
|
||||||
};
|
};
|
||||||
|
use crate::introspection::Locator;
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Alignment, Axes, BlockElem, Cell, CellGrid, Em, Fragment, GridLayouter, HAlignment,
|
Alignment, Axes, BlockElem, Cell, CellGrid, Em, Fragment, GridLayouter, HAlignment,
|
||||||
Length, Regions, Sizing, Spacing, VAlignment, VElem,
|
Length, Regions, Sizing, Spacing, VAlignment, VElem,
|
||||||
@ -215,7 +216,9 @@ impl EnumElem {
|
|||||||
|
|
||||||
impl Show for Packed<EnumElem> {
|
impl Show for Packed<EnumElem> {
|
||||||
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||||
let mut realized = BlockElem::multi_layouter(self.clone(), layout_enum).pack();
|
let mut realized = BlockElem::multi_layouter(self.clone(), layout_enum)
|
||||||
|
.pack()
|
||||||
|
.spanned(self.span());
|
||||||
|
|
||||||
if self.tight(styles) {
|
if self.tight(styles) {
|
||||||
let leading = ParElem::leading_in(styles);
|
let leading = ParElem::leading_in(styles);
|
||||||
@ -232,6 +235,7 @@ impl Show for Packed<EnumElem> {
|
|||||||
fn layout_enum(
|
fn layout_enum(
|
||||||
elem: &Packed<EnumElem>,
|
elem: &Packed<EnumElem>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -246,6 +250,7 @@ fn layout_enum(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut cells = vec![];
|
let mut cells = vec![];
|
||||||
|
let mut locator = locator.split();
|
||||||
let mut number = elem.start(styles);
|
let mut number = elem.start(styles);
|
||||||
let mut parents = EnumElem::parents_in(styles);
|
let mut parents = EnumElem::parents_in(styles);
|
||||||
|
|
||||||
@ -280,11 +285,12 @@ fn layout_enum(
|
|||||||
let resolved =
|
let resolved =
|
||||||
resolved.aligned(number_align).styled(TextElem::set_overhang(false));
|
resolved.aligned(number_align).styled(TextElem::set_overhang(false));
|
||||||
|
|
||||||
cells.push(Cell::from(Content::empty()));
|
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
||||||
cells.push(Cell::from(resolved));
|
cells.push(Cell::new(resolved, locator.next(&())));
|
||||||
cells.push(Cell::from(Content::empty()));
|
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
||||||
cells.push(Cell::from(
|
cells.push(Cell::new(
|
||||||
item.body().clone().styled(EnumElem::set_parents(smallvec![number])),
|
item.body.clone().styled(EnumElem::set_parents(smallvec![number])),
|
||||||
|
locator.next(&item.body.span()),
|
||||||
));
|
));
|
||||||
number = number.saturating_add(1);
|
number = number.saturating_add(1);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,9 @@ use crate::foundations::{
|
|||||||
elem, Content, NativeElement, Packed, Resolve, Show, ShowSet, Smart, StyleChain,
|
elem, Content, NativeElement, Packed, Resolve, Show, ShowSet, Smart, StyleChain,
|
||||||
Styles, Synthesize,
|
Styles, Synthesize,
|
||||||
};
|
};
|
||||||
use crate::introspection::{Count, Counter, CounterUpdate, Locatable};
|
use crate::introspection::{
|
||||||
|
Count, Counter, CounterUpdate, Locatable, Locator, LocatorLink,
|
||||||
|
};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, Axes, BlockChild, BlockElem, Em, HElem, Length, Regions, VElem,
|
Abs, Axes, BlockChild, BlockElem, Em, HElem, Length, Regions, VElem,
|
||||||
};
|
};
|
||||||
@ -221,20 +223,27 @@ impl Show for Packed<HeadingElem> {
|
|||||||
let mut realized = self.body().clone();
|
let mut realized = self.body().clone();
|
||||||
|
|
||||||
let hanging_indent = self.hanging_indent(styles);
|
let hanging_indent = self.hanging_indent(styles);
|
||||||
|
|
||||||
let mut indent = match hanging_indent {
|
let mut indent = match hanging_indent {
|
||||||
Smart::Custom(length) => length.resolve(styles),
|
Smart::Custom(length) => length.resolve(styles),
|
||||||
Smart::Auto => Abs::zero(),
|
Smart::Auto => Abs::zero(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(numbering) = (**self).numbering(styles).as_ref() {
|
if let Some(numbering) = (**self).numbering(styles).as_ref() {
|
||||||
|
let location = self.location().unwrap();
|
||||||
let numbering = Counter::of(HeadingElem::elem())
|
let numbering = Counter::of(HeadingElem::elem())
|
||||||
.display_at_loc(engine, self.location().unwrap(), styles, numbering)?
|
.display_at_loc(engine, location, styles, numbering)?
|
||||||
.spanned(span);
|
.spanned(span);
|
||||||
|
|
||||||
if hanging_indent.is_auto() {
|
if hanging_indent.is_auto() {
|
||||||
let pod = Regions::one(Axes::splat(Abs::inf()), Axes::splat(false));
|
let pod = Regions::one(Axes::splat(Abs::inf()), Axes::splat(false));
|
||||||
let size = numbering.measure(engine, styles, pod)?.into_frame().size();
|
|
||||||
|
// We don't have a locator for the numbering here, so we just
|
||||||
|
// use the measurement infrastructure for now.
|
||||||
|
let link = LocatorLink::measure(location);
|
||||||
|
let size = numbering
|
||||||
|
.layout(engine, Locator::link(&link), styles, pod)?
|
||||||
|
.into_frame()
|
||||||
|
.size();
|
||||||
|
|
||||||
indent = size.x + SPACING_TO_NUMBERING.resolve(styles);
|
indent = size.x + SPACING_TO_NUMBERING.resolve(styles);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ use crate::foundations::{
|
|||||||
cast, elem, scope, Array, Content, Context, Depth, Func, NativeElement, Packed, Show,
|
cast, elem, scope, Array, Content, Context, Depth, Func, NativeElement, Packed, Show,
|
||||||
Smart, StyleChain, Styles, Value,
|
Smart, StyleChain, Styles, Value,
|
||||||
};
|
};
|
||||||
|
use crate::introspection::Locator;
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Axes, BlockElem, Cell, CellGrid, Em, Fragment, GridLayouter, HAlignment, Length,
|
Axes, BlockElem, Cell, CellGrid, Em, Fragment, GridLayouter, HAlignment, Length,
|
||||||
Regions, Sizing, Spacing, VAlignment, VElem,
|
Regions, Sizing, Spacing, VAlignment, VElem,
|
||||||
@ -139,7 +140,9 @@ impl ListElem {
|
|||||||
|
|
||||||
impl Show for Packed<ListElem> {
|
impl Show for Packed<ListElem> {
|
||||||
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||||
let mut realized = BlockElem::multi_layouter(self.clone(), layout_list).pack();
|
let mut realized = BlockElem::multi_layouter(self.clone(), layout_list)
|
||||||
|
.pack()
|
||||||
|
.spanned(self.span());
|
||||||
|
|
||||||
if self.tight(styles) {
|
if self.tight(styles) {
|
||||||
let leading = ParElem::leading_in(styles);
|
let leading = ParElem::leading_in(styles);
|
||||||
@ -156,6 +159,7 @@ impl Show for Packed<ListElem> {
|
|||||||
fn layout_list(
|
fn layout_list(
|
||||||
elem: &Packed<ListElem>,
|
elem: &Packed<ListElem>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -176,11 +180,16 @@ fn layout_list(
|
|||||||
.aligned(HAlignment::Start + VAlignment::Top);
|
.aligned(HAlignment::Start + VAlignment::Top);
|
||||||
|
|
||||||
let mut cells = vec![];
|
let mut cells = vec![];
|
||||||
|
let mut locator = locator.split();
|
||||||
|
|
||||||
for item in elem.children() {
|
for item in elem.children() {
|
||||||
cells.push(Cell::from(Content::empty()));
|
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
||||||
cells.push(Cell::from(marker.clone()));
|
cells.push(Cell::new(marker.clone(), locator.next(&marker.span())));
|
||||||
cells.push(Cell::from(Content::empty()));
|
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
||||||
cells.push(Cell::from(item.body().clone().styled(ListElem::set_depth(Depth(1)))));
|
cells.push(Cell::new(
|
||||||
|
item.body.clone().styled(ListElem::set_depth(Depth(1))),
|
||||||
|
locator.next(&item.body.span()),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let grid = CellGrid::new(
|
let grid = CellGrid::new(
|
||||||
|
@ -6,6 +6,7 @@ use crate::foundations::{
|
|||||||
elem, Args, Cast, Construct, Content, NativeElement, Packed, Set, Smart, StyleChain,
|
elem, Args, Cast, Construct, Content, NativeElement, Packed, Set, Smart, StyleChain,
|
||||||
Unlabellable,
|
Unlabellable,
|
||||||
};
|
};
|
||||||
|
use crate::introspection::Locator;
|
||||||
use crate::layout::{Em, Fragment, Length, Size};
|
use crate::layout::{Em, Fragment, Length, Size};
|
||||||
use crate::realize::StyleVec;
|
use crate::realize::StyleVec;
|
||||||
|
|
||||||
@ -138,6 +139,7 @@ impl Packed<ParElem> {
|
|||||||
pub fn layout(
|
pub fn layout(
|
||||||
&self,
|
&self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
consecutive: bool,
|
consecutive: bool,
|
||||||
region: Size,
|
region: Size,
|
||||||
@ -146,6 +148,7 @@ impl Packed<ParElem> {
|
|||||||
crate::layout::layout_inline(
|
crate::layout::layout_inline(
|
||||||
&self.children,
|
&self.children,
|
||||||
engine,
|
engine,
|
||||||
|
locator,
|
||||||
styles,
|
styles,
|
||||||
consecutive,
|
consecutive,
|
||||||
region,
|
region,
|
||||||
|
@ -8,6 +8,7 @@ use crate::engine::Engine;
|
|||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
cast, elem, scope, Content, Fold, NativeElement, Packed, Show, Smart, StyleChain,
|
cast, elem, scope, Content, Fold, NativeElement, Packed, Show, Smart, StyleChain,
|
||||||
};
|
};
|
||||||
|
use crate::introspection::Locator;
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
show_grid_cell, Abs, Alignment, Axes, BlockElem, Cell, CellGrid, Celled, Dir,
|
show_grid_cell, Abs, Alignment, Axes, BlockElem, Cell, CellGrid, Celled, Dir,
|
||||||
Fragment, GridCell, GridFooter, GridHLine, GridHeader, GridLayouter, GridVLine,
|
Fragment, GridCell, GridFooter, GridHLine, GridHeader, GridLayouter, GridVLine,
|
||||||
@ -262,7 +263,9 @@ impl TableElem {
|
|||||||
|
|
||||||
impl Show for Packed<TableElem> {
|
impl Show for Packed<TableElem> {
|
||||||
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(BlockElem::multi_layouter(self.clone(), layout_table).pack())
|
Ok(BlockElem::multi_layouter(self.clone(), layout_table)
|
||||||
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,6 +274,7 @@ impl Show for Packed<TableElem> {
|
|||||||
fn layout_table(
|
fn layout_table(
|
||||||
elem: &Packed<TableElem>,
|
elem: &Packed<TableElem>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -304,6 +308,7 @@ fn layout_table(
|
|||||||
let grid = CellGrid::resolve(
|
let grid = CellGrid::resolve(
|
||||||
tracks,
|
tracks,
|
||||||
gutter,
|
gutter,
|
||||||
|
locator,
|
||||||
children,
|
children,
|
||||||
fill,
|
fill,
|
||||||
align,
|
align,
|
||||||
@ -800,7 +805,7 @@ impl Default for Packed<TableCell> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ResolvableCell for Packed<TableCell> {
|
impl ResolvableCell for Packed<TableCell> {
|
||||||
fn resolve_cell(
|
fn resolve_cell<'a>(
|
||||||
mut self,
|
mut self,
|
||||||
x: usize,
|
x: usize,
|
||||||
y: usize,
|
y: usize,
|
||||||
@ -809,8 +814,9 @@ impl ResolvableCell for Packed<TableCell> {
|
|||||||
inset: Sides<Option<Rel<Length>>>,
|
inset: Sides<Option<Rel<Length>>>,
|
||||||
stroke: Sides<Option<Option<Arc<Stroke<Abs>>>>>,
|
stroke: Sides<Option<Option<Arc<Stroke<Abs>>>>>,
|
||||||
breakable: bool,
|
breakable: bool,
|
||||||
|
locator: Locator<'a>,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Cell {
|
) -> Cell<'a> {
|
||||||
let cell = &mut *self;
|
let cell = &mut *self;
|
||||||
let colspan = cell.colspan(styles);
|
let colspan = cell.colspan(styles);
|
||||||
let rowspan = cell.rowspan(styles);
|
let rowspan = cell.rowspan(styles);
|
||||||
@ -862,6 +868,7 @@ impl ResolvableCell for Packed<TableCell> {
|
|||||||
cell.push_breakable(Smart::Custom(breakable));
|
cell.push_breakable(Smart::Custom(breakable));
|
||||||
Cell {
|
Cell {
|
||||||
body: self.pack(),
|
body: self.pack(),
|
||||||
|
locator,
|
||||||
fill,
|
fill,
|
||||||
colspan,
|
colspan,
|
||||||
rowspan,
|
rowspan,
|
||||||
|
@ -21,7 +21,7 @@ use crate::engine::{Engine, Route};
|
|||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
Content, NativeElement, Packed, SequenceElem, StyleChain, StyledElem, Styles,
|
Content, NativeElement, Packed, SequenceElem, StyleChain, StyledElem, Styles,
|
||||||
};
|
};
|
||||||
use crate::introspection::TagElem;
|
use crate::introspection::{Locator, SplitLocator, TagElem};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
AlignElem, BlockElem, BoxElem, ColbreakElem, FlowElem, FlushElem, HElem, InlineElem,
|
AlignElem, BlockElem, BoxElem, ColbreakElem, FlowElem, FlushElem, HElem, InlineElem,
|
||||||
PageElem, PagebreakElem, Parity, PlaceElem, VElem,
|
PageElem, PagebreakElem, Parity, PlaceElem, VElem,
|
||||||
@ -39,11 +39,12 @@ use crate::text::{LinebreakElem, SmartQuoteElem, SpaceElem, TextElem};
|
|||||||
#[typst_macros::time(name = "realize doc")]
|
#[typst_macros::time(name = "realize doc")]
|
||||||
pub fn realize_doc<'a>(
|
pub fn realize_doc<'a>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
arenas: &'a Arenas<'a>,
|
arenas: &'a Arenas<'a>,
|
||||||
content: &'a Content,
|
content: &'a Content,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
) -> SourceResult<(Packed<DocumentElem>, StyleChain<'a>)> {
|
) -> SourceResult<(Packed<DocumentElem>, StyleChain<'a>)> {
|
||||||
let mut builder = Builder::new(engine, arenas, true);
|
let mut builder = Builder::new(engine, locator, arenas, true);
|
||||||
builder.accept(content, styles)?;
|
builder.accept(content, styles)?;
|
||||||
builder.interrupt_page(Some(styles), true)?;
|
builder.interrupt_page(Some(styles), true)?;
|
||||||
Ok(builder.doc.unwrap().finish())
|
Ok(builder.doc.unwrap().finish())
|
||||||
@ -53,11 +54,12 @@ pub fn realize_doc<'a>(
|
|||||||
#[typst_macros::time(name = "realize flow")]
|
#[typst_macros::time(name = "realize flow")]
|
||||||
pub fn realize_flow<'a>(
|
pub fn realize_flow<'a>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
arenas: &'a Arenas<'a>,
|
arenas: &'a Arenas<'a>,
|
||||||
content: &'a Content,
|
content: &'a Content,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
) -> SourceResult<(Packed<FlowElem>, StyleChain<'a>)> {
|
) -> SourceResult<(Packed<FlowElem>, StyleChain<'a>)> {
|
||||||
let mut builder = Builder::new(engine, arenas, false);
|
let mut builder = Builder::new(engine, locator, arenas, false);
|
||||||
builder.accept(content, styles)?;
|
builder.accept(content, styles)?;
|
||||||
builder.interrupt_par()?;
|
builder.interrupt_par()?;
|
||||||
Ok(builder.flow.finish())
|
Ok(builder.flow.finish())
|
||||||
@ -67,6 +69,8 @@ pub fn realize_flow<'a>(
|
|||||||
struct Builder<'a, 'v, 't> {
|
struct Builder<'a, 'v, 't> {
|
||||||
/// The engine.
|
/// The engine.
|
||||||
engine: &'v mut Engine<'t>,
|
engine: &'v mut Engine<'t>,
|
||||||
|
/// Assigns unique locations to elements.
|
||||||
|
locator: SplitLocator<'v>,
|
||||||
/// Scratch arenas for building.
|
/// Scratch arenas for building.
|
||||||
arenas: &'a Arenas<'a>,
|
arenas: &'a Arenas<'a>,
|
||||||
/// The current document building state.
|
/// The current document building state.
|
||||||
@ -82,9 +86,15 @@ struct Builder<'a, 'v, 't> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
||||||
fn new(engine: &'v mut Engine<'t>, arenas: &'a Arenas<'a>, top: bool) -> Self {
|
fn new(
|
||||||
|
engine: &'v mut Engine<'t>,
|
||||||
|
locator: Locator<'v>,
|
||||||
|
arenas: &'a Arenas<'a>,
|
||||||
|
top: bool,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
engine,
|
engine,
|
||||||
|
locator: locator.split(),
|
||||||
arenas,
|
arenas,
|
||||||
doc: top.then(DocBuilder::default),
|
doc: top.then(DocBuilder::default),
|
||||||
flow: FlowBuilder::default(),
|
flow: FlowBuilder::default(),
|
||||||
@ -107,7 +117,8 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
.store(EquationElem::new(content.clone()).pack().spanned(content.span()));
|
.store(EquationElem::new(content.clone()).pack().spanned(content.span()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(realized) = process(self.engine, content, styles)? {
|
if let Some(realized) = process(self.engine, &mut self.locator, content, styles)?
|
||||||
|
{
|
||||||
self.engine.route.increase();
|
self.engine.route.increase();
|
||||||
if !self.engine.route.within(Route::MAX_SHOW_RULE_DEPTH) {
|
if !self.engine.route.within(Route::MAX_SHOW_RULE_DEPTH) {
|
||||||
bail!(
|
bail!(
|
||||||
|
@ -8,9 +8,9 @@ use crate::foundations::{
|
|||||||
Content, Context, Packed, Recipe, RecipeIndex, Regex, Selector, Show, ShowSet, Style,
|
Content, Context, Packed, Recipe, RecipeIndex, Regex, Selector, Show, ShowSet, Style,
|
||||||
StyleChain, Styles, Synthesize, Transformation,
|
StyleChain, Styles, Synthesize, Transformation,
|
||||||
};
|
};
|
||||||
use crate::introspection::{Locatable, TagElem};
|
use crate::introspection::{Locatable, SplitLocator, Tag, TagElem};
|
||||||
use crate::text::TextElem;
|
use crate::text::TextElem;
|
||||||
use crate::utils::{hash128, SmallBitSet};
|
use crate::utils::SmallBitSet;
|
||||||
|
|
||||||
/// What to do with an element when encountering it during realization.
|
/// What to do with an element when encountering it during realization.
|
||||||
struct Verdict<'a> {
|
struct Verdict<'a> {
|
||||||
@ -34,6 +34,7 @@ enum ShowStep<'a> {
|
|||||||
/// Processes the given `target` element when encountering it during realization.
|
/// Processes the given `target` element when encountering it during realization.
|
||||||
pub fn process(
|
pub fn process(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: &mut SplitLocator,
|
||||||
target: &Content,
|
target: &Content,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<Option<Content>> {
|
) -> SourceResult<Option<Content>> {
|
||||||
@ -49,7 +50,7 @@ pub fn process(
|
|||||||
// prepare it.
|
// prepare it.
|
||||||
let mut tag = None;
|
let mut tag = None;
|
||||||
if !prepared {
|
if !prepared {
|
||||||
tag = prepare(engine, &mut target, &mut map, styles)?;
|
tag = prepare(engine, locator, &mut target, &mut map, styles)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply a step, if there is one.
|
// Apply a step, if there is one.
|
||||||
@ -181,6 +182,7 @@ fn verdict<'a>(
|
|||||||
/// This is only executed the first time an element is visited.
|
/// This is only executed the first time an element is visited.
|
||||||
fn prepare(
|
fn prepare(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: &mut SplitLocator,
|
||||||
target: &mut Content,
|
target: &mut Content,
|
||||||
map: &mut Styles,
|
map: &mut Styles,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
@ -191,11 +193,14 @@ fn prepare(
|
|||||||
//
|
//
|
||||||
// The element could already have a location even if it is not prepared
|
// The element could already have a location even if it is not prepared
|
||||||
// when it stems from a query.
|
// when it stems from a query.
|
||||||
let mut located = target.location().is_some();
|
let mut key = None;
|
||||||
if !located && (target.can::<dyn Locatable>() || target.label().is_some()) {
|
if target.location().is_some() {
|
||||||
let location = engine.locator.locate(hash128(&target));
|
key = Some(crate::utils::hash128(&target));
|
||||||
|
} else if target.can::<dyn Locatable>() || target.label().is_some() {
|
||||||
|
let hash = crate::utils::hash128(&target);
|
||||||
|
let location = locator.next_location(engine.introspector, hash);
|
||||||
target.set_location(location);
|
target.set_location(location);
|
||||||
located = true;
|
key = Some(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply built-in show-set rules. User-defined show-set rules are already
|
// Apply built-in show-set rules. User-defined show-set rules are already
|
||||||
@ -220,7 +225,7 @@ fn prepare(
|
|||||||
// materialization, so that it includes the synthesized fields. Do it before
|
// materialization, so that it includes the synthesized fields. Do it before
|
||||||
// marking as prepared so that show-set rules will apply to this element
|
// marking as prepared so that show-set rules will apply to this element
|
||||||
// when queried.
|
// when queried.
|
||||||
let tag = located.then(|| TagElem::packed(target.clone()));
|
let tag = key.map(|key| TagElem::packed(Tag::new(target.clone(), key)));
|
||||||
|
|
||||||
// Ensure that this preparation only runs once by marking the element as
|
// Ensure that this preparation only runs once by marking the element as
|
||||||
// prepared.
|
// prepared.
|
||||||
|
@ -19,6 +19,7 @@ use crate::foundations::{
|
|||||||
cast, elem, func, scope, Bytes, Cast, Content, NativeElement, Packed, Show, Smart,
|
cast, elem, func, scope, Bytes, Cast, Content, NativeElement, Packed, Show, Smart,
|
||||||
StyleChain,
|
StyleChain,
|
||||||
};
|
};
|
||||||
|
use crate::introspection::Locator;
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, Axes, BlockElem, FixedAlignment, Frame, FrameItem, Length, Point, Region, Rel,
|
Abs, Axes, BlockElem, FixedAlignment, Frame, FrameItem, Length, Point, Region, Rel,
|
||||||
Size,
|
Size,
|
||||||
@ -159,7 +160,8 @@ impl Show for Packed<ImageElem> {
|
|||||||
Ok(BlockElem::single_layouter(self.clone(), layout_image)
|
Ok(BlockElem::single_layouter(self.clone(), layout_image)
|
||||||
.with_width(self.width(styles))
|
.with_width(self.width(styles))
|
||||||
.with_height(self.height(styles))
|
.with_height(self.height(styles))
|
||||||
.pack())
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,6 +176,7 @@ impl Figurable for Packed<ImageElem> {}
|
|||||||
fn layout_image(
|
fn layout_image(
|
||||||
elem: &Packed<ImageElem>,
|
elem: &Packed<ImageElem>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
_: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> SourceResult<Frame> {
|
) -> SourceResult<Frame> {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::diag::{bail, SourceResult};
|
use crate::diag::{bail, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{elem, Content, NativeElement, Packed, Show, StyleChain};
|
use crate::foundations::{elem, Content, NativeElement, Packed, Show, StyleChain};
|
||||||
|
use crate::introspection::Locator;
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, Angle, Axes, BlockElem, Frame, FrameItem, Length, Region, Rel, Size,
|
Abs, Angle, Axes, BlockElem, Frame, FrameItem, Length, Region, Rel, Size,
|
||||||
};
|
};
|
||||||
@ -60,7 +61,9 @@ pub struct LineElem {
|
|||||||
|
|
||||||
impl Show for Packed<LineElem> {
|
impl Show for Packed<LineElem> {
|
||||||
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(BlockElem::single_layouter(self.clone(), layout_line).pack())
|
Ok(BlockElem::single_layouter(self.clone(), layout_line)
|
||||||
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +72,7 @@ impl Show for Packed<LineElem> {
|
|||||||
fn layout_line(
|
fn layout_line(
|
||||||
elem: &Packed<LineElem>,
|
elem: &Packed<LineElem>,
|
||||||
_: &mut Engine,
|
_: &mut Engine,
|
||||||
|
_: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> SourceResult<Frame> {
|
) -> SourceResult<Frame> {
|
||||||
|
@ -6,6 +6,7 @@ use crate::foundations::{
|
|||||||
array, cast, elem, Array, Content, NativeElement, Packed, Reflect, Resolve, Show,
|
array, cast, elem, Array, Content, NativeElement, Packed, Reflect, Resolve, Show,
|
||||||
Smart, StyleChain,
|
Smart, StyleChain,
|
||||||
};
|
};
|
||||||
|
use crate::introspection::Locator;
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, Axes, BlockElem, Frame, FrameItem, Length, Point, Region, Rel, Size,
|
Abs, Axes, BlockElem, Frame, FrameItem, Length, Point, Region, Rel, Size,
|
||||||
};
|
};
|
||||||
@ -72,7 +73,9 @@ pub struct PathElem {
|
|||||||
|
|
||||||
impl Show for Packed<PathElem> {
|
impl Show for Packed<PathElem> {
|
||||||
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(BlockElem::single_layouter(self.clone(), layout_path).pack())
|
Ok(BlockElem::single_layouter(self.clone(), layout_path)
|
||||||
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,6 +84,7 @@ impl Show for Packed<PathElem> {
|
|||||||
fn layout_path(
|
fn layout_path(
|
||||||
elem: &Packed<PathElem>,
|
elem: &Packed<PathElem>,
|
||||||
_: &mut Engine,
|
_: &mut Engine,
|
||||||
|
_: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> SourceResult<Frame> {
|
) -> SourceResult<Frame> {
|
||||||
|
@ -6,6 +6,7 @@ use ecow::{eco_format, EcoString};
|
|||||||
use crate::diag::{bail, SourceResult};
|
use crate::diag::{bail, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{func, repr, scope, ty, Content, Smart, StyleChain};
|
use crate::foundations::{func, repr, scope, ty, Content, Smart, StyleChain};
|
||||||
|
use crate::introspection::Locator;
|
||||||
use crate::layout::{Abs, Axes, Frame, Length, Regions, Size};
|
use crate::layout::{Abs, Axes, Frame, Length, Regions, Size};
|
||||||
use crate::syntax::{Span, Spanned};
|
use crate::syntax::{Span, Spanned};
|
||||||
use crate::utils::{LazyHash, Numeric};
|
use crate::utils::{LazyHash, Numeric};
|
||||||
@ -189,9 +190,10 @@ impl Pattern {
|
|||||||
// Layout the pattern.
|
// Layout the pattern.
|
||||||
let world = engine.world;
|
let world = engine.world;
|
||||||
let library = world.library();
|
let library = world.library();
|
||||||
|
let locator = Locator::root();
|
||||||
let styles = StyleChain::new(&library.styles);
|
let styles = StyleChain::new(&library.styles);
|
||||||
let pod = Regions::one(region, Axes::splat(false));
|
let pod = Regions::one(region, Axes::splat(false));
|
||||||
let mut frame = body.layout(engine, styles, pod)?.into_frame();
|
let mut frame = body.layout(engine, locator, styles, pod)?.into_frame();
|
||||||
|
|
||||||
// Set the size of the frame if the size is enforced.
|
// Set the size of the frame if the size is enforced.
|
||||||
if let Smart::Custom(size) = size {
|
if let Smart::Custom(size) = size {
|
||||||
|
@ -5,6 +5,7 @@ use crate::engine::Engine;
|
|||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
elem, func, scope, Content, NativeElement, Packed, Resolve, Show, Smart, StyleChain,
|
elem, func, scope, Content, NativeElement, Packed, Resolve, Show, Smart, StyleChain,
|
||||||
};
|
};
|
||||||
|
use crate::introspection::Locator;
|
||||||
use crate::layout::{Axes, BlockElem, Em, Frame, FrameItem, Length, Point, Region, Rel};
|
use crate::layout::{Axes, BlockElem, Em, Frame, FrameItem, Length, Point, Region, Rel};
|
||||||
use crate::syntax::Span;
|
use crate::syntax::Span;
|
||||||
use crate::utils::Numeric;
|
use crate::utils::Numeric;
|
||||||
@ -125,7 +126,9 @@ impl PolygonElem {
|
|||||||
|
|
||||||
impl Show for Packed<PolygonElem> {
|
impl Show for Packed<PolygonElem> {
|
||||||
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(BlockElem::single_layouter(self.clone(), layout_polygon).pack())
|
Ok(BlockElem::single_layouter(self.clone(), layout_polygon)
|
||||||
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,6 +137,7 @@ impl Show for Packed<PolygonElem> {
|
|||||||
fn layout_polygon(
|
fn layout_polygon(
|
||||||
elem: &Packed<PolygonElem>,
|
elem: &Packed<PolygonElem>,
|
||||||
_: &mut Engine,
|
_: &mut Engine,
|
||||||
|
_: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> SourceResult<Frame> {
|
) -> SourceResult<Frame> {
|
||||||
|
@ -3,6 +3,7 @@ use std::f64::consts::SQRT_2;
|
|||||||
use crate::diag::SourceResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{elem, Content, NativeElement, Packed, Show, Smart, StyleChain};
|
use crate::foundations::{elem, Content, NativeElement, Packed, Show, Smart, StyleChain};
|
||||||
|
use crate::introspection::Locator;
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, Axes, BlockElem, Corner, Corners, Frame, FrameItem, Length, Point, Ratio,
|
Abs, Axes, BlockElem, Corner, Corners, Frame, FrameItem, Length, Point, Ratio,
|
||||||
Region, Regions, Rel, Sides, Size,
|
Region, Regions, Rel, Sides, Size,
|
||||||
@ -134,9 +135,12 @@ pub struct RectElem {
|
|||||||
|
|
||||||
impl Show for Packed<RectElem> {
|
impl Show for Packed<RectElem> {
|
||||||
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(BlockElem::single_layouter(self.clone(), |elem, engine, styles, region| {
|
Ok(BlockElem::single_layouter(
|
||||||
|
self.clone(),
|
||||||
|
|elem, engine, locator, styles, region| {
|
||||||
layout_shape(
|
layout_shape(
|
||||||
engine,
|
engine,
|
||||||
|
locator,
|
||||||
styles,
|
styles,
|
||||||
region,
|
region,
|
||||||
ShapeKind::Rect,
|
ShapeKind::Rect,
|
||||||
@ -148,10 +152,12 @@ impl Show for Packed<RectElem> {
|
|||||||
elem.radius(styles),
|
elem.radius(styles),
|
||||||
elem.span(),
|
elem.span(),
|
||||||
)
|
)
|
||||||
})
|
},
|
||||||
|
)
|
||||||
.with_width(self.width(styles))
|
.with_width(self.width(styles))
|
||||||
.with_height(self.height(styles))
|
.with_height(self.height(styles))
|
||||||
.pack())
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,9 +245,12 @@ pub struct SquareElem {
|
|||||||
|
|
||||||
impl Show for Packed<SquareElem> {
|
impl Show for Packed<SquareElem> {
|
||||||
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(BlockElem::single_layouter(self.clone(), |elem, engine, styles, regions| {
|
Ok(BlockElem::single_layouter(
|
||||||
|
self.clone(),
|
||||||
|
|elem, engine, locator, styles, regions| {
|
||||||
layout_shape(
|
layout_shape(
|
||||||
engine,
|
engine,
|
||||||
|
locator,
|
||||||
styles,
|
styles,
|
||||||
regions,
|
regions,
|
||||||
ShapeKind::Square,
|
ShapeKind::Square,
|
||||||
@ -253,10 +262,12 @@ impl Show for Packed<SquareElem> {
|
|||||||
elem.radius(styles),
|
elem.radius(styles),
|
||||||
elem.span(),
|
elem.span(),
|
||||||
)
|
)
|
||||||
})
|
},
|
||||||
|
)
|
||||||
.with_width(self.width(styles))
|
.with_width(self.width(styles))
|
||||||
.with_height(self.height(styles))
|
.with_height(self.height(styles))
|
||||||
.pack())
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,9 +327,12 @@ pub struct EllipseElem {
|
|||||||
|
|
||||||
impl Show for Packed<EllipseElem> {
|
impl Show for Packed<EllipseElem> {
|
||||||
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(BlockElem::single_layouter(self.clone(), |elem, engine, styles, regions| {
|
Ok(BlockElem::single_layouter(
|
||||||
|
self.clone(),
|
||||||
|
|elem, engine, locator, styles, regions| {
|
||||||
layout_shape(
|
layout_shape(
|
||||||
engine,
|
engine,
|
||||||
|
locator,
|
||||||
styles,
|
styles,
|
||||||
regions,
|
regions,
|
||||||
ShapeKind::Ellipse,
|
ShapeKind::Ellipse,
|
||||||
@ -330,10 +344,12 @@ impl Show for Packed<EllipseElem> {
|
|||||||
Corners::splat(None),
|
Corners::splat(None),
|
||||||
elem.span(),
|
elem.span(),
|
||||||
)
|
)
|
||||||
})
|
},
|
||||||
|
)
|
||||||
.with_width(self.width(styles))
|
.with_width(self.width(styles))
|
||||||
.with_height(self.height(styles))
|
.with_height(self.height(styles))
|
||||||
.pack())
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,9 +434,12 @@ pub struct CircleElem {
|
|||||||
|
|
||||||
impl Show for Packed<CircleElem> {
|
impl Show for Packed<CircleElem> {
|
||||||
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(BlockElem::single_layouter(self.clone(), |elem, engine, styles, regions| {
|
Ok(BlockElem::single_layouter(
|
||||||
|
self.clone(),
|
||||||
|
|elem, engine, locator, styles, regions| {
|
||||||
layout_shape(
|
layout_shape(
|
||||||
engine,
|
engine,
|
||||||
|
locator,
|
||||||
styles,
|
styles,
|
||||||
regions,
|
regions,
|
||||||
ShapeKind::Circle,
|
ShapeKind::Circle,
|
||||||
@ -432,10 +451,12 @@ impl Show for Packed<CircleElem> {
|
|||||||
Corners::splat(None),
|
Corners::splat(None),
|
||||||
elem.span(),
|
elem.span(),
|
||||||
)
|
)
|
||||||
})
|
},
|
||||||
|
)
|
||||||
.with_width(self.width(styles))
|
.with_width(self.width(styles))
|
||||||
.with_height(self.height(styles))
|
.with_height(self.height(styles))
|
||||||
.pack())
|
.pack()
|
||||||
|
.spanned(self.span()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -444,6 +465,7 @@ impl Show for Packed<CircleElem> {
|
|||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn layout_shape(
|
fn layout_shape(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
region: Region,
|
region: Region,
|
||||||
kind: ShapeKind,
|
kind: ShapeKind,
|
||||||
@ -471,14 +493,16 @@ fn layout_shape(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Layout the child.
|
// Layout the child.
|
||||||
frame = child.layout(engine, styles, pod.into_regions())?.into_frame();
|
frame = child
|
||||||
|
.layout(engine, locator.relayout(), styles, pod.into_regions())?
|
||||||
|
.into_frame();
|
||||||
|
|
||||||
// If the child is a square or circle, relayout with full expansion into
|
// If the child is a square or circle, relayout with full expansion into
|
||||||
// square region to make sure the result is really quadratic.
|
// square region to make sure the result is really quadratic.
|
||||||
if kind.is_quadratic() {
|
if kind.is_quadratic() {
|
||||||
let length = frame.size().max_by_side().min(pod.size.min_by_side());
|
let length = frame.size().max_by_side().min(pod.size.min_by_side());
|
||||||
let quad_pod = Regions::one(Size::splat(length), Axes::splat(true));
|
let quad_pod = Regions::one(Size::splat(length), Axes::splat(true));
|
||||||
frame = child.layout(engine, styles, quad_pod)?.into_frame();
|
frame = child.layout(engine, locator, styles, quad_pod)?.into_frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the inset.
|
// Apply the inset.
|
||||||
|
BIN
tests/ref/issue-2480-counter-reset-2.png
Normal file
After Width: | Height: | Size: 576 B |
BIN
tests/ref/issue-2480-counter-reset.png
Normal file
After Width: | Height: | Size: 355 B |
BIN
tests/ref/measure-citation-deeply-nested.png
Normal file
After Width: | Height: | Size: 706 B |
BIN
tests/ref/measure-citation-in-flow.png
Normal file
After Width: | Height: | Size: 743 B |
BIN
tests/ref/measure-counter-multiple-times.png
Normal file
After Width: | Height: | Size: 471 B |
BIN
tests/ref/measure-counter-width.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
tests/ref/table-contextual-measurement.png
Normal file
After Width: | Height: | Size: 453 B |
BIN
tests/ref/table-header-citation.png
Normal file
After Width: | Height: | Size: 624 B |
BIN
tests/ref/table-header-counter.png
Normal file
After Width: | Height: | Size: 359 B |
BIN
tests/ref/table-header-footer-madness.png
Normal file
After Width: | Height: | Size: 613 B |
@ -76,3 +76,31 @@ At Beta, it was #context {
|
|||||||
// Hint: 2-28 try wrapping this in a `context` expression
|
// Hint: 2-28 try wrapping this in a `context` expression
|
||||||
// Hint: 2-28 the `context` expression should wrap everything that depends on this function
|
// Hint: 2-28 the `context` expression should wrap everything that depends on this function
|
||||||
#counter("key").at(<label>)
|
#counter("key").at(<label>)
|
||||||
|
|
||||||
|
--- issue-2480-counter-reset ---
|
||||||
|
#let q = counter("question")
|
||||||
|
#let step-show = q.step() + q.display("1")
|
||||||
|
#let g = grid(step-show, step-show, gutter: 2pt)
|
||||||
|
|
||||||
|
#g
|
||||||
|
#pagebreak()
|
||||||
|
#step-show
|
||||||
|
#q.update(10)
|
||||||
|
#g
|
||||||
|
|
||||||
|
--- issue-2480-counter-reset-2 ---
|
||||||
|
#set block(spacing: 3pt)
|
||||||
|
#let c = counter("c")
|
||||||
|
#let foo() = context {
|
||||||
|
c.step()
|
||||||
|
c.display("1")
|
||||||
|
str(c.get().first())
|
||||||
|
}
|
||||||
|
|
||||||
|
#foo()
|
||||||
|
#block(foo())
|
||||||
|
#foo()
|
||||||
|
#foo()
|
||||||
|
#block(foo())
|
||||||
|
#block(foo())
|
||||||
|
#foo()
|
||||||
|
@ -20,3 +20,75 @@
|
|||||||
assert(d2.width < 400pt)
|
assert(d2.width < 400pt)
|
||||||
assert(d2.height > 50pt)
|
assert(d2.height > 50pt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--- measure-counter-width ---
|
||||||
|
// Measure a counter. Tests that the introspector-assisted location assignment
|
||||||
|
// is able to take `here()` from the context into account to find the closest
|
||||||
|
// matching element instaed of any single one. Crucially, we need to reuse
|
||||||
|
// the same `context c.display()` to get the same span, hence `it`.
|
||||||
|
#let f(it) = context [
|
||||||
|
Is #measure(it).width wide: #it \
|
||||||
|
]
|
||||||
|
|
||||||
|
#let c = counter("c")
|
||||||
|
#let it = context c.display()
|
||||||
|
|
||||||
|
#c.update(10000)
|
||||||
|
#f(it)
|
||||||
|
#c.update(100)
|
||||||
|
#f(it)
|
||||||
|
#c.update(1)
|
||||||
|
#f(it)
|
||||||
|
|
||||||
|
--- measure-citation-in-flow ---
|
||||||
|
// Try measuring a citation that appears inline with other stuff. The
|
||||||
|
// introspection-assisted location assignment will ensure that the citation
|
||||||
|
// in the measurement is matched up with the real one.
|
||||||
|
#context {
|
||||||
|
let it = [@netwok]
|
||||||
|
let size = measure(it)
|
||||||
|
place(line(length: size.width))
|
||||||
|
v(1mm)
|
||||||
|
it + [ is cited]
|
||||||
|
}
|
||||||
|
|
||||||
|
#show bibliography: none
|
||||||
|
#bibliography("/assets/bib/works.bib")
|
||||||
|
|
||||||
|
--- measure-citation-in-flow-different-span ---
|
||||||
|
// When the citation has a different span, it stops working.
|
||||||
|
#context {
|
||||||
|
// Error: 22-29 cannot format citation in isolation
|
||||||
|
// Hint: 22-29 check whether this citation is measured without being inserted into the document
|
||||||
|
let size = measure[@netwok]
|
||||||
|
place(line(length: size.width))
|
||||||
|
v(1mm)
|
||||||
|
[@netwok is cited]
|
||||||
|
}
|
||||||
|
|
||||||
|
#show bibliography: none
|
||||||
|
#bibliography("/assets/bib/works.bib")
|
||||||
|
|
||||||
|
--- measure-citation-deeply-nested ---
|
||||||
|
// Nested the citation deeply to test that introspector-assisted measurement
|
||||||
|
// is able to deal with memoization boundaries.
|
||||||
|
#context {
|
||||||
|
let it = box(pad(x: 5pt, grid(stack[@netwok])))
|
||||||
|
[#measure(it).width]
|
||||||
|
it
|
||||||
|
}
|
||||||
|
|
||||||
|
#show bibliography: none
|
||||||
|
#bibliography("/assets/bib/works.bib")
|
||||||
|
|
||||||
|
--- measure-counter-multiple-times ---
|
||||||
|
// When the thing we measure appears multiple times, we measure as if it was
|
||||||
|
// the first one.
|
||||||
|
#context {
|
||||||
|
let c = counter("c")
|
||||||
|
let u(n) = c.update(n)
|
||||||
|
let it = context c.get().first() * h(1pt)
|
||||||
|
let size = measure(it)
|
||||||
|
table(columns: 5, u(17), it, u(1), it, u(5))
|
||||||
|
[#size.width] // 17pt
|
||||||
|
}
|
||||||
|
@ -139,6 +139,55 @@
|
|||||||
[G], [H]
|
[G], [H]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
--- table-contextual-measurement ---
|
||||||
|
// Test that table cells with varying contextual results are properly
|
||||||
|
// measured.
|
||||||
|
#let c = counter("c")
|
||||||
|
#let k = context square(width: c.get().first() * 5pt)
|
||||||
|
#let u(n) = [#n] + c.update(n)
|
||||||
|
#table(
|
||||||
|
columns: 3,
|
||||||
|
u(1), k, u(2),
|
||||||
|
k, u(4), k,
|
||||||
|
k, k, k,
|
||||||
|
)
|
||||||
|
|
||||||
|
--- table-header-citation ---
|
||||||
|
#set page(height: 60pt)
|
||||||
|
#table(
|
||||||
|
table.header[@netwok],
|
||||||
|
[A],
|
||||||
|
[A],
|
||||||
|
)
|
||||||
|
|
||||||
|
#show bibliography: none
|
||||||
|
#bibliography("/assets/bib/works.bib")
|
||||||
|
|
||||||
|
--- table-header-counter ---
|
||||||
|
#set page(height: 60pt)
|
||||||
|
#let c = counter("c")
|
||||||
|
#table(
|
||||||
|
table.header(c.step() + context c.display()),
|
||||||
|
[A],
|
||||||
|
[A],
|
||||||
|
)
|
||||||
|
|
||||||
|
--- table-header-footer-madness ---
|
||||||
|
#set page(height: 100pt)
|
||||||
|
#let c = counter("c")
|
||||||
|
#let it = context c.get().first() * v(10pt)
|
||||||
|
#table(
|
||||||
|
table.header(c.step()),
|
||||||
|
[A],
|
||||||
|
[A],
|
||||||
|
[A],
|
||||||
|
[A],
|
||||||
|
[A],
|
||||||
|
[A],
|
||||||
|
[A],
|
||||||
|
table.footer(it),
|
||||||
|
)
|
||||||
|
|
||||||
--- table-cell-override ---
|
--- table-cell-override ---
|
||||||
// Cell override
|
// Cell override
|
||||||
#table(
|
#table(
|
||||||
|