summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-03-08 13:02:41 +0100
committerLaurenz <laurmaedje@gmail.com>2023-03-08 14:35:33 +0100
commitd7a65fa26d131179d9d82226e5ee1b562084e48a (patch)
treec21ab20e9fb851e14e1ebea3e14fc351b1fdbcc9 /src
parente5eab73374880077971f3f22acbdd3d302877128 (diff)
Rework style chain access
Diffstat (limited to 'src')
-rw-r--r--src/doc.rs2
-rw-r--r--src/eval/mod.rs4
-rw-r--r--src/model/content.rs26
-rw-r--r--src/model/styles.rs252
4 files changed, 75 insertions, 209 deletions
diff --git a/src/doc.rs b/src/doc.rs
index 0c01dcd2..ce6f4c96 100644
--- a/src/doc.rs
+++ b/src/doc.rs
@@ -274,7 +274,7 @@ impl Frame {
if self.is_empty() {
return;
}
- for meta in styles.get(MetaNode::DATA) {
+ for meta in MetaNode::data_in(styles) {
if matches!(meta, Meta::Hidden) {
self.clear();
break;
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 8180f11d..0e0828e3 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -354,7 +354,7 @@ fn eval_markup(
}
let tail = eval_markup(vm, exprs)?;
- seq.push(tail.styled_with_recipe(vm.world, recipe)?)
+ seq.push(tail.apply_recipe(vm.world, recipe)?)
}
expr => match expr.eval(vm)? {
Value::Label(label) => {
@@ -783,7 +783,7 @@ fn eval_code(
}
let tail = eval_code(vm, exprs)?.display();
- Value::Content(tail.styled_with_recipe(vm.world, recipe)?)
+ Value::Content(tail.apply_recipe(vm.world, recipe)?)
}
_ => expr.eval(vm)?,
};
diff --git a/src/model/content.rs b/src/model/content.rs
index 1b87aaea..05c5d430 100644
--- a/src/model/content.rs
+++ b/src/model/content.rs
@@ -7,7 +7,7 @@ use std::ops::{Add, AddAssign};
use comemo::Tracked;
use ecow::{EcoString, EcoVec};
-use super::{node, Guard, Key, Property, Recipe, Style, StyleMap};
+use super::{node, Guard, Recipe, Style, StyleMap};
use crate::diag::{SourceResult, StrResult};
use crate::eval::{cast_from_value, Args, Cast, ParamInfo, Value, Vm};
use crate::syntax::Span;
@@ -66,14 +66,9 @@ impl Content {
self.with_field("label", label)
}
- /// Style this content with a single style property.
- 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(self, style: Style) -> Self {
- self.styled_with_map(style.into())
+ pub fn styled(self, style: impl Into<Style>) -> Self {
+ self.styled_with_map(style.into().into())
}
/// Style this content with a full style map.
@@ -90,7 +85,7 @@ impl Content {
}
/// Style this content with a recipe, eagerly applying it if possible.
- pub fn styled_with_recipe(
+ pub fn apply_recipe(
self,
world: Tracked<dyn World>,
recipe: Recipe,
@@ -98,7 +93,7 @@ impl Content {
if recipe.selector.is_none() {
recipe.apply(world, self)
} else {
- Ok(self.styled_with_entry(Style::Recipe(recipe)))
+ Ok(self.styled(Style::Recipe(recipe)))
}
}
@@ -135,6 +130,7 @@ impl Content {
}
}
+ /// Attach a field to the content.
pub fn with_field(
mut self,
name: impl Into<EcoString>,
@@ -154,6 +150,7 @@ impl Content {
}
}
+ /// Access a field on the content.
pub fn field(&self, name: &str) -> Option<&Value> {
static NONE: Value = Value::None;
self.fields
@@ -163,10 +160,6 @@ impl Content {
.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) {
@@ -175,6 +168,11 @@ impl Content {
}
}
+ /// List all fields on the content.
+ pub fn fields(&self) -> &[(EcoString, Value)] {
+ &self.fields
+ }
+
/// Whether the contained node is of type `T`.
pub fn is<T>(&self) -> bool
where
diff --git a/src/model/styles.rs b/src/model/styles.rs
index cbf4cfb2..bd7a062f 100644
--- a/src/model/styles.rs
+++ b/src/model/styles.rs
@@ -1,8 +1,5 @@
-use std::any::Any;
use std::fmt::{self, Debug, Formatter};
-use std::hash::{Hash, Hasher};
use std::iter;
-use std::marker::PhantomData;
use comemo::Tracked;
use ecow::EcoString;
@@ -33,15 +30,13 @@ 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<K: Key>(&mut self, key: K, value: K::Value) {
- self.0.push(Style::Property(Property::new(key, value)));
+ pub fn set(&mut self, property: Property) {
+ self.0.push(Style::Property(property));
}
/// Set an inner value for a style property if it is `Some(_)`.
- pub fn set_opt<K: Key>(&mut self, key: K, value: Option<K::Value>) {
- if let Some(value) = value {
- self.set(key, value);
- }
+ pub fn set_opt(&mut self, property: Option<Property>) {
+ self.0.extend(property.map(Style::Property));
}
/// Remove the style that was last set.
@@ -49,14 +44,6 @@ impl StyleMap {
self.0.pop();
}
- /// Whether the map contains a style property for the given key.
- pub fn contains<K: Key>(&self, _: K) -> bool {
- self.0
- .iter()
- .filter_map(|entry| entry.property())
- .any(|property| property.is::<K>())
- }
-
/// Apply outer styles. Like [`chain`](StyleChain::chain), but in-place.
pub fn apply(&mut self, outer: Self) {
self.0.splice(0..0, outer.0.iter().cloned());
@@ -75,7 +62,7 @@ impl StyleMap {
pub fn scoped(mut self) -> Self {
for entry in &mut self.0 {
if let Style::Property(property) = entry {
- property.make_scoped();
+ property.scoped = true;
}
}
self
@@ -163,37 +150,37 @@ impl Debug for Style {
}
}
+impl From<Property> for Style {
+ fn from(property: Property) -> Self {
+ Self::Property(property)
+ }
+}
+
/// A style property originating from a set rule or constructor.
#[derive(Clone, Hash)]
pub struct Property {
- /// The id of the property's [key](Key).
- key: KeyId,
/// The id of the node the property belongs to.
node: NodeId,
+ /// The property's name.
+ name: EcoString,
+ /// The property's value.
+ value: Value,
/// Whether the property should only affect the first node down the
/// hierarchy. Used by constructors.
scoped: bool,
- /// The property's value.
- value: Value,
/// The span of the set rule the property stems from.
origin: Option<Span>,
}
impl Property {
/// Create a new property from a key-value pair.
- pub fn new<K: Key>(_: K, value: K::Value) -> Self {
- Self {
- key: KeyId::of::<K>(),
- node: K::node(),
- value: value.into(),
- scoped: false,
- origin: None,
- }
+ pub fn new(node: NodeId, name: EcoString, value: Value) -> Self {
+ Self { node, name, value, scoped: false, origin: None }
}
- /// Whether this property has the given key.
- pub fn is<K: Key>(&self) -> bool {
- self.key == KeyId::of::<K>()
+ /// Whether this property is the given one.
+ pub fn is(&self, node: NodeId, name: &str) -> bool {
+ self.node == node && self.name == name
}
/// Whether this property belongs to the node with the given id.
@@ -201,37 +188,24 @@ impl Property {
self.node == node
}
- /// Access the property's value if it is of the given key.
+ /// Access the property's value as the given type.
#[track_caller]
- pub fn cast<K: Key>(&self) -> Option<K::Value> {
- if self.key == KeyId::of::<K>() {
- Some(self.value.clone().cast().unwrap_or_else(|err| {
- panic!("{} (for {} with value {:?})", err, self.key.name(), self.value)
- }))
- } else {
- None
- }
- }
-
- /// The node this property is for.
- pub fn node(&self) -> NodeId {
- self.node
- }
-
- /// Whether the property is scoped.
- pub fn scoped(&self) -> bool {
- self.scoped
- }
-
- /// Make the property scoped.
- pub fn make_scoped(&mut self) {
- self.scoped = true;
+ pub fn cast<T: Cast>(&self) -> T {
+ self.value.clone().cast().unwrap_or_else(|err| {
+ panic!(
+ "{} (for {}.{} with value {:?})",
+ err,
+ self.node.name(),
+ self.name,
+ self.value
+ )
+ })
}
}
impl Debug for Property {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "#set {}({}: {:?})", self.node.name(), self.key.name(), self.value)?;
+ write!(f, "#set {}({}: {:?})", self.node.name(), self.name, self.value)?;
if self.scoped {
write!(f, " [scoped]")?;
}
@@ -239,92 +213,6 @@ impl Debug for Property {
}
}
-impl PartialEq for Property {
- fn eq(&self, other: &Self) -> bool {
- self.key == other.key
- && self.value.eq(&other.value)
- && self.scoped == other.scoped
- }
-}
-
-trait Bounds: Debug + Sync + Send + 'static {
- fn as_any(&self) -> &dyn Any;
-}
-
-impl<T> Bounds for T
-where
- T: Debug + Sync + Send + 'static,
-{
- fn as_any(&self) -> &dyn Any {
- self
- }
-}
-
-/// A style property key.
-///
-/// 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: Cast + Into<Value>;
-
- /// The folded type of value that is returned when reading this property
- /// from a style chain.
- type Output;
-
- /// 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(chain: StyleChain, values: impl Iterator<Item = Self::Value>) -> Self::Output;
-}
-
-/// A unique identifier for a style key.
-#[derive(Copy, Clone)]
-pub struct KeyId(&'static KeyMeta);
-
-impl KeyId {
- pub fn of<T: Key>() -> Self {
- 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 {
- 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 {
@@ -497,7 +385,7 @@ impl<'a> StyleChain<'a> {
if !self
.entries()
.filter_map(Style::property)
- .any(|p| p.scoped() && *id == p.node())
+ .any(|p| p.scoped && *id == p.node)
{
return *self;
}
@@ -509,27 +397,39 @@ impl<'a> StyleChain<'a> {
}
}
- /// Get the output value of a style property.
- ///
- /// 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 {
- K::get(self, self.values(key))
- }
-
/// Iterate over all style recipes in the chain.
pub fn recipes(self) -> impl Iterator<Item = &'a Recipe> {
self.entries().filter_map(Style::recipe)
}
+ /// Cast the first value for the given property in the chain.
+ pub fn property<T: Cast>(self, node: NodeId, name: &'a str) -> Option<T> {
+ self.properties(node, name).next()
+ }
+
/// Iterate over all values for the given property in the chain.
- fn values<K: Key>(self, _: K) -> Values<'a, K> {
- Values {
- entries: self.entries(),
- key: PhantomData,
- barriers: 0,
- }
+ pub fn properties<T: Cast>(
+ self,
+ node: NodeId,
+ name: &'a str,
+ ) -> impl Iterator<Item = T> + '_ {
+ let mut barriers = 0;
+ self.entries().filter_map(move |entry| {
+ match entry {
+ Style::Property(property) => {
+ if property.is(node, name) {
+ if !property.scoped || barriers <= 1 {
+ return Some(property.cast());
+ }
+ }
+ }
+ Style::Barrier(id) => {
+ barriers += (*id == node) as usize;
+ }
+ _ => {}
+ }
+ None
+ })
}
/// Iterate over the entries of the chain.
@@ -610,38 +510,6 @@ impl<'a> Iterator for Links<'a> {
}
}
-/// An iterator over the values in a style chain.
-struct Values<'a, K> {
- entries: Entries<'a>,
- key: PhantomData<K>,
- barriers: usize,
-}
-
-impl<'a, K: Key> Iterator for Values<'a, K> {
- 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.cast::<K>() {
- if !property.scoped() || self.barriers <= 1 {
- return Some(value);
- }
- }
- }
- Style::Barrier(id) => {
- self.barriers += (*id == K::node()) as usize;
- }
- _ => {}
- }
- }
-
- None
- }
-}
-
/// A sequence of items with associated styles.
#[derive(Clone, Hash)]
pub struct StyleVec<T> {