New macro setup
This commit is contained in:
parent
4c73456fc1
commit
9ba4d2c134
@ -6,6 +6,8 @@ use crate::prelude::*;
|
||||
use crate::text::{SpaceNode, TextNode, TextSize};
|
||||
|
||||
/// A section heading.
|
||||
#[func]
|
||||
#[capable(Prepare, Show, Finalize)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct HeadingNode {
|
||||
/// The logical nesting depth of the section, starting from one. In the
|
||||
@ -15,7 +17,7 @@ pub struct HeadingNode {
|
||||
pub body: Content,
|
||||
}
|
||||
|
||||
#[node(Prepare, Show, Finalize)]
|
||||
#[node]
|
||||
impl HeadingNode {
|
||||
/// How to number the heading.
|
||||
#[property(referenced)]
|
||||
|
@ -4,6 +4,8 @@ use crate::prelude::*;
|
||||
use crate::text::{SpaceNode, TextNode};
|
||||
|
||||
/// An unordered (bulleted) or ordered (numbered) list.
|
||||
#[func]
|
||||
#[capable(Layout)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct ListNode<const L: ListKind = LIST> {
|
||||
/// If true, the items are separated by leading instead of list spacing.
|
||||
@ -18,7 +20,7 @@ pub type EnumNode = ListNode<ENUM>;
|
||||
/// A description list.
|
||||
pub type DescNode = ListNode<DESC>;
|
||||
|
||||
#[node(Layout)]
|
||||
#[node]
|
||||
impl<const L: ListKind> ListNode<L> {
|
||||
/// How the list is labelled.
|
||||
#[property(referenced)]
|
||||
@ -143,6 +145,7 @@ impl<const L: ListKind> Layout for ListNode<L> {
|
||||
}
|
||||
|
||||
/// An item in a list.
|
||||
#[capable]
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub enum ListItem {
|
||||
/// An item of an unordered list.
|
||||
|
@ -2,6 +2,8 @@ use crate::layout::{GridNode, TrackSizing, TrackSizings};
|
||||
use crate::prelude::*;
|
||||
|
||||
/// A table of items.
|
||||
#[func]
|
||||
#[capable(Layout)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct TableNode {
|
||||
/// Defines sizing for content rows and columns.
|
||||
@ -12,7 +14,7 @@ pub struct TableNode {
|
||||
pub cells: Vec<Content>,
|
||||
}
|
||||
|
||||
#[node(Layout)]
|
||||
#[node]
|
||||
impl TableNode {
|
||||
/// How to fill the cells.
|
||||
#[property(referenced)]
|
||||
|
@ -3,7 +3,8 @@ use std::cmp::Ordering;
|
||||
use crate::prelude::*;
|
||||
|
||||
/// The absolute value of a numeric value.
|
||||
pub fn abs(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn abs(args: &mut Args) -> SourceResult<Value> {
|
||||
let Spanned { v, span } = args.expect("numeric value")?;
|
||||
Ok(match v {
|
||||
Value::Int(v) => Value::Int(v.abs()),
|
||||
@ -19,12 +20,14 @@ pub fn abs(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
}
|
||||
|
||||
/// The minimum of a sequence of values.
|
||||
pub fn min(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn min(args: &mut Args) -> SourceResult<Value> {
|
||||
minmax(args, Ordering::Less)
|
||||
}
|
||||
|
||||
/// The maximum of a sequence of values.
|
||||
pub fn max(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn max(args: &mut Args) -> SourceResult<Value> {
|
||||
minmax(args, Ordering::Greater)
|
||||
}
|
||||
|
||||
@ -50,17 +53,20 @@ fn minmax(args: &mut Args, goal: Ordering) -> SourceResult<Value> {
|
||||
}
|
||||
|
||||
/// Whether an integer is even.
|
||||
pub fn even(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn even(args: &mut Args) -> SourceResult<Value> {
|
||||
Ok(Value::Bool(args.expect::<i64>("integer")? % 2 == 0))
|
||||
}
|
||||
|
||||
/// Whether an integer is odd.
|
||||
pub fn odd(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn odd(args: &mut Args) -> SourceResult<Value> {
|
||||
Ok(Value::Bool(args.expect::<i64>("integer")? % 2 != 0))
|
||||
}
|
||||
|
||||
/// The modulo of two numbers.
|
||||
pub fn mod_(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn mod_(args: &mut Args) -> SourceResult<Value> {
|
||||
let Spanned { v: v1, span: span1 } = args.expect("integer or float")?;
|
||||
let Spanned { v: v2, span: span2 } = args.expect("integer or float")?;
|
||||
|
||||
|
@ -5,7 +5,8 @@ use typst::model::Regex;
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Convert a value to an integer.
|
||||
pub fn int(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn int(args: &mut Args) -> SourceResult<Value> {
|
||||
let Spanned { v, span } = args.expect("value")?;
|
||||
Ok(Value::Int(match v {
|
||||
Value::Bool(v) => v as i64,
|
||||
@ -20,7 +21,8 @@ pub fn int(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
}
|
||||
|
||||
/// Convert a value to a float.
|
||||
pub fn float(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn float(args: &mut Args) -> SourceResult<Value> {
|
||||
let Spanned { v, span } = args.expect("value")?;
|
||||
Ok(Value::Float(match v {
|
||||
Value::Int(v) => v as f64,
|
||||
@ -34,13 +36,15 @@ pub fn float(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
}
|
||||
|
||||
/// Create a grayscale color.
|
||||
pub fn luma(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn luma(args: &mut Args) -> SourceResult<Value> {
|
||||
let Component(luma) = args.expect("gray component")?;
|
||||
Ok(Value::Color(LumaColor::new(luma).into()))
|
||||
}
|
||||
|
||||
/// Create an RGB(A) color.
|
||||
pub fn rgb(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn rgb(args: &mut Args) -> SourceResult<Value> {
|
||||
Ok(Value::Color(if let Some(string) = args.find::<Spanned<EcoString>>()? {
|
||||
match RgbaColor::from_str(&string.v) {
|
||||
Ok(color) => color.into(),
|
||||
@ -56,7 +60,8 @@ pub fn rgb(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
}
|
||||
|
||||
/// Create a CMYK color.
|
||||
pub fn cmyk(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn cmyk(args: &mut Args) -> SourceResult<Value> {
|
||||
let RatioComponent(c) = args.expect("cyan component")?;
|
||||
let RatioComponent(m) = args.expect("magenta component")?;
|
||||
let RatioComponent(y) = args.expect("yellow component")?;
|
||||
@ -95,7 +100,8 @@ castable! {
|
||||
}
|
||||
|
||||
/// Convert a value to a string.
|
||||
pub fn str(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn str(args: &mut Args) -> SourceResult<Value> {
|
||||
let Spanned { v, span } = args.expect("value")?;
|
||||
Ok(Value::Str(match v {
|
||||
Value::Int(v) => format_str!("{}", v),
|
||||
@ -107,18 +113,21 @@ pub fn str(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
}
|
||||
|
||||
/// Create a label from a string.
|
||||
pub fn label(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn label(args: &mut Args) -> SourceResult<Value> {
|
||||
Ok(Value::Label(Label(args.expect("string")?)))
|
||||
}
|
||||
|
||||
/// Create a regular expression from a string.
|
||||
pub fn regex(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn regex(args: &mut Args) -> SourceResult<Value> {
|
||||
let Spanned { v, span } = args.expect::<Spanned<EcoString>>("regular expression")?;
|
||||
Ok(Regex::new(&v).at(span)?.into())
|
||||
}
|
||||
|
||||
/// Create an array consisting of a sequence of numbers.
|
||||
pub fn range(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn range(args: &mut Args) -> SourceResult<Value> {
|
||||
let first = args.expect::<i64>("end")?;
|
||||
let (start, end) = match args.eat::<i64>()? {
|
||||
Some(second) => (first, second),
|
||||
|
@ -5,6 +5,7 @@ use typst::diag::{format_xml_like_error, FileError};
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Read structured data from a CSV file.
|
||||
#[func]
|
||||
pub fn csv(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
let Spanned { v: path, span } =
|
||||
args.expect::<Spanned<EcoString>>("path to csv file")?;
|
||||
@ -45,6 +46,7 @@ fn format_csv_error(error: csv::Error) -> String {
|
||||
}
|
||||
|
||||
/// Read structured data from a JSON file.
|
||||
#[func]
|
||||
pub fn json(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
let Spanned { v: path, span } =
|
||||
args.expect::<Spanned<EcoString>>("path to json file")?;
|
||||
@ -85,6 +87,7 @@ fn format_json_error(error: serde_json::Error) -> String {
|
||||
}
|
||||
|
||||
/// Read structured data from an XML file.
|
||||
#[func]
|
||||
pub fn xml(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
let Spanned { v: path, span } =
|
||||
args.expect::<Spanned<EcoString>>("path to xml file")?;
|
||||
|
@ -5,17 +5,20 @@ use typst::model;
|
||||
use typst::syntax::Source;
|
||||
|
||||
/// The name of a value's type.
|
||||
pub fn type_(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn type_(args: &mut Args) -> SourceResult<Value> {
|
||||
Ok(args.expect::<Value>("value")?.type_name().into())
|
||||
}
|
||||
|
||||
/// The string representation of a value.
|
||||
pub fn repr(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn repr(args: &mut Args) -> SourceResult<Value> {
|
||||
Ok(args.expect::<Value>("value")?.repr().into())
|
||||
}
|
||||
|
||||
/// Ensure that a condition is fulfilled.
|
||||
pub fn assert(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn assert(args: &mut Args) -> SourceResult<Value> {
|
||||
let Spanned { v, span } = args.expect::<Spanned<bool>>("condition")?;
|
||||
if !v {
|
||||
bail!(span, "assertion failed");
|
||||
@ -24,6 +27,7 @@ pub fn assert(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
}
|
||||
|
||||
/// Evaluate a string as Typst markup.
|
||||
#[func]
|
||||
pub fn eval(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
let Spanned { v: text, span } = args.expect::<Spanned<String>>("source")?;
|
||||
let source = Source::synthesized(text, span);
|
||||
|
@ -4,13 +4,15 @@ use crate::prelude::*;
|
||||
use crate::text::Case;
|
||||
|
||||
/// Create a blind text string.
|
||||
pub fn lorem(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn lorem(args: &mut Args) -> SourceResult<Value> {
|
||||
let words: usize = args.expect("number of words")?;
|
||||
Ok(Value::Str(lipsum::lipsum(words).into()))
|
||||
}
|
||||
|
||||
/// Apply a numbering pattern to a number.
|
||||
pub fn numbering(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn numbering(args: &mut Args) -> SourceResult<Value> {
|
||||
let pattern = args.expect::<NumberingPattern>("pattern")?;
|
||||
let numbers = args.all::<NonZeroUsize>()?;
|
||||
Ok(Value::Str(pattern.apply(&numbers).into()))
|
||||
|
@ -1,6 +1,8 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Just an empty shell to scope styles.
|
||||
/// Align content horizontally and vertically.
|
||||
#[func]
|
||||
#[capable]
|
||||
#[derive(Debug, Hash)]
|
||||
pub enum AlignNode {}
|
||||
|
||||
|
@ -2,6 +2,8 @@ use crate::prelude::*;
|
||||
use crate::text::TextNode;
|
||||
|
||||
/// Separate a region into multiple equally sized columns.
|
||||
#[func]
|
||||
#[capable(Layout)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct ColumnsNode {
|
||||
/// How many columns there should be.
|
||||
@ -11,7 +13,7 @@ pub struct ColumnsNode {
|
||||
pub body: Content,
|
||||
}
|
||||
|
||||
#[node(Layout)]
|
||||
#[node]
|
||||
impl ColumnsNode {
|
||||
/// The size of the gutter space between each column.
|
||||
#[property(resolve)]
|
||||
@ -101,12 +103,14 @@ impl Layout for ColumnsNode {
|
||||
}
|
||||
|
||||
/// A column break.
|
||||
#[func]
|
||||
#[capable(Behave)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct ColbreakNode {
|
||||
pub weak: bool,
|
||||
}
|
||||
|
||||
#[node(Behave)]
|
||||
#[node]
|
||||
impl ColbreakNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
let weak = args.named("weak")?.unwrap_or(false);
|
||||
|
@ -2,6 +2,8 @@ use super::VNode;
|
||||
use crate::prelude::*;
|
||||
|
||||
/// An inline-level container that sizes content.
|
||||
#[func]
|
||||
#[capable(Layout, Inline)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct BoxNode {
|
||||
/// How to size the content horizontally and vertically.
|
||||
@ -10,7 +12,7 @@ pub struct BoxNode {
|
||||
pub body: Content,
|
||||
}
|
||||
|
||||
#[node(Layout, Inline)]
|
||||
#[node]
|
||||
impl BoxNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
let width = args.named("width")?;
|
||||
@ -60,10 +62,12 @@ impl Layout for BoxNode {
|
||||
impl Inline for BoxNode {}
|
||||
|
||||
/// A block-level container that places content into a separate flow.
|
||||
#[func]
|
||||
#[capable(Layout)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct BlockNode(pub Content);
|
||||
|
||||
#[node(Layout)]
|
||||
#[node]
|
||||
impl BlockNode {
|
||||
/// The spacing between the previous and this block.
|
||||
#[property(skip)]
|
||||
|
@ -7,15 +7,12 @@ use crate::prelude::*;
|
||||
///
|
||||
/// This node is reponsible for layouting both the top-level content flow and
|
||||
/// the contents of boxes.
|
||||
#[capable(Layout)]
|
||||
#[derive(Hash)]
|
||||
pub struct FlowNode(pub StyleVec<Content>, pub bool);
|
||||
|
||||
#[node(Layout)]
|
||||
impl FlowNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(BlockNode(args.expect("body")?).pack())
|
||||
}
|
||||
}
|
||||
#[node]
|
||||
impl FlowNode {}
|
||||
|
||||
impl Layout for FlowNode {
|
||||
fn layout(
|
||||
|
@ -3,6 +3,8 @@ use crate::prelude::*;
|
||||
use super::Spacing;
|
||||
|
||||
/// Arrange content in a grid.
|
||||
#[func]
|
||||
#[capable(Layout)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct GridNode {
|
||||
/// Defines sizing for content rows and columns.
|
||||
@ -13,7 +15,7 @@ pub struct GridNode {
|
||||
pub cells: Vec<Content>,
|
||||
}
|
||||
|
||||
#[node(Layout)]
|
||||
#[node]
|
||||
impl GridNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
let TrackSizings(columns) = args.named("columns")?.unwrap_or_default();
|
||||
|
@ -1,10 +1,12 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Hide content without affecting layout.
|
||||
#[func]
|
||||
#[capable(Layout, Inline)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct HideNode(pub Content);
|
||||
|
||||
#[node(Layout, Inline)]
|
||||
#[node]
|
||||
impl HideNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("body")?).pack())
|
||||
|
@ -1,6 +1,8 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Pad content at the sides.
|
||||
#[func]
|
||||
#[capable(Layout)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct PadNode {
|
||||
/// The amount of padding.
|
||||
@ -9,7 +11,7 @@ pub struct PadNode {
|
||||
pub body: Content,
|
||||
}
|
||||
|
||||
#[node(Layout)]
|
||||
#[node]
|
||||
impl PadNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
let all = args.named("rest")?.or(args.find()?);
|
||||
|
@ -5,6 +5,8 @@ use crate::prelude::*;
|
||||
use crate::text::TextNode;
|
||||
|
||||
/// Layouts its child onto one or multiple pages.
|
||||
#[func]
|
||||
#[capable]
|
||||
#[derive(Clone, Hash)]
|
||||
pub struct PageNode(pub Content);
|
||||
|
||||
@ -143,6 +145,8 @@ impl Debug for PageNode {
|
||||
}
|
||||
|
||||
/// A page break.
|
||||
#[func]
|
||||
#[capable]
|
||||
#[derive(Debug, Copy, Clone, Hash)]
|
||||
pub struct PagebreakNode {
|
||||
pub weak: bool,
|
||||
|
@ -12,6 +12,8 @@ use crate::text::{
|
||||
};
|
||||
|
||||
/// Arrange text, spacing and inline-level nodes into a paragraph.
|
||||
#[func]
|
||||
#[capable]
|
||||
#[derive(Hash)]
|
||||
pub struct ParNode(pub StyleVec<Content>);
|
||||
|
||||
@ -142,10 +144,12 @@ castable! {
|
||||
}
|
||||
|
||||
/// A paragraph break.
|
||||
#[func]
|
||||
#[capable(Unlabellable)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct ParbreakNode;
|
||||
|
||||
#[node(Unlabellable)]
|
||||
#[node]
|
||||
impl ParbreakNode {
|
||||
fn construct(_: &Vm, _: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self.pack())
|
||||
|
@ -1,10 +1,12 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Place content at an absolute position.
|
||||
#[func]
|
||||
#[capable(Layout, Behave)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct PlaceNode(pub Content, bool);
|
||||
|
||||
#[node(Layout, Behave)]
|
||||
#[node]
|
||||
impl PlaceNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
let aligns = args.find()?.unwrap_or(Axes::with_x(Some(GenAlign::Start)));
|
||||
|
@ -1,10 +1,12 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Repeats content to fill a line.
|
||||
#[func]
|
||||
#[capable(Layout, Inline)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct RepeatNode(pub Content);
|
||||
|
||||
#[node(Layout, Inline)]
|
||||
#[node]
|
||||
impl RepeatNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("body")?).pack())
|
||||
|
@ -3,6 +3,8 @@ use std::cmp::Ordering;
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Horizontal spacing.
|
||||
#[func]
|
||||
#[capable(Behave)]
|
||||
#[derive(Debug, Copy, Clone, Hash)]
|
||||
pub struct HNode {
|
||||
/// The amount of horizontal spacing.
|
||||
@ -11,7 +13,7 @@ pub struct HNode {
|
||||
pub weak: bool,
|
||||
}
|
||||
|
||||
#[node(Behave)]
|
||||
#[node]
|
||||
impl HNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
let amount = args.expect("spacing")?;
|
||||
@ -50,6 +52,8 @@ impl Behave for HNode {
|
||||
}
|
||||
|
||||
/// Vertical spacing.
|
||||
#[func]
|
||||
#[capable(Behave)]
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd)]
|
||||
pub struct VNode {
|
||||
/// The amount of vertical spacing.
|
||||
@ -58,7 +62,7 @@ pub struct VNode {
|
||||
pub weakness: u8,
|
||||
}
|
||||
|
||||
#[node(Behave)]
|
||||
#[node]
|
||||
impl VNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
let amount = args.expect("spacing")?;
|
||||
|
@ -4,6 +4,8 @@ use super::{AlignNode, Spacing};
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Arrange content and spacing along an axis.
|
||||
#[func]
|
||||
#[capable(Layout)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct StackNode {
|
||||
/// The stacking direction.
|
||||
@ -14,7 +16,7 @@ pub struct StackNode {
|
||||
pub children: Vec<StackChild>,
|
||||
}
|
||||
|
||||
#[node(Layout)]
|
||||
#[node]
|
||||
impl StackNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self {
|
||||
|
@ -3,6 +3,8 @@ use typst::geom::Transform;
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Move content without affecting layout.
|
||||
#[func]
|
||||
#[capable(Layout, Inline)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct MoveNode {
|
||||
/// The offset by which to move the content.
|
||||
@ -11,7 +13,7 @@ pub struct MoveNode {
|
||||
pub body: Content,
|
||||
}
|
||||
|
||||
#[node(Layout, Inline)]
|
||||
#[node]
|
||||
impl MoveNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
let dx = args.named("dx")?.unwrap_or_default();
|
||||
@ -44,6 +46,8 @@ impl Layout for MoveNode {
|
||||
impl Inline for MoveNode {}
|
||||
|
||||
/// Transform content without affecting layout.
|
||||
#[func]
|
||||
#[capable(Layout, Inline)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct TransformNode<const T: TransformKind> {
|
||||
/// Transformation to apply to the content.
|
||||
@ -58,7 +62,7 @@ pub type RotateNode = TransformNode<ROTATE>;
|
||||
/// Scale content without affecting layout.
|
||||
pub type ScaleNode = TransformNode<SCALE>;
|
||||
|
||||
#[node(Layout, Inline)]
|
||||
#[node]
|
||||
impl<const T: TransformKind> TransformNode<T> {
|
||||
/// The origin of the transformation.
|
||||
#[property(resolve)]
|
||||
|
@ -25,111 +25,110 @@ fn scope() -> Scope {
|
||||
let mut std = Scope::new();
|
||||
|
||||
// Basics.
|
||||
std.def_node::<basics::HeadingNode>("heading");
|
||||
std.def_node::<basics::ListNode>("list");
|
||||
std.def_node::<basics::EnumNode>("enum");
|
||||
std.def_node::<basics::DescNode>("desc");
|
||||
std.def_node::<basics::TableNode>("table");
|
||||
std.def_func::<basics::HeadingNode>("heading");
|
||||
std.def_func::<basics::ListNode>("list");
|
||||
std.def_func::<basics::EnumNode>("enum");
|
||||
std.def_func::<basics::DescNode>("desc");
|
||||
std.def_func::<basics::TableNode>("table");
|
||||
|
||||
// Text.
|
||||
std.def_node::<text::TextNode>("text");
|
||||
std.def_node::<text::LinebreakNode>("linebreak");
|
||||
std.def_node::<text::SymbolNode>("symbol");
|
||||
std.def_node::<text::SmartQuoteNode>("smartquote");
|
||||
std.def_node::<text::StrongNode>("strong");
|
||||
std.def_node::<text::EmphNode>("emph");
|
||||
std.def_fn("lower", text::lower);
|
||||
std.def_fn("upper", text::upper);
|
||||
std.def_fn("smallcaps", text::smallcaps);
|
||||
std.def_node::<text::SubNode>("sub");
|
||||
std.def_node::<text::SuperNode>("super");
|
||||
std.def_node::<text::UnderlineNode>("underline");
|
||||
std.def_node::<text::StrikeNode>("strike");
|
||||
std.def_node::<text::OverlineNode>("overline");
|
||||
std.def_node::<text::RawNode>("raw");
|
||||
std.def_func::<text::TextNode>("text");
|
||||
std.def_func::<text::LinebreakNode>("linebreak");
|
||||
std.def_func::<text::SymbolNode>("symbol");
|
||||
std.def_func::<text::SmartQuoteNode>("smartquote");
|
||||
std.def_func::<text::StrongNode>("strong");
|
||||
std.def_func::<text::EmphNode>("emph");
|
||||
std.def_func::<text::LowerFunc>("lower");
|
||||
std.def_func::<text::UpperFunc>("upper");
|
||||
std.def_func::<text::SmallcapsFunc>("smallcaps");
|
||||
std.def_func::<text::SubNode>("sub");
|
||||
std.def_func::<text::SuperNode>("super");
|
||||
std.def_func::<text::UnderlineNode>("underline");
|
||||
std.def_func::<text::StrikeNode>("strike");
|
||||
std.def_func::<text::OverlineNode>("overline");
|
||||
std.def_func::<text::RawNode>("raw");
|
||||
|
||||
// Math.
|
||||
std.def_node::<math::MathNode>("math");
|
||||
std.def_node::<math::AtomNode>("atom");
|
||||
std.def_node::<math::AccNode>("acc");
|
||||
std.def_node::<math::FracNode>("frac");
|
||||
std.def_node::<math::BinomNode>("binom");
|
||||
std.def_node::<math::SqrtNode>("sqrt");
|
||||
std.def_node::<math::FloorNode>("floor");
|
||||
std.def_node::<math::CeilNode>("ceil");
|
||||
std.def_node::<math::VecNode>("vec");
|
||||
std.def_node::<math::CasesNode>("cases");
|
||||
std.def_node::<math::SerifNode>("serif");
|
||||
std.def_node::<math::SansNode>("sans");
|
||||
std.def_node::<math::BoldNode>("bold");
|
||||
std.def_node::<math::ItalNode>("ital");
|
||||
std.def_node::<math::CalNode>("cal");
|
||||
std.def_node::<math::FrakNode>("frak");
|
||||
std.def_node::<math::MonoNode>("mono");
|
||||
std.def_node::<math::BbNode>("bb");
|
||||
std.def_func::<math::MathNode>("math");
|
||||
std.def_func::<math::AtomNode>("atom");
|
||||
std.def_func::<math::AccNode>("acc");
|
||||
std.def_func::<math::FracNode>("frac");
|
||||
std.def_func::<math::BinomNode>("binom");
|
||||
std.def_func::<math::SqrtNode>("sqrt");
|
||||
std.def_func::<math::FloorNode>("floor");
|
||||
std.def_func::<math::CeilNode>("ceil");
|
||||
std.def_func::<math::VecNode>("vec");
|
||||
std.def_func::<math::CasesNode>("cases");
|
||||
std.def_func::<math::SerifNode>("serif");
|
||||
std.def_func::<math::SansNode>("sans");
|
||||
std.def_func::<math::BoldNode>("bold");
|
||||
std.def_func::<math::ItalNode>("ital");
|
||||
std.def_func::<math::CalNode>("cal");
|
||||
std.def_func::<math::FrakNode>("frak");
|
||||
std.def_func::<math::MonoNode>("mono");
|
||||
std.def_func::<math::BbNode>("bb");
|
||||
|
||||
// Layout.
|
||||
std.def_node::<layout::PageNode>("page");
|
||||
std.def_node::<layout::PagebreakNode>("pagebreak");
|
||||
std.def_node::<layout::FlowNode>("flow");
|
||||
std.def_node::<layout::VNode>("v");
|
||||
std.def_node::<layout::ParNode>("par");
|
||||
std.def_node::<layout::ParbreakNode>("parbreak");
|
||||
std.def_node::<layout::HNode>("h");
|
||||
std.def_node::<layout::BoxNode>("box");
|
||||
std.def_node::<layout::BlockNode>("block");
|
||||
std.def_node::<layout::StackNode>("stack");
|
||||
std.def_node::<layout::GridNode>("grid");
|
||||
std.def_node::<layout::ColumnsNode>("columns");
|
||||
std.def_node::<layout::ColbreakNode>("colbreak");
|
||||
std.def_node::<layout::PlaceNode>("place");
|
||||
std.def_node::<layout::AlignNode>("align");
|
||||
std.def_node::<layout::PadNode>("pad");
|
||||
std.def_node::<layout::RepeatNode>("repeat");
|
||||
std.def_node::<layout::MoveNode>("move");
|
||||
std.def_node::<layout::ScaleNode>("scale");
|
||||
std.def_node::<layout::RotateNode>("rotate");
|
||||
std.def_node::<layout::HideNode>("hide");
|
||||
std.def_func::<layout::PageNode>("page");
|
||||
std.def_func::<layout::PagebreakNode>("pagebreak");
|
||||
std.def_func::<layout::VNode>("v");
|
||||
std.def_func::<layout::ParNode>("par");
|
||||
std.def_func::<layout::ParbreakNode>("parbreak");
|
||||
std.def_func::<layout::HNode>("h");
|
||||
std.def_func::<layout::BoxNode>("box");
|
||||
std.def_func::<layout::BlockNode>("block");
|
||||
std.def_func::<layout::StackNode>("stack");
|
||||
std.def_func::<layout::GridNode>("grid");
|
||||
std.def_func::<layout::ColumnsNode>("columns");
|
||||
std.def_func::<layout::ColbreakNode>("colbreak");
|
||||
std.def_func::<layout::PlaceNode>("place");
|
||||
std.def_func::<layout::AlignNode>("align");
|
||||
std.def_func::<layout::PadNode>("pad");
|
||||
std.def_func::<layout::RepeatNode>("repeat");
|
||||
std.def_func::<layout::MoveNode>("move");
|
||||
std.def_func::<layout::ScaleNode>("scale");
|
||||
std.def_func::<layout::RotateNode>("rotate");
|
||||
std.def_func::<layout::HideNode>("hide");
|
||||
|
||||
// Visualize.
|
||||
std.def_node::<visualize::ImageNode>("image");
|
||||
std.def_node::<visualize::LineNode>("line");
|
||||
std.def_node::<visualize::RectNode>("rect");
|
||||
std.def_node::<visualize::SquareNode>("square");
|
||||
std.def_node::<visualize::EllipseNode>("ellipse");
|
||||
std.def_node::<visualize::CircleNode>("circle");
|
||||
std.def_func::<visualize::ImageNode>("image");
|
||||
std.def_func::<visualize::LineNode>("line");
|
||||
std.def_func::<visualize::RectNode>("rect");
|
||||
std.def_func::<visualize::SquareNode>("square");
|
||||
std.def_func::<visualize::EllipseNode>("ellipse");
|
||||
std.def_func::<visualize::CircleNode>("circle");
|
||||
|
||||
// Meta.
|
||||
std.def_node::<meta::DocumentNode>("document");
|
||||
std.def_node::<meta::RefNode>("ref");
|
||||
std.def_node::<meta::LinkNode>("link");
|
||||
std.def_node::<meta::OutlineNode>("outline");
|
||||
std.def_func::<meta::DocumentNode>("document");
|
||||
std.def_func::<meta::RefNode>("ref");
|
||||
std.def_func::<meta::LinkNode>("link");
|
||||
std.def_func::<meta::OutlineNode>("outline");
|
||||
|
||||
// Compute.
|
||||
std.def_fn("type", compute::type_);
|
||||
std.def_fn("repr", compute::repr);
|
||||
std.def_fn("assert", compute::assert);
|
||||
std.def_fn("eval", compute::eval);
|
||||
std.def_fn("int", compute::int);
|
||||
std.def_fn("float", compute::float);
|
||||
std.def_fn("luma", compute::luma);
|
||||
std.def_fn("rgb", compute::rgb);
|
||||
std.def_fn("cmyk", compute::cmyk);
|
||||
std.def_fn("str", compute::str);
|
||||
std.def_fn("label", compute::label);
|
||||
std.def_fn("regex", compute::regex);
|
||||
std.def_fn("range", compute::range);
|
||||
std.def_fn("abs", compute::abs);
|
||||
std.def_fn("min", compute::min);
|
||||
std.def_fn("max", compute::max);
|
||||
std.def_fn("even", compute::even);
|
||||
std.def_fn("odd", compute::odd);
|
||||
std.def_fn("mod", compute::mod_);
|
||||
std.def_fn("csv", compute::csv);
|
||||
std.def_fn("json", compute::json);
|
||||
std.def_fn("xml", compute::xml);
|
||||
std.def_fn("lorem", compute::lorem);
|
||||
std.def_fn("numbering", compute::numbering);
|
||||
std.def_func::<compute::TypeFunc>("type");
|
||||
std.def_func::<compute::ReprFunc>("repr");
|
||||
std.def_func::<compute::AssertFunc>("assert");
|
||||
std.def_func::<compute::EvalFunc>("eval");
|
||||
std.def_func::<compute::IntFunc>("int");
|
||||
std.def_func::<compute::FloatFunc>("float");
|
||||
std.def_func::<compute::LumaFunc>("luma");
|
||||
std.def_func::<compute::RgbFunc>("rgb");
|
||||
std.def_func::<compute::CmykFunc>("cmyk");
|
||||
std.def_func::<compute::StrFunc>("str");
|
||||
std.def_func::<compute::LabelFunc>("label");
|
||||
std.def_func::<compute::RegexFunc>("regex");
|
||||
std.def_func::<compute::RangeFunc>("range");
|
||||
std.def_func::<compute::AbsFunc>("abs");
|
||||
std.def_func::<compute::MinFunc>("min");
|
||||
std.def_func::<compute::MaxFunc>("max");
|
||||
std.def_func::<compute::EvenFunc>("even");
|
||||
std.def_func::<compute::OddFunc>("odd");
|
||||
std.def_func::<compute::ModFunc>("mod");
|
||||
std.def_func::<compute::CsvFunc>("csv");
|
||||
std.def_func::<compute::JsonFunc>("json");
|
||||
std.def_func::<compute::XmlFunc>("xml");
|
||||
std.def_func::<compute::LoremFunc>("lorem");
|
||||
std.def_func::<compute::NumberingFunc>("numbering");
|
||||
|
||||
// Colors.
|
||||
std.define("black", Color::BLACK);
|
||||
|
@ -1,10 +1,12 @@
|
||||
use super::*;
|
||||
|
||||
/// A column vector.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct VecNode(Vec<Content>);
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl VecNode {
|
||||
/// The kind of delimiter.
|
||||
pub const DELIM: Delimiter = Delimiter::Paren;
|
||||
@ -61,10 +63,12 @@ castable! {
|
||||
}
|
||||
|
||||
/// A case distinction.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct CasesNode(Vec<Content>);
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl CasesNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.all()?).pack())
|
||||
|
@ -15,6 +15,8 @@ use crate::prelude::*;
|
||||
use crate::text::{FontFamily, LinebreakNode, SpaceNode, SymbolNode, TextNode};
|
||||
|
||||
/// A piece of a mathematical formula.
|
||||
#[func]
|
||||
#[capable(Show, Layout, Inline, Texify)]
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct MathNode {
|
||||
/// Whether the formula is display-level.
|
||||
@ -23,7 +25,7 @@ pub struct MathNode {
|
||||
pub children: Vec<Content>,
|
||||
}
|
||||
|
||||
#[node(Show, Layout, Inline, Texify)]
|
||||
#[node]
|
||||
impl MathNode {
|
||||
fn field(&self, name: &str) -> Option<Value> {
|
||||
match name {
|
||||
@ -242,10 +244,12 @@ impl Texify for Content {
|
||||
}
|
||||
|
||||
/// An atom in a math formula: `x`, `+`, `12`.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct AtomNode(pub EcoString);
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl AtomNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("text")?).pack())
|
||||
@ -279,6 +283,8 @@ impl Texify for AtomNode {
|
||||
}
|
||||
|
||||
/// An accented node.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct AccNode {
|
||||
/// The accent base.
|
||||
@ -287,7 +293,7 @@ pub struct AccNode {
|
||||
pub accent: char,
|
||||
}
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl AccNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
let base = args.expect("base")?;
|
||||
@ -352,6 +358,8 @@ impl Texify for AccNode {
|
||||
}
|
||||
|
||||
/// A fraction.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct FracNode {
|
||||
/// The numerator.
|
||||
@ -360,7 +368,7 @@ pub struct FracNode {
|
||||
pub denom: Content,
|
||||
}
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl FracNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
let num = args.expect("numerator")?;
|
||||
@ -381,6 +389,8 @@ impl Texify for FracNode {
|
||||
}
|
||||
|
||||
/// A binomial.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct BinomNode {
|
||||
/// The upper index.
|
||||
@ -389,7 +399,7 @@ pub struct BinomNode {
|
||||
pub lower: Content,
|
||||
}
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl BinomNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
let upper = args.expect("upper index")?;
|
||||
@ -410,6 +420,8 @@ impl Texify for BinomNode {
|
||||
}
|
||||
|
||||
/// A sub- and/or superscript.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct ScriptNode {
|
||||
/// The base.
|
||||
@ -420,7 +432,7 @@ pub struct ScriptNode {
|
||||
pub sup: Option<Content>,
|
||||
}
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl ScriptNode {}
|
||||
|
||||
impl Texify for ScriptNode {
|
||||
@ -444,10 +456,12 @@ impl Texify for ScriptNode {
|
||||
}
|
||||
|
||||
/// A math alignment point: `&`, `&&`.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct AlignPointNode(pub usize);
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl AlignPointNode {}
|
||||
|
||||
impl Texify for AlignPointNode {
|
||||
@ -457,10 +471,12 @@ impl Texify for AlignPointNode {
|
||||
}
|
||||
|
||||
/// A square root.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct SqrtNode(pub Content);
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl SqrtNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("body")?).pack())
|
||||
@ -477,10 +493,12 @@ impl Texify for SqrtNode {
|
||||
}
|
||||
|
||||
/// A floored expression.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct FloorNode(pub Content);
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl FloorNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("body")?).pack())
|
||||
@ -497,10 +515,12 @@ impl Texify for FloorNode {
|
||||
}
|
||||
|
||||
/// A ceiled expression.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct CeilNode(pub Content);
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl CeilNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("body")?).pack())
|
||||
|
@ -1,10 +1,12 @@
|
||||
use super::*;
|
||||
|
||||
/// Serif (roman) font style.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct SerifNode(pub Content);
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl SerifNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("body")?).pack())
|
||||
@ -21,10 +23,12 @@ impl Texify for SerifNode {
|
||||
}
|
||||
|
||||
/// Sans-serif font style.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct SansNode(pub Content);
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl SansNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("body")?).pack())
|
||||
@ -41,10 +45,12 @@ impl Texify for SansNode {
|
||||
}
|
||||
|
||||
/// Bold font style.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct BoldNode(pub Content);
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl BoldNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("body")?).pack())
|
||||
@ -61,10 +67,12 @@ impl Texify for BoldNode {
|
||||
}
|
||||
|
||||
/// Italic font style.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct ItalNode(pub Content);
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl ItalNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("body")?).pack())
|
||||
@ -81,10 +89,12 @@ impl Texify for ItalNode {
|
||||
}
|
||||
|
||||
/// Calligraphic font style.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct CalNode(pub Content);
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl CalNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("body")?).pack())
|
||||
@ -101,10 +111,12 @@ impl Texify for CalNode {
|
||||
}
|
||||
|
||||
/// Fraktur font style.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct FrakNode(pub Content);
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl FrakNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("body")?).pack())
|
||||
@ -121,10 +133,12 @@ impl Texify for FrakNode {
|
||||
}
|
||||
|
||||
/// Monospace font style.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct MonoNode(pub Content);
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl MonoNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("body")?).pack())
|
||||
@ -141,10 +155,12 @@ impl Texify for MonoNode {
|
||||
}
|
||||
|
||||
/// Blackboard bold (double-struck) font style.
|
||||
#[func]
|
||||
#[capable(Texify)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct BbNode(pub Content);
|
||||
|
||||
#[node(Texify)]
|
||||
#[node]
|
||||
impl BbNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("body")?).pack())
|
||||
|
@ -2,10 +2,12 @@ use crate::layout::{LayoutRoot, PageNode};
|
||||
use crate::prelude::*;
|
||||
|
||||
/// The root node that represents a full document.
|
||||
#[func]
|
||||
#[capable(LayoutRoot)]
|
||||
#[derive(Hash)]
|
||||
pub struct DocumentNode(pub StyleVec<PageNode>);
|
||||
|
||||
#[node(LayoutRoot)]
|
||||
#[node]
|
||||
impl DocumentNode {
|
||||
/// The document's title.
|
||||
#[property(referenced)]
|
||||
|
@ -2,6 +2,8 @@ use crate::prelude::*;
|
||||
use crate::text::TextNode;
|
||||
|
||||
/// Link text and other elements to a destination.
|
||||
#[func]
|
||||
#[capable(Show, Finalize)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct LinkNode {
|
||||
/// The destination the link points to.
|
||||
@ -23,7 +25,7 @@ impl LinkNode {
|
||||
}
|
||||
}
|
||||
|
||||
#[node(Show, Finalize)]
|
||||
#[node]
|
||||
impl LinkNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
let dest = args.expect::<Destination>("destination")?;
|
||||
|
@ -4,10 +4,12 @@ use crate::prelude::*;
|
||||
use crate::text::{LinebreakNode, SpaceNode, TextNode};
|
||||
|
||||
/// A section outline (table of contents).
|
||||
#[func]
|
||||
#[capable(Prepare, Show)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct OutlineNode;
|
||||
|
||||
#[node(Prepare, Show)]
|
||||
#[node]
|
||||
impl OutlineNode {
|
||||
/// The title of the outline.
|
||||
#[property(referenced)]
|
||||
|
@ -2,10 +2,12 @@ use crate::prelude::*;
|
||||
use crate::text::TextNode;
|
||||
|
||||
/// A reference to a label.
|
||||
#[func]
|
||||
#[capable(Show)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct RefNode(pub EcoString);
|
||||
|
||||
#[node(Show)]
|
||||
#[node]
|
||||
impl RefNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("target")?).pack())
|
||||
|
@ -15,10 +15,10 @@ pub use typst::doc::*;
|
||||
pub use typst::geom::*;
|
||||
#[doc(no_inline)]
|
||||
pub use typst::model::{
|
||||
array, capability, castable, dict, dynamic, format_str, node, Args, Array, Cast,
|
||||
Content, Dict, Finalize, Fold, Func, Introspector, Label, Node, NodeId, Prepare,
|
||||
Resolve, Selector, Show, Smart, StabilityProvider, Str, StyleChain, StyleMap,
|
||||
StyleVec, Unlabellable, Value, Vm, Vt,
|
||||
array, capability, capable, castable, dict, dynamic, format_str, func, node, Args,
|
||||
Array, Cast, Content, Dict, Finalize, Fold, Func, Introspector, Label, Node, NodeId,
|
||||
Prepare, Resolve, Selector, Show, Smart, StabilityProvider, Str, StyleChain,
|
||||
StyleMap, StyleVec, Unlabellable, Value, Vm, Vt,
|
||||
};
|
||||
#[doc(no_inline)]
|
||||
pub use typst::syntax::{Span, Spanned};
|
||||
|
@ -98,6 +98,7 @@ impl StyleMapExt for StyleMap {
|
||||
}
|
||||
|
||||
/// Fill the frames resulting from content.
|
||||
#[capable(Layout)]
|
||||
#[derive(Debug, Hash)]
|
||||
struct FillNode {
|
||||
/// How to fill the frames resulting from the `child`.
|
||||
@ -106,7 +107,7 @@ struct FillNode {
|
||||
child: Content,
|
||||
}
|
||||
|
||||
#[node(Layout)]
|
||||
#[node]
|
||||
impl FillNode {}
|
||||
|
||||
impl Layout for FillNode {
|
||||
@ -126,6 +127,7 @@ impl Layout for FillNode {
|
||||
}
|
||||
|
||||
/// Stroke the frames resulting from content.
|
||||
#[capable(Layout)]
|
||||
#[derive(Debug, Hash)]
|
||||
struct StrokeNode {
|
||||
/// How to stroke the frames resulting from the `child`.
|
||||
@ -134,7 +136,7 @@ struct StrokeNode {
|
||||
child: Content,
|
||||
}
|
||||
|
||||
#[node(Layout)]
|
||||
#[node]
|
||||
impl StrokeNode {}
|
||||
|
||||
impl Layout for StrokeNode {
|
||||
|
@ -5,6 +5,8 @@ use super::TextNode;
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Typeset underline, stricken-through or overlined text.
|
||||
#[func]
|
||||
#[capable(Show)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct DecoNode<const L: DecoLine>(pub Content);
|
||||
|
||||
@ -17,7 +19,7 @@ pub type StrikeNode = DecoNode<STRIKETHROUGH>;
|
||||
/// Typeset overlined text.
|
||||
pub type OverlineNode = DecoNode<OVERLINE>;
|
||||
|
||||
#[node(Show)]
|
||||
#[node]
|
||||
impl<const L: DecoLine> DecoNode<L> {
|
||||
/// How to stroke the line. The text color and thickness are read from the
|
||||
/// font tables if `auto`.
|
||||
|
@ -2,10 +2,12 @@ use super::TextNode;
|
||||
use crate::prelude::*;
|
||||
|
||||
/// A text space.
|
||||
#[func]
|
||||
#[capable(Unlabellable, Behave)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct SpaceNode;
|
||||
|
||||
#[node(Unlabellable, Behave)]
|
||||
#[node]
|
||||
impl SpaceNode {
|
||||
fn construct(_: &Vm, _: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self.pack())
|
||||
@ -21,12 +23,14 @@ impl Behave for SpaceNode {
|
||||
}
|
||||
|
||||
/// A line break.
|
||||
#[func]
|
||||
#[capable(Behave)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct LinebreakNode {
|
||||
pub justify: bool,
|
||||
}
|
||||
|
||||
#[node(Behave)]
|
||||
#[node]
|
||||
impl LinebreakNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
let justify = args.named("justify")?.unwrap_or(false);
|
||||
@ -41,10 +45,12 @@ impl Behave for LinebreakNode {
|
||||
}
|
||||
|
||||
/// Strongly emphasizes content by increasing the font weight.
|
||||
#[func]
|
||||
#[capable(Show)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct StrongNode(pub Content);
|
||||
|
||||
#[node(Show)]
|
||||
#[node]
|
||||
impl StrongNode {
|
||||
/// The delta to apply on the font weight.
|
||||
pub const DELTA: i64 = 300;
|
||||
@ -86,10 +92,12 @@ impl Fold for Delta {
|
||||
}
|
||||
|
||||
/// Emphasizes content by flipping the italicness.
|
||||
#[func]
|
||||
#[capable(Show)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct EmphNode(pub Content);
|
||||
|
||||
#[node(Show)]
|
||||
#[node]
|
||||
impl EmphNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("body")?).pack())
|
||||
@ -122,12 +130,14 @@ impl Fold for Toggle {
|
||||
}
|
||||
|
||||
/// Convert a string or content to lowercase.
|
||||
pub fn lower(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn lower(args: &mut Args) -> SourceResult<Value> {
|
||||
case(Case::Lower, args)
|
||||
}
|
||||
|
||||
/// Convert a string or content to uppercase.
|
||||
pub fn upper(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn upper(args: &mut Args) -> SourceResult<Value> {
|
||||
case(Case::Upper, args)
|
||||
}
|
||||
|
||||
@ -161,7 +171,8 @@ impl Case {
|
||||
}
|
||||
|
||||
/// Display text in small capitals.
|
||||
pub fn smallcaps(_: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
#[func]
|
||||
pub fn smallcaps(args: &mut Args) -> SourceResult<Value> {
|
||||
let body: Content = args.expect("content")?;
|
||||
Ok(Value::Content(body.styled(TextNode::SMALLCAPS, true)))
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ use crate::layout::ParNode;
|
||||
use crate::prelude::*;
|
||||
|
||||
/// A single run of text with the same style.
|
||||
#[func]
|
||||
#[capable]
|
||||
#[derive(Clone, Hash)]
|
||||
pub struct TextNode(pub EcoString);
|
||||
|
||||
|
@ -3,6 +3,8 @@ use typst::syntax::is_newline;
|
||||
use crate::prelude::*;
|
||||
|
||||
/// A smart quote.
|
||||
#[func]
|
||||
#[capable]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct SmartQuoteNode {
|
||||
pub double: bool,
|
||||
|
@ -7,6 +7,8 @@ use crate::layout::BlockNode;
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Raw text with optional syntax highlighting.
|
||||
#[func]
|
||||
#[capable(Show)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct RawNode {
|
||||
/// The raw text.
|
||||
@ -15,7 +17,7 @@ pub struct RawNode {
|
||||
pub block: bool,
|
||||
}
|
||||
|
||||
#[node(Show)]
|
||||
#[node]
|
||||
impl RawNode {
|
||||
/// The language to syntax-highlight in.
|
||||
#[property(referenced)]
|
||||
|
@ -10,6 +10,8 @@ use crate::prelude::*;
|
||||
/// typography possible, we first try to transform the text to superscript
|
||||
/// codepoints. If that fails, we fall back to rendering shrunk normal letters
|
||||
/// in a raised way.
|
||||
#[func]
|
||||
#[capable(Show)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct ShiftNode<const S: ShiftKind>(pub Content);
|
||||
|
||||
@ -19,7 +21,7 @@ pub type SuperNode = ShiftNode<SUPERSCRIPT>;
|
||||
/// Shift the text into subscript.
|
||||
pub type SubNode = ShiftNode<SUBSCRIPT>;
|
||||
|
||||
#[node(Show)]
|
||||
#[node]
|
||||
impl<const S: ShiftKind> ShiftNode<S> {
|
||||
/// Whether to prefer the dedicated sub- and superscript characters of the
|
||||
/// font.
|
||||
|
@ -2,10 +2,12 @@ use crate::prelude::*;
|
||||
use crate::text::TextNode;
|
||||
|
||||
/// A symbol identified by symmie notation.
|
||||
#[func]
|
||||
#[capable(Show)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct SymbolNode(pub EcoString);
|
||||
|
||||
#[node(Show)]
|
||||
#[node]
|
||||
impl SymbolNode {
|
||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||
Ok(Self(args.expect("notation")?).pack())
|
||||
|
@ -5,10 +5,12 @@ use typst::image::{Image, ImageFormat, RasterFormat, VectorFormat};
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Show a raster or vector graphic.
|
||||
#[func]
|
||||
#[capable(Layout, Inline)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct ImageNode(pub Image);
|
||||
|
||||
#[node(Layout, Inline)]
|
||||
#[node]
|
||||
impl ImageNode {
|
||||
/// How the image should adjust itself to a given area.
|
||||
pub const FIT: ImageFit = ImageFit::Cover;
|
||||
|
@ -1,6 +1,8 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Display a line without affecting the layout.
|
||||
#[func]
|
||||
#[capable(Layout, Inline)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct LineNode {
|
||||
/// Where the line starts.
|
||||
@ -9,7 +11,7 @@ pub struct LineNode {
|
||||
pub delta: Axes<Rel<Length>>,
|
||||
}
|
||||
|
||||
#[node(Layout, Inline)]
|
||||
#[node]
|
||||
impl LineNode {
|
||||
/// How to stroke the line.
|
||||
#[property(resolve, fold)]
|
||||
|
@ -3,6 +3,8 @@ use std::f64::consts::SQRT_2;
|
||||
use crate::prelude::*;
|
||||
|
||||
/// A sizable and fillable shape with optional content.
|
||||
#[func]
|
||||
#[capable(Layout, Inline)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct ShapeNode<const S: ShapeKind>(pub Option<Content>);
|
||||
|
||||
@ -18,7 +20,7 @@ pub type CircleNode = ShapeNode<CIRCLE>;
|
||||
/// A ellipse with optional content.
|
||||
pub type EllipseNode = ShapeNode<ELLIPSE>;
|
||||
|
||||
#[node(Layout, Inline)]
|
||||
#[node]
|
||||
impl<const S: ShapeKind> ShapeNode<S> {
|
||||
/// How to fill the shape.
|
||||
pub const FILL: Option<Paint> = None;
|
||||
|
@ -1,10 +1,48 @@
|
||||
use super::*;
|
||||
|
||||
use syn::parse::Parser;
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::Token;
|
||||
|
||||
/// Expand the `#[capability]` macro.
|
||||
pub fn expand(body: syn::ItemTrait) -> Result<TokenStream> {
|
||||
let ident = &body.ident;
|
||||
pub fn capability(item: syn::ItemTrait) -> Result<TokenStream> {
|
||||
let ident = &item.ident;
|
||||
Ok(quote! {
|
||||
#body
|
||||
#item
|
||||
impl ::typst::model::Capability for dyn #ident {}
|
||||
})
|
||||
}
|
||||
|
||||
/// Expand the `#[capable(..)]` macro.
|
||||
pub fn capable(attr: TokenStream, item: syn::Item) -> Result<TokenStream> {
|
||||
let (ident, generics) = match &item {
|
||||
syn::Item::Struct(s) => (&s.ident, &s.generics),
|
||||
syn::Item::Enum(s) => (&s.ident, &s.generics),
|
||||
_ => bail!(item, "only structs and enums are supported"),
|
||||
};
|
||||
|
||||
let (params, args, clause) = generics.split_for_impl();
|
||||
let checks = Punctuated::<Ident, Token![,]>::parse_terminated
|
||||
.parse2(attr)?
|
||||
.into_iter()
|
||||
.map(|capability| {
|
||||
quote! {
|
||||
if id == ::std::any::TypeId::of::<dyn #capability>() {
|
||||
return Some(unsafe {
|
||||
::typst::util::fat::vtable(self as &dyn #capability)
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(quote! {
|
||||
#item
|
||||
|
||||
unsafe impl #params ::typst::model::Capable for #ident #args #clause {
|
||||
fn vtable(&self, id: ::std::any::TypeId) -> ::std::option::Option<*const ()> {
|
||||
#(#checks)*
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
82
macros/src/func.rs
Normal file
82
macros/src/func.rs
Normal file
@ -0,0 +1,82 @@
|
||||
use super::*;
|
||||
|
||||
/// Expand the `#[func]` macro.
|
||||
pub fn func(item: syn::Item) -> Result<TokenStream> {
|
||||
let doc = documentation(&item)?;
|
||||
|
||||
if let syn::Item::Fn(item) = &item {
|
||||
let vis = &item.vis;
|
||||
let ident = &item.sig.ident;
|
||||
let s = ident.to_string();
|
||||
let mut chars = s.trim_end_matches("_").chars();
|
||||
let ty = quote::format_ident!(
|
||||
"{}{}Func",
|
||||
chars.next().unwrap().to_ascii_uppercase(),
|
||||
chars.as_str()
|
||||
);
|
||||
|
||||
let full = if item.sig.inputs.len() == 1 {
|
||||
quote! { |_, args| #ident(args) }
|
||||
} else {
|
||||
quote! { #ident }
|
||||
};
|
||||
|
||||
Ok(quote! {
|
||||
#item
|
||||
|
||||
#[doc(hidden)]
|
||||
#vis enum #ty {}
|
||||
|
||||
impl::typst::model::FuncType for #ty {
|
||||
fn create_func(name: &'static str) -> ::typst::model::Func {
|
||||
::typst::model::Func::from_fn(name, #full, #doc)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
let (ident, generics) = match &item {
|
||||
syn::Item::Struct(s) => (&s.ident, &s.generics),
|
||||
syn::Item::Enum(s) => (&s.ident, &s.generics),
|
||||
_ => bail!(item, "only structs, enums, and functions are supported"),
|
||||
};
|
||||
|
||||
let (params, args, clause) = generics.split_for_impl();
|
||||
|
||||
Ok(quote! {
|
||||
#item
|
||||
|
||||
impl #params ::typst::model::FuncType for #ident #args #clause {
|
||||
fn create_func(name: &'static str) -> ::typst::model::Func {
|
||||
::typst::model::Func::from_node::<Self>(name, #doc)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the item's documentation.
|
||||
fn documentation(item: &syn::Item) -> Result<String> {
|
||||
let mut doc = String::new();
|
||||
|
||||
// Extract attributes.
|
||||
let attrs = match item {
|
||||
syn::Item::Struct(item) => &item.attrs,
|
||||
syn::Item::Enum(item) => &item.attrs,
|
||||
syn::Item::Fn(item) => &item.attrs,
|
||||
_ => return Ok(doc),
|
||||
};
|
||||
|
||||
// Parse doc comments.
|
||||
for attr in attrs {
|
||||
if let syn::Meta::NameValue(meta) = attr.parse_meta()? {
|
||||
if meta.path.is_ident("doc") {
|
||||
if let syn::Lit::Str(string) = &meta.lit {
|
||||
doc.push_str(&string.value());
|
||||
doc.push('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(doc.trim().into())
|
||||
}
|
@ -13,6 +13,7 @@ macro_rules! bail {
|
||||
}
|
||||
|
||||
mod capability;
|
||||
mod func;
|
||||
mod node;
|
||||
|
||||
use proc_macro::TokenStream as BoundaryStream;
|
||||
@ -21,20 +22,34 @@ use quote::{quote, quote_spanned};
|
||||
use syn::parse_quote;
|
||||
use syn::{Error, Ident, Result};
|
||||
|
||||
/// Implement `FuncType` for a type or function.
|
||||
#[proc_macro_attribute]
|
||||
pub fn func(_: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
|
||||
let item = syn::parse_macro_input!(item as syn::Item);
|
||||
func::func(item).unwrap_or_else(|err| err.to_compile_error()).into()
|
||||
}
|
||||
|
||||
/// Implement `Node` for a struct.
|
||||
#[proc_macro_attribute]
|
||||
pub fn node(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
|
||||
pub fn node(_: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
|
||||
let item = syn::parse_macro_input!(item as syn::ItemImpl);
|
||||
node::expand(stream.into(), item)
|
||||
.unwrap_or_else(|err| err.to_compile_error())
|
||||
.into()
|
||||
node::node(item).unwrap_or_else(|err| err.to_compile_error()).into()
|
||||
}
|
||||
|
||||
/// Implement `Capability` for a trait.
|
||||
#[proc_macro_attribute]
|
||||
pub fn capability(_: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
|
||||
let item = syn::parse_macro_input!(item as syn::ItemTrait);
|
||||
capability::expand(item)
|
||||
capability::capability(item)
|
||||
.unwrap_or_else(|err| err.to_compile_error())
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Implement `Capable` for a type.
|
||||
#[proc_macro_attribute]
|
||||
pub fn capable(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
|
||||
let item = syn::parse_macro_input!(item as syn::Item);
|
||||
capability::capable(stream.into(), item)
|
||||
.unwrap_or_else(|err| err.to_compile_error())
|
||||
.into()
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
use syn::parse::Parser;
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::Token;
|
||||
@ -6,8 +5,8 @@ use syn::Token;
|
||||
use super::*;
|
||||
|
||||
/// Expand the `#[node]` macro.
|
||||
pub fn expand(attr: TokenStream, body: syn::ItemImpl) -> Result<TokenStream> {
|
||||
let node = prepare(attr, body)?;
|
||||
pub fn node(body: syn::ItemImpl) -> Result<TokenStream> {
|
||||
let node = prepare(body)?;
|
||||
create(&node)
|
||||
}
|
||||
|
||||
@ -18,7 +17,6 @@ struct Node {
|
||||
self_ty: syn::Type,
|
||||
self_name: String,
|
||||
self_args: Punctuated<syn::GenericArgument, Token![,]>,
|
||||
capabilities: Vec<syn::Ident>,
|
||||
properties: Vec<Property>,
|
||||
construct: Option<syn::ImplItemMethod>,
|
||||
set: Option<syn::ImplItemMethod>,
|
||||
@ -47,7 +45,7 @@ enum Shorthand {
|
||||
}
|
||||
|
||||
/// Preprocess the impl block of a node.
|
||||
fn prepare(attr: TokenStream, body: syn::ItemImpl) -> Result<Node> {
|
||||
fn prepare(body: syn::ItemImpl) -> Result<Node> {
|
||||
// Extract the generic type arguments.
|
||||
let params = body.generics.params.clone();
|
||||
|
||||
@ -66,12 +64,6 @@ fn prepare(attr: TokenStream, body: syn::ItemImpl) -> Result<Node> {
|
||||
_ => Punctuated::new(),
|
||||
};
|
||||
|
||||
// Parse the capabilities.
|
||||
let capabilities: Vec<_> = Punctuated::<Ident, Token![,]>::parse_terminated
|
||||
.parse2(attr)?
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let mut properties = vec![];
|
||||
let mut construct = None;
|
||||
let mut set = None;
|
||||
@ -101,7 +93,6 @@ fn prepare(attr: TokenStream, body: syn::ItemImpl) -> Result<Node> {
|
||||
self_ty,
|
||||
self_name,
|
||||
self_args,
|
||||
capabilities,
|
||||
properties,
|
||||
construct,
|
||||
set,
|
||||
@ -215,7 +206,6 @@ fn create(node: &Node) -> Result<TokenStream> {
|
||||
let construct_func = create_node_construct_func(node);
|
||||
let set_func = create_node_set_func(node);
|
||||
let field_method = create_node_field_method(node);
|
||||
let vtable_method = create_node_vtable_method(node);
|
||||
|
||||
let node_impl = quote! {
|
||||
impl<#params> ::typst::model::Node for #self_ty {
|
||||
@ -225,10 +215,6 @@ fn create(node: &Node) -> Result<TokenStream> {
|
||||
#set_func
|
||||
#field_method
|
||||
}
|
||||
|
||||
unsafe impl<#params> ::typst::model::Capable for #self_ty {
|
||||
#vtable_method
|
||||
}
|
||||
};
|
||||
|
||||
let mut modules: Vec<syn::ItemMod> = vec![];
|
||||
@ -359,26 +345,6 @@ fn create_node_field_method(node: &Node) -> syn::ImplItemMethod {
|
||||
})
|
||||
}
|
||||
|
||||
/// Create the node's capability accessor method.
|
||||
fn create_node_vtable_method(node: &Node) -> syn::ImplItemMethod {
|
||||
let checks = node.capabilities.iter().map(|capability| {
|
||||
quote! {
|
||||
if id == ::std::any::TypeId::of::<dyn #capability>() {
|
||||
return Some(unsafe {
|
||||
::typst::util::fat::vtable(self as &dyn #capability)
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
parse_quote! {
|
||||
fn vtable(&self, id: ::std::any::TypeId) -> ::std::option::Option<*const ()> {
|
||||
#(#checks)*
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Process a single const item.
|
||||
fn create_property_module(node: &Node, property: &Property) -> (syn::Type, syn::ItemMod) {
|
||||
let params = &node.params;
|
||||
|
@ -10,7 +10,9 @@ use crate::geom::{
|
||||
Abs, Align, Axes, Dir, Em, Numeric, Paint, Point, Shape, Size, Transform,
|
||||
};
|
||||
use crate::image::Image;
|
||||
use crate::model::{dict, node, Content, Dict, Fold, StableId, StyleChain, Value};
|
||||
use crate::model::{
|
||||
capable, dict, node, Content, Dict, Fold, StableId, StyleChain, Value,
|
||||
};
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// A finished document with metadata and page frames.
|
||||
@ -539,6 +541,7 @@ impl FromStr for Region {
|
||||
}
|
||||
|
||||
/// Meta information that isn't visible or renderable.
|
||||
#[capable]
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub enum Meta {
|
||||
/// An internal or external link.
|
||||
|
@ -10,7 +10,9 @@ use siphasher::sip128::{Hasher128, SipHasher};
|
||||
use thin_vec::ThinVec;
|
||||
use typst_macros::node;
|
||||
|
||||
use super::{capability, Args, Guard, Key, Property, Recipe, Style, StyleMap, Value, Vm};
|
||||
use super::{
|
||||
capability, capable, Args, Guard, Key, Property, Recipe, Style, StyleMap, Value, Vm,
|
||||
};
|
||||
use crate::diag::{SourceResult, StrResult};
|
||||
use crate::syntax::Span;
|
||||
use crate::util::{EcoString, ReadableTypeId};
|
||||
@ -342,6 +344,7 @@ impl Hash for dyn Bounds {
|
||||
}
|
||||
|
||||
/// A node with applied styles.
|
||||
#[capable]
|
||||
#[derive(Clone, Hash)]
|
||||
pub struct StyledNode {
|
||||
/// The styled content.
|
||||
@ -364,6 +367,7 @@ impl Debug for StyledNode {
|
||||
///
|
||||
/// Combines other arbitrary content. So, when you write `[Hi] + [you]` in
|
||||
/// Typst, the two text nodes are combined into a single sequence node.
|
||||
#[capable]
|
||||
#[derive(Clone, Hash)]
|
||||
pub struct SequenceNode(pub Vec<Content>);
|
||||
|
||||
|
@ -1147,7 +1147,7 @@ impl Eval for ast::ModuleImport {
|
||||
match self.imports() {
|
||||
ast::Imports::Wildcard => {
|
||||
for (var, value) in module.scope.iter() {
|
||||
vm.scopes.top.define(var, value.clone());
|
||||
vm.scopes.top.define(var.clone(), value.clone());
|
||||
}
|
||||
}
|
||||
ast::Imports::Items(idents) => {
|
||||
|
@ -30,16 +30,22 @@ enum Repr {
|
||||
}
|
||||
|
||||
impl Func {
|
||||
/// Create a new function from a type that can be turned into a function.
|
||||
pub fn from_type<T: FuncType>(name: &'static str) -> Self {
|
||||
T::create_func(name)
|
||||
}
|
||||
|
||||
/// Create a new function from a native rust function.
|
||||
pub fn from_fn(
|
||||
name: &'static str,
|
||||
func: fn(&Vm, &mut Args) -> SourceResult<Value>,
|
||||
doc: &'static str,
|
||||
) -> Self {
|
||||
Self(Arc::new(Repr::Native(Native { name, func, set: None, node: None })))
|
||||
Self(Arc::new(Repr::Native(Native { name, func, set: None, node: None, doc })))
|
||||
}
|
||||
|
||||
/// Create a new function from a native rust node.
|
||||
pub fn from_node<T: Node>(name: &'static str) -> Self {
|
||||
pub fn from_node<T: Node>(name: &'static str, doc: &'static str) -> Self {
|
||||
Self(Arc::new(Repr::Native(Native {
|
||||
name,
|
||||
func: |ctx, args| {
|
||||
@ -49,6 +55,7 @@ impl Func {
|
||||
},
|
||||
set: Some(|args| T::set(args, false)),
|
||||
node: Some(NodeId::of::<T>()),
|
||||
doc,
|
||||
})))
|
||||
}
|
||||
|
||||
@ -66,6 +73,15 @@ impl Func {
|
||||
}
|
||||
}
|
||||
|
||||
/// Documentation for the function.
|
||||
pub fn doc(&self) -> Option<&str> {
|
||||
match self.0.as_ref() {
|
||||
Repr::Native(native) => Some(native.doc),
|
||||
Repr::With(func, _) => func.doc(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// The number of positional arguments this function takes, if known.
|
||||
pub fn argc(&self) -> Option<usize> {
|
||||
match self.0.as_ref() {
|
||||
@ -159,16 +175,24 @@ impl PartialEq for Func {
|
||||
}
|
||||
}
|
||||
|
||||
/// Types that can be turned into functions.
|
||||
pub trait FuncType {
|
||||
/// Create a function with the given name from this type.
|
||||
fn create_func(name: &'static str) -> Func;
|
||||
}
|
||||
|
||||
/// A function defined by a native rust function or node.
|
||||
struct Native {
|
||||
/// The name of the function.
|
||||
pub name: &'static str,
|
||||
name: &'static str,
|
||||
/// The function pointer.
|
||||
pub func: fn(&Vm, &mut Args) -> SourceResult<Value>,
|
||||
func: fn(&Vm, &mut Args) -> SourceResult<Value>,
|
||||
/// The set rule.
|
||||
pub set: Option<fn(&mut Args) -> SourceResult<StyleMap>>,
|
||||
set: Option<fn(&mut Args) -> SourceResult<StyleMap>>,
|
||||
/// The id of the node to customize with this function's show rule.
|
||||
pub node: Option<NodeId>,
|
||||
node: Option<NodeId>,
|
||||
/// Documentation of the function.
|
||||
doc: &'static str,
|
||||
}
|
||||
|
||||
impl Hash for Native {
|
||||
@ -177,6 +201,7 @@ impl Hash for Native {
|
||||
(self.func as usize).hash(state);
|
||||
self.set.map(|set| set as usize).hash(state);
|
||||
self.node.hash(state);
|
||||
self.doc.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ mod typeset;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use once_cell;
|
||||
pub use typst_macros::{capability, node};
|
||||
pub use typst_macros::{capability, capable, func, node};
|
||||
|
||||
pub use self::args::*;
|
||||
pub use self::array::*;
|
||||
|
@ -2,8 +2,8 @@ use std::collections::BTreeMap;
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::hash::Hash;
|
||||
|
||||
use super::{Args, Func, Node, Value, Vm};
|
||||
use crate::diag::{SourceResult, StrResult};
|
||||
use super::{Func, FuncType, Value};
|
||||
use crate::diag::StrResult;
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// A stack of scopes.
|
||||
@ -75,17 +75,8 @@ impl Scope {
|
||||
}
|
||||
|
||||
/// Define a function through a native rust function.
|
||||
pub fn def_fn(
|
||||
&mut self,
|
||||
name: &'static str,
|
||||
func: fn(&Vm, &mut Args) -> SourceResult<Value>,
|
||||
) {
|
||||
self.define(name, Func::from_fn(name, func));
|
||||
}
|
||||
|
||||
/// Define a function through a native rust node.
|
||||
pub fn def_node<T: Node>(&mut self, name: &'static str) {
|
||||
self.define(name, Func::from_node::<T>(name));
|
||||
pub fn def_func<T: FuncType>(&mut self, name: &'static str) {
|
||||
self.define(name, Func::from_type::<T>(name));
|
||||
}
|
||||
|
||||
/// Define a captured, immutable binding.
|
||||
@ -108,8 +99,8 @@ impl Scope {
|
||||
}
|
||||
|
||||
/// Iterate over all definitions.
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
|
||||
self.0.iter().map(|(k, v)| (k.as_str(), v.read()))
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&EcoString, &Value)> {
|
||||
self.0.iter().map(|(k, v)| (k, v.read()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -424,9 +424,5 @@ mod tests {
|
||||
test(dict![], "(:)");
|
||||
test(dict!["one" => 1], "(one: 1)");
|
||||
test(dict!["two" => false, "one" => 1], "(one: 1, two: false)");
|
||||
|
||||
// Functions, content and dynamics.
|
||||
test(Func::from_fn("nil", |_, _| Ok(Value::None)), "<function nil>");
|
||||
test(Dynamic::new(1), "1");
|
||||
}
|
||||
}
|
||||
|
@ -11,11 +11,11 @@ use comemo::{Prehashed, Track};
|
||||
use elsa::FrozenVec;
|
||||
use once_cell::unsync::OnceCell;
|
||||
use tiny_skia as sk;
|
||||
use typst::diag::{bail, FileError, FileResult};
|
||||
use typst::diag::{bail, FileError, FileResult, SourceResult};
|
||||
use typst::doc::{Document, Element, Frame, Meta};
|
||||
use typst::font::{Font, FontBook};
|
||||
use typst::geom::{Abs, RgbaColor, Sides};
|
||||
use typst::model::{Library, Smart, Value};
|
||||
use typst::model::{func, Library, Smart, Value};
|
||||
use typst::syntax::{Source, SourceId, SyntaxNode};
|
||||
use typst::util::{Buffer, PathExt};
|
||||
use typst::World;
|
||||
@ -145,6 +145,29 @@ impl Args {
|
||||
}
|
||||
|
||||
fn library() -> Library {
|
||||
#[func]
|
||||
fn test(args: &mut typst::model::Args) -> SourceResult<Value> {
|
||||
let lhs = args.expect::<Value>("left-hand side")?;
|
||||
let rhs = args.expect::<Value>("right-hand side")?;
|
||||
if lhs != rhs {
|
||||
bail!(args.span, "Assertion failed: {:?} != {:?}", lhs, rhs,);
|
||||
}
|
||||
Ok(Value::None)
|
||||
}
|
||||
|
||||
#[func]
|
||||
fn print(args: &mut typst::model::Args) -> SourceResult<Value> {
|
||||
print!("> ");
|
||||
for (i, value) in args.all::<Value>()?.into_iter().enumerate() {
|
||||
if i > 0 {
|
||||
print!(", ")
|
||||
}
|
||||
print!("{value:?}");
|
||||
}
|
||||
println!();
|
||||
Ok(Value::None)
|
||||
}
|
||||
|
||||
let mut lib = typst_library::build();
|
||||
|
||||
// Set page width to 120pt with 10pt margins, so that the inner page is
|
||||
@ -157,27 +180,10 @@ fn library() -> Library {
|
||||
lib.styles.set(TextNode::SIZE, TextSize(Abs::pt(10.0).into()));
|
||||
|
||||
// Hook up helpers into the global scope.
|
||||
lib.scope.def_func::<TestFunc>("test");
|
||||
lib.scope.def_func::<PrintFunc>("print");
|
||||
lib.scope.define("conifer", RgbaColor::new(0x9f, 0xEB, 0x52, 0xFF));
|
||||
lib.scope.define("forest", RgbaColor::new(0x43, 0xA1, 0x27, 0xFF));
|
||||
lib.scope.def_fn("test", move |_, args| {
|
||||
let lhs = args.expect::<Value>("left-hand side")?;
|
||||
let rhs = args.expect::<Value>("right-hand side")?;
|
||||
if lhs != rhs {
|
||||
bail!(args.span, "Assertion failed: {:?} != {:?}", lhs, rhs,);
|
||||
}
|
||||
Ok(Value::None)
|
||||
});
|
||||
lib.scope.def_fn("print", move |_, args| {
|
||||
print!("> ");
|
||||
for (i, value) in args.all::<Value>()?.into_iter().enumerate() {
|
||||
if i > 0 {
|
||||
print!(", ")
|
||||
}
|
||||
print!("{value:?}");
|
||||
}
|
||||
println!();
|
||||
Ok(Value::None)
|
||||
});
|
||||
|
||||
lib
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user