Remove root node

This commit is contained in:
Laurenz 2022-02-02 20:33:19 +01:00
parent c5e05ac0ea
commit 20a1fd8bc7
6 changed files with 102 additions and 111 deletions

View File

@ -75,8 +75,7 @@ fn bench_eval(iai: &mut Iai) {
fn bench_layout(iai: &mut Iai) { fn bench_layout(iai: &mut Iai) {
let (mut ctx, id) = context(); let (mut ctx, id) = context();
let module = ctx.evaluate(id).unwrap(); let module = ctx.evaluate(id).unwrap();
let tree = module.into_root(); iai.run(|| module.template.layout(&mut ctx));
iai.run(|| tree.layout(&mut ctx));
} }
fn bench_highlight(iai: &mut Iai) { fn bench_highlight(iai: &mut Iai) {

View File

@ -40,7 +40,7 @@ use unicode_segmentation::UnicodeSegmentation;
use crate::diag::{At, Error, StrResult, Trace, Tracepoint, TypResult}; use crate::diag::{At, Error, StrResult, Trace, Tracepoint, TypResult};
use crate::geom::{Angle, Color, Fractional, Length, Paint, Relative}; use crate::geom::{Angle, Color, Fractional, Length, Paint, Relative};
use crate::image::ImageStore; use crate::image::ImageStore;
use crate::layout::RootNode; use crate::layout::Layout;
use crate::library::{self, DecoLine, TextNode}; use crate::library::{self, DecoLine, TextNode};
use crate::loading::Loader; use crate::loading::Loader;
use crate::parse; use crate::parse;
@ -66,13 +66,6 @@ pub struct Module {
pub template: Template, pub template: Template,
} }
impl Module {
/// Convert this module's template into a layout tree.
pub fn into_root(self) -> RootNode {
self.template.into_root()
}
}
/// Evaluate an expression. /// Evaluate an expression.
pub trait Eval { pub trait Eval {
/// The output of evaluating the expression. /// The output of evaluating the expression.

View File

@ -8,11 +8,13 @@ use std::ops::{Add, AddAssign};
use super::{Property, StyleMap, Styled}; use super::{Property, StyleMap, Styled};
use crate::diag::StrResult; use crate::diag::StrResult;
use crate::geom::SpecAxis; use crate::geom::SpecAxis;
use crate::layout::{Layout, PackedNode, RootNode}; use crate::layout::{Layout, PackedNode};
use crate::library::prelude::*;
use crate::library::{ use crate::library::{
FlowChild, FlowNode, PageNode, ParChild, ParNode, PlaceNode, SpacingKind, TextNode, FlowChild, FlowNode, PageNode, ParChild, ParNode, PlaceNode, SpacingKind, TextNode,
}; };
use crate::util::EcoString; use crate::util::EcoString;
use crate::Context;
/// Composable representation of styled content. /// Composable representation of styled content.
/// ///
@ -89,6 +91,18 @@ impl Template {
Self::Block(node.pack()) Self::Block(node.pack())
} }
/// Layout this template into a collection of pages.
pub fn layout(&self, ctx: &mut Context) -> Vec<Arc<Frame>> {
let (mut ctx, styles) = LayoutContext::new(ctx);
let mut packer = Packer::new(true);
packer.walk(self.clone(), StyleMap::new());
packer
.into_root()
.iter()
.flat_map(|styled| styled.item.layout(&mut ctx, styled.map.chain(&styles)))
.collect()
}
/// Style this template with a single property. /// Style this template with a single property.
pub fn styled<P: Property>(mut self, key: P, value: P::Value) -> Self { pub fn styled<P: Property>(mut self, key: P, value: P::Value) -> Self {
if let Self::Styled(_, map) = &mut self { if let Self::Styled(_, map) = &mut self {
@ -123,24 +137,6 @@ impl Template {
Ok(Self::Sequence(vec![self.clone(); count])) Ok(Self::Sequence(vec![self.clone(); count]))
} }
/// Convert to a type-erased block-level node.
pub fn pack(self) -> PackedNode {
if let Template::Block(packed) = self {
packed
} else {
let mut packer = Packer::new(false);
packer.walk(self, StyleMap::new());
packer.into_block()
}
}
/// Lift to a root layout tree node.
pub fn into_root(self) -> RootNode {
let mut packer = Packer::new(true);
packer.walk(self, StyleMap::new());
packer.into_root()
}
} }
impl Default for Template { impl Default for Template {
@ -185,6 +181,27 @@ impl Sum for Template {
} }
} }
impl Layout for Template {
fn layout(
&self,
ctx: &mut LayoutContext,
regions: &Regions,
styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> {
let mut packer = Packer::new(false);
packer.walk(self.clone(), StyleMap::new());
packer.into_block().layout(ctx, regions, styles)
}
fn pack(self) -> PackedNode {
if let Template::Block(packed) = self {
packed
} else {
PackedNode::new(self)
}
}
}
/// Packs a [`Template`] into a flow or root node. /// Packs a [`Template`] into a flow or root node.
struct Packer { struct Packer {
/// Whether this packer produces a root node. /// Whether this packer produces a root node.
@ -215,9 +232,9 @@ impl Packer {
} }
/// Finish up and return the resulting root node. /// Finish up and return the resulting root node.
fn into_root(mut self) -> RootNode { fn into_root(mut self) -> Vec<Styled<PageNode>> {
self.pagebreak(); self.pagebreak();
RootNode(self.pages) self.pages
} }
/// Consider a template with the given styles. /// Consider a template with the given styles.

View File

@ -15,36 +15,14 @@ use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::sync::Arc; use std::sync::Arc;
use crate::eval::{StyleChain, Styled}; use crate::eval::StyleChain;
use crate::font::FontStore; use crate::font::FontStore;
use crate::frame::{Element, Frame, Geometry, Shape, Stroke}; use crate::frame::{Element, Frame, Geometry, Shape, Stroke};
use crate::geom::{Align, Linear, Paint, Point, Sides, Size, Spec}; use crate::geom::{Align, Linear, Paint, Point, Sides, Size, Spec};
use crate::image::ImageStore; use crate::image::ImageStore;
use crate::library::{AlignNode, Move, PadNode, PageNode, TransformNode}; use crate::library::{AlignNode, Move, PadNode, TransformNode};
use crate::Context; use crate::Context;
/// The root layout node, a document consisting of top-level page runs.
#[derive(Hash)]
pub struct RootNode(pub Vec<Styled<PageNode>>);
impl RootNode {
/// Layout the document into a sequence of frames, one per page.
pub fn layout(&self, ctx: &mut Context) -> Vec<Arc<Frame>> {
let (mut ctx, styles) = LayoutContext::new(ctx);
self.0
.iter()
.flat_map(|styled| styled.item.layout(&mut ctx, styled.map.chain(&styles)))
.collect()
}
}
impl Debug for RootNode {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("Root ")?;
f.debug_list().entries(&self.0).finish()
}
}
/// A node that can be layouted into a sequence of regions. /// A node that can be layouted into a sequence of regions.
/// ///
/// Layout return one frame per used region alongside constraints that define /// Layout return one frame per used region alongside constraints that define
@ -63,11 +41,7 @@ pub trait Layout {
where where
Self: Debug + Hash + Sized + Sync + Send + 'static, Self: Debug + Hash + Sized + Sync + Send + 'static,
{ {
PackedNode { PackedNode::new(self)
#[cfg(feature = "layout-cache")]
hash: self.hash64(),
node: Arc::new(self),
}
} }
} }
@ -86,8 +60,8 @@ pub struct LayoutContext<'a> {
} }
impl<'a> LayoutContext<'a> { impl<'a> LayoutContext<'a> {
/// Create a new layout context. /// Create a new layout context and style chain.
fn new(ctx: &'a mut Context) -> (Self, StyleChain<'a>) { pub fn new(ctx: &'a mut Context) -> (Self, StyleChain<'a>) {
let this = Self { let this = Self {
fonts: &mut ctx.fonts, fonts: &mut ctx.fonts,
images: &mut ctx.images, images: &mut ctx.images,
@ -100,27 +74,7 @@ impl<'a> LayoutContext<'a> {
} }
} }
/// A layout node that produces an empty frame. /// A type-erased layouting node with a precomputed hash.
///
/// The packed version of this is returned by [`PackedNode::default`].
#[derive(Debug, Hash)]
pub struct EmptyNode;
impl Layout for EmptyNode {
fn layout(
&self,
_: &mut LayoutContext,
regions: &Regions,
_: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> {
let size = regions.expand.select(regions.current, Size::zero());
let mut cts = Constraints::new(regions.expand);
cts.exact = regions.current.filter(regions.expand);
vec![Frame::new(size).constrain(cts)]
}
}
/// A packed layouting node with a precomputed hash.
#[derive(Clone)] #[derive(Clone)]
pub struct PackedNode { pub struct PackedNode {
/// The type-erased node. /// The type-erased node.
@ -131,6 +85,18 @@ pub struct PackedNode {
} }
impl PackedNode { impl PackedNode {
/// Pack any layoutable node.
pub fn new<T>(node: T) -> Self
where
T: Layout + Debug + Hash + Sync + Send + 'static,
{
Self {
#[cfg(feature = "layout-cache")]
hash: node.hash64(),
node: Arc::new(node),
}
}
/// Check whether the contained node is a specific layout node. /// Check whether the contained node is a specific layout node.
pub fn is<T: 'static>(&self) -> bool { pub fn is<T: 'static>(&self) -> bool {
self.node.as_any().is::<T>() self.node.as_any().is::<T>()
@ -293,7 +259,7 @@ trait Bounds: Layout + Debug + Sync + Send + 'static {
impl<T> Bounds for T impl<T> Bounds for T
where where
T: Layout + Hash + Debug + Sync + Send + 'static, T: Layout + Debug + Hash + Sync + Send + 'static,
{ {
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
@ -309,13 +275,33 @@ where
} }
} }
/// A layout node that produces an empty frame.
///
/// The packed version of this is returned by [`PackedNode::default`].
#[derive(Debug, Hash)]
struct EmptyNode;
impl Layout for EmptyNode {
fn layout(
&self,
_: &mut LayoutContext,
regions: &Regions,
_: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> {
let size = regions.expand.select(regions.current, Size::zero());
let mut cts = Constraints::new(regions.expand);
cts.exact = regions.current.filter(regions.expand);
vec![Frame::new(size).constrain(cts)]
}
}
/// Fix the size of a node. /// Fix the size of a node.
#[derive(Debug, Hash)] #[derive(Debug, Hash)]
pub struct SizedNode { struct SizedNode {
/// How to size the node horizontally and vertically. /// How to size the node horizontally and vertically.
pub sizing: Spec<Option<Linear>>, sizing: Spec<Option<Linear>>,
/// The node to be sized. /// The node to be sized.
pub child: PackedNode, child: PackedNode,
} }
impl Layout for SizedNode { impl Layout for SizedNode {
@ -365,11 +351,11 @@ impl Layout for SizedNode {
/// Fill the frames resulting from a node. /// Fill the frames resulting from a node.
#[derive(Debug, Hash)] #[derive(Debug, Hash)]
pub struct FillNode { struct FillNode {
/// How to fill the frames resulting from the `child`. /// How to fill the frames resulting from the `child`.
pub fill: Paint, fill: Paint,
/// The node to fill. /// The node to fill.
pub child: PackedNode, child: PackedNode,
} }
impl Layout for FillNode { impl Layout for FillNode {
@ -390,11 +376,11 @@ impl Layout for FillNode {
/// Stroke the frames resulting from a node. /// Stroke the frames resulting from a node.
#[derive(Debug, Hash)] #[derive(Debug, Hash)]
pub struct StrokeNode { struct StrokeNode {
/// How to stroke the frames resulting from the `child`. /// How to stroke the frames resulting from the `child`.
pub stroke: Stroke, stroke: Stroke,
/// The node to stroke. /// The node to stroke.
pub child: PackedNode, child: PackedNode,
} }
impl Layout for StrokeNode { impl Layout for StrokeNode {

View File

@ -2,14 +2,13 @@
//! //!
//! # Steps //! # Steps
//! - **Parsing:** The parsing step first transforms a plain string into an //! - **Parsing:** The parsing step first transforms a plain string into an
//! [iterator of tokens][tokens]. This token stream is [parsed] into a //! [iterator of tokens][tokens]. This token stream is [parsed] into a [green
//! [green tree]. The green tree itself is untyped, but a typed layer over it //! tree]. The green tree itself is untyped, but a typed layer over it is
//! is provided in the [AST] module. //! provided in the [AST] module.
//! - **Evaluation:** The next step is to [evaluate] the markup. This produces a //! - **Evaluation:** The next step is to [evaluate] the markup. This produces a
//! [module], consisting of a scope of values that were exported by the code //! [module], consisting of a scope of values that were exported by the code
//! and a [template] with the contents of the module. This node can be //! and a [template], a hierarchical, styled representation with the contents
//! converted into a [layout tree], a hierarchical, styled representation of //! of the module. The nodes of this tree are well structured and
//! the document. The nodes of this tree are well structured and
//! order-independent and thus much better suited for layouting than the raw //! order-independent and thus much better suited for layouting than the raw
//! markup. //! markup.
//! - **Layouting:** Next, the tree is [layouted] into a portable version of the //! - **Layouting:** Next, the tree is [layouted] into a portable version of the
@ -26,8 +25,7 @@
//! [evaluate]: Context::evaluate //! [evaluate]: Context::evaluate
//! [module]: eval::Module //! [module]: eval::Module
//! [template]: eval::Template //! [template]: eval::Template
//! [layout tree]: layout::RootNode //! [layouted]: eval::Template::layout
//! [layouted]: layout::RootNode::layout
//! [cache]: layout::LayoutCache //! [cache]: layout::LayoutCache
//! [PDF]: export::pdf //! [PDF]: export::pdf
@ -127,8 +125,7 @@ impl Context {
/// information. /// information.
pub fn typeset(&mut self, id: SourceId) -> TypResult<Vec<Arc<Frame>>> { pub fn typeset(&mut self, id: SourceId) -> TypResult<Vec<Arc<Frame>>> {
let module = self.evaluate(id)?; let module = self.evaluate(id)?;
let tree = module.into_root(); let frames = module.template.layout(self);
let frames = tree.layout(self);
Ok(frames) Ok(frames)
} }

View File

@ -23,7 +23,7 @@ use typst::Context;
use { use {
filedescriptor::{FileDescriptor, StdioDescriptor::*}, filedescriptor::{FileDescriptor, StdioDescriptor::*},
std::fs::File, std::fs::File,
typst::layout::RootNode, typst::eval::Template,
}; };
const TYP_DIR: &str = "./typ"; const TYP_DIR: &str = "./typ";
@ -266,7 +266,7 @@ fn test_part(
let id = ctx.sources.provide(src_path, src); let id = ctx.sources.provide(src_path, src);
let source = ctx.sources.get(id); let source = ctx.sources.get(id);
if debug { if debug {
println!("Syntax: {:#?}", source.root()) println!("Syntax Tree: {:#?}", source.root())
} }
let (local_compare_ref, mut ref_errors) = parse_metadata(&source); let (local_compare_ref, mut ref_errors) = parse_metadata(&source);
@ -276,15 +276,14 @@ fn test_part(
let (frames, mut errors) = match ctx.evaluate(id) { let (frames, mut errors) = match ctx.evaluate(id) {
Ok(module) => { Ok(module) => {
let tree = module.into_root();
if debug { if debug {
println!("Layout: {tree:#?}"); println!("Template: {:#?}", module.template);
} }
let mut frames = tree.layout(ctx); let mut frames = module.template.layout(ctx);
#[cfg(feature = "layout-cache")] #[cfg(feature = "layout-cache")]
(ok &= test_incremental(ctx, i, &tree, &frames)); (ok &= test_incremental(ctx, i, &module.template, &frames));
if !compare_ref { if !compare_ref {
frames.clear(); frames.clear();
@ -484,7 +483,7 @@ fn test_reparse(src: &str, i: usize, rng: &mut LinearShift) -> bool {
fn test_incremental( fn test_incremental(
ctx: &mut Context, ctx: &mut Context,
i: usize, i: usize,
tree: &RootNode, template: &Template,
frames: &[Arc<Frame>], frames: &[Arc<Frame>],
) -> bool { ) -> bool {
let mut ok = true; let mut ok = true;
@ -499,7 +498,7 @@ fn test_incremental(
ctx.layout_cache.turnaround(); ctx.layout_cache.turnaround();
let cached = silenced(|| tree.layout(ctx)); let cached = silenced(|| template.layout(ctx));
let total = reference.levels() - 1; let total = reference.levels() - 1;
let misses = ctx let misses = ctx
.layout_cache .layout_cache