summaryrefslogtreecommitdiff
path: root/library/src/layout/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'library/src/layout/mod.rs')
-rw-r--r--library/src/layout/mod.rs165
1 files changed, 85 insertions, 80 deletions
diff --git a/library/src/layout/mod.rs b/library/src/layout/mod.rs
index 9ee77a61..afdfd795 100644
--- a/library/src/layout/mod.rs
+++ b/library/src/layout/mod.rs
@@ -48,8 +48,8 @@ use std::mem;
use typed_arena::Arena;
use typst::diag::SourceResult;
use typst::model::{
- applicable, capability, realize, Content, Node, SequenceNode, Style, StyleChain,
- StyleVecBuilder, StyledNode,
+ applicable, realize, Content, Node, SequenceNode, Style, StyleChain, StyleVecBuilder,
+ StyledNode,
};
use crate::math::{FormulaNode, LayoutMath};
@@ -60,7 +60,6 @@ use crate::text::{LinebreakNode, SmartQuoteNode, SpaceNode, TextNode};
use crate::visualize::{CircleNode, EllipseNode, ImageNode, RectNode, SquareNode};
/// Root-level layout.
-#[capability]
pub trait LayoutRoot {
/// Layout into one frame per page.
fn layout_root(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Document>;
@@ -96,7 +95,6 @@ impl LayoutRoot for Content {
}
/// Layout into regions.
-#[capability]
pub trait Layout {
/// Layout into one frame per region.
fn layout(
@@ -160,7 +158,7 @@ fn realize_root<'a>(
builder.accept(content, styles)?;
builder.interrupt_page(Some(styles))?;
let (pages, shared) = builder.doc.unwrap().pages.finish();
- Ok((DocumentNode(pages).pack(), shared))
+ Ok((DocumentNode::new(pages.to_vec()).pack(), shared))
}
/// Realize into a node that is capable of block-level layout.
@@ -185,7 +183,7 @@ fn realize_block<'a>(
builder.accept(content, styles)?;
builder.interrupt_par()?;
let (children, shared) = builder.flow.0.finish();
- Ok((FlowNode(children).pack(), shared))
+ Ok((FlowNode::new(children.to_vec()).pack(), shared))
}
/// Builds a document or a flow node from content.
@@ -211,6 +209,7 @@ struct Scratch<'a> {
styles: Arena<StyleChain<'a>>,
/// An arena where intermediate content resulting from show rules is stored.
content: Arena<Content>,
+ maps: Arena<StyleMap>,
}
impl<'a, 'v, 't> Builder<'a, 'v, 't> {
@@ -231,10 +230,8 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
styles: StyleChain<'a>,
) -> SourceResult<()> {
if content.has::<dyn LayoutMath>() && !content.is::<FormulaNode>() {
- content = self
- .scratch
- .content
- .alloc(FormulaNode { body: content.clone(), block: false }.pack());
+ content =
+ self.scratch.content.alloc(FormulaNode::new(content.clone()).pack());
}
// Prepare only if this is the first application for this node.
@@ -252,8 +249,9 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
}
if let Some(seq) = content.to::<SequenceNode>() {
- for sub in &seq.0 {
- self.accept(sub, styles)?;
+ for sub in seq.children() {
+ let stored = self.scratch.content.alloc(sub);
+ self.accept(stored, styles)?;
}
return Ok(());
}
@@ -269,8 +267,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
self.interrupt_list()?;
- if content.is::<ListItem>() {
- self.list.accept(content, styles);
+ if self.list.accept(content, styles) {
return Ok(());
}
@@ -286,7 +283,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
let keep = content
.to::<PagebreakNode>()
- .map_or(false, |pagebreak| !pagebreak.weak);
+ .map_or(false, |pagebreak| !pagebreak.weak());
self.interrupt_page(keep.then(|| styles))?;
@@ -308,11 +305,13 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
styled: &'a StyledNode,
styles: StyleChain<'a>,
) -> SourceResult<()> {
+ let map = self.scratch.maps.alloc(styled.map());
let stored = self.scratch.styles.alloc(styles);
- let styles = stored.chain(&styled.map);
- self.interrupt_style(&styled.map, None)?;
- self.accept(&styled.sub, styles)?;
- self.interrupt_style(&styled.map, Some(styles))?;
+ let content = self.scratch.content.alloc(styled.sub());
+ let styles = stored.chain(map);
+ self.interrupt_style(&map, None)?;
+ self.accept(content, styles)?;
+ self.interrupt_style(map, Some(styles))?;
Ok(())
}
@@ -381,7 +380,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
let (flow, shared) = mem::take(&mut self.flow).0.finish();
let styles =
if shared == StyleChain::default() { styles.unwrap() } else { shared };
- let page = PageNode(FlowNode(flow).pack()).pack();
+ let page = PageNode::new(FlowNode::new(flow.to_vec()).pack()).pack();
let stored = self.scratch.content.alloc(page);
self.accept(stored, styles)?;
}
@@ -392,7 +391,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
/// Accepts pagebreaks and pages.
struct DocBuilder<'a> {
/// The page runs built so far.
- pages: StyleVecBuilder<'a, PageNode>,
+ pages: StyleVecBuilder<'a, Content>,
/// Whether to keep a following page even if it is empty.
keep_next: bool,
}
@@ -400,12 +399,12 @@ struct DocBuilder<'a> {
impl<'a> DocBuilder<'a> {
fn accept(&mut self, content: &Content, styles: StyleChain<'a>) -> bool {
if let Some(pagebreak) = content.to::<PagebreakNode>() {
- self.keep_next = !pagebreak.weak;
+ self.keep_next = !pagebreak.weak();
return true;
}
- if let Some(page) = content.to::<PageNode>() {
- self.pages.push(page.clone(), styles);
+ if content.is::<PageNode>() {
+ self.pages.push(content.clone(), styles);
self.keep_next = false;
return true;
}
@@ -441,11 +440,11 @@ impl<'a> FlowBuilder<'a> {
if content.has::<dyn Layout>() || content.is::<ParNode>() {
let is_tight_list = if let Some(node) = content.to::<ListNode>() {
- node.tight
+ node.tight()
} else if let Some(node) = content.to::<EnumNode>() {
- node.tight
+ node.tight()
} else if let Some(node) = content.to::<TermsNode>() {
- node.tight
+ node.tight()
} else {
false
};
@@ -458,9 +457,9 @@ impl<'a> FlowBuilder<'a> {
let above = styles.get(BlockNode::ABOVE);
let below = styles.get(BlockNode::BELOW);
- self.0.push(above.pack(), styles);
+ self.0.push(above.clone().pack(), styles);
self.0.push(content.clone(), styles);
- self.0.push(below.pack(), styles);
+ self.0.push(below.clone().pack(), styles);
return true;
}
@@ -479,7 +478,7 @@ impl<'a> ParBuilder<'a> {
|| content.is::<HNode>()
|| content.is::<LinebreakNode>()
|| content.is::<SmartQuoteNode>()
- || content.to::<FormulaNode>().map_or(false, |node| !node.block)
+ || content.to::<FormulaNode>().map_or(false, |node| !node.block())
|| content.is::<BoxNode>()
{
self.0.push(content.clone(), styles);
@@ -491,14 +490,14 @@ impl<'a> ParBuilder<'a> {
fn finish(self) -> (Content, StyleChain<'a>) {
let (children, shared) = self.0.finish();
- (ParNode(children).pack(), shared)
+ (ParNode::new(children.to_vec()).pack(), shared)
}
}
/// Accepts list / enum items, spaces, paragraph breaks.
struct ListBuilder<'a> {
/// The list items collected so far.
- items: StyleVecBuilder<'a, ListItem>,
+ items: StyleVecBuilder<'a, Content>,
/// Whether the list contains no paragraph breaks.
tight: bool,
/// Trailing content for which it is unclear whether it is part of the list.
@@ -514,14 +513,18 @@ impl<'a> ListBuilder<'a> {
return true;
}
- if let Some(item) = content.to::<ListItem>() {
- if self.items.items().next().map_or(true, |first| {
- std::mem::discriminant(item) == std::mem::discriminant(first)
- }) {
- self.items.push(item.clone(), styles);
- self.tight &= self.staged.drain(..).all(|(t, _)| !t.is::<ParbreakNode>());
- return true;
- }
+ if (content.is::<ListItem>()
+ || content.is::<EnumItem>()
+ || content.is::<TermItem>())
+ && self
+ .items
+ .items()
+ .next()
+ .map_or(true, |first| first.id() == content.id())
+ {
+ self.items.push(content.clone(), styles);
+ self.tight &= self.staged.drain(..).all(|(t, _)| !t.is::<ParbreakNode>());
+ return true;
}
false
@@ -530,31 +533,48 @@ impl<'a> ListBuilder<'a> {
fn finish(self) -> (Content, StyleChain<'a>) {
let (items, shared) = self.items.finish();
let item = items.items().next().unwrap();
- let output = match item {
- ListItem::List(_) => ListNode {
- tight: self.tight,
- items: items.map(|item| match item {
- ListItem::List(item) => item.clone(),
- _ => panic!("wrong list item"),
- }),
- }
- .pack(),
- ListItem::Enum(..) => EnumNode {
- tight: self.tight,
- items: items.map(|item| match item {
- ListItem::Enum(number, body) => (*number, body.clone()),
- _ => panic!("wrong list item"),
- }),
- }
- .pack(),
- ListItem::Term(_) => TermsNode {
- tight: self.tight,
- items: items.map(|item| match item {
- ListItem::Term(item) => item.clone(),
- _ => panic!("wrong list item"),
- }),
- }
- .pack(),
+ let output = if item.is::<ListItem>() {
+ ListNode::new(
+ items
+ .iter()
+ .map(|(item, map)| {
+ let item = item.to::<ListItem>().unwrap();
+ ListItem::new(item.body().styled_with_map(map.clone()))
+ })
+ .collect::<Vec<_>>(),
+ )
+ .with_tight(self.tight)
+ .pack()
+ } else if item.is::<EnumItem>() {
+ EnumNode::new(
+ items
+ .iter()
+ .map(|(item, map)| {
+ let item = item.to::<EnumItem>().unwrap();
+ EnumItem::new(item.body().styled_with_map(map.clone()))
+ .with_number(item.number())
+ })
+ .collect::<Vec<_>>(),
+ )
+ .with_tight(self.tight)
+ .pack()
+ } else if item.is::<TermItem>() {
+ TermsNode::new(
+ items
+ .iter()
+ .map(|(item, map)| {
+ let item = item.to::<TermItem>().unwrap();
+ TermItem::new(
+ item.term().styled_with_map(map.clone()),
+ item.description().styled_with_map(map.clone()),
+ )
+ })
+ .collect::<Vec<_>>(),
+ )
+ .with_tight(self.tight)
+ .pack()
+ } else {
+ unreachable!()
};
(output, shared)
}
@@ -569,18 +589,3 @@ impl Default for ListBuilder<'_> {
}
}
}
-
-/// An item in a list.
-#[capable]
-#[derive(Debug, Clone, Hash)]
-pub enum ListItem {
- /// An item of a bullet list.
- List(Content),
- /// An item of a numbered list.
- Enum(Option<NonZeroUsize>, Content),
- /// An item of a term list.
- Term(TermItem),
-}
-
-#[node]
-impl ListItem {}