summaryrefslogtreecommitdiff
path: root/src/model
diff options
context:
space:
mode:
Diffstat (limited to 'src/model')
-rw-r--r--src/model/content.rs38
-rw-r--r--src/model/layout.rs50
-rw-r--r--src/model/locate.rs397
-rw-r--r--src/model/mod.rs2
-rw-r--r--src/model/styles.rs2
5 files changed, 12 insertions, 477 deletions
diff --git a/src/model/content.rs b/src/model/content.rs
index 264785ec..efbaed0e 100644
--- a/src/model/content.rs
+++ b/src/model/content.rs
@@ -7,8 +7,8 @@ use std::ops::{Add, AddAssign};
use typed_arena::Arena;
use super::{
- Barrier, CollapsingBuilder, Interruption, Key, Layout, LayoutNode, LocateNode,
- Property, Show, ShowNode, StyleEntry, StyleMap, StyleVecBuilder, Target,
+ Barrier, CollapsingBuilder, Interruption, Key, Layout, LayoutNode, Property, Show,
+ ShowNode, StyleEntry, StyleMap, StyleVecBuilder, Target,
};
use crate::diag::StrResult;
use crate::library::layout::{FlowChild, FlowNode, PageNode, PlaceNode, Spacing};
@@ -23,30 +23,6 @@ use crate::util::EcoString;
///
/// Relayouts until all pinned locations are converged.
pub fn layout(ctx: &mut Context, content: &Content) -> TypResult<Vec<Frame>> {
- let mut pass = 0;
- let mut frames;
-
- loop {
- let prev = ctx.pins.clone();
- let result = layout_once(ctx, content);
- ctx.pins.reset();
- frames = result?;
- pass += 1;
-
- ctx.pins.locate(&frames);
-
- // Quit if we're done or if we've had five passes.
- let unresolved = ctx.pins.unresolved(&prev);
- if unresolved == 0 || pass >= 5 {
- break;
- }
- }
-
- Ok(frames)
-}
-
-/// Layout content into a collection of pages once.
-fn layout_once(ctx: &mut Context, content: &Content) -> TypResult<Vec<Frame>> {
let copy = ctx.config.styles.clone();
let styles = StyleChain::with_root(&copy);
let scratch = Scratch::default();
@@ -114,8 +90,6 @@ pub enum Content {
/// A node that can be realized with styles, optionally with attached
/// properties.
Show(ShowNode, Option<Dict>),
- /// A node that can be realized with its location on the page.
- Locate(LocateNode),
/// A pin identified by index.
Pin(usize),
/// Content with attached styles.
@@ -307,7 +281,6 @@ impl Debug for Content {
Self::Pagebreak { weak } => write!(f, "Pagebreak({weak})"),
Self::Page(page) => page.fmt(f),
Self::Show(node, _) => node.fmt(f),
- Self::Locate(node) => node.fmt(f),
Self::Pin(idx) => write!(f, "Pin({idx})"),
Self::Styled(styled) => {
let (sub, map) = styled.as_ref();
@@ -425,7 +398,6 @@ impl<'a, 'ctx> Builder<'a, 'ctx> {
}
Content::Show(node, _) => return self.show(node, styles),
- Content::Locate(node) => return self.locate(node, styles),
Content::Styled(styled) => return self.styled(styled, styles),
Content::Sequence(seq) => return self.sequence(seq, styles),
@@ -474,12 +446,6 @@ impl<'a, 'ctx> Builder<'a, 'ctx> {
Ok(())
}
- fn locate(&mut self, node: &LocateNode, styles: StyleChain<'a>) -> TypResult<()> {
- let realized = node.realize(self.ctx)?;
- let stored = self.scratch.templates.alloc(realized);
- self.accept(stored, styles)
- }
-
fn styled(
&mut self,
(content, map): &'a (Content, StyleMap),
diff --git a/src/model/layout.rs b/src/model/layout.rs
index ffe41725..d712a178 100644
--- a/src/model/layout.rs
+++ b/src/model/layout.rs
@@ -5,7 +5,7 @@ use std::fmt::{self, Debug, Formatter, Write};
use std::hash::Hash;
use std::sync::Arc;
-use super::{Barrier, NodeId, PinConstraint, Resolve, StyleChain, StyleEntry};
+use super::{Barrier, NodeId, Resolve, StyleChain, StyleEntry};
use crate::diag::TypResult;
use crate::eval::{RawAlign, RawLength};
use crate::frame::{Element, Frame};
@@ -131,8 +131,6 @@ impl Regions {
}
}
-impl_track_hash!(Regions);
-
/// A type-erased layouting node with a precomputed hash.
#[derive(Clone, Hash)]
pub struct LayoutNode(Arc<Prehashed<dyn Bounds>>);
@@ -222,43 +220,17 @@ impl Layout for LayoutNode {
regions: &Regions,
styles: StyleChain,
) -> TypResult<Vec<Frame>> {
- let prev = ctx.pins.dirty.replace(false);
-
- let (result, at, fresh, dirty) = crate::memo::memoized(
- (self, &mut *ctx, regions, styles),
- |(node, ctx, regions, styles)| {
- let hash = fxhash::hash64(&ctx.pins);
- let at = ctx.pins.cursor();
-
- let barrier = StyleEntry::Barrier(Barrier::new(node.id()));
- let styles = barrier.chain(&styles);
-
- let mut result = node.0.layout(ctx, regions, styles);
- if let Some(role) = styles.role() {
- result = result.map(|mut frames| {
- for frame in frames.iter_mut() {
- frame.apply_role(role);
- }
- frames
- });
- }
-
- let fresh = ctx.pins.from(at);
- let dirty = ctx.pins.dirty.get();
- let constraint = PinConstraint(dirty.then(|| hash));
- ((result, at, fresh, dirty), ((), constraint, (), ()))
- },
- );
-
- ctx.pins.dirty.replace(prev || dirty);
-
- // Replay the side effect in case of caching. This should currently be
- // more or less the only relevant side effect on the context.
- if dirty {
- ctx.pins.replay(at, fresh);
+ let barrier = StyleEntry::Barrier(Barrier::new(self.id()));
+ let styles = barrier.chain(&styles);
+
+ let mut frames = self.0.layout(ctx, regions, styles)?;
+ if let Some(role) = styles.role() {
+ for frame in &mut frames {
+ frame.apply_role(role);
+ }
}
- result
+ Ok(frames)
}
fn pack(self) -> LayoutNode {
@@ -266,8 +238,6 @@ impl Layout for LayoutNode {
}
}
-impl_track_hash!(LayoutNode);
-
impl Default for LayoutNode {
fn default() -> Self {
EmptyNode.pack()
diff --git a/src/model/locate.rs b/src/model/locate.rs
deleted file mode 100644
index d5c71423..00000000
--- a/src/model/locate.rs
+++ /dev/null
@@ -1,397 +0,0 @@
-use std::cell::Cell;
-use std::fmt::{self, Debug, Formatter};
-use std::hash::{Hash, Hasher};
-use std::num::NonZeroUsize;
-use std::sync::Arc;
-
-use super::Content;
-use crate::diag::TypResult;
-use crate::eval::{Args, Array, Dict, Func, Value};
-use crate::frame::{Element, Frame, Location};
-use crate::geom::{Point, Transform};
-use crate::memo::Track;
-use crate::syntax::Spanned;
-use crate::util::EcoString;
-use crate::Context;
-
-/// A group of locatable elements.
-#[derive(Clone, Eq, PartialEq, Hash)]
-pub struct Group(EcoString);
-
-impl Group {
- /// Create a group of elements that is identified by a string key.
- pub fn new(key: EcoString) -> Self {
- Self(key)
- }
-
- /// Add an entry to the group.
- pub fn entry(&self, recipe: Spanned<Func>, value: Option<Value>) -> LocateNode {
- LocateNode::entry(self.clone(), recipe, value)
- }
-
- /// Do something with all entries of a group.
- pub fn all(&self, recipe: Spanned<Func>) -> LocateNode {
- LocateNode::all(self.clone(), recipe)
- }
-}
-
-impl Debug for Group {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "group({:?})", self.0)
- }
-}
-
-/// A node that can be realized with pinned document locations.
-#[derive(Debug, Clone, PartialEq, Hash)]
-pub struct LocateNode(Arc<Repr>);
-
-impl LocateNode {
- /// Create a new locatable single node.
- pub fn single(recipe: Spanned<Func>) -> Self {
- Self(Arc::new(Repr::Single(SingleNode(recipe))))
- }
-
- /// Create a new locatable group entry node.
- pub fn entry(group: Group, recipe: Spanned<Func>, value: Option<Value>) -> Self {
- Self(Arc::new(Repr::Entry(EntryNode { group, recipe, value })))
- }
-
- /// Create a new node with access to all of a group's members.
- pub fn all(group: Group, recipe: Spanned<Func>) -> Self {
- Self(Arc::new(Repr::All(AllNode { group, recipe })))
- }
-
- /// Realize the node.
- pub fn realize(&self, ctx: &mut Context) -> TypResult<Content> {
- match self.0.as_ref() {
- Repr::Single(single) => single.realize(ctx),
- Repr::Entry(entry) => entry.realize(ctx),
- Repr::All(all) => all.realize(ctx),
- }
- }
-}
-
-/// The different kinds of locate nodes.
-#[derive(Debug, Clone, PartialEq, Hash)]
-enum Repr {
- /// A single `locate(me => ...)`.
- Single(SingleNode),
- /// A locatable group entry.
- Entry(EntryNode),
- /// A recipe for all entries of a group.
- All(AllNode),
-}
-
-/// An ungrouped locatable node.
-#[derive(Debug, Clone, PartialEq, Hash)]
-struct SingleNode(Spanned<Func>);
-
-impl SingleNode {
- fn realize(&self, ctx: &mut Context) -> TypResult<Content> {
- let idx = ctx.pins.cursor;
- let pin = ctx.pins.get_or_create(None, None);
- let dict = pin.encode(None);
- let args = Args::new(self.0.span, [Value::Dict(dict)]);
- Ok(Content::Pin(idx) + self.0.v.call_detached(ctx, args)?.display())
- }
-}
-
-/// A locatable grouped node which can interact with its peers' details.
-#[derive(Debug, Clone, PartialEq, Hash)]
-struct EntryNode {
- /// Which group the node belongs to.
- group: Group,
- /// The recipe to execute.
- recipe: Spanned<Func>,
- /// An arbitrary attached value.
- value: Option<Value>,
-}
-
-impl EntryNode {
- fn realize(&self, ctx: &mut Context) -> TypResult<Content> {
- let idx = ctx.pins.cursor;
- let pin = ctx.pins.get_or_create(Some(self.group.clone()), self.value.clone());
-
- // Determine the index among the peers.
- let index = ctx
- .pins
- .iter()
- .enumerate()
- .filter(|&(k, other)| {
- other.is_in(&self.group)
- && if k < idx {
- other.flow <= pin.flow
- } else {
- other.flow < pin.flow
- }
- })
- .count();
-
- // Prepare first argument.
- let dict = pin.encode(Some(index));
- let mut args = Args::new(self.recipe.span, [Value::Dict(dict)]);
-
- // Collect all group members if second argument is requested.
- if self.recipe.v.argc() == Some(2) {
- let all = ctx.pins.encode_group(&self.group);
- args.push(self.recipe.span, Value::Array(all))
- }
-
- Ok(Content::Pin(idx) + self.recipe.v.call_detached(ctx, args)?.display())
- }
-}
-
-/// A node with access to a group's members.
-#[derive(Debug, Clone, PartialEq, Hash)]
-struct AllNode {
- /// Which group the node has access to.
- group: Group,
- /// The recipe to execute.
- recipe: Spanned<Func>,
-}
-
-impl AllNode {
- fn realize(&self, ctx: &mut Context) -> TypResult<Content> {
- let all = ctx.pins.encode_group(&self.group);
- let args = Args::new(self.recipe.span, [Value::Array(all)]);
- Ok(self.recipe.v.call_detached(ctx, args)?.display())
- }
-}
-
-/// Manages document pins.
-#[derive(Debug, Clone)]
-pub struct PinBoard {
- /// All currently active pins.
- list: Vec<Pin>,
- /// The index of the next pin, in order.
- cursor: usize,
- /// If larger than zero, the board is frozen and the cursor will not be
- /// advanced. This is used to disable pinning during measure-only layouting.
- frozen: usize,
- /// Whether the board was accessed.
- pub(super) dirty: Cell<bool>,
-}
-
-impl PinBoard {
- /// Create an empty pin board.
- pub fn new() -> Self {
- Self {
- list: vec![],
- cursor: 0,
- frozen: 0,
- dirty: Cell::new(false),
- }
- }
-}
-
-/// Internal methods for implementation of locatable nodes.
-impl PinBoard {
- /// Access or create the next pin.
- fn get_or_create(&mut self, group: Option<Group>, value: Option<Value>) -> Pin {
- self.dirty.set(true);
- if self.frozen() {
- return Pin::default();
- }
-
- let cursor = self.cursor;
- self.cursor += 1;
- if self.cursor >= self.list.len() {
- self.list.resize(self.cursor, Pin::default());
- }
-
- let pin = &mut self.list[cursor];
- pin.group = group;
- pin.value = value;
- pin.clone()
- }
-
- /// Encode a group into a user-facing array.
- fn encode_group(&self, group: &Group) -> Array {
- self.dirty.set(true);
- let mut all: Vec<_> = self.iter().filter(|pin| pin.is_in(group)).collect();
- all.sort_by_key(|pin| pin.flow);
- all.iter()
- .enumerate()
- .map(|(index, member)| Value::Dict(member.encode(Some(index))))
- .collect()
- }
-
- /// Iterate over all pins on the board.
- fn iter(&self) -> std::slice::Iter<Pin> {
- self.dirty.set(true);
- self.list.iter()
- }
-}
-
-/// Caching related methods.
-impl PinBoard {
- /// The current cursor.
- pub fn cursor(&self) -> usize {
- self.cursor
- }
-
- /// All pins from `prev` to the current cursor.
- pub fn from(&self, prev: usize) -> Vec<Pin> {
- self.list[prev .. self.cursor].to_vec()
- }
-
- /// Add the given pins at the given location and set the cursor behind them.
- pub fn replay(&mut self, at: usize, pins: Vec<Pin>) {
- if !self.frozen() {
- self.cursor = at + pins.len();
- let end = self.cursor.min(self.list.len());
- self.list.splice(at .. end, pins);
- }
- }
-}
-
-/// Control methods that are called during layout.
-impl PinBoard {
- /// Freeze the board to prevent modifications.
- pub fn freeze(&mut self) {
- self.frozen += 1;
- }
-
- /// Freeze the board to prevent modifications.
- pub fn unfreeze(&mut self) {
- self.frozen -= 1;
- }
-
- /// Whether the board is currently frozen.
- pub fn frozen(&self) -> bool {
- self.frozen > 0
- }
-}
-
-/// Methods that are called in between layout passes.
-impl PinBoard {
- /// Reset the cursor and remove all unused pins.
- pub fn reset(&mut self) {
- self.list.truncate(self.cursor);
- self.cursor = 0;
- self.dirty.set(false);
- }
-
- /// Locate all pins in the frames.
- pub fn locate(&mut self, frames: &[Frame]) {
- let mut flow = 0;
- for (i, frame) in frames.iter().enumerate() {
- locate_in_frame(
- &mut self.list,
- &mut flow,
- NonZeroUsize::new(1 + i).unwrap(),
- frame,
- Transform::identity(),
- );
- }
- }
-
- /// How many pins are unresolved in comparison to an earlier snapshot.
- pub fn unresolved(&self, prev: &Self) -> usize {
- self.list.len() - self.list.iter().zip(&prev.list).filter(|(a, b)| a == b).count()
- }
-}
-
-/// Locate all pins in a frame.
-fn locate_in_frame(
- pins: &mut [Pin],
- flow: &mut usize,
- page: NonZeroUsize,
- frame: &Frame,
- ts: Transform,
-) {
- for &(pos, ref element) in frame.elements() {
- match element {
- Element::Group(group) => {
- let ts = ts
- .pre_concat(Transform::translate(pos.x, pos.y))
- .pre_concat(group.transform);
- locate_in_frame(pins, flow, page, &group.frame, ts);
- }
-
- Element::Pin(idx) => {
- let pin = &mut pins[*idx];
- pin.loc.page = page;
- pin.loc.pos = pos.transform(ts);
- pin.flow = *flow;
- *flow += 1;
- }
-
- _ => {}
- }
- }
-}
-
-impl Hash for PinBoard {
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.list.hash(state);
- self.cursor.hash(state);
- self.frozen.hash(state);
- }
-}
-
-/// Describes pin usage.
-#[derive(Debug, Copy, Clone)]
-pub struct PinConstraint(pub Option<u64>);
-
-impl Track for PinBoard {
- type Constraint = PinConstraint;
-
- fn key<H: Hasher>(&self, _: &mut H) {}
-
- fn matches(&self, constraint: &Self::Constraint) -> bool {
- match constraint.0 {
- Some(hash) => fxhash::hash64(self) == hash,
- None => true,
- }
- }
-}
-
-/// A document pin.
-#[derive(Debug, Clone, PartialEq, Hash)]
-pub struct Pin {
- /// The physical location of the pin in the document.
- loc: Location,
- /// The flow index.
- flow: usize,
- /// The group the pin belongs to, if any.
- group: Option<Group>,
- /// An arbitrary attached value.
- value: Option<Value>,
-}
-
-impl Pin {
- /// Whether the pin is part of the given group.
- fn is_in(&self, group: &Group) -> bool {
- self.group.as_ref() == Some(group)
- }
-
- /// Encode into a user-facing dictionary.
- fn encode(&self, index: Option<usize>) -> Dict {
- let mut dict = self.loc.encode();
-
- if let Some(value) = &self.value {
- dict.insert("value".into(), value.clone());
- }
-
- if let Some(index) = index {
- dict.insert("index".into(), Value::Int(index as i64));
- }
-
- dict
- }
-}
-
-impl Default for Pin {
- fn default() -> Self {
- Self {
- loc: Location {
- page: NonZeroUsize::new(1).unwrap(),
- pos: Point::zero(),
- },
- flow: 0,
- group: None,
- value: None,
- }
- }
-}
diff --git a/src/model/mod.rs b/src/model/mod.rs
index 379b633f..5c8b82c0 100644
--- a/src/model/mod.rs
+++ b/src/model/mod.rs
@@ -5,7 +5,6 @@ mod styles;
mod collapse;
mod content;
mod layout;
-mod locate;
mod property;
mod recipe;
mod show;
@@ -13,7 +12,6 @@ mod show;
pub use collapse::*;
pub use content::*;
pub use layout::*;
-pub use locate::*;
pub use property::*;
pub use recipe::*;
pub use show::*;
diff --git a/src/model/styles.rs b/src/model/styles.rs
index 03cdcaac..eab33402 100644
--- a/src/model/styles.rs
+++ b/src/model/styles.rs
@@ -413,8 +413,6 @@ impl PartialEq for StyleChain<'_> {
}
}
-impl_track_hash!(StyleChain<'_>);
-
/// An iterator over the values in a style chain.
struct Values<'a, K> {
entries: Entries<'a>,