mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Remove root node
This commit is contained in:
parent
c5e05ac0ea
commit
20a1fd8bc7
@ -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) {
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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 {
|
||||||
|
17
src/lib.rs
17
src/lib.rs
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user