diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-11-21 16:12:24 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-11-21 16:12:24 +0100 |
| commit | 1d7e082d1d83d4c7e454a2d08258794d716aea1a (patch) | |
| tree | 73ee43a9d039edbe34c81a4ea6f4ec4f7f9e11cf /src/model | |
| parent | fd7b9d9e1eb8eef60c20e65dfc946c4424f02c8f (diff) | |
Labels
Diffstat (limited to 'src/model')
| -rw-r--r-- | src/model/content.rs | 77 | ||||
| -rw-r--r-- | src/model/eval.rs | 29 | ||||
| -rw-r--r-- | src/model/styles.rs | 8 |
3 files changed, 75 insertions, 39 deletions
diff --git a/src/model/content.rs b/src/model/content.rs index c28082c2..d72e4e19 100644 --- a/src/model/content.rs +++ b/src/model/content.rs @@ -12,7 +12,7 @@ use typst_macros::node; use super::{Args, Key, Property, Recipe, RecipeId, Style, StyleMap, Value, Vm}; use crate::diag::{SourceResult, StrResult}; use crate::syntax::Span; -use crate::util::ReadableTypeId; +use crate::util::{EcoString, ReadableTypeId}; use crate::World; /// Composable representation of styled content. @@ -21,7 +21,7 @@ use crate::World; /// - anything written between square brackets in Typst /// - any constructor function #[derive(Clone, Hash)] -pub struct Content(Arc<dyn Bounds>, Vec<RecipeId>, Option<Span>); +pub struct Content(Arc<dyn Bounds>, Vec<RecipeId>, Option<Span>, Option<EcoString>); impl Content { /// Create empty content. @@ -42,11 +42,6 @@ impl Content { self.downcast::<SequenceNode>().map_or(false, |seq| seq.0.is_empty()) } - /// The node's span. - pub fn span(&self) -> Option<Span> { - self.2 - } - /// The node's human-readable name. pub fn name(&self) -> &'static str { (*self.0).name() @@ -74,6 +69,13 @@ impl Content { /// 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, + }); + } + self.0.field(name) } @@ -150,35 +152,68 @@ impl Content { StyledNode { sub: self, map: styles }.pack() } - /// Attach a span to the content. - pub fn spanned(mut self, span: Span) -> Self { + /// 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 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 { + *self = StyledNode { sub: styled.sub.clone().spanned(span), map: styled.map.clone(), } .pack(); } self.2 = Some(span); - self } - /// Disable a show rule recipe. - pub fn guard(mut self, id: RecipeId) -> Self { - self.1.push(id); + /// Attach a span to the content. + pub fn spanned(mut self, span: Span) -> Self { + self.set_span(span); self } - /// Whether no show rule was executed for this node so far. - pub fn pristine(&self) -> bool { - self.1.is_empty() + /// The content's label. + pub fn label(&self) -> Option<&EcoString> { + self.3.as_ref() } - /// Check whether a show rule recipe is disabled. - pub fn guarded(&self, id: RecipeId) -> bool { - self.1.contains(&id) + /// Set the content's label. + pub fn set_label(&mut self, label: EcoString) { + self.3 = Some(label); + } + + /// Attacha label to the content. + pub fn labelled(mut self, label: EcoString) -> Self { + self.set_label(label); + self + } + + /// 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(); } } @@ -278,7 +313,7 @@ pub trait Node: 'static { where Self: Debug + Hash + Sync + Send + Sized + 'static, { - Content(Arc::new(self), vec![], None) + Content(Arc::new(self), vec![], None, None) } /// A unique identifier of the node type. diff --git a/src/model/eval.rs b/src/model/eval.rs index 8a596afb..eb6b8ddb 100644 --- a/src/model/eval.rs +++ b/src/model/eval.rs @@ -7,7 +7,7 @@ use unicode_segmentation::UnicodeSegmentation; use super::{ methods, ops, Arg, Args, Array, CapturesVisitor, Closure, Content, Dict, Flow, Func, - Recipe, Scope, Scopes, Selector, StyleMap, Transform, Value, Vm, + Recipe, Scope, Scopes, Selector, StyleMap, Transform, Unlabellable, Value, Vm, }; use crate::diag::{bail, error, At, SourceResult, StrResult, Trace, Tracepoint}; use crate::geom::{Abs, Angle, Em, Fr, Ratio}; @@ -118,14 +118,14 @@ fn eval_markup( let mut seq = Vec::with_capacity(nodes.size_hint().1.unwrap_or_default()); while let Some(node) = nodes.next() { - seq.push(match node { + match node { ast::MarkupNode::Expr(ast::Expr::Set(set)) => { let styles = set.eval(vm)?; if vm.flow.is_some() { break; } - eval_markup(vm, nodes)?.styled_with_map(styles) + seq.push(eval_markup(vm, nodes)?.styled_with_map(styles)) } ast::MarkupNode::Expr(ast::Expr::Show(show)) => { let recipe = show.eval(vm)?; @@ -134,10 +134,17 @@ fn eval_markup( } let tail = eval_markup(vm, nodes)?; - tail.styled_with_recipe(vm.world, recipe)? + seq.push(tail.styled_with_recipe(vm.world, recipe)?) } - _ => node.eval(vm)?, - }); + ast::MarkupNode::Label(label) => { + if let Some(node) = + seq.iter_mut().rev().find(|node| !node.has::<dyn Unlabellable>()) + { + node.set_label(label.get().clone()); + } + } + _ => seq.push(node.eval(vm)?), + } if vm.flow.is_some() { break; @@ -174,7 +181,7 @@ 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(v) => v.eval(vm)?, + Self::Label(_) => unimplemented!("handled above"), Self::Ref(v) => v.eval(vm)?, Self::Expr(v) => v.eval(vm)?.display(vm.world), } @@ -249,14 +256,6 @@ impl Eval for ast::Link { } } -impl Eval for ast::Label { - type Output = Content; - - fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> { - Ok(Content::empty()) - } -} - impl Eval for ast::Ref { type Output = Content; diff --git a/src/model/styles.rs b/src/model/styles.rs index 76c67370..324b52f5 100644 --- a/src/model/styles.rs +++ b/src/model/styles.rs @@ -350,6 +350,10 @@ pub trait Finalize: 'static + Sync + Send { ) -> SourceResult<Content>; } +/// Indicates that a node cannot be labelled. +#[capability] +pub trait Unlabellable: 'static + Sync + Send {} + /// A show rule recipe. #[derive(Clone, PartialEq, Hash)] pub struct Recipe { @@ -392,9 +396,7 @@ impl Recipe { let make = |s| { let mut content = item!(text)(s); - if let Some(span) = target.span() { - content = content.spanned(span); - } + content.copy_meta(target); content }; |
