From 1d7e082d1d83d4c7e454a2d08258794d716aea1a Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 21 Nov 2022 16:12:24 +0100 Subject: [PATCH] Labels --- library/src/prelude.rs | 2 +- library/src/text/mod.rs | 4 +- library/src/text/par.rs | 4 +- src/model/content.rs | 81 +++++++++++++++++++++++++++----------- src/model/eval.rs | 29 +++++++------- src/model/styles.rs | 8 ++-- tests/ref/style/label.png | Bin 0 -> 18402 bytes tests/typ/style/label.typ | 54 +++++++++++++++++++++++++ 8 files changed, 138 insertions(+), 44 deletions(-) create mode 100644 tests/ref/style/label.png create mode 100644 tests/typ/style/label.typ diff --git a/library/src/prelude.rs b/library/src/prelude.rs index c6bbe676b..02d4ad10f 100644 --- a/library/src/prelude.rs +++ b/library/src/prelude.rs @@ -17,7 +17,7 @@ pub use typst::geom::*; pub use typst::model::{ array, capability, castable, dict, dynamic, format_str, node, Args, Array, Cast, Content, Dict, Finalize, Fold, Func, Node, NodeId, RecipeId, Resolve, Show, Smart, - Str, StyleChain, StyleMap, StyleVec, Value, Vm, + Str, StyleChain, StyleMap, StyleVec, Unlabellable, Value, Vm, }; #[doc(no_inline)] pub use typst::syntax::{Span, Spanned}; diff --git a/library/src/text/mod.rs b/library/src/text/mod.rs index c12a61cb0..458083108 100644 --- a/library/src/text/mod.rs +++ b/library/src/text/mod.rs @@ -414,13 +414,15 @@ impl Fold for FontFeatures { #[derive(Debug, Hash)] pub struct SpaceNode; -#[node(Behave)] +#[node(Unlabellable, Behave)] impl SpaceNode { fn construct(_: &mut Vm, _: &mut Args) -> SourceResult { Ok(Self.pack()) } } +impl Unlabellable for SpaceNode {} + impl Behave for SpaceNode { fn behaviour(&self) -> Behaviour { Behaviour::Weak(2) diff --git a/library/src/text/par.rs b/library/src/text/par.rs index 6551ae9c4..37d5e3965 100644 --- a/library/src/text/par.rs +++ b/library/src/text/par.rs @@ -117,13 +117,15 @@ castable! { #[derive(Debug, Hash)] pub struct ParbreakNode; -#[node] +#[node(Unlabellable)] impl ParbreakNode { fn construct(_: &mut Vm, _: &mut Args) -> SourceResult { Ok(Self.pack()) } } +impl Unlabellable for ParbreakNode {} + /// Repeats content to fill a line. #[derive(Debug, Hash)] pub struct RepeatNode(pub Content); diff --git a/src/model/content.rs b/src/model/content.rs index c28082c2f..d72e4e19a 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, Vec, Option); +pub struct Content(Arc, Vec, Option, Option); impl Content { /// Create empty content. @@ -42,11 +42,6 @@ impl Content { self.downcast::().map_or(false, |seq| seq.0.is_empty()) } - /// The node's span. - pub fn span(&self) -> Option { - 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 { + if name == "label" { + return Some(match &self.3 { + Some(label) => Value::Str(label.clone().into()), + None => Value::None, + }); + } + self.0.field(name) } @@ -150,21 +152,6 @@ impl Content { StyledNode { sub: self, map: styles }.pack() } - /// Attach a span to the content. - pub fn spanned(mut self, span: Span) -> Self { - if let Some(styled) = self.try_downcast_mut::() { - styled.sub.2 = Some(span); - } else if let Some(styled) = self.downcast::() { - 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); @@ -180,6 +167,54 @@ impl Content { pub fn guarded(&self, id: RecipeId) -> bool { self.1.contains(&id) } + + /// The node's span. + pub fn span(&self) -> Option { + self.2 + } + + /// Set the content's span. + pub fn set_span(&mut self, span: Span) { + if let Some(styled) = self.try_downcast_mut::() { + styled.sub.2 = Some(span); + } else if let Some(styled) = self.downcast::() { + *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 + } + + /// The content's label. + pub fn label(&self) -> Option<&EcoString> { + self.3.as_ref() + } + + /// 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(); + } } impl Default for Content { @@ -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 8a596afbf..eb6b8ddb7 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::()) + { + 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 { - Ok(Content::empty()) - } -} - impl Eval for ast::Ref { type Output = Content; diff --git a/src/model/styles.rs b/src/model/styles.rs index 76c673707..324b52f50 100644 --- a/src/model/styles.rs +++ b/src/model/styles.rs @@ -350,6 +350,10 @@ pub trait Finalize: 'static + Sync + Send { ) -> SourceResult; } +/// 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 }; diff --git a/tests/ref/style/label.png b/tests/ref/style/label.png new file mode 100644 index 0000000000000000000000000000000000000000..836899e1ed37140c257ba7d6c5049f8615fbf522 GIT binary patch literal 18402 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 Fe*gl?KqUYG literal 0 HcmV?d00001 diff --git a/tests/typ/style/label.typ b/tests/typ/style/label.typ new file mode 100644 index 000000000..0b6677835 --- /dev/null +++ b/tests/typ/style/label.typ @@ -0,0 +1,54 @@ +// Test labels. + +--- +// Test labelled headings. +#show heading: text.with(10pt) +#show heading.where(label: "intro"): underline + += Introduction +The beginning. + += Conclusion +The end. + +--- +// Test label after expression. +#show strong.where(label: "v"): text.with(red) + +#let a = [*A*] +#let b = [*B*] +#a #b + +--- +// Test labelled text. +#show "t": it => { + set text(blue) if it.label == "last" + it +} + +This is a thing [that ] happened. + +--- +// Test abusing labels for styling. +#show strong.where(label: "red"): text.with(red) +#show strong.where(label: "blue"): text.with(blue) + +*A* *B* *C* *D* + +--- +// Test that label ignores parbreak. +#show emph.where(label: "hide"): none + +_Hidden_ + + +_Hidden_ + + +_Visible_ + +--- +// Test that label only works within one content block. +#show strong.where(label: "strike"): strike +*This is* [] *protected.* +*This is not.*