mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +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)
|
Pass::new(module, ctx.diags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Caches evaluated modules.
|
||||||
|
pub type ModuleCache = HashMap<FileId, Module>;
|
||||||
|
|
||||||
/// An evaluated module, ready for importing or execution.
|
/// An evaluated module, ready for importing or execution.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
@ -52,20 +55,29 @@ pub struct Module {
|
|||||||
pub template: Template,
|
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.
|
/// The context for evaluation.
|
||||||
pub struct EvalContext<'a> {
|
pub struct EvalContext<'a> {
|
||||||
/// The loader from which resources (files and images) are loaded.
|
/// The loader from which resources (files and images) are loaded.
|
||||||
pub loader: &'a dyn Loader,
|
pub loader: &'a dyn Loader,
|
||||||
/// The cache for decoded images.
|
/// The cache for decoded images.
|
||||||
pub images: &'a mut ImageCache,
|
pub images: &'a mut ImageCache,
|
||||||
|
/// The cache for loaded modules.
|
||||||
|
pub modules: &'a mut ModuleCache,
|
||||||
/// The active scopes.
|
/// The active scopes.
|
||||||
pub scopes: Scopes<'a>,
|
pub scopes: Scopes<'a>,
|
||||||
/// Evaluation diagnostics.
|
/// Evaluation diagnostics.
|
||||||
pub diags: DiagSet,
|
pub diags: DiagSet,
|
||||||
/// The stack of imported files that led to evaluation of the current file.
|
/// The stack of imported files that led to evaluation of the current file.
|
||||||
pub route: Vec<FileId>,
|
pub route: Vec<FileId>,
|
||||||
/// A map of loaded module.
|
|
||||||
pub modules: HashMap<FileId, Module>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> EvalContext<'a> {
|
impl<'a> EvalContext<'a> {
|
||||||
@ -74,10 +86,10 @@ impl<'a> EvalContext<'a> {
|
|||||||
Self {
|
Self {
|
||||||
loader: ctx.loader.as_ref(),
|
loader: ctx.loader.as_ref(),
|
||||||
images: &mut ctx.images,
|
images: &mut ctx.images,
|
||||||
|
modules: &mut ctx.modules,
|
||||||
scopes: Scopes::new(Some(&ctx.std)),
|
scopes: Scopes::new(Some(&ctx.std)),
|
||||||
diags: DiagSet::new(),
|
diags: DiagSet::new(),
|
||||||
route: vec![file],
|
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> {
|
impl Eval for Rc<SyntaxTree> {
|
||||||
type Output = Template;
|
type Output = Template;
|
||||||
|
|
||||||
|
@ -5,14 +5,16 @@ use std::ops::Deref;
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// Caches layouting artifacts.
|
/// Caches layouting artifacts.
|
||||||
#[derive(Default, Debug, Clone)]
|
///
|
||||||
|
/// _This is only available when the `layout-cache` feature is enabled._
|
||||||
#[cfg(feature = "layout-cache")]
|
#[cfg(feature = "layout-cache")]
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
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. The right hand side of the hash map is a vector of
|
/// frames are valid. The right hand side of the hash map is a vector of
|
||||||
/// results because across one or more compilations, multiple different
|
/// results because across one or more compilations, multiple different
|
||||||
/// layouts of the same node may have been requested.
|
/// 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.
|
/// In how many compilations this cache has been used.
|
||||||
age: usize,
|
age: usize,
|
||||||
}
|
}
|
||||||
@ -24,67 +26,42 @@ impl LayoutCache {
|
|||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear the cache.
|
/// Whether the cache is empty.
|
||||||
pub fn clear(&mut self) {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.frames.clear();
|
self.len() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Amount of items in the cache.
|
/// Amount of items in the cache.
|
||||||
pub fn len(&self) -> usize {
|
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`.
|
/// The number of levels stored in the cache.
|
||||||
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.
|
|
||||||
pub fn levels(&self) -> usize {
|
pub fn levels(&self) -> usize {
|
||||||
self.frames
|
self.entries().map(|entry| entry.level + 1).max().unwrap_or(0)
|
||||||
.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.
|
/// 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(
|
pub fn get(
|
||||||
&mut self,
|
&mut self,
|
||||||
hash: u64,
|
hash: u64,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> Option<Vec<Constrained<Rc<Frame>>>> {
|
) -> Option<Vec<Constrained<Rc<Frame>>>> {
|
||||||
self.frames.get_mut(&hash).and_then(|frames| {
|
let entries = self.frames.get_mut(&hash)?;
|
||||||
for frame in frames {
|
for entry in entries {
|
||||||
let res = frame.check(regions.clone());
|
if let Some(frames) = entry.check(regions.clone()) {
|
||||||
if res.is_some() {
|
return Some(frames);
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts a new frame set into the cache.
|
/// Insert a new frame entry into the cache.
|
||||||
pub fn insert(
|
pub fn insert(
|
||||||
&mut self,
|
&mut self,
|
||||||
hash: u64,
|
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.
|
/// Cached frames from past layouting.
|
||||||
#[derive(Debug, Clone)]
|
///
|
||||||
|
/// _This is only available when the `layout-cache` feature is enabled._
|
||||||
#[cfg(feature = "layout-cache")]
|
#[cfg(feature = "layout-cache")]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct FramesEntry {
|
pub struct FramesEntry {
|
||||||
/// The cached frames for a node.
|
/// 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.
|
/// 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.
|
/// For how long the element already exists.
|
||||||
age: usize,
|
age: usize,
|
||||||
/// How much the element was accessed during the last five compilations, the
|
/// 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>>>> {
|
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) {
|
||||||
@ -137,18 +144,25 @@ impl FramesEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.temperature[0] += 1;
|
self.temperature[0] += 1;
|
||||||
|
|
||||||
Some(self.frames.clone())
|
Some(self.frames.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the amount of compilation cycles this item has remained in the
|
/// How nested the frame was in the context is was originally appearing in.
|
||||||
/// cache.
|
pub fn level(&self) -> usize {
|
||||||
|
self.level
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The number of compilation cycles this item has remained in the cache.
|
||||||
pub fn age(&self) -> usize {
|
pub fn age(&self) -> usize {
|
||||||
self.age
|
self.age
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the amount of consecutive cycles in which this item has not
|
/// Whether this element was used in the last compilation cycle.
|
||||||
/// been used.
|
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 {
|
pub fn cooldown(&self) -> usize {
|
||||||
let mut cycle = 0;
|
let mut cycle = 0;
|
||||||
for &temp in &self.temperature[.. self.age] {
|
for &temp in &self.temperature[.. self.age] {
|
||||||
@ -157,13 +171,23 @@ impl FramesEntry {
|
|||||||
}
|
}
|
||||||
cycle += 1;
|
cycle += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
cycle
|
cycle
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether this element was used in the last compilation cycle.
|
/// Carries an item that only applies to certain regions and the constraints
|
||||||
pub fn hit(&self) -> bool {
|
/// that describe these regions.
|
||||||
self.temperature[0] != 0
|
#[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
|
/// Set the appropriate base constraints for (relative) width and height
|
||||||
/// metrics, respectively.
|
/// metrics, respectively.
|
||||||
pub fn set_base_using_linears(
|
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`.
|
/// Changes all constraints by adding the `size` to them if they are `Some`.
|
||||||
pub fn mutate(&mut self, size: Size, regions: &Regions) {
|
pub fn mutate(&mut self, size: Size, regions: &Regions) {
|
||||||
for spec in [
|
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
|
/// Extends length-related options by providing convenience methods for setting
|
||||||
/// minimum and maximum lengths on them, even if they are `None`.
|
/// minimum and maximum lengths on them, even if they are `None`.
|
||||||
pub trait OptionExt {
|
pub trait OptionExt {
|
||||||
|
@ -10,6 +10,7 @@ mod pad;
|
|||||||
mod par;
|
mod par;
|
||||||
mod shaping;
|
mod shaping;
|
||||||
mod stack;
|
mod stack;
|
||||||
|
mod tree;
|
||||||
|
|
||||||
pub use self::image::*;
|
pub use self::image::*;
|
||||||
pub use background::*;
|
pub use background::*;
|
||||||
@ -21,21 +22,16 @@ pub use pad::*;
|
|||||||
pub use par::*;
|
pub use par::*;
|
||||||
pub use shaping::*;
|
pub use shaping::*;
|
||||||
pub use stack::*;
|
pub use stack::*;
|
||||||
|
pub use tree::*;
|
||||||
|
|
||||||
use std::any::Any;
|
|
||||||
use std::fmt::{self, Debug, Formatter};
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
#[cfg(feature = "layout-cache")]
|
#[cfg(feature = "layout-cache")]
|
||||||
use std::hash::Hasher;
|
use std::hash::Hasher;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[cfg(feature = "layout-cache")]
|
|
||||||
use fxhash::FxHasher64;
|
|
||||||
|
|
||||||
use crate::font::FontCache;
|
use crate::font::FontCache;
|
||||||
use crate::geom::*;
|
use crate::geom::*;
|
||||||
use crate::image::ImageCache;
|
use crate::image::ImageCache;
|
||||||
use crate::loading::Loader;
|
|
||||||
use crate::Context;
|
use crate::Context;
|
||||||
|
|
||||||
/// Layout a tree into a collection of frames.
|
/// 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)
|
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.
|
/// Layout a node.
|
||||||
pub trait Layout {
|
pub trait Layout {
|
||||||
/// Layout the node into the given regions.
|
/// Layout the node into the given regions.
|
||||||
@ -207,8 +52,6 @@ pub trait Layout {
|
|||||||
|
|
||||||
/// The context for layouting.
|
/// The context for layouting.
|
||||||
pub struct LayoutContext<'a> {
|
pub struct LayoutContext<'a> {
|
||||||
/// The loader from which fonts are loaded.
|
|
||||||
pub loader: &'a dyn Loader,
|
|
||||||
/// The cache for parsed font faces.
|
/// The cache for parsed font faces.
|
||||||
pub fonts: &'a mut FontCache,
|
pub fonts: &'a mut FontCache,
|
||||||
/// The cache for decoded imges.
|
/// The cache for decoded imges.
|
||||||
@ -225,7 +68,6 @@ impl<'a> LayoutContext<'a> {
|
|||||||
/// Create a new layout context.
|
/// Create a new layout context.
|
||||||
pub fn new(ctx: &'a mut Context) -> Self {
|
pub fn new(ctx: &'a mut Context) -> Self {
|
||||||
Self {
|
Self {
|
||||||
loader: ctx.loader.as_ref(),
|
|
||||||
fonts: &mut ctx.fonts,
|
fonts: &mut ctx.fonts,
|
||||||
images: &mut ctx.images,
|
images: &mut ctx.images,
|
||||||
#[cfg(feature = "layout-cache")]
|
#[cfg(feature = "layout-cache")]
|
||||||
|
@ -47,7 +47,7 @@ impl Layout for ParNode {
|
|||||||
// Find out the BiDi embedding levels.
|
// Find out the BiDi embedding levels.
|
||||||
let bidi = BidiInfo::new(&text, Level::from_dir(self.dir));
|
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.
|
// do line breaking without layouting each and every line from scratch.
|
||||||
let layouter = ParLayouter::new(self, ctx, regions, bidi);
|
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 std::rc::Rc;
|
||||||
|
|
||||||
use crate::diag::Pass;
|
use crate::diag::Pass;
|
||||||
use crate::eval::Scope;
|
use crate::eval::{ModuleCache, Scope};
|
||||||
use crate::exec::State;
|
use crate::exec::State;
|
||||||
use crate::font::FontCache;
|
use crate::font::FontCache;
|
||||||
use crate::image::ImageCache;
|
use crate::image::ImageCache;
|
||||||
@ -68,6 +68,8 @@ pub struct Context {
|
|||||||
pub fonts: FontCache,
|
pub fonts: FontCache,
|
||||||
/// Caches decoded images.
|
/// Caches decoded images.
|
||||||
pub images: ImageCache,
|
pub images: ImageCache,
|
||||||
|
/// Caches evaluated modules.
|
||||||
|
pub modules: ModuleCache,
|
||||||
/// Caches layouting artifacts.
|
/// Caches layouting artifacts.
|
||||||
#[cfg(feature = "layout-cache")]
|
#[cfg(feature = "layout-cache")]
|
||||||
pub layouts: LayoutCache,
|
pub layouts: LayoutCache,
|
||||||
@ -145,6 +147,7 @@ impl ContextBuilder {
|
|||||||
loader: Rc::clone(&loader),
|
loader: Rc::clone(&loader),
|
||||||
fonts: FontCache::new(Rc::clone(&loader)),
|
fonts: FontCache::new(Rc::clone(&loader)),
|
||||||
images: ImageCache::new(loader),
|
images: ImageCache::new(loader),
|
||||||
|
modules: ModuleCache::new(),
|
||||||
#[cfg(feature = "layout-cache")]
|
#[cfg(feature = "layout-cache")]
|
||||||
layouts: LayoutCache::new(),
|
layouts: LayoutCache::new(),
|
||||||
std: self.std.unwrap_or(library::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 (local_compare_ref, ref_diags) = parse_metadata(src, &map);
|
||||||
let compare_ref = local_compare_ref.unwrap_or(compare_ref);
|
let compare_ref = local_compare_ref.unwrap_or(compare_ref);
|
||||||
|
|
||||||
|
// Clear the module cache between tests.
|
||||||
|
ctx.modules.clear();
|
||||||
|
|
||||||
let ast = parse(src);
|
let ast = parse(src);
|
||||||
let module = eval(ctx, src_id, Rc::new(ast.output));
|
let module = eval(ctx, src_id, Rc::new(ast.output));
|
||||||
let tree = exec(ctx, &module.output.template);
|
let tree = exec(ctx, &module.output.template);
|
||||||
@ -295,7 +298,7 @@ fn test_part(
|
|||||||
for level in 0 .. reference.levels() {
|
for level in 0 .. reference.levels() {
|
||||||
ctx.layouts = reference.clone();
|
ctx.layouts = reference.clone();
|
||||||
ctx.layouts.retain(|x| x == level);
|
ctx.layouts.retain(|x| x == level);
|
||||||
if ctx.layouts.frames.is_empty() {
|
if ctx.layouts.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,10 +307,8 @@ fn test_part(
|
|||||||
let cached = layout(ctx, &tree.output);
|
let cached = layout(ctx, &tree.output);
|
||||||
let misses = ctx
|
let misses = ctx
|
||||||
.layouts
|
.layouts
|
||||||
.frames
|
.entries()
|
||||||
.iter()
|
.filter(|e| e.level() == level && !e.hit() && e.age() == 2)
|
||||||
.flat_map(|(_, e)| e)
|
|
||||||
.filter(|e| e.level == level && !e.hit() && e.age() == 2)
|
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
if misses > 0 {
|
if misses > 0 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user