summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2019-12-04 19:34:29 +0100
committerLaurenz <laurmaedje@gmail.com>2019-12-04 19:35:28 +0100
commit9fb31defd037a90bf8f9e38fa33acae23a70b269 (patch)
treee0fd887792a59cbb3262a5d3157d0c786df56d60 /src/library
parentace57c34206a13b4bc3885b944cc51e274f30b0f (diff)
Expand functionality of function! macro 🛰
Diffstat (limited to 'src/library')
-rw-r--r--src/library/align.rs193
-rw-r--r--src/library/boxed.rs59
-rw-r--r--src/library/mod.rs270
-rw-r--r--src/library/page.rs79
-rw-r--r--src/library/spacing.rs71
-rw-r--r--src/library/style.rs39
6 files changed, 341 insertions, 370 deletions
diff --git a/src/library/align.rs b/src/library/align.rs
index 530120e7..14e329e3 100644
--- a/src/library/align.rs
+++ b/src/library/align.rs
@@ -1,155 +1,68 @@
use crate::func::prelude::*;
-/// 📐 `align`: Aligns content in different ways.
-#[derive(Debug, PartialEq)]
-pub struct Align {
- body: Option<SyntaxTree>,
- positional_1: Option<AlignSpecifier>,
- positional_2: Option<AlignSpecifier>,
- primary: Option<AlignSpecifier>,
- secondary: Option<AlignSpecifier>,
- horizontal: Option<AlignSpecifier>,
- vertical: Option<AlignSpecifier>,
-}
-
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-enum AlignSpecifier {
- Origin,
- Center,
- End,
- Left,
- Right,
- Top,
- Bottom,
-}
-
function! {
- data: Align,
+ /// `align`: Aligns content along the layouting axes.
+ #[derive(Debug, PartialEq)]
+ pub struct Align {
+ body: Option<SyntaxTree>,
+ map: ArgMap<Key, AlignmentKey>,
+ }
parse(args, body, ctx) {
- let body = parse!(optional: body, ctx);
-
- let mut align = Align {
- body,
- positional_1: None,
- positional_2: None,
- primary: None,
- secondary: None,
- horizontal: None,
- vertical: None,
- };
-
- if let Some(arg) = args.get_pos_opt::<ArgIdent>()? {
- align.positional_1 = Some(parse_align_specifier(arg)?);
+ let mut map = ArgMap::new();
+ map.put(Key::First, args.get_pos_opt::<ArgIdent>()?)?;
+ map.put(Key::Second, args.get_pos_opt::<ArgIdent>()?)?;
+
+ for arg in args.keys() {
+ let key = match arg.val.0.val {
+ "horizontal" => Key::Axis(AxisKey::Horizontal),
+ "vertical" => Key::Axis(AxisKey::Vertical),
+ "primary" => Key::Axis(AxisKey::Primary),
+ "secondary" => Key::Axis(AxisKey::Secondary),
+ _ => pr!("unexpected argument"),
+ };
+
+ let value = AlignmentKey::parse(arg.val.1.val)?;
+ map.add(key, value);
}
- if let Some(arg) = args.get_pos_opt::<ArgIdent>()? {
- align.positional_2 = Some(parse_align_specifier(arg)?);
+ Align {
+ body: parse!(optional: body, ctx),
+ map,
}
-
- let mut parse_arg = |axis, target: &mut Option<AlignSpecifier>| {
- Ok(if let Some(arg) = args.get_key_opt::<ArgIdent>(axis)? {
- if target.is_none() {
- *target = Some(parse_align_specifier(arg)?);
- } else {
- perr!("duplicate alignment specification for {} axis", axis);
- }
- })
- };
-
- parse_arg("primary", &mut align.primary)?;
- parse_arg("secondary", &mut align.secondary)?;
- parse_arg("horizontal", &mut align.horizontal)?;
- parse_arg("vertical", &mut align.vertical)?;
-
- args.done()?;
-
- Ok(align)
}
- layout(this, ctx) {
- let mut axes = ctx.axes;
- let primary_horizontal = axes.primary.is_horizontal();
-
- let mut primary = false;
- let mut secondary = false;
-
- let mut set_axis = |is_primary: bool, spec: Option<AlignSpecifier>| -> LayoutResult<()> {
- if let Some(spec) = spec {
- let (axis, was_set, name) = match is_primary {
- true => (&mut axes.primary, &mut primary, "primary"),
- false => (&mut axes.secondary, &mut secondary, "secondary"),
- };
-
- if *was_set {
- panic!("duplicate alignment for {} axis", name);
- }
-
- *was_set = true;
-
- let horizontal = axis.is_horizontal();
- let alignment = generic_alignment(spec, horizontal)?;
-
- if axis.alignment == Alignment::End && alignment == Alignment::Origin {
- axis.expand = true;
- }
-
- axis.alignment = alignment;
- }
-
- Ok(())
- };
-
- if let Some(spec) = this.positional_1 {
- let positional = generic_alignment(spec, primary_horizontal).is_ok();
- set_axis(positional, this.positional_1)?;
+ layout(self, mut ctx) {
+ let axes = ctx.axes;
+ let basic = axes.primary.is_horizontal();
+
+ let map = self.map.dedup(|key, val| {
+ let axis = match key {
+ Key::First => val.axis(axes, GenericAxisKind::Primary),
+ Key::Second => val.axis(axes, GenericAxisKind::Secondary),
+ Key::Axis(AxisKey::Primary) => GenericAxisKind::Primary,
+ Key::Axis(AxisKey::Secondary) => GenericAxisKind::Secondary,
+ Key::Axis(AxisKey::Horizontal) => axes.horizontal(),
+ Key::Axis(AxisKey::Vertical) => axes.vertical(),
+ };
+
+ let alignment = val.generic(axes, axis)?;
+ Ok((key, alignment))
+ })?;
+
+ map.with(GenericAxisKind::Primary, |val| ctx.alignment.primary = val);
+ map.with(GenericAxisKind::Secondary, |val| ctx.alignment.secondary = val);
+
+ match &self.body {
+ Some(body) => vec![AddMultiple(layout_tree(&body, ctx)?)],
+ None => vec![Command::SetAlignment(ctx.alignment)],
}
-
- if let Some(spec) = this.positional_2 {
- let positional = generic_alignment(spec, primary_horizontal).is_ok();
- set_axis(positional, this.positional_2)?;
- }
-
- set_axis(true, this.primary)?;
- set_axis(false, this.secondary)?;
- set_axis(primary_horizontal, this.horizontal)?;
- set_axis(!primary_horizontal, this.vertical)?;
-
- Ok(match &this.body {
- Some(body) => vec![AddMultiple(
- layout_tree(body, LayoutContext {
- axes,
- .. ctx.clone()
- })?
- )],
- None => vec![Command::SetAxes(axes)]
- })
}
}
-fn parse_align_specifier(arg: Spanned<&str>) -> ParseResult<AlignSpecifier> {
- Ok(match arg.val {
- "origin" => AlignSpecifier::Origin,
- "center" => AlignSpecifier::Center,
- "end" => AlignSpecifier::End,
- "left" => AlignSpecifier::Left,
- "right" => AlignSpecifier::Right,
- "top" => AlignSpecifier::Top,
- "bottom" => AlignSpecifier::Bottom,
- s => perr!("invalid alignment specifier: {}", s),
- })
-}
-
-fn generic_alignment(spec: AlignSpecifier, horizontal: bool) -> LayoutResult<Alignment> {
- use AlignSpecifier::*;
- Ok(match (spec, horizontal) {
- (Origin, _) | (Left, true) | (Top, false) => Alignment::Origin,
- (Center, _) => Alignment::Center,
- (End, _) | (Right, true) | (Bottom, false) => Alignment::End,
- _ => lerr!(
- "invalid alignment specifier `{}` for {} axis",
- format!("{:?}", spec).to_lowercase(),
- if horizontal { "horizontal" } else { "vertical" },
- ),
- })
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+enum Key {
+ First,
+ Second,
+ Axis(AxisKey),
}
diff --git a/src/library/boxed.rs b/src/library/boxed.rs
index 8984417c..d4e39450 100644
--- a/src/library/boxed.rs
+++ b/src/library/boxed.rs
@@ -1,37 +1,44 @@
use crate::func::prelude::*;
-/// `box`: Layouts content into a box.
-#[derive(Debug, PartialEq)]
-pub struct Boxed {
- body: SyntaxTree,
- width: Option<Size>,
- height: Option<Size>,
-}
-
function! {
- data: Boxed,
+ /// `box`: Layouts content into a box.
+ #[derive(Debug, PartialEq)]
+ pub struct Boxed {
+ body: SyntaxTree,
+ map: ArgMap<AxisKey, Size>,
+ }
parse(args, body, ctx) {
- let width = args.get_key_opt::<ArgSize>("width")?.map(|a| a.val);
- let height = args.get_key_opt::<ArgSize>("height")?.map(|a| a.val);
- args.done()?;
-
- let body = parse!(required: body, ctx);
- Ok(Boxed {
- body,
- width,
- height,
- })
- }
+ let mut map = ArgMap::new();
+
+ for arg in args.keys() {
+ let key = match arg.val.0.val {
+ "width" | "w" => AxisKey::Horizontal,
+ "height" | "h" => AxisKey::Vertical,
+ "primary-size" => AxisKey::Primary,
+ "secondary-size" => AxisKey::Secondary,
+ _ => pr!("unexpected argument"),
+ };
- layout(this, mut ctx) {
- if let Some(width) = this.width {
- ctx.spaces[0].dimensions.x = width;
+ let size = ArgParser::convert::<ArgSize>(arg.val.1.val)?;
+ map.add(key, size);
}
- if let Some(height) = this.height {
- ctx.spaces[0].dimensions.y = height;
+
+ Boxed {
+ body: parse!(expected: body, ctx),
+ map,
}
+ }
+
+ layout(self, mut ctx) {
+ let map = self.map.dedup(|key, val| {
+ Ok((key.specific(ctx.axes), val))
+ });
+
+ let mut dimensions = &mut ctx.spaces[0].dimensions;
+ map.with(AxisKey::Horizontal, |val| dimensions.x = val);
+ map.with(AxisKey::Vertical, |val| dimensions.y = val);
- Ok(vec![AddMultiple(layout_tree(&this.body, ctx)?)])
+ vec![AddMultiple(layout_tree(&self.body, ctx)?)]
}
}
diff --git a/src/library/mod.rs b/src/library/mod.rs
index adcb974b..b09303bb 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -1,34 +1,274 @@
//! The standard library for the _Typst_ language.
-use crate::func::Scope;
+use crate::func::prelude::*;
+use toddle::query::FontClass;
-pub_use_mod!(boxed);
pub_use_mod!(align);
-pub_use_mod!(spacing);
-pub_use_mod!(style);
-pub_use_mod!(page);
+pub_use_mod!(boxed);
/// Create a scope with all standard functions.
pub fn std() -> Scope {
let mut std = Scope::new();
- std.add::<Boxed>("box");
-
std.add::<Align>("align");
+ std.add::<Boxed>("box");
+ std.add::<PageSize>("page.size");
+ std.add::<PageMargins>("page.margins");
std.add::<LineBreak>("n");
std.add::<LineBreak>("line.break");
- std.add::<ParagraphBreak>("paragraph.break");
+ std.add::<ParBreak>("par.break");
std.add::<PageBreak>("page.break");
- std.add::<HorizontalSpace>("h");
- std.add::<VerticalSpace>("v");
- std.add::<Bold>("bold");
- std.add::<Italic>("italic");
- std.add::<Monospace>("mono");
+ std.add_with_metadata::<Spacing, Option<AxisKey>>("spacing", None);
+ for (name, key) in &[
+ ("h", AxisKey::Horizontal),
+ ("v", AxisKey::Vertical),
+ ] {
+ std.add_with_metadata::<Spacing, Option<AxisKey>>(name, Some(*key));
+ }
- std.add::<PageSize>("page.size");
- std.add::<PageMargins>("page.margins");
+ for (name, class) in &[
+ ("bold", FontClass::Bold),
+ ("italic", FontClass::Italic),
+ ("mono", FontClass::Monospace),
+ ] {
+ std.add_with_metadata::<StyleChange, FontClass>(name, *class);
+ }
std
}
+
+function! {
+ /// `line.break`, `n`: Ends the current line.
+ #[derive(Debug, Default, PartialEq)]
+ pub struct LineBreak;
+
+ parse(default)
+ layout() { vec![FinishLine] }
+}
+
+function! {
+ /// `par.break`: Ends the current paragraph.
+ ///
+ /// self has the same effect as two subsequent newlines.
+ #[derive(Debug, Default, PartialEq)]
+ pub struct ParBreak;
+
+ parse(default)
+ layout() { vec![BreakParagraph] }
+}
+
+
+function! {
+ /// `page.break`: Ends the current page.
+ #[derive(Debug, Default, PartialEq)]
+ pub struct PageBreak;
+
+ parse(default)
+ layout() { vec![FinishSpace] }
+}
+
+function! {
+ /// `page.size`: Set the size of pages.
+ #[derive(Debug, PartialEq)]
+ pub struct PageSize {
+ width: Option<Size>,
+ height: Option<Size>,
+ }
+
+ parse(args, body) {
+ parse!(forbidden: body);
+ PageSize {
+ width: args.get_key_opt::<ArgSize>("width")?.map(|a| a.val),
+ height: args.get_key_opt::<ArgSize>("height")?.map(|a| a.val),
+ }
+ }
+
+ layout(self, ctx) {
+ let mut style = ctx.style.page;
+ if let Some(width) = self.width { style.dimensions.x = width; }
+ if let Some(height) = self.height { style.dimensions.y = height; }
+ vec![SetPageStyle(style)]
+ }
+}
+
+function! {
+ /// `page.margins`: Set the margins of pages.
+ #[derive(Debug, PartialEq)]
+ pub struct PageMargins {
+ map: ArgMap<PaddingKey, Size>,
+ }
+
+ parse(args, body) {
+ use PaddingKey::*;
+ use AlignmentKey::*;
+
+ let mut map = ArgMap::new();
+ map.add_opt(All, args.get_pos_opt::<ArgSize>()?);
+
+ for arg in args.keys() {
+ let key = match arg.val.0.val {
+ "horizontal" => Axis(AxisKey::Horizontal),
+ "vertical" => Axis(AxisKey::Vertical),
+ "primary" => Axis(AxisKey::Primary),
+ "secondary" => Axis(AxisKey::Secondary),
+
+ "left" => AxisAligned(AxisKey::Horizontal, Left),
+ "right" => AxisAligned(AxisKey::Horizontal, Right),
+ "top" => AxisAligned(AxisKey::Vertical, Top),
+ "bottom" => AxisAligned(AxisKey::Vertical, Bottom),
+
+ "primary-origin" => AxisAligned(AxisKey::Primary, Origin),
+ "primary-end" => AxisAligned(AxisKey::Primary, End),
+ "secondary-origin" => AxisAligned(AxisKey::Secondary, Origin),
+ "secondary-end" => AxisAligned(AxisKey::Secondary, End),
+ "horizontal-origin" => AxisAligned(AxisKey::Horizontal, Origin),
+ "horizontal-end" => AxisAligned(AxisKey::Horizontal, End),
+ "vertical-origin" => AxisAligned(AxisKey::Vertical, Origin),
+ "vertical-end" => AxisAligned(AxisKey::Vertical, End),
+
+ _ => pr!("unexpected argument"),
+ };
+
+ let size = ArgParser::convert::<ArgSize>(arg.val.1.val)?;
+ map.add(key, size);
+ }
+
+ parse!(forbidden: body);
+ PageMargins { map }
+ }
+
+ layout(self, ctx) {
+ use PaddingKey::*;
+
+ let axes = ctx.axes;
+ let map = self.map.dedup(|key, val| {
+ match key {
+ All => All,
+ Axis(axis) => Axis(axis.specific(axes)),
+ AxisAligned(axis, alignment) => {
+ let axis = axis.specific(axes);
+ AxisAligned(axis, alignment.specific(axes, axis))
+ }
+ }
+ });
+
+ let style = ctx.style.page;
+ let padding = &mut style.margins;
+
+ map.with(All, |val| padding.set_all(val));
+ map.with(Axis(AxisKey::Horizontal), |val| padding.set_horizontal(val));
+ map.with(Axis(AxisKey::Vertical), |val| padding.set_vertical(val));
+
+ for (key, val) in map.iter() {
+ if let AxisAligned(axis, alignment) = key {
+ match alignment {
+ AlignmentKey::Left => padding.left = val,
+ AlignmentKey::Right => padding.right = val,
+ AlignmentKey::Top => padding.top = val,
+ AlignmentKey::Bottom => padding.bottom = val,
+ _ => {},
+ }
+ }
+ }
+
+ vec![SetPageStyle(style)]
+ }
+}
+
+function! {
+ /// `spacing`, `h`, `v`: Add spacing along an axis.
+ #[derive(Debug, PartialEq)]
+ pub struct Spacing {
+ axis: AxisKey,
+ spacing: SpacingValue,
+ }
+
+ type Meta = Option<AxisKey>;
+
+ parse(args, body, _, meta) {
+ let spacing = if let Some(axis) = meta {
+ Spacing {
+ axis,
+ spacing: SpacingValue::from_expr(args.get_pos::<ArgExpr>()?)?,
+ }
+ } else {
+ if let Some(arg) = args.get_key_next() {
+ let axis = match arg.val.0.val {
+ "horizontal" => AxisKey::Horizontal,
+ "vertical" => AxisKey::Vertical,
+ "primary" => AxisKey::Primary,
+ "secondary" => AxisKey::Secondary,
+ _ => pr!("unexpected argument"),
+ };
+
+ let spacing = SpacingValue::from_expr(arg.val.1.val)?;
+ Spacing { axis, spacing }
+ } else {
+ pr!("expected axis and expression")
+ }
+ };
+
+ parse!(forbidden: body);
+ spacing
+ }
+
+ layout(self, ctx) {
+ let axis = self.axis.generic(ctx.axes);
+ let spacing = match self.spacing {
+ SpacingValue::Absolute(s) => s,
+ SpacingValue::Relative(f) => f * ctx.style.text.font_size,
+ };
+
+ vec![AddSpacing(spacing, SpacingKind::Hard, axis)]
+ }
+}
+
+#[derive(Debug, PartialEq)]
+enum SpacingValue {
+ Absolute(Size),
+ Relative(f32),
+}
+
+impl SpacingValue {
+ fn from_expr(expr: Spanned<&Expression>) -> ParseResult<SpacingValue> {
+ Ok(match expr.val {
+ Expression::Size(s) => SpacingValue::Absolute(*s),
+ Expression::Num(f) => SpacingValue::Relative(*f as f32),
+ _ => pr!("invalid spacing: expected size or number"),
+ })
+ }
+}
+
+function! {
+ /// Sets text with a different style.
+ #[derive(Debug, PartialEq)]
+ pub struct StyleChange {
+ body: Option<SyntaxTree>,
+ class: FontClass,
+ }
+
+ type Meta = FontClass;
+
+ parse(args, body, ctx, meta) {
+ StyleChange {
+ body: parse!(optional: body, ctx),
+ class: meta,
+ }
+ }
+
+ layout(self, ctx) {
+ let mut style = ctx.style.text.clone();
+ style.toggle_class(self.class);
+
+ match &self.body {
+ Some(body) => vec![
+ SetTextStyle(style),
+ LayoutTree(body),
+ SetTextStyle(ctx.style.text.clone()),
+ ],
+ None => vec![SetTextStyle(style)]
+ }
+ }
+}
diff --git a/src/library/page.rs b/src/library/page.rs
deleted file mode 100644
index e8a80870..00000000
--- a/src/library/page.rs
+++ /dev/null
@@ -1,79 +0,0 @@
-use crate::func::prelude::*;
-
-/// `page.break`: Ends the current page.
-#[derive(Debug, PartialEq)]
-pub struct PageBreak;
-
-function! {
- data: PageBreak,
- parse: plain,
- layout(_, _) { Ok(vec![FinishSpace]) }
-}
-
-/// `page.size`: Set the size of pages.
-#[derive(Debug, PartialEq)]
-pub struct PageSize {
- width: Option<Size>,
- height: Option<Size>,
-}
-
-function! {
- data: PageSize,
-
- parse(args, body, _ctx) {
- parse!(forbidden: body);
- Ok(PageSize {
- width: args.get_key_opt::<ArgSize>("width")?.map(|a| a.val),
- height: args.get_key_opt::<ArgSize>("height")?.map(|a| a.val),
- })
- }
-
- layout(this, ctx) {
- let mut style = ctx.style.page;
-
- if let Some(width) = this.width { style.dimensions.x = width; }
- if let Some(height) = this.height { style.dimensions.y = height; }
-
- Ok(vec![SetPageStyle(style)])
- }
-}
-
-/// `page.margins`: Set the margins of pages.
-#[derive(Debug, PartialEq)]
-pub struct PageMargins {
- left: Option<Size>,
- top: Option<Size>,
- right: Option<Size>,
- bottom: Option<Size>,
-}
-
-function! {
- data: PageMargins,
-
- parse(args, body, _ctx) {
- parse!(forbidden: body);
- let default = args.get_pos_opt::<ArgSize>()?;
- let mut get = |which| {
- args.get_key_opt::<ArgSize>(which)
- .map(|size| size.or(default).map(|a| a.val))
- };
-
- Ok(PageMargins {
- left: get("left")?,
- top: get("top")?,
- right: get("right")?,
- bottom: get("bottom")?,
- })
- }
-
- layout(this, ctx) {
- let mut style = ctx.style.page;
-
- if let Some(left) = this.left { style.margins.left = left; }
- if let Some(top) = this.top { style.margins.top = top; }
- if let Some(right) = this.right { style.margins.right = right; }
- if let Some(bottom) = this.bottom { style.margins.bottom = bottom; }
-
- Ok(vec![SetPageStyle(style)])
- }
-}
diff --git a/src/library/spacing.rs b/src/library/spacing.rs
deleted file mode 100644
index 47fe9fff..00000000
--- a/src/library/spacing.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-use crate::func::prelude::*;
-
-/// `line.break`, `n`: Ends the current line.
-#[derive(Debug, PartialEq)]
-pub struct LineBreak;
-
-function! {
- data: LineBreak,
- parse: plain,
- layout(_, _) { Ok(vec![FinishLine]) }
-}
-
-/// `paragraph.break`: Ends the current paragraph.
-///
-/// This has the same effect as two subsequent newlines.
-#[derive(Debug, PartialEq)]
-pub struct ParagraphBreak;
-
-function! {
- data: ParagraphBreak,
- parse: plain,
- layout(_, _) { Ok(vec![BreakParagraph]) }
-}
-
-macro_rules! space_func {
- ($ident:ident, $doc:expr, $var:ident => $command:expr) => (
- #[doc = $doc]
- #[derive(Debug, PartialEq)]
- pub struct $ident(Spacing);
-
- function! {
- data: $ident,
-
- parse(args, body, _ctx) {
- parse!(forbidden: body);
-
- let arg = args.get_pos::<ArgExpr>()?;
- let spacing = match arg.val {
- Expression::Size(s) => Spacing::Absolute(*s),
- Expression::Num(f) => Spacing::Relative(*f as f32),
- _ => perr!("invalid spacing, expected size or number"),
- };
-
- Ok($ident(spacing))
- }
-
- layout(this, ctx) {
- let $var = match this.0 {
- Spacing::Absolute(s) => s,
- Spacing::Relative(f) => f * ctx.style.text.font_size,
- };
-
- Ok(vec![$command])
- }
- }
- );
-}
-
-/// Absolute or font-relative spacing.
-#[derive(Debug, PartialEq)]
-enum Spacing {
- Absolute(Size),
- Relative(f32),
-}
-
-// FIXME: h != primary and v != secondary.
-space_func!(HorizontalSpace, "📖 `h`: Adds horizontal whitespace.",
- space => AddSpacing(space, SpacingKind::Hard, AxisKind::Primary));
-
-space_func!(VerticalSpace, "📑 `v`: Adds vertical whitespace.",
- space => AddSpacing(space, SpacingKind::Hard, AxisKind::Secondary));
diff --git a/src/library/style.rs b/src/library/style.rs
deleted file mode 100644
index b2de19bb..00000000
--- a/src/library/style.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-use crate::func::prelude::*;
-use toddle::query::FontClass;
-
-macro_rules! stylefunc {
- ($ident:ident, $doc:expr) => (
- #[doc = $doc]
- #[derive(Debug, PartialEq)]
- pub struct $ident {
- body: Option<SyntaxTree>
- }
-
- function! {
- data: $ident,
-
- parse(args, body, ctx) {
- args.done()?;
- Ok($ident { body: parse!(optional: body, ctx) })
- }
-
- layout(this, ctx) {
- let mut style = ctx.style.text.clone();
- style.toggle_class(FontClass::$ident);
-
- Ok(match &this.body {
- Some(body) => vec![
- SetTextStyle(style),
- LayoutTree(body),
- SetTextStyle(ctx.style.text.clone()),
- ],
- None => vec![SetTextStyle(style)]
- })
- }
- }
- );
-}
-
-stylefunc!(Italic, "`italic`: Sets text in _italics_.");
-stylefunc!(Bold, "`bold`: Sets text in **bold**.");
-stylefunc!(Monospace, "`mono`: Sets text in `monospace`.");