mirror of
https://github.com/typst/typst
synced 2025-06-28 16:22:53 +08:00
Testing for incremental
Also, constraint bugfixes.
This commit is contained in:
parent
f64c772b6d
commit
6b6cdae7ce
@ -108,6 +108,11 @@ impl Length {
|
|||||||
self.raw + 1e-6 >= other.raw
|
self.raw + 1e-6 >= other.raw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compares to lengths for whether they are approximately equal.
|
||||||
|
pub fn approx_eq(self, other: Self) -> bool {
|
||||||
|
self == other || (self - other).to_raw().abs() < 1e-6
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether the length is zero.
|
/// Whether the length is zero.
|
||||||
pub fn is_zero(self) -> bool {
|
pub fn is_zero(self) -> bool {
|
||||||
self.raw == 0.0
|
self.raw == 0.0
|
||||||
|
@ -64,6 +64,8 @@ struct GridLayouter<'a> {
|
|||||||
children: &'a [AnyNode],
|
children: &'a [AnyNode],
|
||||||
/// The region to layout into.
|
/// The region to layout into.
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
|
/// The original expand state of the target region.
|
||||||
|
original_expand: Spec<bool>,
|
||||||
/// Resolved column sizes.
|
/// Resolved column sizes.
|
||||||
rcols: Vec<Length>,
|
rcols: Vec<Length>,
|
||||||
/// The full main size of the current region.
|
/// The full main size of the current region.
|
||||||
@ -135,7 +137,8 @@ impl<'a> GridLayouter<'a> {
|
|||||||
let full = regions.current.get(main);
|
let full = regions.current.get(main);
|
||||||
let rcols = vec![Length::zero(); cols.len()];
|
let rcols = vec![Length::zero(); cols.len()];
|
||||||
|
|
||||||
// We use the regions only for auto row measurement.
|
// We use the regions only for auto row measurement and constraints.
|
||||||
|
let original_expand = regions.expand;
|
||||||
regions.expand = Gen::new(true, false).to_spec(main);
|
regions.expand = Gen::new(true, false).to_spec(main);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@ -144,8 +147,9 @@ impl<'a> GridLayouter<'a> {
|
|||||||
cols,
|
cols,
|
||||||
rows,
|
rows,
|
||||||
children: &grid.children,
|
children: &grid.children,
|
||||||
constraints: Constraints::new(regions.expand),
|
constraints: Constraints::new(original_expand),
|
||||||
regions,
|
regions,
|
||||||
|
original_expand,
|
||||||
rcols,
|
rcols,
|
||||||
lrows: vec![],
|
lrows: vec![],
|
||||||
full,
|
full,
|
||||||
@ -506,7 +510,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
self.used.main = Length::zero();
|
self.used.main = Length::zero();
|
||||||
self.fr = Fractional::zero();
|
self.fr = Fractional::zero();
|
||||||
self.finished.push(output.constrain(self.constraints));
|
self.finished.push(output.constrain(self.constraints));
|
||||||
self.constraints = Constraints::new(self.regions.expand);
|
self.constraints = Constraints::new(self.original_expand);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the node in the cell in column `x` and row `y`.
|
/// Get the node in the cell in column `x` and row `y`.
|
||||||
|
@ -7,13 +7,15 @@ use super::*;
|
|||||||
pub struct LayoutCache {
|
pub struct LayoutCache {
|
||||||
/// Maps from node hashes to the resulting frames and regions in which the
|
/// Maps from node hashes to the resulting frames and regions in which the
|
||||||
/// frames are valid.
|
/// frames are valid.
|
||||||
pub frames: HashMap<u64, FramesEntry>,
|
pub frames: HashMap<u64, Vec<FramesEntry>>,
|
||||||
|
/// In how many compilations this cache has been used.
|
||||||
|
age: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutCache {
|
impl LayoutCache {
|
||||||
/// Create a new, empty layout cache.
|
/// Create a new, empty layout cache.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self { frames: HashMap::new() }
|
Self { frames: HashMap::new(), age: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear the cache.
|
/// Clear the cache.
|
||||||
@ -26,12 +28,68 @@ impl LayoutCache {
|
|||||||
where
|
where
|
||||||
F: FnMut(usize) -> bool,
|
F: FnMut(usize) -> bool,
|
||||||
{
|
{
|
||||||
self.frames.retain(|_, entry| f(entry.level));
|
for (_, hash_ident) in self.frames.iter_mut() {
|
||||||
|
hash_ident.retain(|entry| f(entry.level));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Amount of items in the cache.
|
/// Amount of items in the cache.
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.frames.len()
|
self.frames.iter().map(|(_, e)| e.len()).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prepare the cache for the next round of compilation
|
||||||
|
pub fn turnaround(&mut self) {
|
||||||
|
self.age += 1;
|
||||||
|
for entry in self.frames.iter_mut().flat_map(|(_, x)| x.iter_mut()) {
|
||||||
|
for i in 0 .. (entry.temperature.len() - 1) {
|
||||||
|
entry.temperature[i] = entry.temperature[i + 1];
|
||||||
|
}
|
||||||
|
*entry.temperature.last_mut().unwrap() = Some(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// What is the deepest level in the cache?
|
||||||
|
pub fn levels(&self) -> usize {
|
||||||
|
self.frames
|
||||||
|
.iter()
|
||||||
|
.flat_map(|(_, x)| x)
|
||||||
|
.map(|entry| entry.level + 1)
|
||||||
|
.max()
|
||||||
|
.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetches the appropriate entry from the cache if there is any.
|
||||||
|
pub fn get(
|
||||||
|
&mut self,
|
||||||
|
hash: u64,
|
||||||
|
regions: Regions,
|
||||||
|
) -> Option<Vec<Constrained<Rc<Frame>>>> {
|
||||||
|
self.frames.get_mut(&hash).and_then(|frames| {
|
||||||
|
for frame in frames {
|
||||||
|
let res = frame.check(regions.clone());
|
||||||
|
if res.is_some() {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts a new frame set into the cache.
|
||||||
|
pub fn insert(
|
||||||
|
&mut self,
|
||||||
|
hash: u64,
|
||||||
|
frames: Vec<Constrained<Rc<Frame>>>,
|
||||||
|
level: usize,
|
||||||
|
) {
|
||||||
|
let entry = FramesEntry::new(frames, level);
|
||||||
|
if let Some(variants) = self.frames.get_mut(&hash) {
|
||||||
|
variants.push(entry);
|
||||||
|
} else {
|
||||||
|
self.frames.insert(hash, vec![entry]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,19 +100,72 @@ pub struct FramesEntry {
|
|||||||
pub frames: Vec<Constrained<Rc<Frame>>>,
|
pub frames: Vec<Constrained<Rc<Frame>>>,
|
||||||
/// How nested the frame was in the context is was originally appearing in.
|
/// How nested the frame was in the context is was originally appearing in.
|
||||||
pub level: usize,
|
pub level: usize,
|
||||||
|
/// How much the element was accessed during the last five compilations, the
|
||||||
|
/// most current one being the last element. `None` variants indicate that
|
||||||
|
/// the element is younger than five compilations.
|
||||||
|
temperature: [Option<usize>; 5],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FramesEntry {
|
impl FramesEntry {
|
||||||
|
/// Construct a new instance.
|
||||||
|
pub fn new(frames: Vec<Constrained<Rc<Frame>>>, level: usize) -> Self {
|
||||||
|
let mut temperature = [None; 5];
|
||||||
|
temperature[4] = Some(0);
|
||||||
|
Self { frames, level, temperature }
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks if the cached [`Frame`] is valid for the given regions.
|
/// Checks if the cached [`Frame`] is valid for the given regions.
|
||||||
pub fn check(&self, mut regions: Regions) -> Option<Vec<Constrained<Rc<Frame>>>> {
|
pub fn check(&mut self, mut regions: Regions) -> Option<Vec<Constrained<Rc<Frame>>>> {
|
||||||
for (i, frame) in self.frames.iter().enumerate() {
|
for (i, frame) in self.frames.iter().enumerate() {
|
||||||
if (i != 0 && !regions.next()) || !frame.constraints.check(®ions) {
|
if (i != 0 && !regions.next()) || !frame.constraints.check(®ions) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tmp = self.temperature.get_mut(4).unwrap();
|
||||||
|
*tmp = Some(tmp.map_or(1, |x| x + 1));
|
||||||
|
|
||||||
Some(self.frames.clone())
|
Some(self.frames.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the amount of compilation cycles this item has remained in the
|
||||||
|
/// cache.
|
||||||
|
pub fn age(&self) -> usize {
|
||||||
|
let mut age = 0;
|
||||||
|
for &temp in self.temperature.iter().rev() {
|
||||||
|
if temp.is_none() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
age += 1;
|
||||||
|
}
|
||||||
|
age
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the amount of consecutive cycles in which this item has not
|
||||||
|
/// been used.
|
||||||
|
pub fn cooldown(&self) -> usize {
|
||||||
|
let mut age = 0;
|
||||||
|
for (i, &temp) in self.temperature.iter().enumerate().rev() {
|
||||||
|
match temp {
|
||||||
|
Some(temp) => {
|
||||||
|
if temp > 0 {
|
||||||
|
return self.temperature.len() - 1 - i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return age;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
age += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
age
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this element was used in the last compilation cycle.
|
||||||
|
pub fn hit(&self) -> bool {
|
||||||
|
self.temperature.last().unwrap().unwrap_or(0) != 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
@ -107,15 +218,22 @@ impl Constraints {
|
|||||||
let base = regions.base.to_spec();
|
let base = regions.base.to_spec();
|
||||||
let current = regions.current.to_spec();
|
let current = regions.current.to_spec();
|
||||||
|
|
||||||
current.eq_by(&self.min, |x, y| y.map_or(true, |y| x >= &y))
|
let res = current.eq_by(&self.min, |&x, y| y.map_or(true, |y| x.fits(y)))
|
||||||
&& current.eq_by(&self.max, |x, y| y.map_or(true, |y| x < &y))
|
&& current.eq_by(&self.max, |x, y| y.map_or(true, |y| x < &y))
|
||||||
&& current.eq_by(&self.exact, |x, y| y.map_or(true, |y| x == &y))
|
&& current.eq_by(&self.exact, |&x, y| y.map_or(true, |y| x.approx_eq(y)))
|
||||||
&& base.eq_by(&self.base, |x, y| y.map_or(true, |y| x == &y))
|
&& base.eq_by(&self.base, |&x, y| y.map_or(true, |y| x.approx_eq(y)));
|
||||||
|
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changes all constraints by adding the argument to them if they are set.
|
/// Changes all constraints by adding the argument to them if they are set.
|
||||||
pub fn mutate(&mut self, size: Size) {
|
pub fn mutate(&mut self, size: Size, regions: &Regions) {
|
||||||
for x in &mut [self.min, self.max, self.exact, self.base] {
|
for x in std::array::IntoIter::new([
|
||||||
|
&mut self.min,
|
||||||
|
&mut self.max,
|
||||||
|
&mut self.exact,
|
||||||
|
&mut self.base,
|
||||||
|
]) {
|
||||||
if let Some(horizontal) = x.horizontal.as_mut() {
|
if let Some(horizontal) = x.horizontal.as_mut() {
|
||||||
*horizontal += size.width;
|
*horizontal += size.width;
|
||||||
}
|
}
|
||||||
@ -123,6 +241,23 @@ impl Constraints {
|
|||||||
*vertical += size.height;
|
*vertical += size.height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.exact = zip(self.exact, regions.current.to_spec(), |_, o| o);
|
||||||
|
self.base = zip(self.base, regions.base.to_spec(), |_, o| o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zip<F>(
|
||||||
|
one: Spec<Option<Length>>,
|
||||||
|
other: Spec<Length>,
|
||||||
|
mut f: F,
|
||||||
|
) -> Spec<Option<Length>>
|
||||||
|
where
|
||||||
|
F: FnMut(Length, Length) -> Length,
|
||||||
|
{
|
||||||
|
Spec {
|
||||||
|
vertical: one.vertical.map(|r| f(r, other.vertical)),
|
||||||
|
horizontal: one.horizontal.map(|r| f(r, other.horizontal)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,18 +102,10 @@ impl Layout for AnyNode {
|
|||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> Vec<Constrained<Rc<Frame>>> {
|
) -> Vec<Constrained<Rc<Frame>>> {
|
||||||
ctx.level += 1;
|
ctx.level += 1;
|
||||||
let frames = ctx
|
let frames =
|
||||||
.cache
|
ctx.cache.layout.get(self.hash, regions.clone()).unwrap_or_else(|| {
|
||||||
.layout
|
|
||||||
.frames
|
|
||||||
.get(&self.hash)
|
|
||||||
.and_then(|x| x.check(regions.clone()))
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
let frames = self.node.layout(ctx, regions);
|
let frames = self.node.layout(ctx, regions);
|
||||||
ctx.cache.layout.frames.insert(self.hash, FramesEntry {
|
ctx.cache.layout.insert(self.hash, frames.clone(), ctx.level - 1);
|
||||||
frames: frames.clone(),
|
|
||||||
level: ctx.level - 1,
|
|
||||||
});
|
|
||||||
frames
|
frames
|
||||||
});
|
});
|
||||||
ctx.level -= 1;
|
ctx.level -= 1;
|
||||||
|
@ -15,6 +15,7 @@ impl Layout for PadNode {
|
|||||||
ctx: &mut LayoutContext,
|
ctx: &mut LayoutContext,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> Vec<Constrained<Rc<Frame>>> {
|
) -> Vec<Constrained<Rc<Frame>>> {
|
||||||
|
let original = regions.clone();
|
||||||
let mut regions = regions.map(|size| size - self.padding.resolve(size).size());
|
let mut regions = regions.map(|size| size - self.padding.resolve(size).size());
|
||||||
|
|
||||||
let mut frames = self.child.layout(ctx, ®ions);
|
let mut frames = self.child.layout(ctx, ®ions);
|
||||||
@ -28,13 +29,13 @@ impl Layout for PadNode {
|
|||||||
let prev = std::mem::take(&mut frame.item);
|
let prev = std::mem::take(&mut frame.item);
|
||||||
new.push_frame(origin, prev);
|
new.push_frame(origin, prev);
|
||||||
|
|
||||||
frame.constraints.mutate(padding.size() * -1.0);
|
frame.constraints.mutate(padding.size(), &original);
|
||||||
|
|
||||||
if self.padding.left.is_relative() || self.padding.right.is_relative() {
|
if self.padding.left.is_relative() || self.padding.right.is_relative() {
|
||||||
frame.constraints.base.horizontal = Some(regions.base.width);
|
frame.constraints.base.horizontal = Some(original.base.width);
|
||||||
}
|
}
|
||||||
if self.padding.top.is_relative() || self.padding.bottom.is_relative() {
|
if self.padding.top.is_relative() || self.padding.bottom.is_relative() {
|
||||||
frame.constraints.base.vertical = Some(regions.base.height);
|
frame.constraints.base.vertical = Some(original.base.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
regions.next();
|
regions.next();
|
||||||
|
@ -213,10 +213,18 @@ impl<'a> ParLayouter<'a> {
|
|||||||
while !stack.regions.current.height.fits(line.size.height)
|
while !stack.regions.current.height.fits(line.size.height)
|
||||||
&& !stack.regions.in_full_last()
|
&& !stack.regions.in_full_last()
|
||||||
{
|
{
|
||||||
stack.constraints.max.vertical.set_min(line.size.height);
|
stack.constraints.max.vertical.set_min(
|
||||||
|
stack.full.height - stack.regions.current.height + line.size.height,
|
||||||
|
);
|
||||||
stack.finish_region(ctx);
|
stack.finish_region(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !stack.regions.current.height.fits(line.size.height)
|
||||||
|
&& stack.regions.in_full_last()
|
||||||
|
{
|
||||||
|
stack.overflowing = true;
|
||||||
|
}
|
||||||
|
|
||||||
// If the line does not fit horizontally or we have a mandatory
|
// If the line does not fit horizontally or we have a mandatory
|
||||||
// line break (i.e. due to "\n"), we push the line into the
|
// line break (i.e. due to "\n"), we push the line into the
|
||||||
// stack.
|
// stack.
|
||||||
@ -303,11 +311,13 @@ impl ParItem<'_> {
|
|||||||
/// Stacks lines on top of each other.
|
/// Stacks lines on top of each other.
|
||||||
struct LineStack<'a> {
|
struct LineStack<'a> {
|
||||||
line_spacing: Length,
|
line_spacing: Length,
|
||||||
|
full: Size,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
size: Size,
|
size: Size,
|
||||||
lines: Vec<LineLayout<'a>>,
|
lines: Vec<LineLayout<'a>>,
|
||||||
finished: Vec<Constrained<Rc<Frame>>>,
|
finished: Vec<Constrained<Rc<Frame>>>,
|
||||||
constraints: Constraints,
|
constraints: Constraints,
|
||||||
|
overflowing: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> LineStack<'a> {
|
impl<'a> LineStack<'a> {
|
||||||
@ -316,10 +326,12 @@ impl<'a> LineStack<'a> {
|
|||||||
Self {
|
Self {
|
||||||
line_spacing,
|
line_spacing,
|
||||||
constraints: Constraints::new(regions.expand),
|
constraints: Constraints::new(regions.expand),
|
||||||
|
full: regions.current,
|
||||||
regions,
|
regions,
|
||||||
size: Size::zero(),
|
size: Size::zero(),
|
||||||
lines: vec![],
|
lines: vec![],
|
||||||
finished: vec![],
|
finished: vec![],
|
||||||
|
overflowing: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,6 +355,12 @@ impl<'a> LineStack<'a> {
|
|||||||
self.constraints.exact.horizontal = Some(self.regions.current.width);
|
self.constraints.exact.horizontal = Some(self.regions.current.width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.overflowing {
|
||||||
|
self.constraints.min.vertical = None;
|
||||||
|
self.constraints.max.vertical = None;
|
||||||
|
self.constraints.exact = self.full.to_spec().map(Some);
|
||||||
|
}
|
||||||
|
|
||||||
let mut output = Frame::new(self.size, self.size.height);
|
let mut output = Frame::new(self.size, self.size.height);
|
||||||
let mut offset = Length::zero();
|
let mut offset = Length::zero();
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
@ -362,6 +380,7 @@ impl<'a> LineStack<'a> {
|
|||||||
|
|
||||||
self.finished.push(output.constrain(self.constraints));
|
self.finished.push(output.constrain(self.constraints));
|
||||||
self.regions.next();
|
self.regions.next();
|
||||||
|
self.full = self.regions.current;
|
||||||
self.constraints = Constraints::new(self.regions.expand);
|
self.constraints = Constraints::new(self.regions.expand);
|
||||||
self.size = Size::zero();
|
self.size = Size::zero();
|
||||||
}
|
}
|
||||||
|
@ -86,8 +86,8 @@ impl<'a> StackLayouter<'a> {
|
|||||||
Self {
|
Self {
|
||||||
stack,
|
stack,
|
||||||
main,
|
main,
|
||||||
|
constraints: Constraints::new(expand),
|
||||||
expand,
|
expand,
|
||||||
constraints: Constraints::new(regions.expand),
|
|
||||||
regions,
|
regions,
|
||||||
full,
|
full,
|
||||||
used: Gen::zero(),
|
used: Gen::zero(),
|
||||||
@ -145,7 +145,10 @@ impl<'a> StackLayouter<'a> {
|
|||||||
while !self.regions.current.get(self.main).fits(size.main)
|
while !self.regions.current.get(self.main).fits(size.main)
|
||||||
&& !self.regions.in_full_last()
|
&& !self.regions.in_full_last()
|
||||||
{
|
{
|
||||||
self.constraints.max.get_mut(self.main).set_min(size.main);
|
self.constraints
|
||||||
|
.max
|
||||||
|
.get_mut(self.main)
|
||||||
|
.set_min(size.main + self.used.main);
|
||||||
self.finish_region();
|
self.finish_region();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +191,9 @@ impl<'a> StackLayouter<'a> {
|
|||||||
|
|
||||||
// Make sure the stack's size satisfies the aspect ratio.
|
// Make sure the stack's size satisfies the aspect ratio.
|
||||||
if let Some(aspect) = self.stack.aspect {
|
if let Some(aspect) = self.stack.aspect {
|
||||||
self.constraints.exact = self.regions.current.to_spec().map(Some);
|
self.constraints.exact = self.full.to_spec().map(Some);
|
||||||
|
self.constraints.min = Spec::splat(None);
|
||||||
|
self.constraints.max = Spec::splat(None);
|
||||||
let width = size
|
let width = size
|
||||||
.width
|
.width
|
||||||
.max(aspect.into_inner() * size.height)
|
.max(aspect.into_inner() * size.height)
|
||||||
@ -244,6 +249,6 @@ impl<'a> StackLayouter<'a> {
|
|||||||
self.used = Gen::zero();
|
self.used = Gen::zero();
|
||||||
self.ruler = Align::Start;
|
self.ruler = Align::Start;
|
||||||
self.finished.push(output.constrain(self.constraints));
|
self.finished.push(output.constrain(self.constraints));
|
||||||
self.constraints = Constraints::new(self.regions.expand);
|
self.constraints = Constraints::new(expand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,10 +224,8 @@ fn test_part(
|
|||||||
state.page.size = Size::new(Length::pt(120.0), Length::inf());
|
state.page.size = Size::new(Length::pt(120.0), Length::inf());
|
||||||
state.page.margins = Sides::splat(Some(Length::pt(10.0).into()));
|
state.page.margins = Sides::splat(Some(Length::pt(10.0).into()));
|
||||||
|
|
||||||
// Clear cache between tests (for now).
|
let mut pass =
|
||||||
cache.layout.clear();
|
typst::typeset(loader, cache, Some(src_path), &src, &scope, state.clone());
|
||||||
|
|
||||||
let mut pass = typst::typeset(loader, cache, Some(src_path), &src, &scope, state);
|
|
||||||
|
|
||||||
if !compare_ref {
|
if !compare_ref {
|
||||||
pass.output.clear();
|
pass.output.clear();
|
||||||
@ -266,6 +264,56 @@ fn test_part(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let reference_cache = cache.layout.clone();
|
||||||
|
for level in 0 .. reference_cache.levels() {
|
||||||
|
cache.layout = reference_cache.clone();
|
||||||
|
cache.layout.retain(|x| x == level);
|
||||||
|
if cache.layout.frames.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.layout.turnaround();
|
||||||
|
|
||||||
|
let mut cached_result =
|
||||||
|
typst::typeset(loader, cache, Some(src_path), &src, &scope, state.clone());
|
||||||
|
|
||||||
|
if !compare_ref {
|
||||||
|
cached_result.output.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
let misses: Vec<_> = cache
|
||||||
|
.layout
|
||||||
|
.frames
|
||||||
|
.iter()
|
||||||
|
.flat_map(|(_, e)| e)
|
||||||
|
.filter(|e| e.level == level && !e.hit() && e.age() == 2)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if !misses.is_empty() {
|
||||||
|
ok = false;
|
||||||
|
let mut miss_count = 0;
|
||||||
|
for miss in misses {
|
||||||
|
dbg!(miss.frames.iter().map(|f| f.constraints).collect::<Vec<_>>());
|
||||||
|
miss_count += 1;
|
||||||
|
}
|
||||||
|
println!(
|
||||||
|
" Recompilation had {} cache misses on level {} (Subtest {}) ❌",
|
||||||
|
miss_count, level, i
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if cached_result != pass {
|
||||||
|
ok = false;
|
||||||
|
println!(
|
||||||
|
" Recompilation of subtest {} differs from clean pass ❌",
|
||||||
|
i
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.layout = reference_cache;
|
||||||
|
cache.layout.turnaround();
|
||||||
|
|
||||||
(ok, compare_ref, pass.output)
|
(ok, compare_ref, pass.output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user