Reorganize content type

This commit is contained in:
Laurenz 2022-11-22 20:12:37 +01:00
parent b476de87b7
commit 5992f11b4c
15 changed files with 299 additions and 298 deletions

View File

@ -4,7 +4,7 @@ use typst::model::{capability, Content, StyleChain, StyleVec, StyleVecBuilder};
/// How a node interacts with other nodes.
#[capability]
pub trait Behave: 'static + Send + Sync {
pub trait Behave {
/// The node's interaction behaviour.
fn behaviour(&self) -> Behaviour;
@ -62,13 +62,13 @@ impl<'a> BehavedBuilder<'a> {
/// Push an item into the sequence.
pub fn push(&mut self, item: Content, styles: StyleChain<'a>) {
let interaction = item
.to::<dyn Behave>()
.with::<dyn Behave>()
.map_or(Behaviour::Supportive, Behave::behaviour);
match interaction {
Behaviour::Weak(level) => {
if matches!(self.last, Behaviour::Weak(_)) {
let item = item.to::<dyn Behave>().unwrap();
let item = item.with::<dyn Behave>().unwrap();
let i = self.staged.iter().position(|prev| {
let Behaviour::Weak(prev_level) = prev.1 else { return false };
level < prev_level

View File

@ -25,7 +25,7 @@ impl LayoutBlock for FlowNode {
for (child, map) in self.0.iter() {
let styles = map.chain(&styles);
if let Some(&node) = child.downcast::<VNode>() {
if let Some(&node) = child.to::<VNode>() {
layouter.layout_spacing(node.amount, styles);
} else if child.has::<dyn LayoutBlock>() {
layouter.layout_block(world, child, styles)?;
@ -134,7 +134,7 @@ impl FlowLayouter {
// Placed nodes that are out of flow produce placed items which aren't
// aligned later.
if let Some(placed) = block.downcast::<PlaceNode>() {
if let Some(placed) = block.to::<PlaceNode>() {
if placed.out_of_flow() {
let frame = block.layout_block(world, &self.regions, styles)?.remove(0);
self.items.push(FlowItem::Placed(frame));
@ -149,7 +149,7 @@ impl FlowLayouter {
styles.get(ParNode::ALIGN),
// Vertical align node alignment is respected by the flow.
block
.downcast::<AlignNode>()
.to::<AlignNode>()
.and_then(|aligned| aligned.aligns.y)
.map(|align| align.resolve(styles))
.unwrap_or(Align::Top),

View File

@ -32,8 +32,7 @@ use typst::diag::SourceResult;
use typst::frame::Frame;
use typst::geom::*;
use typst::model::{
capability, Content, Node, SequenceNode, Style, StyleChain, StyleVecBuilder,
StyledNode,
capability, Content, SequenceNode, Style, StyleChain, StyleVecBuilder, StyledNode,
};
use typst::World;
@ -48,7 +47,7 @@ use crate::text::{
/// Root-level layout.
#[capability]
pub trait LayoutRoot: 'static + Sync + Send {
pub trait LayoutRoot {
/// Layout into one frame per page.
fn layout_root(&self, world: Tracked<dyn World>) -> SourceResult<Vec<Frame>>;
}
@ -69,7 +68,7 @@ impl LayoutRoot for Content {
/// Block-level layout.
#[capability]
pub trait LayoutBlock: 'static + Sync + Send {
pub trait LayoutBlock {
/// Layout into one frame per region.
fn layout_block(
&self,
@ -88,7 +87,7 @@ impl LayoutBlock for Content {
styles: StyleChain,
) -> SourceResult<Vec<Frame>> {
if !styles.applicable(self) {
if let Some(node) = self.to::<dyn LayoutBlock>() {
if let Some(node) = self.with::<dyn LayoutBlock>() {
let barrier = Style::Barrier(self.id());
let styles = barrier.chain(&styles);
return node.layout_block(world, regions, styles);
@ -105,7 +104,7 @@ impl LayoutBlock for Content {
/// Inline-level layout.
#[capability]
pub trait LayoutInline: 'static + Sync + Send {
pub trait LayoutInline {
/// Layout into a single frame.
fn layout_inline(
&self,
@ -127,13 +126,13 @@ impl LayoutInline for Content {
assert!(regions.last.is_none());
if !styles.applicable(self) {
if let Some(node) = self.to::<dyn LayoutInline>() {
if let Some(node) = self.with::<dyn LayoutInline>() {
let barrier = Style::Barrier(self.id());
let styles = barrier.chain(&styles);
return node.layout_inline(world, regions, styles);
}
if let Some(node) = self.to::<dyn LayoutBlock>() {
if let Some(node) = self.with::<dyn LayoutBlock>() {
let barrier = Style::Barrier(self.id());
let styles = barrier.chain(&styles);
return Ok(node.layout_block(world, regions, styles)?.remove(0));
@ -312,11 +311,11 @@ impl<'a> Builder<'a> {
content: &'a Content,
styles: StyleChain<'a>,
) -> SourceResult<()> {
if let Some(styled) = content.downcast::<StyledNode>() {
if let Some(styled) = content.to::<StyledNode>() {
return self.styled(styled, styles);
}
if let Some(seq) = content.downcast::<SequenceNode>() {
if let Some(seq) = content.to::<SequenceNode>() {
return self.sequence(seq, styles);
}
@ -347,7 +346,7 @@ impl<'a> Builder<'a> {
}
let keep = content
.downcast::<PagebreakNode>()
.to::<PagebreakNode>()
.map_or(false, |pagebreak| !pagebreak.weak);
self.interrupt(Interruption::Page, styles, keep)?;
@ -457,12 +456,12 @@ struct DocBuilder<'a> {
impl<'a> DocBuilder<'a> {
fn accept(&mut self, content: &Content, styles: StyleChain<'a>) -> bool {
if let Some(pagebreak) = content.downcast::<PagebreakNode>() {
if let Some(pagebreak) = content.to::<PagebreakNode>() {
self.keep_next = !pagebreak.weak;
return true;
}
if let Some(page) = content.downcast::<PageNode>() {
if let Some(page) = content.to::<PageNode>() {
self.pages.push(page.clone(), styles);
self.keep_next = false;
return true;
@ -498,11 +497,11 @@ impl<'a> FlowBuilder<'a> {
}
if content.has::<dyn LayoutBlock>() {
let is_tight_list = if let Some(node) = content.downcast::<ListNode>() {
let is_tight_list = if let Some(node) = content.to::<ListNode>() {
node.tight
} else if let Some(node) = content.downcast::<EnumNode>() {
} else if let Some(node) = content.to::<EnumNode>() {
node.tight
} else if let Some(node) = content.downcast::<DescNode>() {
} else if let Some(node) = content.to::<DescNode>() {
node.tight
} else {
false
@ -576,7 +575,7 @@ impl<'a> ListBuilder<'a> {
return true;
}
if let Some(item) = content.downcast::<ListItem>() {
if let Some(item) = content.to::<ListItem>() {
if self
.items
.items()

View File

@ -49,9 +49,7 @@ impl PlaceNode {
/// origin. Instead of relative to the parent's current flow/cursor
/// position.
pub fn out_of_flow(&self) -> bool {
self.0
.downcast::<AlignNode>()
.map_or(false, |node| node.aligns.y.is_some())
self.0.to::<AlignNode>().map_or(false, |node| node.aligns.y.is_some())
}
}

View File

@ -44,7 +44,7 @@ impl Behave for HNode {
}
fn larger(&self, prev: &Content) -> bool {
let Some(prev) = prev.downcast::<Self>() else { return false };
let Some(prev) = prev.to::<Self>() else { return false };
self.amount > prev.amount
}
}
@ -110,7 +110,7 @@ impl Behave for VNode {
}
fn larger(&self, prev: &Content) -> bool {
let Some(prev) = prev.downcast::<Self>() else { return false };
let Some(prev) = prev.to::<Self>() else { return false };
self.amount > prev.amount
}
}

View File

@ -182,11 +182,11 @@ impl<'a> StackLayouter<'a> {
// Block-axis alignment of the `AlignNode` is respected
// by the stack node.
let align = block
.downcast::<AlignNode>()
.to::<AlignNode>()
.and_then(|node| node.aligns.get(self.axis))
.map(|align| align.resolve(styles))
.unwrap_or_else(|| {
if let Some(styled) = block.downcast::<StyledNode>() {
if let Some(styled) = block.to::<StyledNode>() {
let map = &styled.map;
if map.contains(ParNode::ALIGN) {
return StyleChain::with_root(map).get(ParNode::ALIGN);

View File

@ -161,7 +161,7 @@ pub fn items() -> LangItems {
linebreak: |justify| text::LinebreakNode { justify }.pack(),
text: |text| text::TextNode(text).pack(),
text_id: NodeId::of::<text::TextNode>(),
text_str: |content| Some(&content.downcast::<text::TextNode>()?.0),
text_str: |content| Some(&content.to::<text::TextNode>()?.0),
smart_quote: |double| text::SmartQuoteNode { double }.pack(),
parbreak: || text::ParbreakNode.pack(),
strong: |body| text::StrongNode(body).pack(),

View File

@ -35,7 +35,7 @@ impl Show for MathNode {
let mut realized = self
.clone()
.pack()
.guard(RecipeId::Base(NodeId::of::<Self>()))
.guarded(RecipeId::Base(NodeId::of::<Self>()))
.styled_with_map(map);
if self.display {

View File

@ -10,7 +10,7 @@ use crate::text::{families, variant, LinebreakNode, SpaceNode, TextNode};
/// Turn a math node into TeX math code.
#[capability]
pub trait Texify: 'static + Sync + Send {
pub trait Texify {
/// Perform the conversion.
fn texify(&self) -> EcoString;
}
@ -25,7 +25,7 @@ impl Texify for Content {
return r"\\".into();
}
if let Some(node) = self.to::<dyn Texify>() {
if let Some(node) = self.with::<dyn Texify>() {
return node.texify();
}

View File

@ -420,7 +420,7 @@ fn collect<'a>(
let segment = if child.is::<SpaceNode>() {
full.push(' ');
Segment::Text(1)
} else if let Some(node) = child.downcast::<TextNode>() {
} else if let Some(node) = child.to::<TextNode>() {
let prev = full.len();
if let Some(case) = styles.get(TextNode::CASE) {
full.push_str(&case.apply(&node.0));
@ -428,18 +428,18 @@ fn collect<'a>(
full.push_str(&node.0);
}
Segment::Text(full.len() - prev)
} else if let Some(node) = child.downcast::<LinebreakNode>() {
} else if let Some(node) = child.to::<LinebreakNode>() {
let c = if node.justify { '\u{2028}' } else { '\n' };
full.push(c);
Segment::Text(c.len_utf8())
} else if let Some(node) = child.downcast::<SmartQuoteNode>() {
} else if let Some(node) = child.to::<SmartQuoteNode>() {
let prev = full.len();
if styles.get(TextNode::SMART_QUOTES) {
let lang = styles.get(TextNode::LANG);
let region = styles.get(TextNode::REGION);
let quotes = Quotes::from_lang(lang, region);
let peeked = iter.peek().and_then(|(child, _)| {
if let Some(node) = child.downcast::<TextNode>() {
if let Some(node) = child.to::<TextNode>() {
node.0.chars().next()
} else if child.is::<SmartQuoteNode>() {
Some('"')
@ -455,7 +455,7 @@ fn collect<'a>(
full.push(if node.double { '"' } else { '\'' });
}
Segment::Text(full.len() - prev)
} else if let Some(&node) = child.downcast::<HNode>() {
} else if let Some(&node) = child.to::<HNode>() {
full.push(SPACING_REPLACE);
Segment::Spacing(node.amount)
} else if child.has::<dyn LayoutInline>() {
@ -523,7 +523,7 @@ fn prepare<'a>(
}
},
Segment::Inline(inline) => {
if let Some(repeat) = inline.downcast::<RepeatNode>() {
if let Some(repeat) = inline.to::<RepeatNode>() {
items.push(Item::Repeat(repeat, styles));
} else {
let size = Size::new(regions.first.x, regions.base.y);
@ -606,11 +606,11 @@ fn is_compatible(a: Script, b: Script) -> bool {
/// Get a style property, but only if it is the same for all children of the
/// paragraph.
fn shared_get<'a, K: Key<'a>>(
fn shared_get<'a, K: Key>(
styles: StyleChain<'a>,
children: &StyleVec<Content>,
key: K,
) -> Option<K::Output> {
) -> Option<K::Output<'a>> {
children
.styles()
.all(|map| !map.contains(key))

View File

@ -69,13 +69,11 @@ impl<const S: ShiftKind> Show for ShiftNode<S> {
/// Find and transform the text contained in `content` to the given script kind
/// if and only if it only consists of `Text`, `Space`, and `Empty` leaf nodes.
fn search_text(content: &Content, mode: ShiftKind) -> Option<EcoString> {
if content.is_empty() {
Some(EcoString::new())
} else if content.is::<SpaceNode>() {
if content.is::<SpaceNode>() {
Some(' '.into())
} else if let Some(text) = content.downcast::<TextNode>() {
} else if let Some(text) = content.to::<TextNode>() {
convert_script(&text.0, mode)
} else if let Some(seq) = content.downcast::<SequenceNode>() {
} else if let Some(seq) = content.to::<SequenceNode>() {
let mut full = EcoString::new();
for item in seq.0.iter() {
match search_text(item, mode) {

View File

@ -227,6 +227,9 @@ fn create(node: &Node) -> Result<TokenStream> {
#construct_func
#set_func
#field_method
}
unsafe impl<#params> ::typst::model::Capable for #self_ty {
#vtable_method
}
};
@ -411,9 +414,9 @@ fn create_property_module(node: &Node, property: &Property) -> (syn::Type, syn::
fn clone(&self) -> Self { *self }
}
impl<'a, #params> ::typst::model::Key<'a> for #key {
impl<#params> ::typst::model::Key for #key {
type Value = #value_ty;
type Output = #output_ty;
type Output<'a> = #output_ty;
#name_const
#node_func
#get_method
@ -492,10 +495,10 @@ fn create_property_get_method(property: &Property) -> syn::ImplItemMethod {
};
parse_quote! {
fn get(
fn get<'a>(
chain: ::typst::model::StyleChain<'a>,
mut values: impl ::std::iter::Iterator<Item = &'a Self::Value>,
) -> Self::Output {
) -> Self::Output<'a> {
#value
}
}

View File

@ -9,19 +9,22 @@ use comemo::Tracked;
use siphasher::sip128::{Hasher128, SipHasher};
use typst_macros::node;
use super::{Args, Key, Property, Recipe, RecipeId, Style, StyleMap, Value, Vm};
use super::{
Args, Key, Property, Recipe, RecipeId, Style, StyleMap, Unlabellable, Value, Vm,
};
use crate::diag::{SourceResult, StrResult};
use crate::syntax::Span;
use crate::util::{EcoString, ReadableTypeId};
use crate::World;
/// Composable representation of styled content.
///
/// This results from:
/// - anything written between square brackets in Typst
/// - any constructor function
#[derive(Clone, Hash)]
pub struct Content(Arc<dyn Bounds>, Vec<RecipeId>, Option<Span>, Option<EcoString>);
pub struct Content {
obj: Arc<dyn Bounds>,
guards: Vec<RecipeId>,
span: Option<Span>,
label: Option<EcoString>,
}
impl Content {
/// Create empty content.
@ -37,80 +40,60 @@ impl Content {
}
}
/// Whether the content is empty.
pub fn is_empty(&self) -> bool {
self.downcast::<SequenceNode>().map_or(false, |seq| seq.0.is_empty())
}
/// The node's human-readable name.
pub fn name(&self) -> &'static str {
(*self.0).name()
}
/// The id of the contained node.
pub fn id(&self) -> NodeId {
(*self.0).id()
}
/// Whether the contained node is of type `T`.
pub fn is<T: 'static>(&self) -> bool {
(*self.0).as_any().is::<T>()
}
/// Cast to `T` if the contained node is of type `T`.
pub fn downcast<T: 'static>(&self) -> Option<&T> {
(*self.0).as_any().downcast_ref::<T>()
}
/// Try to cast to a mutable instance of `T`.
fn try_downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
Arc::get_mut(&mut self.0)?.as_any_mut().downcast_mut::<T>()
}
/// Access a field on this content.
pub fn field(&self, name: &str) -> Option<Value> {
if name == "label" {
return Some(match &self.3 {
Some(label) => Value::Str(label.clone().into()),
None => Value::None,
});
/// Attach a span to the content.
pub fn spanned(mut self, span: Span) -> Self {
if let Some(styled) = self.to_mut::<StyledNode>() {
styled.sub.span = Some(span);
} else if let Some(styled) = self.to::<StyledNode>() {
self = StyledNode {
sub: styled.sub.clone().spanned(span),
map: styled.map.clone(),
}
.pack();
}
self.0.field(name)
self.span = Some(span);
self
}
/// Whether this content has the given capability.
pub fn has<C>(&self) -> bool
where
C: Capability + ?Sized,
{
self.0.vtable(TypeId::of::<C>()).is_some()
}
/// Cast to a trait object if this content has the given capability.
pub fn to<C>(&self) -> Option<&C>
where
C: Capability + ?Sized,
{
let node: &dyn Bounds = &*self.0;
let vtable = node.vtable(TypeId::of::<C>())?;
let data = node as *const dyn Bounds as *const ();
Some(unsafe { &*crate::util::fat::from_raw_parts(data, vtable) })
}
/// Repeat this content `n` times.
pub fn repeat(&self, n: i64) -> StrResult<Self> {
let count = usize::try_from(n)
.map_err(|_| format!("cannot repeat this content {} times", n))?;
Ok(Self::sequence(vec![self.clone(); count]))
/// Attach a label to the content.
pub fn labelled(mut self, label: EcoString) -> Self {
self.label = Some(label);
self
}
/// Style this content with a single style property.
pub fn styled<'k, K: Key<'k>>(self, key: K, value: K::Value) -> Self {
pub fn styled<K: Key>(self, key: K, value: K::Value) -> Self {
self.styled_with_entry(Style::Property(Property::new(key, value)))
}
/// Style this content with a style entry.
pub fn styled_with_entry(mut self, style: Style) -> Self {
if let Some(styled) = self.to_mut::<StyledNode>() {
styled.map.apply(style);
self
} else if let Some(styled) = self.to::<StyledNode>() {
let mut map = styled.map.clone();
map.apply(style);
StyledNode { sub: styled.sub.clone(), map }.pack()
} else {
StyledNode { sub: self, map: style.into() }.pack()
}
}
/// Style this content with a full style map.
pub fn styled_with_map(mut self, styles: StyleMap) -> Self {
if styles.is_empty() {
return self;
}
if let Some(styled) = self.to_mut::<StyledNode>() {
styled.map.apply_map(&styles);
return self;
}
StyledNode { sub: self, map: styles }.pack()
}
/// Style this content with a recipe, eagerly applying it if possible.
pub fn styled_with_recipe(
self,
@ -124,96 +107,115 @@ impl Content {
}
}
/// Style this content with a style entry.
pub fn styled_with_entry(mut self, style: Style) -> Self {
if let Some(styled) = self.try_downcast_mut::<StyledNode>() {
styled.map.apply(style);
self
} else if let Some(styled) = self.downcast::<StyledNode>() {
let mut map = styled.map.clone();
map.apply(style);
StyledNode { sub: styled.sub.clone(), map }.pack()
} else {
StyledNode { sub: self, map: style.into() }.pack()
}
/// Repeat this content `n` times.
pub fn repeat(&self, n: i64) -> StrResult<Self> {
let count = usize::try_from(n)
.map_err(|_| format!("cannot repeat this content {} times", n))?;
Ok(Self::sequence(vec![self.clone(); count]))
}
}
impl Content {
/// The id of the contained node.
pub fn id(&self) -> NodeId {
(*self.obj).id()
}
/// Style this content with a full style map.
pub fn styled_with_map(mut self, styles: StyleMap) -> Self {
if styles.is_empty() {
return self;
}
if let Some(styled) = self.try_downcast_mut::<StyledNode>() {
styled.map.apply_map(&styles);
return self;
}
StyledNode { sub: self, map: styles }.pack()
}
/// Disable a show rule recipe.
pub fn guard(mut self, id: RecipeId) -> Self {
self.1.push(id);
self
}
/// Whether no show rule was executed for this node so far.
pub fn pristine(&self) -> bool {
self.1.is_empty()
}
/// Check whether a show rule recipe is disabled.
pub fn guarded(&self, id: RecipeId) -> bool {
self.1.contains(&id)
/// The node's human-readable name.
pub fn name(&self) -> &'static str {
(*self.obj).name()
}
/// The node's span.
pub fn span(&self) -> Option<Span> {
self.2
}
/// Set the content's span.
pub fn set_span(&mut self, span: Span) {
if let Some(styled) = self.try_downcast_mut::<StyledNode>() {
styled.sub.2 = Some(span);
} else if let Some(styled) = self.downcast::<StyledNode>() {
*self = StyledNode {
sub: styled.sub.clone().spanned(span),
map: styled.map.clone(),
}
.pack();
}
self.2 = Some(span);
}
/// Attach a span to the content.
pub fn spanned(mut self, span: Span) -> Self {
self.set_span(span);
self
self.span
}
/// The content's label.
pub fn label(&self) -> Option<&EcoString> {
self.3.as_ref()
self.label.as_ref()
}
/// Set the content's label.
pub fn set_label(&mut self, label: EcoString) {
self.3 = Some(label);
/// Access a field on this content.
pub fn field(&self, name: &str) -> Option<Value> {
if name == "label" {
return Some(match &self.label {
Some(label) => Value::Str(label.clone().into()),
None => Value::None,
});
}
self.obj.field(name)
}
/// Attacha label to the content.
pub fn labelled(mut self, label: EcoString) -> Self {
self.set_label(label);
/// Whether the contained node is of type `T`.
pub fn is<T: 'static>(&self) -> bool {
(*self.obj).as_any().is::<T>()
}
/// Cast to `T` if the contained node is of type `T`.
pub fn to<T: 'static>(&self) -> Option<&T> {
(*self.obj).as_any().downcast_ref::<T>()
}
/// Whether this content has the given capability.
pub fn has<C>(&self) -> bool
where
C: Capability + ?Sized,
{
self.obj.vtable(TypeId::of::<C>()).is_some()
}
/// Cast to a trait object if this content has the given capability.
pub fn with<C>(&self) -> Option<&C>
where
C: Capability + ?Sized,
{
let node: &dyn Bounds = &*self.obj;
let vtable = node.vtable(TypeId::of::<C>())?;
let data = node as *const dyn Bounds as *const ();
Some(unsafe { &*crate::util::fat::from_raw_parts(data, vtable) })
}
/// Try to cast to a mutable instance of `T`.
fn to_mut<T: 'static>(&mut self) -> Option<&mut T> {
Arc::get_mut(&mut self.obj)?.as_any_mut().downcast_mut::<T>()
}
/// Disable a show rule recipe.
#[doc(hidden)]
pub fn guarded(mut self, id: RecipeId) -> Self {
self.guards.push(id);
self
}
/// Whether a label can be attached to the content.
pub(super) fn labellable(&self) -> bool {
!self.has::<dyn Unlabellable>()
}
/// Whether no show rule was executed for this node so far.
pub(super) fn is_pristine(&self) -> bool {
self.guards.is_empty()
}
/// Check whether a show rule recipe is disabled.
pub(super) fn is_guarded(&self, id: RecipeId) -> bool {
self.guards.contains(&id)
}
/// Copy the metadata from other content.
pub fn copy_meta(&mut self, from: &Content) {
self.1 = from.1.clone();
self.2 = from.2;
self.3 = from.3.clone();
pub(super) fn copy_meta(&mut self, from: &Content) {
self.guards = from.guards.clone();
self.span = from.span;
self.label = from.label.clone();
}
}
impl Debug for Content {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.obj.fmt(f)
}
}
@ -223,15 +225,9 @@ impl Default for Content {
}
}
impl Debug for Content {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl PartialEq for Content {
fn eq(&self, other: &Self) -> bool {
(*self.0).hash128() == (*other.0).hash128()
(*self.obj).hash128() == (*other.obj).hash128()
}
}
@ -240,10 +236,10 @@ impl Add for Content {
fn add(self, mut rhs: Self) -> Self::Output {
let mut lhs = self;
if let Some(lhs_mut) = lhs.try_downcast_mut::<SequenceNode>() {
if let Some(rhs_mut) = rhs.try_downcast_mut::<SequenceNode>() {
if let Some(lhs_mut) = lhs.to_mut::<SequenceNode>() {
if let Some(rhs_mut) = rhs.to_mut::<SequenceNode>() {
lhs_mut.0.append(&mut rhs_mut.0);
} else if let Some(rhs) = rhs.downcast::<SequenceNode>() {
} else if let Some(rhs) = rhs.to::<SequenceNode>() {
lhs_mut.0.extend(rhs.0.iter().cloned());
} else {
lhs_mut.0.push(rhs);
@ -251,7 +247,7 @@ impl Add for Content {
return lhs;
}
let seq = match (lhs.downcast::<SequenceNode>(), rhs.downcast::<SequenceNode>()) {
let seq = match (lhs.to::<SequenceNode>(), rhs.to::<SequenceNode>()) {
(Some(lhs), Some(rhs)) => lhs.0.iter().chain(&rhs.0).cloned().collect(),
(Some(lhs), None) => lhs.0.iter().cloned().chain(iter::once(rhs)).collect(),
(None, Some(rhs)) => iter::once(lhs).chain(rhs.0.iter().cloned()).collect(),
@ -306,68 +302,6 @@ impl Hash for dyn Bounds {
}
}
/// A constructable, stylable content node.
pub trait Node: 'static {
/// Pack into type-erased content.
fn pack(self) -> Content
where
Self: Debug + Hash + Sync + Send + Sized + 'static,
{
Content(Arc::new(self), vec![], None, None)
}
/// A unique identifier of the node type.
fn id(&self) -> NodeId;
/// The node's name.
fn name(&self) -> &'static str;
/// Construct a node from the arguments.
///
/// This is passed only the arguments that remain after execution of the
/// node's set rule.
fn construct(vm: &mut Vm, args: &mut Args) -> SourceResult<Content>
where
Self: Sized;
/// Parse relevant arguments into style properties for this node.
///
/// When `constructor` is true, [`construct`](Self::construct) will run
/// after this invocation of `set` with the remaining arguments.
fn set(args: &mut Args, constructor: bool) -> SourceResult<StyleMap>
where
Self: Sized;
/// Access a field on this node.
fn field(&self, name: &str) -> Option<Value>;
/// Extract the pointer of the vtable of the trait object with the
/// given type `id` if this node implements that trait.
fn vtable(&self, id: TypeId) -> Option<*const ()>;
}
/// A capability a node can have.
///
/// This is implemented by trait objects.
pub trait Capability: 'static + Send + Sync {}
/// A unique identifier for a node.
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct NodeId(ReadableTypeId);
impl NodeId {
/// The id of the given node.
pub fn of<T: 'static>() -> Self {
Self(ReadableTypeId::of::<T>())
}
}
impl Debug for NodeId {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
/// A node with applied styles.
#[derive(Clone, Hash)]
pub struct StyledNode {
@ -402,3 +336,73 @@ impl Debug for SequenceNode {
f.debug_list().entries(self.0.iter()).finish()
}
}
/// A constructable, stylable content node.
pub trait Node: 'static + Capable {
/// Pack a node into type-erased content.
fn pack(self) -> Content
where
Self: Node + Debug + Hash + Sync + Send + Sized + 'static,
{
Content {
obj: Arc::new(self),
guards: vec![],
span: None,
label: None,
}
}
/// A unique identifier of the node type.
fn id(&self) -> NodeId;
/// The node's name.
fn name(&self) -> &'static str;
/// Construct a node from the arguments.
///
/// This is passed only the arguments that remain after execution of the
/// node's set rule.
fn construct(vm: &mut Vm, args: &mut Args) -> SourceResult<Content>
where
Self: Sized;
/// Parse relevant arguments into style properties for this node.
///
/// When `constructor` is true, [`construct`](Self::construct) will run
/// after this invocation of `set` with the remaining arguments.
fn set(args: &mut Args, constructor: bool) -> SourceResult<StyleMap>
where
Self: Sized;
/// Access a field on this node.
fn field(&self, name: &str) -> Option<Value>;
}
/// A unique identifier for a node.
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct NodeId(ReadableTypeId);
impl NodeId {
/// The id of the given node.
pub fn of<T: 'static>() -> Self {
Self(ReadableTypeId::of::<T>())
}
}
impl Debug for NodeId {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
/// A capability a node can have.
///
/// This is implemented by trait objects.
pub trait Capability: 'static {}
/// Dynamically access a trait implementation at runtime.
pub unsafe trait Capable {
/// Return the vtable pointer of the trait object with given type `id`
/// if `self` implements the trait.
fn vtable(&self, of: TypeId) -> Option<*const ()>;
}

View File

@ -1,13 +1,14 @@
//! Evaluation of markup into modules.
use std::collections::BTreeMap;
use std::mem;
use comemo::{Track, Tracked};
use unicode_segmentation::UnicodeSegmentation;
use super::{
methods, ops, Arg, Args, Array, CapturesVisitor, Closure, Content, Dict, Flow, Func,
Recipe, Scope, Scopes, Selector, StyleMap, Transform, Unlabellable, Value, Vm,
Recipe, Scope, Scopes, Selector, StyleMap, Transform, Value, Vm,
};
use crate::diag::{bail, error, At, SourceResult, StrResult, Trace, Tracepoint};
use crate::geom::{Abs, Angle, Em, Fr, Ratio};
@ -137,10 +138,8 @@ fn eval_markup(
seq.push(tail.styled_with_recipe(vm.world, recipe)?)
}
ast::MarkupNode::Label(label) => {
if let Some(node) =
seq.iter_mut().rev().find(|node| !node.has::<dyn Unlabellable>())
{
node.set_label(label.get().clone());
if let Some(node) = seq.iter_mut().rev().find(|node| node.labellable()) {
*node = mem::take(node).labelled(label.get().clone());
}
}
_ => seq.push(node.eval(vm)?),
@ -1080,7 +1079,7 @@ impl Eval for ast::FuncReturn {
}
/// Access an expression mutably.
pub trait Access {
trait Access {
/// Access the value.
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value>;
}

View File

@ -36,19 +36,19 @@ impl StyleMap {
/// If the property needs folding and the value is already contained in the
/// style map, `self` contributes the outer values and `value` is the inner
/// one.
pub fn set<'a, K: Key<'a>>(&mut self, key: K, value: K::Value) {
pub fn set<K: Key>(&mut self, key: K, value: K::Value) {
self.0.push(Style::Property(Property::new(key, value)));
}
/// Set an inner value for a style property if it is `Some(_)`.
pub fn set_opt<'a, K: Key<'a>>(&mut self, key: K, value: Option<K::Value>) {
pub fn set_opt<K: Key>(&mut self, key: K, value: Option<K::Value>) {
if let Some(value) = value {
self.set(key, value);
}
}
/// Whether the map contains a style property for the given key.
pub fn contains<'a, K: Key<'a>>(&self, _: K) -> bool {
pub fn contains<K: Key>(&self, _: K) -> bool {
self.0
.iter()
.filter_map(|entry| entry.property())
@ -188,13 +188,13 @@ impl Debug for Style {
///
/// This trait is not intended to be implemented manually, but rather through
/// the `#[node]` proc-macro.
pub trait Key<'a>: Copy + 'static {
pub trait Key: Copy + 'static {
/// The unfolded type which this property is stored as in a style map.
type Value: Debug + Clone + Hash + Sync + Send + 'static;
/// The folded type of value that is returned when reading this property
/// from a style chain.
type Output;
type Output<'a>;
/// The name of the property, used for debug printing.
const NAME: &'static str;
@ -204,10 +204,10 @@ pub trait Key<'a>: Copy + 'static {
/// Compute an output value from a sequence of values belonging to this key,
/// folding if necessary.
fn get(
fn get<'a>(
chain: StyleChain<'a>,
values: impl Iterator<Item = &'a Self::Value>,
) -> Self::Output;
) -> Self::Output<'a>;
}
/// A style property originating from a set rule or constructor.
@ -229,7 +229,7 @@ pub struct Property {
impl Property {
/// Create a new property from a key-value pair.
pub fn new<'a, K: Key<'a>>(_: K, value: K::Value) -> Self {
pub fn new<K: Key>(_: K, value: K::Value) -> Self {
Self {
key: KeyId::of::<K>(),
node: K::node(),
@ -241,7 +241,7 @@ impl Property {
}
/// Whether this property has the given key.
pub fn is<'a, K: Key<'a>>(&self) -> bool {
pub fn is<K: Key>(&self) -> bool {
self.key == KeyId::of::<K>()
}
@ -251,7 +251,7 @@ impl Property {
}
/// Access the property's value if it is of the given key.
pub fn downcast<'a, K: Key<'a>>(&'a self) -> Option<&'a K::Value> {
pub fn downcast<K: Key>(&self) -> Option<&K::Value> {
if self.key == KeyId::of::<K>() {
(**self.value).as_any().downcast_ref()
} else {
@ -314,7 +314,7 @@ struct KeyId(ReadableTypeId);
impl KeyId {
/// The id of the given key.
pub fn of<'a, T: Key<'a>>() -> Self {
pub fn of<T: Key>() -> Self {
Self(ReadableTypeId::of::<T>())
}
}
@ -327,7 +327,7 @@ impl Debug for KeyId {
/// A built-in show rule for a node.
#[capability]
pub trait Show: 'static + Sync + Send {
pub trait Show {
/// Execute the base recipe for this node.
fn show(
&self,
@ -338,7 +338,7 @@ pub trait Show: 'static + Sync + Send {
/// Post-process a node after it was realized.
#[capability]
pub trait Finalize: 'static + Sync + Send {
pub trait Finalize {
/// Finalize the fully realized form of the node. Use this for effects that
/// should work even in the face of a user-defined show rule, for example
/// the linking behaviour of a link node.
@ -352,7 +352,7 @@ pub trait Finalize: 'static + Sync + Send {
/// Indicates that a node cannot be labelled.
#[capability]
pub trait Unlabellable: 'static + Sync + Send {}
pub trait Unlabellable {}
/// A show rule recipe.
#[derive(Clone, PartialEq, Hash)]
@ -386,7 +386,7 @@ impl Recipe {
return Ok(None);
}
self.transform.apply(world, self.span, target.clone().guard(sel))?
self.transform.apply(world, self.span, target.clone().guarded(sel))?
}
Some(Selector::Regex(regex)) => {
@ -412,7 +412,7 @@ impl Recipe {
let transformed = self.transform.apply(
world,
self.span,
make(m.as_str().into()).guard(sel),
make(m.as_str().into()).guarded(sel),
)?;
result.push(transformed);
@ -559,7 +559,7 @@ impl<'a> StyleChain<'a> {
/// Returns the property's default value if no map in the chain contains an
/// entry for it. Also takes care of resolving and folding and returns
/// references where applicable.
pub fn get<K: Key<'a>>(self, key: K) -> K::Output {
pub fn get<K: Key>(self, key: K) -> K::Output<'a> {
K::get(self, self.values(key))
}
@ -576,7 +576,7 @@ impl<'a> StyleChain<'a> {
let mut realized = None;
for recipe in self.entries().filter_map(Style::recipe) {
let sel = RecipeId::Nth(n);
if recipe.applicable(target) && !target.guarded(sel) {
if recipe.applicable(target) && !target.is_guarded(sel) {
if let Some(content) = recipe.apply(world, sel, target)? {
realized = Some(content);
break;
@ -587,15 +587,15 @@ impl<'a> StyleChain<'a> {
// Realize if there was no matching recipe.
let base = RecipeId::Base(target.id());
if realized.is_none() && !target.guarded(base) {
if let Some(showable) = target.to::<dyn Show>() {
if realized.is_none() && !target.is_guarded(base) {
if let Some(showable) = target.with::<dyn Show>() {
realized = Some(showable.show(world, self)?);
}
}
// Finalize only if this is the first application for this node.
if let Some(node) = target.to::<dyn Finalize>() {
if target.pristine() {
if let Some(node) = target.with::<dyn Finalize>() {
if target.is_pristine() {
if let Some(content) = realized {
realized = Some(node.finalize(world, self, content)?);
}
@ -612,7 +612,7 @@ impl<'a> StyleChain<'a> {
// Find out whether any recipe matches and is unguarded.
for recipe in self.entries().filter_map(Style::recipe) {
if recipe.applicable(target) && !target.guarded(RecipeId::Nth(n)) {
if recipe.applicable(target) && !target.is_guarded(RecipeId::Nth(n)) {
return true;
}
n -= 1;
@ -638,7 +638,7 @@ impl<'a> StyleChain<'a> {
}
/// Iterate over all values for the given property in the chain.
fn values<K: Key<'a>>(self, _: K) -> Values<'a, K> {
fn values<K: Key>(self, _: K) -> Values<'a, K> {
Values {
entries: self.entries(),
key: PhantomData,
@ -682,7 +682,7 @@ struct Values<'a, K> {
barriers: usize,
}
impl<'a, K: Key<'a>> Iterator for Values<'a, K> {
impl<'a, K: Key> Iterator for Values<'a, K> {
type Item = &'a K::Value;
fn next(&mut self) -> Option<Self::Item> {