mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Move and refactor
This commit is contained in:
parent
927f1154fa
commit
adb71ee040
@ -43,6 +43,9 @@ pub fn eval(ctx: &mut Context, file: FileId, ast: Rc<SyntaxTree>) -> Pass<Module
|
||||
Pass::new(module, ctx.diags)
|
||||
}
|
||||
|
||||
/// Caches evaluated modules.
|
||||
pub type ModuleCache = HashMap<FileId, Module>;
|
||||
|
||||
/// An evaluated module, ready for importing or execution.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Module {
|
||||
@ -52,20 +55,29 @@ pub struct Module {
|
||||
pub template: Template,
|
||||
}
|
||||
|
||||
/// Evaluate an expression.
|
||||
pub trait Eval {
|
||||
/// The output of evaluating the expression.
|
||||
type Output;
|
||||
|
||||
/// Evaluate the expression to the output value.
|
||||
fn eval(&self, ctx: &mut EvalContext) -> Self::Output;
|
||||
}
|
||||
|
||||
/// The context for evaluation.
|
||||
pub struct EvalContext<'a> {
|
||||
/// The loader from which resources (files and images) are loaded.
|
||||
pub loader: &'a dyn Loader,
|
||||
/// The cache for decoded images.
|
||||
pub images: &'a mut ImageCache,
|
||||
/// The cache for loaded modules.
|
||||
pub modules: &'a mut ModuleCache,
|
||||
/// The active scopes.
|
||||
pub scopes: Scopes<'a>,
|
||||
/// Evaluation diagnostics.
|
||||
pub diags: DiagSet,
|
||||
/// The stack of imported files that led to evaluation of the current file.
|
||||
pub route: Vec<FileId>,
|
||||
/// A map of loaded module.
|
||||
pub modules: HashMap<FileId, Module>,
|
||||
}
|
||||
|
||||
impl<'a> EvalContext<'a> {
|
||||
@ -74,10 +86,10 @@ impl<'a> EvalContext<'a> {
|
||||
Self {
|
||||
loader: ctx.loader.as_ref(),
|
||||
images: &mut ctx.images,
|
||||
modules: &mut ctx.modules,
|
||||
scopes: Scopes::new(Some(&ctx.std)),
|
||||
diags: DiagSet::new(),
|
||||
route: vec![file],
|
||||
modules: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,15 +196,6 @@ impl<'a> EvalContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate an expression.
|
||||
pub trait Eval {
|
||||
/// The output of evaluating the expression.
|
||||
type Output;
|
||||
|
||||
/// Evaluate the expression to the output value.
|
||||
fn eval(&self, ctx: &mut EvalContext) -> Self::Output;
|
||||
}
|
||||
|
||||
impl Eval for Rc<SyntaxTree> {
|
||||
type Output = Template;
|
||||
|
||||
|
@ -5,14 +5,16 @@ use std::ops::Deref;
|
||||
use super::*;
|
||||
|
||||
/// Caches layouting artifacts.
|
||||
#[derive(Default, Debug, Clone)]
|
||||
///
|
||||
/// _This is only available when the `layout-cache` feature is enabled._
|
||||
#[cfg(feature = "layout-cache")]
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct LayoutCache {
|
||||
/// Maps from node hashes to the resulting frames and regions in which the
|
||||
/// frames are valid. The right hand side of the hash map is a vector of
|
||||
/// results because across one or more compilations, multiple different
|
||||
/// layouts of the same node may have been requested.
|
||||
pub frames: HashMap<u64, Vec<FramesEntry>>,
|
||||
frames: HashMap<u64, Vec<FramesEntry>>,
|
||||
/// In how many compilations this cache has been used.
|
||||
age: usize,
|
||||
}
|
||||
@ -24,67 +26,42 @@ impl LayoutCache {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Clear the cache.
|
||||
pub fn clear(&mut self) {
|
||||
self.frames.clear();
|
||||
/// Whether the cache is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Amount of items in the cache.
|
||||
pub fn len(&self) -> usize {
|
||||
self.frames.iter().map(|(_, e)| e.len()).sum()
|
||||
self.frames.values().map(Vec::len).sum()
|
||||
}
|
||||
|
||||
/// Retains all elements for which the closure on the level returns `true`.
|
||||
pub fn retain<F>(&mut self, mut f: F)
|
||||
where
|
||||
F: FnMut(usize) -> bool,
|
||||
{
|
||||
for (_, entries) in self.frames.iter_mut() {
|
||||
entries.retain(|entry| f(entry.level));
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 + 1] = entry.temperature[i];
|
||||
}
|
||||
entry.temperature[0] = 0;
|
||||
entry.age += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// The amount of levels stored in the cache.
|
||||
/// The number of levels stored in the cache.
|
||||
pub fn levels(&self) -> usize {
|
||||
self.frames
|
||||
.iter()
|
||||
.flat_map(|(_, x)| x)
|
||||
.map(|entry| entry.level + 1)
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
self.entries().map(|entry| entry.level + 1).max().unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Fetches the appropriate entry from the cache if there is any.
|
||||
/// An iterator over all entries in the cache.
|
||||
pub fn entries(&self) -> impl Iterator<Item = &FramesEntry> + '_ {
|
||||
self.frames.values().flatten()
|
||||
}
|
||||
|
||||
/// Fetch matching cached frames if there are 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;
|
||||
}
|
||||
let entries = self.frames.get_mut(&hash)?;
|
||||
for entry in entries {
|
||||
if let Some(frames) = entry.check(regions.clone()) {
|
||||
return Some(frames);
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Inserts a new frame set into the cache.
|
||||
/// Insert a new frame entry into the cache.
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
hash: u64,
|
||||
@ -99,16 +76,45 @@ impl LayoutCache {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the cache.
|
||||
pub fn clear(&mut self) {
|
||||
self.frames.clear();
|
||||
}
|
||||
|
||||
/// Retain all elements for which the closure on the level returns `true`.
|
||||
pub fn retain<F>(&mut self, mut f: F)
|
||||
where
|
||||
F: FnMut(usize) -> bool,
|
||||
{
|
||||
for entries in self.frames.values_mut() {
|
||||
entries.retain(|entry| f(entry.level));
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepare the cache for the next round of compilation.
|
||||
pub fn turnaround(&mut self) {
|
||||
self.age += 1;
|
||||
for entry in self.frames.values_mut().flatten() {
|
||||
for i in 0 .. (entry.temperature.len() - 1) {
|
||||
entry.temperature[i + 1] = entry.temperature[i];
|
||||
}
|
||||
entry.temperature[0] = 0;
|
||||
entry.age += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Cached frames from past layouting.
|
||||
#[derive(Debug, Clone)]
|
||||
///
|
||||
/// _This is only available when the `layout-cache` feature is enabled._
|
||||
#[cfg(feature = "layout-cache")]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FramesEntry {
|
||||
/// The cached frames for a node.
|
||||
pub frames: Vec<Constrained<Rc<Frame>>>,
|
||||
frames: Vec<Constrained<Rc<Frame>>>,
|
||||
/// How nested the frame was in the context is was originally appearing in.
|
||||
pub level: usize,
|
||||
level: usize,
|
||||
/// For how long the element already exists.
|
||||
age: usize,
|
||||
/// How much the element was accessed during the last five compilations, the
|
||||
@ -128,7 +134,8 @@ impl FramesEntry {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the cached [`Frame`] is valid for the given regions.
|
||||
/// Checks if the cached frames are valid in the given regions and returns
|
||||
/// them if so.
|
||||
pub fn check(&mut self, mut regions: Regions) -> Option<Vec<Constrained<Rc<Frame>>>> {
|
||||
for (i, frame) in self.frames.iter().enumerate() {
|
||||
if (i != 0 && !regions.next()) || !frame.constraints.check(®ions) {
|
||||
@ -137,18 +144,25 @@ impl FramesEntry {
|
||||
}
|
||||
|
||||
self.temperature[0] += 1;
|
||||
|
||||
Some(self.frames.clone())
|
||||
}
|
||||
|
||||
/// Get the amount of compilation cycles this item has remained in the
|
||||
/// cache.
|
||||
/// How nested the frame was in the context is was originally appearing in.
|
||||
pub fn level(&self) -> usize {
|
||||
self.level
|
||||
}
|
||||
|
||||
/// The number of compilation cycles this item has remained in the cache.
|
||||
pub fn age(&self) -> usize {
|
||||
self.age
|
||||
}
|
||||
|
||||
/// Get the amount of consecutive cycles in which this item has not
|
||||
/// been used.
|
||||
/// Whether this element was used in the last compilation cycle.
|
||||
pub fn hit(&self) -> bool {
|
||||
self.temperature[0] != 0
|
||||
}
|
||||
|
||||
/// The amount of consecutive cycles in which this item has not been used.
|
||||
pub fn cooldown(&self) -> usize {
|
||||
let mut cycle = 0;
|
||||
for &temp in &self.temperature[.. self.age] {
|
||||
@ -157,13 +171,23 @@ impl FramesEntry {
|
||||
}
|
||||
cycle += 1;
|
||||
}
|
||||
|
||||
cycle
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this element was used in the last compilation cycle.
|
||||
pub fn hit(&self) -> bool {
|
||||
self.temperature[0] != 0
|
||||
/// Carries an item that only applies to certain regions and the constraints
|
||||
/// that describe these regions.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct Constrained<T> {
|
||||
pub item: T,
|
||||
pub constraints: Constraints,
|
||||
}
|
||||
|
||||
impl<T> Deref for Constrained<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.item
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,6 +218,21 @@ impl Constraints {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "layout-cache")]
|
||||
fn check(&self, regions: &Regions) -> bool {
|
||||
if self.expand != regions.expand {
|
||||
return false;
|
||||
}
|
||||
|
||||
let base = regions.base.to_spec();
|
||||
let current = regions.current.to_spec();
|
||||
|
||||
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.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.approx_eq(y)))
|
||||
}
|
||||
|
||||
/// Set the appropriate base constraints for (relative) width and height
|
||||
/// metrics, respectively.
|
||||
pub fn set_base_using_linears(
|
||||
@ -210,21 +249,6 @@ impl Constraints {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "layout-cache")]
|
||||
fn check(&self, regions: &Regions) -> bool {
|
||||
if self.expand != regions.expand {
|
||||
return false;
|
||||
}
|
||||
|
||||
let base = regions.base.to_spec();
|
||||
let current = regions.current.to_spec();
|
||||
|
||||
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.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.approx_eq(y)))
|
||||
}
|
||||
|
||||
/// Changes all constraints by adding the `size` to them if they are `Some`.
|
||||
pub fn mutate(&mut self, size: Size, regions: &Regions) {
|
||||
for spec in [
|
||||
@ -251,22 +275,6 @@ impl Constraints {
|
||||
}
|
||||
}
|
||||
|
||||
/// Carries an item that only applies to certain regions and the constraints
|
||||
/// that describe these regions.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct Constrained<T> {
|
||||
pub item: T,
|
||||
pub constraints: Constraints,
|
||||
}
|
||||
|
||||
impl<T> Deref for Constrained<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.item
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends length-related options by providing convenience methods for setting
|
||||
/// minimum and maximum lengths on them, even if they are `None`.
|
||||
pub trait OptionExt {
|
||||
|
@ -10,6 +10,7 @@ mod pad;
|
||||
mod par;
|
||||
mod shaping;
|
||||
mod stack;
|
||||
mod tree;
|
||||
|
||||
pub use self::image::*;
|
||||
pub use background::*;
|
||||
@ -21,21 +22,16 @@ pub use pad::*;
|
||||
pub use par::*;
|
||||
pub use shaping::*;
|
||||
pub use stack::*;
|
||||
pub use tree::*;
|
||||
|
||||
use std::any::Any;
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::hash::Hash;
|
||||
#[cfg(feature = "layout-cache")]
|
||||
use std::hash::Hasher;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[cfg(feature = "layout-cache")]
|
||||
use fxhash::FxHasher64;
|
||||
|
||||
use crate::font::FontCache;
|
||||
use crate::geom::*;
|
||||
use crate::image::ImageCache;
|
||||
use crate::loading::Loader;
|
||||
use crate::Context;
|
||||
|
||||
/// Layout a tree into a collection of frames.
|
||||
@ -44,157 +40,6 @@ pub fn layout(ctx: &mut Context, tree: &LayoutTree) -> Vec<Rc<Frame>> {
|
||||
tree.layout(&mut ctx)
|
||||
}
|
||||
|
||||
/// A tree of layout nodes.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct LayoutTree {
|
||||
/// Runs of pages with the same properties.
|
||||
pub runs: Vec<PageRun>,
|
||||
}
|
||||
|
||||
impl LayoutTree {
|
||||
/// Layout the tree into a collection of frames.
|
||||
pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>> {
|
||||
self.runs.iter().flat_map(|run| run.layout(ctx)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// A run of pages that all have the same properties.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct PageRun {
|
||||
/// The size of each page.
|
||||
pub size: Size,
|
||||
/// The layout node that produces the actual pages (typically a
|
||||
/// [`StackNode`]).
|
||||
pub child: LayoutNode,
|
||||
}
|
||||
|
||||
impl PageRun {
|
||||
/// Layout the page run.
|
||||
pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>> {
|
||||
// When one of the lengths is infinite the page fits its content along
|
||||
// that axis.
|
||||
let Size { width, height } = self.size;
|
||||
let expand = Spec::new(width.is_finite(), height.is_finite());
|
||||
let regions = Regions::repeat(self.size, expand);
|
||||
self.child.layout(ctx, ®ions).into_iter().map(|c| c.item).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// A dynamic layouting node.
|
||||
pub struct LayoutNode {
|
||||
node: Box<dyn Bounds>,
|
||||
#[cfg(feature = "layout-cache")]
|
||||
hash: u64,
|
||||
}
|
||||
|
||||
impl LayoutNode {
|
||||
/// Create a new instance from any node that satisifies the required bounds.
|
||||
#[cfg(feature = "layout-cache")]
|
||||
pub fn new<T>(node: T) -> Self
|
||||
where
|
||||
T: Layout + Debug + Clone + Eq + PartialEq + Hash + 'static,
|
||||
{
|
||||
let hash = {
|
||||
let mut state = FxHasher64::default();
|
||||
node.type_id().hash(&mut state);
|
||||
node.hash(&mut state);
|
||||
state.finish()
|
||||
};
|
||||
|
||||
Self { node: Box::new(node), hash }
|
||||
}
|
||||
|
||||
/// Create a new instance from any node that satisifies the required bounds.
|
||||
#[cfg(not(feature = "layout-cache"))]
|
||||
pub fn new<T>(node: T) -> Self
|
||||
where
|
||||
T: Layout + Debug + Clone + Eq + PartialEq + 'static,
|
||||
{
|
||||
Self { node: Box::new(node) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout for LayoutNode {
|
||||
fn layout(
|
||||
&self,
|
||||
ctx: &mut LayoutContext,
|
||||
regions: &Regions,
|
||||
) -> Vec<Constrained<Rc<Frame>>> {
|
||||
#[cfg(feature = "layout-cache")]
|
||||
{
|
||||
ctx.level += 1;
|
||||
let frames = ctx.layouts.get(self.hash, regions.clone()).unwrap_or_else(|| {
|
||||
let frames = self.node.layout(ctx, regions);
|
||||
ctx.layouts.insert(self.hash, frames.clone(), ctx.level - 1);
|
||||
frames
|
||||
});
|
||||
ctx.level -= 1;
|
||||
frames
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "layout-cache"))]
|
||||
self.node.layout(ctx, regions)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for LayoutNode {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
self.node.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for LayoutNode {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
node: self.node.dyn_clone(),
|
||||
#[cfg(feature = "layout-cache")]
|
||||
hash: self.hash,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for LayoutNode {}
|
||||
|
||||
impl PartialEq for LayoutNode {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.node.dyn_eq(other.node.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "layout-cache")]
|
||||
impl Hash for LayoutNode {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u64(self.hash);
|
||||
}
|
||||
}
|
||||
|
||||
trait Bounds: Layout + Debug + 'static {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn dyn_eq(&self, other: &dyn Bounds) -> bool;
|
||||
fn dyn_clone(&self) -> Box<dyn Bounds>;
|
||||
}
|
||||
|
||||
impl<T> Bounds for T
|
||||
where
|
||||
T: Layout + Debug + Eq + PartialEq + Clone + 'static,
|
||||
{
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn dyn_eq(&self, other: &dyn Bounds) -> bool {
|
||||
if let Some(other) = other.as_any().downcast_ref::<Self>() {
|
||||
self == other
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn dyn_clone(&self) -> Box<dyn Bounds> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Layout a node.
|
||||
pub trait Layout {
|
||||
/// Layout the node into the given regions.
|
||||
@ -207,8 +52,6 @@ pub trait Layout {
|
||||
|
||||
/// The context for layouting.
|
||||
pub struct LayoutContext<'a> {
|
||||
/// The loader from which fonts are loaded.
|
||||
pub loader: &'a dyn Loader,
|
||||
/// The cache for parsed font faces.
|
||||
pub fonts: &'a mut FontCache,
|
||||
/// The cache for decoded imges.
|
||||
@ -225,7 +68,6 @@ impl<'a> LayoutContext<'a> {
|
||||
/// Create a new layout context.
|
||||
pub fn new(ctx: &'a mut Context) -> Self {
|
||||
Self {
|
||||
loader: ctx.loader.as_ref(),
|
||||
fonts: &mut ctx.fonts,
|
||||
images: &mut ctx.images,
|
||||
#[cfg(feature = "layout-cache")]
|
||||
|
@ -47,7 +47,7 @@ impl Layout for ParNode {
|
||||
// Find out the BiDi embedding levels.
|
||||
let bidi = BidiInfo::new(&text, Level::from_dir(self.dir));
|
||||
|
||||
// Prepare paragraph layout by bulding a representation on which we can
|
||||
// Prepare paragraph layout by building a representation on which we can
|
||||
// do line breaking without layouting each and every line from scratch.
|
||||
let layouter = ParLayouter::new(self, ctx, regions, bidi);
|
||||
|
||||
|
158
src/layout/tree.rs
Normal file
158
src/layout/tree.rs
Normal file
@ -0,0 +1,158 @@
|
||||
use super::*;
|
||||
|
||||
use std::any::Any;
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
#[cfg(feature = "layout-cache")]
|
||||
use fxhash::FxHasher64;
|
||||
|
||||
/// A tree of layout nodes.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct LayoutTree {
|
||||
/// Runs of pages with the same properties.
|
||||
pub runs: Vec<PageRun>,
|
||||
}
|
||||
|
||||
impl LayoutTree {
|
||||
/// Layout the tree into a collection of frames.
|
||||
pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>> {
|
||||
self.runs.iter().flat_map(|run| run.layout(ctx)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// A run of pages that all have the same properties.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct PageRun {
|
||||
/// The size of each page.
|
||||
pub size: Size,
|
||||
/// The layout node that produces the actual pages (typically a
|
||||
/// [`StackNode`]).
|
||||
pub child: LayoutNode,
|
||||
}
|
||||
|
||||
impl PageRun {
|
||||
/// Layout the page run.
|
||||
pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>> {
|
||||
// When one of the lengths is infinite the page fits its content along
|
||||
// that axis.
|
||||
let Size { width, height } = self.size;
|
||||
let expand = Spec::new(width.is_finite(), height.is_finite());
|
||||
let regions = Regions::repeat(self.size, expand);
|
||||
self.child.layout(ctx, ®ions).into_iter().map(|c| c.item).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// A dynamic layouting node.
|
||||
pub struct LayoutNode {
|
||||
node: Box<dyn Bounds>,
|
||||
#[cfg(feature = "layout-cache")]
|
||||
hash: u64,
|
||||
}
|
||||
|
||||
impl LayoutNode {
|
||||
/// Create a new instance from any node that satisifies the required bounds.
|
||||
#[cfg(feature = "layout-cache")]
|
||||
pub fn new<T>(node: T) -> Self
|
||||
where
|
||||
T: Layout + Debug + Clone + Eq + PartialEq + Hash + 'static,
|
||||
{
|
||||
let hash = {
|
||||
let mut state = FxHasher64::default();
|
||||
node.type_id().hash(&mut state);
|
||||
node.hash(&mut state);
|
||||
state.finish()
|
||||
};
|
||||
|
||||
Self { node: Box::new(node), hash }
|
||||
}
|
||||
|
||||
/// Create a new instance from any node that satisifies the required bounds.
|
||||
#[cfg(not(feature = "layout-cache"))]
|
||||
pub fn new<T>(node: T) -> Self
|
||||
where
|
||||
T: Layout + Debug + Clone + Eq + PartialEq + 'static,
|
||||
{
|
||||
Self { node: Box::new(node) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout for LayoutNode {
|
||||
fn layout(
|
||||
&self,
|
||||
ctx: &mut LayoutContext,
|
||||
regions: &Regions,
|
||||
) -> Vec<Constrained<Rc<Frame>>> {
|
||||
#[cfg(feature = "layout-cache")]
|
||||
{
|
||||
ctx.level += 1;
|
||||
let frames = ctx.layouts.get(self.hash, regions.clone()).unwrap_or_else(|| {
|
||||
let frames = self.node.layout(ctx, regions);
|
||||
ctx.layouts.insert(self.hash, frames.clone(), ctx.level - 1);
|
||||
frames
|
||||
});
|
||||
ctx.level -= 1;
|
||||
frames
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "layout-cache"))]
|
||||
self.node.layout(ctx, regions)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for LayoutNode {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
self.node.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for LayoutNode {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
node: self.node.dyn_clone(),
|
||||
#[cfg(feature = "layout-cache")]
|
||||
hash: self.hash,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for LayoutNode {}
|
||||
|
||||
impl PartialEq for LayoutNode {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.node.dyn_eq(other.node.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "layout-cache")]
|
||||
impl Hash for LayoutNode {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u64(self.hash);
|
||||
}
|
||||
}
|
||||
|
||||
trait Bounds: Layout + Debug + 'static {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn dyn_eq(&self, other: &dyn Bounds) -> bool;
|
||||
fn dyn_clone(&self) -> Box<dyn Bounds>;
|
||||
}
|
||||
|
||||
impl<T> Bounds for T
|
||||
where
|
||||
T: Layout + Debug + Eq + PartialEq + Clone + 'static,
|
||||
{
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn dyn_eq(&self, other: &dyn Bounds) -> bool {
|
||||
if let Some(other) = other.as_any().downcast_ref::<Self>() {
|
||||
self == other
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn dyn_clone(&self) -> Box<dyn Bounds> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
@ -51,7 +51,7 @@ pub mod util;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::diag::Pass;
|
||||
use crate::eval::Scope;
|
||||
use crate::eval::{ModuleCache, Scope};
|
||||
use crate::exec::State;
|
||||
use crate::font::FontCache;
|
||||
use crate::image::ImageCache;
|
||||
@ -68,6 +68,8 @@ pub struct Context {
|
||||
pub fonts: FontCache,
|
||||
/// Caches decoded images.
|
||||
pub images: ImageCache,
|
||||
/// Caches evaluated modules.
|
||||
pub modules: ModuleCache,
|
||||
/// Caches layouting artifacts.
|
||||
#[cfg(feature = "layout-cache")]
|
||||
pub layouts: LayoutCache,
|
||||
@ -145,6 +147,7 @@ impl ContextBuilder {
|
||||
loader: Rc::clone(&loader),
|
||||
fonts: FontCache::new(Rc::clone(&loader)),
|
||||
images: ImageCache::new(loader),
|
||||
modules: ModuleCache::new(),
|
||||
#[cfg(feature = "layout-cache")]
|
||||
layouts: LayoutCache::new(),
|
||||
std: self.std.unwrap_or(library::new()),
|
||||
|
@ -246,6 +246,9 @@ fn test_part(
|
||||
let (local_compare_ref, ref_diags) = parse_metadata(src, &map);
|
||||
let compare_ref = local_compare_ref.unwrap_or(compare_ref);
|
||||
|
||||
// Clear the module cache between tests.
|
||||
ctx.modules.clear();
|
||||
|
||||
let ast = parse(src);
|
||||
let module = eval(ctx, src_id, Rc::new(ast.output));
|
||||
let tree = exec(ctx, &module.output.template);
|
||||
@ -295,7 +298,7 @@ fn test_part(
|
||||
for level in 0 .. reference.levels() {
|
||||
ctx.layouts = reference.clone();
|
||||
ctx.layouts.retain(|x| x == level);
|
||||
if ctx.layouts.frames.is_empty() {
|
||||
if ctx.layouts.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -304,10 +307,8 @@ fn test_part(
|
||||
let cached = layout(ctx, &tree.output);
|
||||
let misses = ctx
|
||||
.layouts
|
||||
.frames
|
||||
.iter()
|
||||
.flat_map(|(_, e)| e)
|
||||
.filter(|e| e.level == level && !e.hit() && e.age() == 2)
|
||||
.entries()
|
||||
.filter(|e| e.level() == level && !e.hit() && e.age() == 2)
|
||||
.count();
|
||||
|
||||
if misses > 0 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user