summaryrefslogtreecommitdiff
path: root/src/model
diff options
context:
space:
mode:
Diffstat (limited to 'src/model')
-rw-r--r--src/model/content.rs412
-rw-r--r--src/model/mod.rs2
-rw-r--r--src/model/realize.rs7
-rw-r--r--src/model/styles.rs337
-rw-r--r--src/model/typeset.rs3
5 files changed, 300 insertions, 461 deletions
diff --git a/src/model/content.rs b/src/model/content.rs
index b10a3409..2af4ae72 100644
--- a/src/model/content.rs
+++ b/src/model/content.rs
@@ -1,27 +1,24 @@
-use std::any::{Any, TypeId};
-use std::fmt::{self, Debug, Formatter};
+use std::any::TypeId;
+use std::fmt::{self, Debug, Formatter, Write};
use std::hash::{Hash, Hasher};
use std::iter::{self, Sum};
use std::ops::{Add, AddAssign};
-use std::sync::Arc;
use comemo::Tracked;
use ecow::{EcoString, EcoVec};
-use siphasher::sip128::{Hasher128, SipHasher};
-use typst_macros::node;
-use super::{capability, capable, Guard, Key, Property, Recipe, Style, StyleMap};
+use super::{node, Guard, Key, Property, Recipe, Style, StyleMap};
use crate::diag::{SourceResult, StrResult};
-use crate::eval::{Args, ParamInfo, Value, Vm};
+use crate::eval::{cast_from_value, Args, Cast, ParamInfo, Value, Vm};
use crate::syntax::Span;
-use crate::util::ReadableTypeId;
use crate::World;
/// Composable representation of styled content.
#[derive(Clone, Hash)]
pub struct Content {
- obj: Arc<dyn Bounds>,
+ id: NodeId,
span: Option<Span>,
+ fields: EcoVec<(EcoString, Value)>,
modifiers: EcoVec<Modifier>,
}
@@ -30,55 +27,43 @@ pub struct Content {
enum Modifier {
Prepared,
Guard(Guard),
- Label(Label),
- Field(EcoString, Value),
}
impl Content {
+ pub fn new<T: Node>() -> Self {
+ Self {
+ id: T::id(),
+ span: None,
+ fields: EcoVec::new(),
+ modifiers: EcoVec::new(),
+ }
+ }
+
/// Create empty content.
pub fn empty() -> Self {
- SequenceNode(vec![]).pack()
+ SequenceNode::new(vec![]).pack()
}
/// Create a new sequence node from multiples nodes.
pub fn sequence(seq: Vec<Self>) -> Self {
match seq.as_slice() {
[_] => seq.into_iter().next().unwrap(),
- _ => SequenceNode(seq).pack(),
+ _ => SequenceNode::new(seq).pack(),
}
}
/// 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();
+ if let Some(styled) = self.to::<StyledNode>() {
+ self = StyledNode::new(styled.sub().spanned(span), styled.map()).pack();
}
self.span = Some(span);
self
}
/// Attach a label to the content.
- pub fn labelled(mut self, label: Label) -> Self {
- for (i, modifier) in self.modifiers.iter().enumerate() {
- if matches!(modifier, Modifier::Label(_)) {
- self.modifiers.make_mut()[i] = Modifier::Label(label);
- return self;
- }
- }
-
- self.modifiers.push(Modifier::Label(label));
- self
- }
-
- /// Attach a field to the content.
- pub fn push_field(&mut self, name: impl Into<EcoString>, value: Value) {
- self.modifiers.push(Modifier::Field(name.into(), value));
+ pub fn labelled(self, label: Label) -> Self {
+ self.with_field("label", label)
}
/// Style this content with a single style property.
@@ -87,31 +72,21 @@ impl Content {
}
/// 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_one(style);
- self
- } else if let Some(styled) = self.to::<StyledNode>() {
- let mut map = styled.map.clone();
- map.apply_one(style);
- StyledNode { sub: styled.sub.clone(), map }.pack()
- } else {
- StyledNode { sub: self, map: style.into() }.pack()
- }
+ pub fn styled_with_entry(self, style: Style) -> Self {
+ self.styled_with_map(style.into())
}
/// Style this content with a full style map.
- pub fn styled_with_map(mut self, styles: StyleMap) -> Self {
+ pub fn styled_with_map(self, styles: StyleMap) -> Self {
if styles.is_empty() {
- return self;
- }
-
- if let Some(styled) = self.to_mut::<StyledNode>() {
- styled.map.apply(styles);
- return self;
+ self
+ } else if let Some(styled) = self.to::<StyledNode>() {
+ let mut map = styled.map();
+ map.apply(styles);
+ StyledNode::new(styled.sub(), map).pack()
+ } else {
+ StyledNode::new(self, styles).pack()
}
-
- StyledNode { sub: self, map: styles }.pack()
}
/// Style this content with a recipe, eagerly applying it if possible.
@@ -139,12 +114,12 @@ impl Content {
impl Content {
/// The id of the contained node.
pub fn id(&self) -> NodeId {
- (*self.obj).id()
+ self.id
}
/// The node's human-readable name.
pub fn name(&self) -> &'static str {
- (*self.obj).name()
+ self.id.name()
}
/// The node's span.
@@ -154,72 +129,86 @@ impl Content {
/// The content's label.
pub fn label(&self) -> Option<&Label> {
- self.modifiers.iter().find_map(|modifier| match modifier {
- Modifier::Label(label) => Some(label),
+ match self.field("label")? {
+ Value::Label(label) => Some(label),
_ => None,
- })
+ }
}
- /// 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::Label(label.clone()),
- None => Value::None,
- });
- }
+ pub fn with_field(
+ mut self,
+ name: impl Into<EcoString>,
+ value: impl Into<Value>,
+ ) -> Self {
+ self.push_field(name, value);
+ self
+ }
- for modifier in &self.modifiers {
- if let Modifier::Field(other, value) = modifier {
- if name == other {
- return Some(value.clone());
- }
- }
+ /// Attach a field to the content.
+ pub fn push_field(&mut self, name: impl Into<EcoString>, value: impl Into<Value>) {
+ let name = name.into();
+ if let Some(i) = self.fields.iter().position(|(field, _)| *field == name) {
+ self.fields.make_mut()[i] = (name, value.into());
+ } else {
+ self.fields.push((name, value.into()));
}
+ }
- self.obj.field(name)
+ pub fn field(&self, name: &str) -> Option<&Value> {
+ static NONE: Value = Value::None;
+ self.fields
+ .iter()
+ .find(|(field, _)| field == name)
+ .map(|(_, value)| value)
+ .or_else(|| (name == "label").then(|| &NONE))
+ }
+
+ pub fn fields(&self) -> &[(EcoString, Value)] {
+ &self.fields
+ }
+
+ #[track_caller]
+ pub fn cast_field<T: Cast>(&self, name: &str) -> T {
+ match self.field(name) {
+ Some(value) => value.clone().cast().unwrap(),
+ None => field_is_missing(name),
+ }
}
/// Whether the contained node is of type `T`.
pub fn is<T>(&self) -> bool
where
- T: Capable + 'static,
+ T: Node + 'static,
{
- (*self.obj).as_any().is::<T>()
+ self.id == NodeId::of::<T>()
}
/// Cast to `T` if the contained node is of type `T`.
pub fn to<T>(&self) -> Option<&T>
where
- T: Capable + 'static,
+ T: Node + 'static,
{
- (*self.obj).as_any().downcast_ref::<T>()
+ self.is::<T>().then(|| unsafe { std::mem::transmute(self) })
}
/// Whether this content has the given capability.
pub fn has<C>(&self) -> bool
where
- C: Capability + ?Sized,
+ C: ?Sized + 'static,
{
- self.obj.vtable(TypeId::of::<C>()).is_some()
+ (self.id.0.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,
+ C: ?Sized + 'static,
{
- let node: &dyn Bounds = &*self.obj;
- let vtable = node.vtable(TypeId::of::<C>())?;
- let data = node as *const dyn Bounds as *const ();
+ let vtable = (self.id.0.vtable)(TypeId::of::<C>())?;
+ let data = self as *const Self 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: Guard) -> Self {
@@ -262,12 +251,40 @@ impl Content {
pub(super) fn copy_modifiers(&mut self, from: &Content) {
self.span = from.span;
self.modifiers = from.modifiers.clone();
+ if let Some(label) = from.label() {
+ self.push_field("label", label.clone())
+ }
}
}
impl Debug for Content {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- self.obj.fmt(f)
+ struct Pad<'a>(&'a str);
+ impl Debug for Pad<'_> {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ f.pad(self.0)
+ }
+ }
+
+ if let Some(styled) = self.to::<StyledNode>() {
+ styled.map().fmt(f)?;
+ styled.sub().fmt(f)
+ } else if let Some(seq) = self.to::<SequenceNode>() {
+ f.debug_list().entries(&seq.children()).finish()
+ } else if self.id.name() == "space" {
+ ' '.fmt(f)
+ } else if self.id.name() == "text" {
+ self.field("text").unwrap().fmt(f)
+ } else {
+ f.write_str(self.name())?;
+ if self.fields.is_empty() {
+ return Ok(());
+ }
+ f.write_char(' ')?;
+ f.debug_map()
+ .entries(self.fields.iter().map(|(name, value)| (Pad(name), value)))
+ .finish()
+ }
}
}
@@ -280,27 +297,19 @@ impl Default for Content {
impl Add for Content {
type Output = Self;
- fn add(self, mut rhs: Self) -> Self::Output {
- let mut lhs = self;
- 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.to::<SequenceNode>() {
- lhs_mut.0.extend(rhs.0.iter().cloned());
- } else {
- lhs_mut.0.push(rhs);
- }
- return lhs;
- }
-
+ fn add(self, rhs: Self) -> Self::Output {
+ let lhs = self;
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(),
+ (Some(lhs), Some(rhs)) => {
+ lhs.children().into_iter().chain(rhs.children()).collect()
+ }
+ (Some(lhs), None) => {
+ lhs.children().into_iter().chain(iter::once(rhs)).collect()
+ }
+ (None, Some(rhs)) => iter::once(lhs).chain(rhs.children()).collect(),
(None, None) => vec![lhs, rhs],
};
-
- SequenceNode(seq).pack()
+ SequenceNode::new(seq).pack()
}
}
@@ -316,73 +325,33 @@ impl Sum for Content {
}
}
-trait Bounds: Node + Debug + Sync + Send + 'static {
- fn as_any(&self) -> &dyn Any;
- fn as_any_mut(&mut self) -> &mut dyn Any;
- fn hash128(&self) -> u128;
-}
-
-impl<T> Bounds for T
-where
- T: Node + Debug + Hash + Sync + Send + 'static,
-{
- fn as_any(&self) -> &dyn Any {
- self
- }
-
- fn as_any_mut(&mut self) -> &mut dyn Any {
- self
- }
-
- fn hash128(&self) -> u128 {
- let mut state = SipHasher::new();
- self.type_id().hash(&mut state);
- self.hash(&mut state);
- state.finish128().as_u128()
- }
-}
-
-impl Hash for dyn Bounds {
- fn hash<H: Hasher>(&self, state: &mut H) {
- state.write_u128(self.hash128());
- }
-}
-
/// A node with applied styles.
-#[capable]
-#[derive(Clone, Hash)]
+#[node]
pub struct StyledNode {
/// The styled content.
+ #[positional]
+ #[required]
pub sub: Content,
+
/// The styles.
+ #[positional]
+ #[required]
pub map: StyleMap,
}
-#[node]
-impl StyledNode {}
-
-impl Debug for StyledNode {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- self.map.fmt(f)?;
- self.sub.fmt(f)
- }
+cast_from_value! {
+ StyleMap: "style map",
}
/// A sequence of nodes.
///
/// Combines other arbitrary content. So, when you write `[Hi] + [you]` in
/// Typst, the two text nodes are combined into a single sequence node.
-#[capable]
-#[derive(Clone, Hash)]
-pub struct SequenceNode(pub Vec<Content>);
-
#[node]
-impl SequenceNode {}
-
-impl Debug for SequenceNode {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.debug_list().entries(self.0.iter()).finish()
- }
+pub struct SequenceNode {
+ #[variadic]
+ #[required]
+ pub children: Vec<Content>,
}
/// A label for a node.
@@ -396,80 +365,83 @@ impl Debug for Label {
}
/// A constructable, stylable content node.
-pub trait Node: 'static + Capable {
+pub trait Node: Construct + Set + Sized + 'static {
+ /// The node's ID.
+ fn id() -> NodeId;
+
/// 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),
- span: None,
- modifiers: EcoVec::new(),
- }
- }
+ fn pack(self) -> Content;
+}
- /// A unique identifier of the node type.
- fn id(&self) -> NodeId;
+/// A unique identifier for a node.
+#[derive(Copy, Clone)]
+pub struct NodeId(&'static NodeMeta);
- /// The node's name.
- fn name(&self) -> &'static str;
+impl NodeId {
+ pub fn of<T: Node>() -> Self {
+ T::id()
+ }
- /// 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: &Vm, args: &mut Args) -> SourceResult<Content>
- where
- Self: Sized;
+ pub fn from_meta(meta: &'static NodeMeta) -> Self {
+ Self(meta)
+ }
- /// 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;
+ /// The name of the identified node.
+ pub fn name(self) -> &'static str {
+ self.0.name
+ }
+}
- /// List the settable properties.
- fn properties() -> Vec<ParamInfo>
- where
- Self: Sized;
+impl Debug for NodeId {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ f.pad(self.name())
+ }
+}
- /// Access a field on this node.
- fn field(&self, name: &str) -> Option<Value>;
+impl Hash for NodeId {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ state.write_usize(self.0 as *const _ as usize);
+ }
}
-/// A unique identifier for a node type.
-#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-pub struct NodeId(ReadableTypeId);
+impl Eq for NodeId {}
-impl NodeId {
- /// The id of the given node type.
- pub fn of<T: 'static>() -> Self {
- Self(ReadableTypeId::of::<T>())
+impl PartialEq for NodeId {
+ fn eq(&self, other: &Self) -> bool {
+ std::ptr::eq(self.0, other.0)
}
}
-impl Debug for NodeId {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- self.0.fmt(f)
- }
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub struct NodeMeta {
+ pub name: &'static str,
+ pub vtable: fn(of: TypeId) -> Option<*const ()>,
}
-/// A capability a node can have.
-///
-/// Should be implemented by trait objects that are accessible through
-/// [`Capable`].
-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 ()>;
+pub trait Construct {
+ /// 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: &Vm, args: &mut Args) -> SourceResult<Content>;
+}
+
+pub trait Set {
+ /// Parse relevant arguments into style properties for this node.
+ ///
+ /// When `constructor` is true, [`construct`](Construct::construct) will run
+ /// after this invocation of `set` with the remaining arguments.
+ fn set(args: &mut Args, constructor: bool) -> SourceResult<StyleMap>;
+
+ /// List the settable properties.
+ fn properties() -> Vec<ParamInfo>;
}
/// Indicates that a node cannot be labelled.
-#[capability]
pub trait Unlabellable {}
+
+#[cold]
+#[track_caller]
+fn field_is_missing(name: &str) -> ! {
+ panic!("required field `{name}` is missing")
+}
diff --git a/src/model/mod.rs b/src/model/mod.rs
index 692d18d5..07329e3f 100644
--- a/src/model/mod.rs
+++ b/src/model/mod.rs
@@ -13,4 +13,4 @@ pub use self::typeset::*;
#[doc(hidden)]
pub use once_cell;
-pub use typst_macros::{capability, capable, node};
+pub use typst_macros::node;
diff --git a/src/model/realize.rs b/src/model/realize.rs
index b33cc0bb..2f38df51 100644
--- a/src/model/realize.rs
+++ b/src/model/realize.rs
@@ -1,4 +1,4 @@
-use super::{capability, Content, NodeId, Recipe, Selector, StyleChain, Vt};
+use super::{Content, NodeId, Recipe, Selector, StyleChain, Vt};
use crate::diag::SourceResult;
/// Whether the target is affected by show rules in the given style chain.
@@ -105,7 +105,7 @@ fn try_apply(
let mut result = vec![];
let mut cursor = 0;
- for m in regex.find_iter(text) {
+ for m in regex.find_iter(&text) {
let start = m.start();
if cursor < start {
result.push(make(text[cursor..start].into()));
@@ -133,7 +133,6 @@ fn try_apply(
}
/// Preparations before execution of any show rule.
-#[capability]
pub trait Prepare {
/// Prepare the node for show rule application.
fn prepare(
@@ -145,7 +144,6 @@ pub trait Prepare {
}
/// The base recipe for a node.
-#[capability]
pub trait Show {
/// Execute the base recipe for this node.
fn show(
@@ -157,7 +155,6 @@ pub trait Show {
}
/// Post-process a node after it was realized.
-#[capability]
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
diff --git a/src/model/styles.rs b/src/model/styles.rs
index 18507491..cbf4cfb2 100644
--- a/src/model/styles.rs
+++ b/src/model/styles.rs
@@ -1,21 +1,16 @@
use std::any::Any;
use std::fmt::{self, Debug, Formatter};
-use std::hash::Hash;
+use std::hash::{Hash, Hasher};
use std::iter;
use std::marker::PhantomData;
-use std::sync::Arc;
-use comemo::{Prehashed, Tracked};
+use comemo::Tracked;
+use ecow::EcoString;
-use super::{Content, Label, NodeId};
+use super::{Content, Label, Node, NodeId};
use crate::diag::{SourceResult, Trace, Tracepoint};
-use crate::eval::{Args, Dict, Func, Regex, Value};
-use crate::geom::{
- Abs, Align, Axes, Corners, Em, GenAlign, Length, Numeric, PartialStroke, Rel, Sides,
- Smart,
-};
+use crate::eval::{cast_from_value, Args, Cast, Dict, Func, Regex, Value};
use crate::syntax::Span;
-use crate::util::ReadableTypeId;
use crate::World;
/// A map of style properties.
@@ -76,7 +71,7 @@ impl StyleMap {
/// Mark all contained properties as _scoped_. This means that they only
/// apply to the first descendant node (of their type) in the hierarchy and
/// not its children, too. This is used by
- /// [constructors](super::Node::construct).
+ /// [constructors](super::Construct::construct).
pub fn scoped(mut self) -> Self {
for entry in &mut self.0 {
if let Style::Property(property) = entry {
@@ -98,7 +93,7 @@ impl StyleMap {
/// Returns `Some(_)` with an optional span if this map contains styles for
/// the given `node`.
- pub fn interruption<T: 'static>(&self) -> Option<Option<Span>> {
+ pub fn interruption<T: Node>(&self) -> Option<Option<Span>> {
let node = NodeId::of::<T>();
self.0.iter().find_map(|entry| match entry {
Style::Property(property) => property.is_of(node).then(|| property.origin),
@@ -114,6 +109,12 @@ impl From<Style> for StyleMap {
}
}
+impl PartialEq for StyleMap {
+ fn eq(&self, other: &Self) -> bool {
+ crate::util::hash128(self) == crate::util::hash128(other)
+ }
+}
+
impl Debug for StyleMap {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
for entry in self.0.iter() {
@@ -154,13 +155,11 @@ impl Style {
impl Debug for Style {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.write_str("#[")?;
match self {
- Self::Property(property) => property.fmt(f)?,
- Self::Recipe(recipe) => recipe.fmt(f)?,
- Self::Barrier(id) => write!(f, "Barrier for {id:?}")?,
+ Self::Property(property) => property.fmt(f),
+ Self::Recipe(recipe) => recipe.fmt(f),
+ Self::Barrier(id) => write!(f, "#[Barrier for {id:?}]"),
}
- f.write_str("]")
}
}
@@ -175,12 +174,9 @@ pub struct Property {
/// hierarchy. Used by constructors.
scoped: bool,
/// The property's value.
- value: Arc<Prehashed<dyn Bounds>>,
+ value: Value,
/// The span of the set rule the property stems from.
origin: Option<Span>,
- /// The name of the property.
- #[cfg(debug_assertions)]
- name: &'static str,
}
impl Property {
@@ -189,11 +185,9 @@ impl Property {
Self {
key: KeyId::of::<K>(),
node: K::node(),
- value: Arc::new(Prehashed::new(value)),
+ value: value.into(),
scoped: false,
origin: None,
- #[cfg(debug_assertions)]
- name: K::NAME,
}
}
@@ -208,9 +202,12 @@ impl Property {
}
/// Access the property's value if it is of the given key.
- pub fn downcast<K: Key>(&self) -> Option<&K::Value> {
+ #[track_caller]
+ pub fn cast<K: Key>(&self) -> Option<K::Value> {
if self.key == KeyId::of::<K>() {
- (**self.value).as_any().downcast_ref()
+ Some(self.value.clone().cast().unwrap_or_else(|err| {
+ panic!("{} (for {} with value {:?})", err, self.key.name(), self.value)
+ }))
} else {
None
}
@@ -234,9 +231,7 @@ impl Property {
impl Debug for Property {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- #[cfg(debug_assertions)]
- write!(f, "{} = ", self.name)?;
- write!(f, "{:?}", self.value)?;
+ write!(f, "#set {}({}: {:?})", self.node.name(), self.key.name(), self.value)?;
if self.scoped {
write!(f, " [scoped]")?;
}
@@ -267,47 +262,69 @@ where
/// A style property key.
///
-/// This trait is not intended to be implemented manually, but rather through
-/// the `#[node]` proc-macro.
+/// This trait is not intended to be implemented manually.
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;
+ type Value: Cast + Into<Value>;
/// The folded type of value that is returned when reading this property
/// from a style chain.
- type Output<'a>;
+ type Output;
- /// The name of the property, used for debug printing.
- const NAME: &'static str;
+ /// The id of the property.
+ fn id() -> KeyId;
/// The id of the node the key belongs to.
fn node() -> NodeId;
/// Compute an output value from a sequence of values belonging to this key,
/// folding if necessary.
- fn get<'a>(
- chain: StyleChain<'a>,
- values: impl Iterator<Item = &'a Self::Value>,
- ) -> Self::Output<'a>;
+ fn get(chain: StyleChain, values: impl Iterator<Item = Self::Value>) -> Self::Output;
}
-/// A unique identifier for a property key.
-#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-struct KeyId(ReadableTypeId);
+/// A unique identifier for a style key.
+#[derive(Copy, Clone)]
+pub struct KeyId(&'static KeyMeta);
impl KeyId {
- /// The id of the given key.
pub fn of<T: Key>() -> Self {
- Self(ReadableTypeId::of::<T>())
+ T::id()
+ }
+
+ pub fn from_meta(meta: &'static KeyMeta) -> Self {
+ Self(meta)
+ }
+
+ pub fn name(self) -> &'static str {
+ self.0.name
}
}
impl Debug for KeyId {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- self.0.fmt(f)
+ f.pad(self.name())
+ }
+}
+
+impl Hash for KeyId {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ state.write_usize(self.0 as *const _ as usize);
+ }
+}
+
+impl Eq for KeyId {}
+
+impl PartialEq for KeyId {
+ fn eq(&self, other: &Self) -> bool {
+ std::ptr::eq(self.0, other.0)
}
}
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub struct KeyMeta {
+ pub name: &'static str,
+}
+
/// A show rule recipe.
#[derive(Clone, Hash)]
pub struct Recipe {
@@ -362,7 +379,7 @@ impl Recipe {
impl Debug for Recipe {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "Recipe matching {:?}", self.selector)
+ write!(f, "#show {:?}: {:?}", self.selector, self.transform)
}
}
@@ -382,7 +399,7 @@ pub enum Selector {
impl Selector {
/// Define a simple node selector.
- pub fn node<T: 'static>() -> Self {
+ pub fn node<T: Node>() -> Self {
Self::Node(NodeId::of::<T>(), None)
}
@@ -399,17 +416,25 @@ impl Selector {
&& dict
.iter()
.flat_map(|dict| dict.iter())
- .all(|(name, value)| target.field(name).as_ref() == Some(value))
+ .all(|(name, value)| target.field(name) == Some(value))
}
Self::Label(label) => target.label() == Some(label),
Self::Regex(regex) => {
target.id() == item!(text_id)
- && item!(text_str)(target).map_or(false, |text| regex.is_match(text))
+ && item!(text_str)(target).map_or(false, |text| regex.is_match(&text))
}
}
}
}
+cast_from_value! {
+ Selector: "selector",
+ text: EcoString => Self::text(&text),
+ label: Label => Self::Label(label),
+ func: Func => func.select(None)?,
+ regex: Regex => Self::Regex(regex),
+}
+
/// A show rule transformation that can be applied to a match.
#[derive(Debug, Clone, Hash)]
pub enum Transform {
@@ -421,6 +446,17 @@ pub enum Transform {
Style(StyleMap),
}
+cast_from_value! {
+ Transform,
+ content: Content => Self::Content(content),
+ func: Func => {
+ if func.argc().map_or(false, |count| count != 1) {
+ Err("function must have exactly one parameter")?
+ }
+ Self::Func(func)
+ },
+}
+
/// A chain of style maps, similar to a linked list.
///
/// A style chain allows to combine properties from multiple style maps in a
@@ -478,7 +514,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>(self, key: K) -> K::Output<'a> {
+ pub fn get<K: Key>(self, key: K) -> K::Output {
K::get(self, self.values(key))
}
@@ -534,10 +570,7 @@ impl Debug for StyleChain<'_> {
impl PartialEq for StyleChain<'_> {
fn eq(&self, other: &Self) -> bool {
- let as_ptr = |s| s as *const _;
- self.head.as_ptr() == other.head.as_ptr()
- && self.head.len() == other.head.len()
- && self.tail.map(as_ptr) == other.tail.map(as_ptr)
+ crate::util::hash128(self) == crate::util::hash128(other)
}
}
@@ -585,13 +618,14 @@ struct Values<'a, K> {
}
impl<'a, K: Key> Iterator for Values<'a, K> {
- type Item = &'a K::Value;
+ type Item = K::Value;
+ #[track_caller]
fn next(&mut self) -> Option<Self::Item> {
for entry in &mut self.entries {
match entry {
Style::Property(property) => {
- if let Some(value) = property.downcast::<K>() {
+ if let Some(value) = property.cast::<K>() {
if !property.scoped() || self.barriers <= 1 {
return Some(value);
}
@@ -672,6 +706,20 @@ impl<T> StyleVec<T> {
}
}
+impl StyleVec<Content> {
+ pub fn to_vec(self) -> Vec<Content> {
+ self.items
+ .into_iter()
+ .zip(
+ self.maps
+ .iter()
+ .flat_map(|(map, count)| iter::repeat(map).take(*count)),
+ )
+ .map(|(content, map)| content.styled_with_map(map.clone()))
+ .collect()
+ }
+}
+
impl<T> Default for StyleVec<T> {
fn default() -> Self {
Self { items: vec![], maps: vec![] }
@@ -791,26 +839,6 @@ pub trait Resolve {
fn resolve(self, styles: StyleChain) -> Self::Output;
}
-impl Resolve for Em {
- type Output = Abs;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- if self.is_zero() {
- Abs::zero()
- } else {
- self.at(item!(em)(styles))
- }
- }
-}
-
-impl Resolve for Length {
- type Output = Abs;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.abs + self.em.resolve(styles)
- }
-}
-
impl<T: Resolve> Resolve for Option<T> {
type Output = Option<T::Output>;
@@ -819,74 +847,6 @@ impl<T: Resolve> Resolve for Option<T> {
}
}
-impl<T: Resolve> Resolve for Smart<T> {
- type Output = Smart<T::Output>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.map(|v| v.resolve(styles))
- }
-}
-
-impl<T: Resolve> Resolve for Axes<T> {
- type Output = Axes<T::Output>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.map(|v| v.resolve(styles))
- }
-}
-
-impl<T: Resolve> Resolve for Sides<T> {
- type Output = Sides<T::Output>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.map(|v| v.resolve(styles))
- }
-}
-
-impl<T: Resolve> Resolve for Corners<T> {
- type Output = Corners<T::Output>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.map(|v| v.resolve(styles))
- }
-}
-
-impl<T> Resolve for Rel<T>
-where
- T: Resolve + Numeric,
- <T as Resolve>::Output: Numeric,
-{
- type Output = Rel<<T as Resolve>::Output>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.map(|abs| abs.resolve(styles))
- }
-}
-
-impl Resolve for GenAlign {
- type Output = Align;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- let dir = item!(dir)(styles);
- match self {
- Self::Start => dir.start().into(),
- Self::End => dir.end().into(),
- Self::Specific(align) => align,
- }
- }
-}
-
-impl Resolve for PartialStroke {
- type Output = PartialStroke<Abs>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- PartialStroke {
- paint: self.paint,
- thickness: self.thickness.resolve(styles),
- }
- }
-}
-
/// A property that is folded to determine its final value.
pub trait Fold {
/// The type of the folded output.
@@ -907,92 +867,3 @@ where
self.map(|inner| inner.fold(outer.unwrap_or_default()))
}
}
-
-impl<T> Fold for Smart<T>
-where
- T: Fold,
- T::Output: Default,
-{
- type Output = Smart<T::Output>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.map(|inner| inner.fold(outer.unwrap_or_default()))
- }
-}
-
-impl<T> Fold for Axes<Option<T>>
-where
- T: Fold,
-{
- type Output = Axes<T::Output>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.zip(outer).map(|(inner, outer)| match inner {
- Some(value) => value.fold(outer),
- None => outer,
- })
- }
-}
-
-impl<T> Fold for Sides<Option<T>>
-where
- T: Fold,
-{
- type Output = Sides<T::Output>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.zip(outer).map(|(inner, outer)| match inner {
- Some(value) => value.fold(outer),
- None => outer,
- })
- }
-}
-
-impl<T> Fold for Corners<Option<T>>
-where
- T: Fold,
-{
- type Output = Corners<T::Output>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.zip(outer).map(|(inner, outer)| match inner {
- Some(value) => value.fold(outer),
- None => outer,
- })
- }
-}
-
-impl Fold for PartialStroke<Abs> {
- type Output = Self;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- Self {
- paint: self.paint.or(outer.paint),
- thickness: self.thickness.or(outer.thickness),
- }
- }
-}
-
-impl Fold for Rel<Length> {
- type Output = Self;
-
- fn fold(self, _: Self::Output) -> Self::Output {
- self
- }
-}
-
-impl Fold for Rel<Abs> {
- type Output = Self;
-
- fn fold(self, _: Self::Output) -> Self::Output {
- self
- }
-}
-
-impl Fold for GenAlign {
- type Output = Self;
-
- fn fold(self, _: Self::Output) -> Self::Output {
- self
- }
-}
diff --git a/src/model/typeset.rs b/src/model/typeset.rs
index f8b5e012..6361e6ce 100644
--- a/src/model/typeset.rs
+++ b/src/model/typeset.rs
@@ -8,7 +8,6 @@ use comemo::{Track, Tracked, TrackedMut};
use super::{Content, Selector, StyleChain};
use crate::diag::SourceResult;
use crate::doc::{Document, Element, Frame, Location, Meta};
-use crate::eval::Value;
use crate::geom::Transform;
use crate::util::hash128;
use crate::World;
@@ -162,7 +161,7 @@ impl Introspector {
let pos = pos.transform(ts);
let mut node = content.clone();
let loc = Location { page, pos };
- node.push_field("loc", Value::Dict(loc.encode()));
+ node.push_field("loc", loc);
self.nodes.push((id, node));
}
}