diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-11-26 13:39:18 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-11-26 13:41:27 +0100 |
| commit | 7af46fc025ee08eb78ae7f6898300083c886bf6f (patch) | |
| tree | 5837d972961844650bc9668d8516d7b5239a8d18 /src/model | |
| parent | 3cdd8bfa40fe5fdf0c676af905c3c2c1f614ef24 (diff) | |
Dynamic labels
Diffstat (limited to 'src/model')
| -rw-r--r-- | src/model/cast.rs | 3 | ||||
| -rw-r--r-- | src/model/content.rs | 18 | ||||
| -rw-r--r-- | src/model/eval.rs | 19 | ||||
| -rw-r--r-- | src/model/ops.rs | 1 | ||||
| -rw-r--r-- | src/model/realize.rs | 8 | ||||
| -rw-r--r-- | src/model/styles.rs | 14 | ||||
| -rw-r--r-- | src/model/value.rs | 8 |
7 files changed, 54 insertions, 17 deletions
diff --git a/src/model/cast.rs b/src/model/cast.rs index d5b4893d..0e7739ac 100644 --- a/src/model/cast.rs +++ b/src/model/cast.rs @@ -183,8 +183,9 @@ dynamic! { dynamic! { Selector: "selector", - Value::Func(func) => Self::Node(func.node()?, None), Value::Str(text) => Self::text(&text), + Value::Label(label) => Self::Label(label), + Value::Func(func) => Self::Node(func.node()?, None), @regex: Regex => Self::Regex(regex.clone()), } diff --git a/src/model/content.rs b/src/model/content.rs index 0faf76cb..a7161798 100644 --- a/src/model/content.rs +++ b/src/model/content.rs @@ -21,7 +21,7 @@ pub struct Content { obj: Arc<dyn Bounds>, guards: Vec<Guard>, span: Option<Span>, - label: Option<EcoString>, + label: Option<Label>, } impl Content { @@ -54,7 +54,7 @@ impl Content { } /// Attach a label to the content. - pub fn labelled(mut self, label: EcoString) -> Self { + pub fn labelled(mut self, label: Label) -> Self { self.label = Some(label); self } @@ -131,7 +131,7 @@ impl Content { } /// The content's label. - pub fn label(&self) -> Option<&EcoString> { + pub fn label(&self) -> Option<&Label> { self.label.as_ref() } @@ -139,7 +139,7 @@ impl Content { pub fn field(&self, name: &str) -> Option<Value> { if name == "label" { return Some(match &self.label { - Some(label) => Value::Str(label.clone().into()), + Some(label) => Value::Label(label.clone()), None => Value::None, }); } @@ -335,6 +335,16 @@ impl Debug for SequenceNode { } } +/// A label for a node. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct Label(pub EcoString); + +impl Debug for Label { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "<{}>", self.0) + } +} + /// A constructable, stylable content node. pub trait Node: 'static + Capable { /// Pack a node into type-erased content. diff --git a/src/model/eval.rs b/src/model/eval.rs index 9ed6195e..6c21a666 100644 --- a/src/model/eval.rs +++ b/src/model/eval.rs @@ -8,7 +8,7 @@ use comemo::{Track, Tracked}; use unicode_segmentation::UnicodeSegmentation; use super::{ - methods, ops, Arg, Args, Array, CapturesVisitor, Closure, Content, Dict, Func, + methods, ops, Arg, Args, Array, CapturesVisitor, Closure, Content, Dict, Func, Label, LangItems, Recipe, Scope, Scopes, Selector, StyleMap, Transform, Value, }; use crate::diag::{ @@ -231,11 +231,16 @@ fn eval_markup( let tail = eval_markup(vm, nodes)?; seq.push(tail.styled_with_recipe(vm.world, recipe)?) } - ast::MarkupNode::Label(label) => { - if let Some(node) = seq.iter_mut().rev().find(|node| node.labellable()) { - *node = mem::take(node).labelled(label.get().clone()); + ast::MarkupNode::Expr(expr) => match expr.eval(vm)? { + Value::Label(label) => { + if let Some(node) = + seq.iter_mut().rev().find(|node| node.labellable()) + { + *node = mem::take(node).labelled(label); + } } - } + value => seq.push(value.display().spanned(expr.span())), + }, _ => seq.push(node.eval(vm)?), } @@ -274,9 +279,8 @@ impl Eval for ast::MarkupNode { Self::List(v) => v.eval(vm)?, Self::Enum(v) => v.eval(vm)?, Self::Desc(v) => v.eval(vm)?, - Self::Label(_) => unimplemented!("handled above"), Self::Ref(v) => v.eval(vm)?, - Self::Expr(v) => v.eval(vm)?.display(), + Self::Expr(_) => unimplemented!("handled above"), } .spanned(self.span())) } @@ -527,6 +531,7 @@ impl Eval for ast::Lit { Unit::Percent => Ratio::new(v / 100.0).into(), }, ast::LitKind::Str(v) => Value::Str(v.into()), + ast::LitKind::Label(v) => Value::Label(Label(v)), }) } } diff --git a/src/model/ops.rs b/src/model/ops.rs index 0110fb96..9a731d65 100644 --- a/src/model/ops.rs +++ b/src/model/ops.rs @@ -307,6 +307,7 @@ pub fn equal(lhs: &Value, rhs: &Value) -> bool { (Fraction(a), Fraction(b)) => a == b, (Color(a), Color(b)) => a == b, (Str(a), Str(b)) => a == b, + (Label(a), Label(b)) => a == b, (Content(a), Content(b)) => a == b, (Array(a), Array(b)) => a == b, (Dict(a), Dict(b)) => a == b, diff --git a/src/model/realize.rs b/src/model/realize.rs index d63c1aac..9d7c4aec 100644 --- a/src/model/realize.rs +++ b/src/model/realize.rs @@ -78,6 +78,14 @@ fn try_apply( recipe.apply(world, target.clone().guarded(guard)).map(Some) } + Some(Selector::Label(label)) => { + if target.label() != Some(label) { + return Ok(None); + } + + recipe.apply(world, target.clone().guarded(guard)).map(Some) + } + Some(Selector::Regex(regex)) => { let Some(text) = item!(text_str)(&target) else { return Ok(None); diff --git a/src/model/styles.rs b/src/model/styles.rs index 966a57ec..f3cfb648 100644 --- a/src/model/styles.rs +++ b/src/model/styles.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use comemo::{Prehashed, Tracked}; -use super::{Args, Content, Dict, Func, NodeId, Regex, Smart, Value}; +use super::{Args, Content, Dict, Func, Label, NodeId, Regex, Smart, Value}; use crate::diag::{SourceResult, Trace, Tracepoint}; use crate::geom::{ Abs, Align, Axes, Corners, Em, GenAlign, Length, Numeric, PartialStroke, Rel, Sides, @@ -354,7 +354,9 @@ pub enum Selector { /// If there is a dictionary, only nodes with the fields from the /// dictionary match. Node(NodeId, Option<Dict>), - /// Matches text through a regular expression. + /// Matches nodes with a specific label. + Label(Label), + /// Matches text nodes through a regular expression. Regex(Regex), } @@ -368,13 +370,17 @@ impl Selector { pub fn matches(&self, target: &Content) -> bool { match self { Self::Node(id, dict) => { - *id == target.id() + target.id() == *id && dict .iter() .flat_map(|dict| dict.iter()) .all(|(name, value)| target.field(name).as_ref() == Some(value)) } - Self::Regex(_) => target.id() == item!(text_id), + 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)) + } } } } diff --git a/src/model/value.rs b/src/model/value.rs index aec68ca1..043fde34 100644 --- a/src/model/value.rs +++ b/src/model/value.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use siphasher::sip128::{Hasher128, SipHasher}; -use super::{format_str, ops, Args, Array, Cast, Content, Dict, Func, Str}; +use super::{format_str, ops, Args, Array, Cast, Content, Dict, Func, Label, Str}; use crate::diag::StrResult; use crate::geom::{Abs, Angle, Color, Em, Fr, Length, Ratio, Rel, RgbaColor}; use crate::util::{format_eco, EcoString}; @@ -38,6 +38,8 @@ pub enum Value { Color(Color), /// A string: `"string"`. Str(Str), + /// A label: `<intro>`. + Label(Label), /// A content value: `[*Hi* there]`. Content(Content), /// An array of values: `(1, "hi", 12cm)`. @@ -76,6 +78,7 @@ impl Value { Self::Fraction(_) => Fr::TYPE_NAME, Self::Color(_) => Color::TYPE_NAME, Self::Str(_) => Str::TYPE_NAME, + Self::Label(_) => Label::TYPE_NAME, Self::Content(_) => Content::TYPE_NAME, Self::Array(_) => Array::TYPE_NAME, Self::Dict(_) => Dict::TYPE_NAME, @@ -130,6 +133,7 @@ impl Debug for Value { Self::Fraction(v) => Debug::fmt(v, f), Self::Color(v) => Debug::fmt(v, f), Self::Str(v) => Debug::fmt(v, f), + Self::Label(v) => Debug::fmt(v, f), Self::Content(_) => f.pad("[...]"), Self::Array(v) => Debug::fmt(v, f), Self::Dict(v) => Debug::fmt(v, f), @@ -168,6 +172,7 @@ impl Hash for Value { Self::Fraction(v) => v.hash(state), Self::Color(v) => v.hash(state), Self::Str(v) => v.hash(state), + Self::Label(v) => v.hash(state), Self::Content(v) => v.hash(state), Self::Array(v) => v.hash(state), Self::Dict(v) => v.hash(state), @@ -373,6 +378,7 @@ primitive! { Rel<Length>: "relative length", primitive! { Fr: "fraction", Fraction } primitive! { Color: "color", Color } primitive! { Str: "string", Str } +primitive! { Label: "label", Label } primitive! { Content: "content", Content, None => Content::empty(), |
