summaryrefslogtreecommitdiff
path: root/library/src/meta
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-03-07 15:17:13 +0100
committerLaurenz <laurmaedje@gmail.com>2023-03-07 15:17:13 +0100
commit25b5bd117529cd04bb789e1988eb3a3db8025a0e (patch)
tree2fbb4650903123da047a1f1f11a0abda95286e12 /library/src/meta
parent6ab7760822ccd24b4ef126d4737d41f1be15fe19 (diff)
Fully untyped model
Diffstat (limited to 'library/src/meta')
-rw-r--r--library/src/meta/document.rs65
-rw-r--r--library/src/meta/heading.rs82
-rw-r--r--library/src/meta/link.rs62
-rw-r--r--library/src/meta/numbering.rs66
-rw-r--r--library/src/meta/outline.rs89
-rw-r--r--library/src/meta/reference.rs34
6 files changed, 199 insertions, 199 deletions
diff --git a/library/src/meta/document.rs b/library/src/meta/document.rs
index 1d349b89..0d03b496 100644
--- a/library/src/meta/document.rs
+++ b/library/src/meta/document.rs
@@ -1,7 +1,8 @@
+use typst::model::StyledNode;
+
use crate::layout::{LayoutRoot, PageNode};
use crate::prelude::*;
-/// # Document
/// The root element of a document and its metadata.
///
/// All documents are automatically wrapped in a `document` element. The main
@@ -11,33 +12,48 @@ use crate::prelude::*;
/// The metadata set with this function is not rendered within the document.
/// Instead, it is embedded in the compiled PDF file.
///
-/// ## Category
-/// meta
-#[func]
-#[capable(LayoutRoot)]
-#[derive(Hash)]
-pub struct DocumentNode(pub StyleVec<PageNode>);
+/// Display: Document
+/// Category: meta
+#[node(LayoutRoot)]
+pub struct DocumentNode {
+ /// The page runs.
+ #[variadic]
+ pub children: Vec<Content>,
-#[node]
-impl DocumentNode {
/// The document's title. This is often rendered as the title of the
/// PDF viewer window.
- #[property(referenced)]
- pub const TITLE: Option<EcoString> = None;
+ #[settable]
+ #[default]
+ pub title: Option<EcoString>,
/// The document's authors.
- #[property(referenced)]
- pub const AUTHOR: Author = Author(vec![]);
+ #[settable]
+ #[default]
+ pub author: Author,
}
impl LayoutRoot for DocumentNode {
/// Layout the document into a sequence of frames, one per page.
fn layout_root(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Document> {
let mut pages = vec![];
- for (page, map) in self.0.iter() {
- let number = 1 + pages.len();
- let fragment = page.layout(vt, number, styles.chain(map))?;
- pages.extend(fragment);
+
+ for mut child in self.children() {
+ let map;
+ let outer = styles;
+ let mut styles = outer;
+ if let Some(node) = child.to::<StyledNode>() {
+ map = node.map();
+ styles = outer.chain(&map);
+ child = node.sub();
+ }
+
+ if let Some(page) = child.to::<PageNode>() {
+ let number = 1 + pages.len();
+ let fragment = page.layout(vt, number, styles)?;
+ pages.extend(fragment);
+ } else if let Some(span) = child.span() {
+ bail!(span, "unexpected document child");
+ }
}
Ok(Document {
@@ -48,19 +64,16 @@ impl LayoutRoot for DocumentNode {
}
}
-impl Debug for DocumentNode {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.write_str("Document ")?;
- self.0.fmt(f)
- }
-}
-
/// A list of authors.
-#[derive(Debug, Clone, Hash)]
+#[derive(Debug, Default, Clone, Hash)]
pub struct Author(Vec<EcoString>);
-castable! {
+cast_from_value! {
Author,
v: EcoString => Self(vec![v]),
v: Array => Self(v.into_iter().map(Value::cast).collect::<StrResult<_>>()?),
}
+
+cast_to_value! {
+ v: Author => v.0.into()
+}
diff --git a/library/src/meta/heading.rs b/library/src/meta/heading.rs
index f108cad1..38890885 100644
--- a/library/src/meta/heading.rs
+++ b/library/src/meta/heading.rs
@@ -5,7 +5,6 @@ use crate::layout::{BlockNode, HNode, VNode};
use crate::prelude::*;
use crate::text::{TextNode, TextSize};
-/// # Heading
/// A section heading.
///
/// With headings, you can structure your document into sections. Each heading
@@ -39,28 +38,20 @@ use crate::text::{TextNode, TextSize};
/// one or multiple equals signs, followed by a space. The number of equals
/// signs determines the heading's logical nesting depth.
///
-/// ## Parameters
-/// - title: `Content` (positional, required)
-/// The heading's title.
-///
-/// - level: `NonZeroUsize` (named)
-/// The logical nesting depth of the heading, starting from one.
-///
-/// ## Category
-/// meta
-#[func]
-#[capable(Prepare, Show, Finalize)]
-#[derive(Debug, Hash)]
+/// Display: Heading
+/// Category: meta
+#[node(Prepare, Show, Finalize)]
pub struct HeadingNode {
- /// The logical nesting depth of the section, starting from one. In the
- /// default style, this controls the text size of the heading.
- pub level: NonZeroUsize,
- /// The heading's contents.
+ /// The heading's title.
+ #[positional]
+ #[required]
pub title: Content,
-}
-#[node]
-impl HeadingNode {
+ /// The logical nesting depth of the heading, starting from one.
+ #[named]
+ #[default(NonZeroUsize::new(1).unwrap())]
+ pub level: NonZeroUsize,
+
/// How to number the heading. Accepts a
/// [numbering pattern or function]($func/numbering).
///
@@ -71,8 +62,9 @@ impl HeadingNode {
/// == A subsection
/// === A sub-subsection
/// ```
- #[property(referenced)]
- pub const NUMBERING: Option<Numbering> = None;
+ #[settable]
+ #[default]
+ pub numbering: Option<Numbering>,
/// Whether the heading should appear in the outline.
///
@@ -86,23 +78,9 @@ impl HeadingNode {
/// This heading does not appear
/// in the outline.
/// ```
- pub const OUTLINED: bool = true;
-
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self {
- title: args.expect("title")?,
- level: args.named("level")?.unwrap_or(NonZeroUsize::new(1).unwrap()),
- }
- .pack())
- }
-
- fn field(&self, name: &str) -> Option<Value> {
- match name {
- "level" => Some(Value::Int(self.level.get() as i64)),
- "title" => Some(Value::Content(self.title.clone())),
- _ => None,
- }
- }
+ #[settable]
+ #[default(true)]
+ pub outlined: bool,
}
impl Prepare for HeadingNode {
@@ -121,7 +99,7 @@ impl Prepare for HeadingNode {
}
let numbers = node.field("numbers").unwrap();
- if numbers != Value::None {
+ if *numbers != Value::None {
let heading = node.to::<Self>().unwrap();
counter.advance(heading);
}
@@ -136,38 +114,34 @@ impl Prepare for HeadingNode {
this.push_field("numbers", numbers);
let meta = Meta::Node(my_id, this.clone());
- Ok(this.styled(Meta::DATA, vec![meta]))
+ Ok(this.styled(MetaNode::DATA, vec![meta]))
}
}
impl Show for HeadingNode {
fn show(&self, _: &mut Vt, this: &Content, _: StyleChain) -> SourceResult<Content> {
- let mut realized = self.title.clone();
+ let mut realized = self.title();
let numbers = this.field("numbers").unwrap();
- if numbers != Value::None {
- realized = numbers.display()
- + HNode { amount: Em::new(0.3).into(), weak: true }.pack()
+ if *numbers != Value::None {
+ realized = numbers.clone().display()
+ + HNode::new(Em::new(0.3).into()).with_weak(true).pack()
+ realized;
}
- Ok(BlockNode {
- body: realized,
- width: Smart::Auto,
- height: Smart::Auto,
- }
- .pack())
+ Ok(BlockNode::new().with_body(realized).pack())
}
}
impl Finalize for HeadingNode {
fn finalize(&self, realized: Content) -> Content {
- let scale = match self.level.get() {
+ let level = self.level().get();
+ let scale = match level {
1 => 1.4,
2 => 1.2,
_ => 1.0,
};
let size = Em::new(scale);
- let above = Em::new(if self.level.get() == 1 { 1.8 } else { 1.44 }) / scale;
+ let above = Em::new(if level == 1 { 1.8 } else { 1.44 }) / scale;
let below = Em::new(0.75) / scale;
let mut map = StyleMap::new();
@@ -191,7 +165,7 @@ impl HeadingCounter {
/// Advance the counter and return the numbers for the given heading.
pub fn advance(&mut self, heading: &HeadingNode) -> &[NonZeroUsize] {
- let level = heading.level.get();
+ let level = heading.level().get();
if self.0.len() >= level {
self.0[level - 1] = self.0[level - 1].saturating_add(1);
diff --git a/library/src/meta/link.rs b/library/src/meta/link.rs
index 61e75b8a..63a8ec98 100644
--- a/library/src/meta/link.rs
+++ b/library/src/meta/link.rs
@@ -1,7 +1,6 @@
use crate::prelude::*;
use crate::text::{Hyphenate, TextNode};
-/// # Link
/// Link to a URL or another location in the document.
///
/// The link function makes its positional `body` argument clickable and links
@@ -50,67 +49,62 @@ use crate::text::{Hyphenate, TextNode};
/// The content that should become a link. If `dest` is an URL string, the
/// parameter can be omitted. In this case, the URL will be shown as the link.
///
-/// ## Category
-/// meta
-#[func]
-#[capable(Show, Finalize)]
-#[derive(Debug, Hash)]
+/// Display: Link
+/// Category: meta
+#[node(Construct, Show, Finalize)]
pub struct LinkNode {
/// The destination the link points to.
+ #[positional]
+ #[required]
pub dest: Destination,
+
/// How the link is represented.
+ #[positional]
+ #[default]
pub body: Content,
}
impl LinkNode {
/// Create a link node from a URL with its bare text.
pub fn from_url(url: EcoString) -> Self {
- let mut text = url.as_str();
- for prefix in ["mailto:", "tel:"] {
- text = text.trim_start_matches(prefix);
- }
- let shorter = text.len() < url.len();
- let body = TextNode::packed(if shorter { text.into() } else { url.clone() });
- Self { dest: Destination::Url(url), body }
+ let body = body_from_url(&url);
+ Self::new(Destination::Url(url)).with_body(body)
}
}
-#[node]
-impl LinkNode {
+impl Construct for LinkNode {
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let dest = args.expect::<Destination>("destination")?;
- Ok(match dest {
+ let body = match &dest {
Destination::Url(url) => match args.eat()? {
- Some(body) => Self { dest: Destination::Url(url), body },
- None => Self::from_url(url),
+ Some(body) => body,
+ None => body_from_url(url),
},
- Destination::Internal(_) => Self { dest, body: args.expect("body")? },
- }
- .pack())
- }
-
- fn field(&self, name: &str) -> Option<Value> {
- match name {
- "dest" => Some(match &self.dest {
- Destination::Url(url) => Value::Str(url.clone().into()),
- Destination::Internal(loc) => Value::Dict(loc.encode()),
- }),
- "body" => Some(Value::Content(self.body.clone())),
- _ => None,
- }
+ Destination::Internal(_) => args.expect("body")?,
+ };
+ Ok(Self::new(dest).with_body(body).pack())
}
}
impl Show for LinkNode {
fn show(&self, _: &mut Vt, _: &Content, _: StyleChain) -> SourceResult<Content> {
- Ok(self.body.clone())
+ Ok(self.body())
}
}
impl Finalize for LinkNode {
fn finalize(&self, realized: Content) -> Content {
realized
- .styled(Meta::DATA, vec![Meta::Link(self.dest.clone())])
+ .styled(MetaNode::DATA, vec![Meta::Link(self.dest())])
.styled(TextNode::HYPHENATE, Hyphenate(Smart::Custom(false)))
}
}
+
+fn body_from_url(url: &EcoString) -> Content {
+ let mut text = url.as_str();
+ for prefix in ["mailto:", "tel:"] {
+ text = text.trim_start_matches(prefix);
+ }
+ let shorter = text.len() < url.len();
+ TextNode::packed(if shorter { text.into() } else { url.clone() })
+}
diff --git a/library/src/meta/numbering.rs b/library/src/meta/numbering.rs
index 5b7cd92b..d3e1ee4d 100644
--- a/library/src/meta/numbering.rs
+++ b/library/src/meta/numbering.rs
@@ -3,7 +3,6 @@ use std::str::FromStr;
use crate::prelude::*;
use crate::text::Case;
-/// # Numbering
/// Apply a numbering to a sequence of numbers.
///
/// A numbering defines how a sequence of numbers should be displayed as
@@ -61,8 +60,8 @@ use crate::text::Case;
///
/// - returns: any
///
-/// ## Category
-/// meta
+/// Display: Numbering
+/// Category: meta
#[func]
pub fn numbering(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
let numbering = args.expect::<Numbering>("pattern or function")?;
@@ -99,12 +98,19 @@ impl Numbering {
}
}
-castable! {
+cast_from_value! {
Numbering,
- v: Str => Self::Pattern(v.parse()?),
+ v: NumberingPattern => Self::Pattern(v),
v: Func => Self::Func(v),
}
+cast_to_value! {
+ v: Numbering => match v {
+ Numbering::Pattern(pattern) => pattern.into(),
+ Numbering::Func(func) => func.into(),
+ }
+}
+
/// How to turn a number into text.
///
/// A pattern consists of a prefix, followed by one of `1`, `a`, `A`, `i`, `I`
@@ -173,12 +179,8 @@ impl FromStr for NumberingPattern {
let mut handled = 0;
for (i, c) in pattern.char_indices() {
- let kind = match c.to_ascii_lowercase() {
- '1' => NumberingKind::Arabic,
- 'a' => NumberingKind::Letter,
- 'i' => NumberingKind::Roman,
- '*' => NumberingKind::Symbol,
- _ => continue,
+ let Some(kind) = NumberingKind::from_char(c.to_ascii_lowercase()) else {
+ continue;
};
let prefix = pattern[handled..i].into();
@@ -196,6 +198,27 @@ impl FromStr for NumberingPattern {
}
}
+cast_from_value! {
+ NumberingPattern,
+ v: Str => v.parse()?,
+}
+
+cast_to_value! {
+ v: NumberingPattern => {
+ let mut pat = EcoString::new();
+ for (prefix, kind, case) in &v.pieces {
+ pat.push_str(prefix);
+ let mut c = kind.to_char();
+ if *case == Case::Upper {
+ c = c.to_ascii_uppercase();
+ }
+ pat.push(c);
+ }
+ pat.push_str(&v.suffix);
+ pat.into()
+ }
+}
+
/// Different kinds of numberings.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
enum NumberingKind {
@@ -206,6 +229,27 @@ enum NumberingKind {
}
impl NumberingKind {
+ /// Create a numbering kind from a lowercase character.
+ pub fn from_char(c: char) -> Option<Self> {
+ Some(match c {
+ '1' => NumberingKind::Arabic,
+ 'a' => NumberingKind::Letter,
+ 'i' => NumberingKind::Roman,
+ '*' => NumberingKind::Symbol,
+ _ => return None,
+ })
+ }
+
+ /// The lowercase character for this numbering kind.
+ pub fn to_char(self) -> char {
+ match self {
+ Self::Arabic => '1',
+ Self::Letter => 'a',
+ Self::Roman => 'i',
+ Self::Symbol => '*',
+ }
+ }
+
/// Apply the numbering to the given number.
pub fn apply(self, n: NonZeroUsize, case: Case) -> EcoString {
let mut n = n.get();
diff --git a/library/src/meta/outline.rs b/library/src/meta/outline.rs
index d9eea0a9..7bc08bf9 100644
--- a/library/src/meta/outline.rs
+++ b/library/src/meta/outline.rs
@@ -1,11 +1,8 @@
use super::HeadingNode;
-use crate::layout::{
- BoxNode, HNode, HideNode, ParbreakNode, RepeatNode, Sizing, Spacing,
-};
+use crate::layout::{BoxNode, HNode, HideNode, ParbreakNode, RepeatNode};
use crate::prelude::*;
use crate::text::{LinebreakNode, SpaceNode, TextNode};
-/// # Outline
/// A section outline / table of contents.
///
/// This function generates a list of all headings in the document, up to a
@@ -23,27 +20,25 @@ use crate::text::{LinebreakNode, SpaceNode, TextNode};
/// #lorem(10)
/// ```
///
-/// ## Category
-/// meta
-#[func]
-#[capable(Prepare, Show)]
-#[derive(Debug, Hash)]
-pub struct OutlineNode;
-
-#[node]
-impl OutlineNode {
+/// Display: Outline
+/// Category: meta
+#[node(Prepare, Show)]
+pub struct OutlineNode {
/// The title of the outline.
///
/// - When set to `{auto}`, an appropriate title for the [text
/// language]($func/text.lang) will be used. This is the default.
/// - When set to `{none}`, the outline will not have a title.
/// - A custom title can be set by passing content.
- #[property(referenced)]
- pub const TITLE: Option<Smart<Content>> = Some(Smart::Auto);
+ #[settable]
+ #[default(Some(Smart::Auto))]
+ pub title: Option<Smart<Content>>,
/// The maximum depth up to which headings are included in the outline. When
/// this argument is `{none}`, all headings are included.
- pub const DEPTH: Option<NonZeroUsize> = None;
+ #[settable]
+ #[default]
+ pub depth: Option<NonZeroUsize>,
/// Whether to indent the subheadings to align the start of their numbering
/// with the title of their parents. This will only have an effect if a
@@ -62,7 +57,9 @@ impl OutlineNode {
/// == Products
/// #lorem(10)
/// ```
- pub const INDENT: bool = false;
+ #[settable]
+ #[default(false)]
+ pub indent: bool,
/// Content to fill the space between the title and the page number. Can be
/// set to `none` to disable filling. The default is `{repeat[.]}`.
@@ -72,12 +69,9 @@ impl OutlineNode {
///
/// = A New Beginning
/// ```
- #[property(referenced)]
- pub const FILL: Option<Content> = Some(RepeatNode(TextNode::packed(".")).pack());
-
- fn construct(_: &Vm, _: &mut Args) -> SourceResult<Content> {
- Ok(Self.pack())
- }
+ #[settable]
+ #[default(Some(RepeatNode::new(TextNode::packed(".")).pack()))]
+ pub fill: Option<Content>,
}
impl Prepare for OutlineNode {
@@ -91,7 +85,7 @@ impl Prepare for OutlineNode {
.locate(Selector::node::<HeadingNode>())
.into_iter()
.map(|(_, node)| node)
- .filter(|node| node.field("outlined").unwrap() == Value::Bool(true))
+ .filter(|node| *node.field("outlined").unwrap() == Value::Bool(true))
.map(|node| Value::Content(node.clone()))
.collect();
@@ -107,7 +101,7 @@ impl Show for OutlineNode {
_: &Content,
styles: StyleChain,
) -> SourceResult<Content> {
- let mut seq = vec![ParbreakNode.pack()];
+ let mut seq = vec![ParbreakNode::new().pack()];
if let Some(title) = styles.get(Self::TITLE) {
let body = title.clone().unwrap_or_else(|| {
TextNode::packed(match styles.get(TextNode::LANG) {
@@ -117,7 +111,7 @@ impl Show for OutlineNode {
});
seq.push(
- HeadingNode { title: body, level: NonZeroUsize::new(1).unwrap() }
+ HeadingNode::new(body)
.pack()
.styled(HeadingNode::NUMBERING, None)
.styled(HeadingNode::OUTLINED, false),
@@ -129,26 +123,26 @@ impl Show for OutlineNode {
let mut ancestors: Vec<&Content> = vec![];
for (_, node) in vt.locate(Selector::node::<HeadingNode>()) {
- if node.field("outlined").unwrap() != Value::Bool(true) {
+ if *node.field("outlined").unwrap() != Value::Bool(true) {
continue;
}
let heading = node.to::<HeadingNode>().unwrap();
if let Some(depth) = depth {
- if depth < heading.level {
+ if depth < heading.level() {
continue;
}
}
while ancestors.last().map_or(false, |last| {
- last.to::<HeadingNode>().unwrap().level >= heading.level
+ last.to::<HeadingNode>().unwrap().level() >= heading.level()
}) {
ancestors.pop();
}
// Adjust the link destination a bit to the topleft so that the
// heading is fully visible.
- let mut loc = node.field("loc").unwrap().cast::<Location>().unwrap();
+ let mut loc = node.field("loc").unwrap().clone().cast::<Location>().unwrap();
loc.pos -= Point::splat(Abs::pt(10.0));
// Add hidden ancestors numberings to realize the indent.
@@ -156,21 +150,21 @@ impl Show for OutlineNode {
let hidden: Vec<_> = ancestors
.iter()
.map(|node| node.field("numbers").unwrap())
- .filter(|numbers| *numbers != Value::None)
- .map(|numbers| numbers.display() + SpaceNode.pack())
+ .filter(|&numbers| *numbers != Value::None)
+ .map(|numbers| numbers.clone().display() + SpaceNode::new().pack())
.collect();
if !hidden.is_empty() {
- seq.push(HideNode(Content::sequence(hidden)).pack());
- seq.push(SpaceNode.pack());
+ seq.push(HideNode::new(Content::sequence(hidden)).pack());
+ seq.push(SpaceNode::new().pack());
}
}
// Format the numbering.
- let mut start = heading.title.clone();
+ let mut start = heading.title();
let numbers = node.field("numbers").unwrap();
- if numbers != Value::None {
- start = numbers.display() + SpaceNode.pack() + start;
+ if *numbers != Value::None {
+ start = numbers.clone().display() + SpaceNode::new().pack() + start;
};
// Add the numbering and section name.
@@ -178,30 +172,27 @@ impl Show for OutlineNode {
// Add filler symbols between the section name and page number.
if let Some(filler) = styles.get(Self::FILL) {
- seq.push(SpaceNode.pack());
+ seq.push(SpaceNode::new().pack());
seq.push(
- BoxNode {
- body: filler.clone(),
- width: Sizing::Fr(Fr::one()),
- height: Smart::Auto,
- }
- .pack(),
+ BoxNode::new()
+ .with_body(filler.clone())
+ .with_width(Fr::one().into())
+ .pack(),
);
- seq.push(SpaceNode.pack());
+ seq.push(SpaceNode::new().pack());
} else {
- let amount = Spacing::Fr(Fr::one());
- seq.push(HNode { amount, weak: false }.pack());
+ seq.push(HNode::new(Fr::one().into()).pack());
}
// Add the page number and linebreak.
let end = TextNode::packed(eco_format!("{}", loc.page));
seq.push(end.linked(Destination::Internal(loc)));
- seq.push(LinebreakNode { justify: false }.pack());
+ seq.push(LinebreakNode::new().pack());
ancestors.push(node);
}
- seq.push(ParbreakNode.pack());
+ seq.push(ParbreakNode::new().pack());
Ok(Content::sequence(seq))
}
diff --git a/library/src/meta/reference.rs b/library/src/meta/reference.rs
index e64751f7..55051b5e 100644
--- a/library/src/meta/reference.rs
+++ b/library/src/meta/reference.rs
@@ -1,7 +1,6 @@
use crate::prelude::*;
use crate::text::TextNode;
-/// # Reference
/// A reference to a label.
///
/// *Note: This function is currently unimplemented.*
@@ -16,33 +15,18 @@ use crate::text::TextNode;
/// created by typing an `@` followed by the name of the label (e.g. `[=
/// Introduction <intro>]` can be referenced by typing `[@intro]`).
///
-/// ## Parameters
-/// - target: `Label` (positional, required)
-/// The label that should be referenced.
-///
-/// ## Category
-/// meta
-#[func]
-#[capable(Show)]
-#[derive(Debug, Hash)]
-pub struct RefNode(pub EcoString);
-
-#[node]
-impl RefNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.expect("target")?).pack())
- }
-
- fn field(&self, name: &str) -> Option<Value> {
- match name {
- "target" => Some(Value::Str(self.0.clone().into())),
- _ => None,
- }
- }
+/// Display: Reference
+/// Category: meta
+#[node(Show)]
+pub struct RefNode {
+ /// The label that should be referenced.
+ #[positional]
+ #[required]
+ pub target: EcoString,
}
impl Show for RefNode {
fn show(&self, _: &mut Vt, _: &Content, _: StyleChain) -> SourceResult<Content> {
- Ok(TextNode::packed(eco_format!("@{}", self.0)))
+ Ok(TextNode::packed(eco_format!("@{}", self.target())))
}
}