Syntax and example sections

This commit is contained in:
Laurenz 2022-12-19 01:16:35 +01:00
parent ba384e5bb6
commit b4b022940b
31 changed files with 199 additions and 37 deletions

View File

@ -664,7 +664,7 @@ impl FontSearcher {
let path = path.as_ref(); let path = path.as_ref();
if let Ok(file) = File::open(path) { if let Ok(file) = File::open(path) {
if let Ok(mmap) = unsafe { Mmap::map(&file) } { if let Ok(mmap) = unsafe { Mmap::map(&file) } {
for (i, info) in FontInfo::from_data(&mmap).enumerate() { for (i, info) in FontInfo::iter(&mmap).enumerate() {
self.book.push(info); self.book.push(info);
self.fonts.push(FontSlot { self.fonts.push(FontSlot {
path: path.into(), path: path.into(),

View File

@ -7,9 +7,23 @@ use crate::text::{SpaceNode, TextNode, TextSize};
/// A section heading. /// A section heading.
/// ///
/// # Example
/// ```
/// #set heading(numbering: "I.")
///
/// = Introduction
/// In recent years, ...
/// ```
///
/// # Syntax
/// Headings can be created by starting a line with one or multiple equals
/// signs. The number of equals signs determines the heading's logical nesting
/// depth.
///
/// # Parameters /// # Parameters
/// - body: Content (positional, required) /// - body: Content (positional, required)
/// The heading's contents. /// The heading's contents.
///
/// - level: NonZeroUsize (named) /// - level: NonZeroUsize (named)
/// The logical nesting depth of the heading, starting from one. /// The logical nesting depth of the heading, starting from one.
/// ///
@ -29,10 +43,31 @@ pub struct HeadingNode {
#[node] #[node]
impl HeadingNode { impl HeadingNode {
/// How to number the heading. /// How to number the heading.
///
/// # Example
/// ```
/// #set heading(numbering: "1.a.")
///
/// = A section
/// == A subsection
/// === A sub-subsection
/// ```
#[property(referenced)] #[property(referenced)]
pub const NUMBERING: Option<NumberingPattern> = None; pub const NUMBERING: Option<NumberingPattern> = None;
/// Whether the heading should appear in the outline. /// Whether the heading should appear in the outline.
///
/// # Example
/// ```
/// #outline()
///
/// #heading[Normal]
/// This is a normal heading.
///
/// #heading(outlined: false)[Hidden]
/// This heading does not appear
/// in the outline.
/// ```
pub const OUTLINED: bool = true; pub const OUTLINED: bool = true;
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {

View File

@ -8,12 +8,22 @@ use crate::text::{SpaceNode, TextNode};
/// # Parameters /// # Parameters
/// - items: Content (positional, variadic) /// - items: Content (positional, variadic)
/// The contents of the list items. /// The contents of the list items.
///
/// - start: NonZeroUsize (named) /// - start: NonZeroUsize (named)
/// Which number to start the enumeration with. /// Which number to start the enumeration with.
///
/// - tight: bool (named) /// - tight: bool (named)
/// Makes the list more compact, if enabled. This looks better if the items /// Makes the list more compact, if enabled. This looks better if the items
/// fit into a single line each. /// fit into a single line each.
/// ///
/// # Example
/// ```
/// #show columns.with(2)
/// #list(tight: true)[Tight][List]
/// #colbreak()
/// #list(tight: false)[Wide][List]
/// ```
///
/// # Tags /// # Tags
/// - basics /// - basics
#[func] #[func]

View File

@ -6,14 +6,19 @@ use crate::prelude::*;
/// # Parameters /// # Parameters
/// - cells: Content (positional, variadic) /// - cells: Content (positional, variadic)
/// The contents of the table cells. /// The contents of the table cells.
///
/// - rows: TrackSizings (named) /// - rows: TrackSizings (named)
/// Defines the row sizes. /// Defines the row sizes.
///
/// - columns: TrackSizings (named) /// - columns: TrackSizings (named)
/// Defines the column sizes. /// Defines the column sizes.
///
/// - gutter: TrackSizings (named) /// - gutter: TrackSizings (named)
/// Defines the gaps between rows & columns. /// Defines the gaps between rows & columns.
///
/// - column-gutter: TrackSizings (named) /// - column-gutter: TrackSizings (named)
/// Defines the gaps between columns. Takes precedence over `gutter`. /// Defines the gaps between columns. Takes precedence over `gutter`.
///
/// - row-gutter: TrackSizings (named) /// - row-gutter: TrackSizings (named)
/// Defines the gaps between rows. Takes precedence over `gutter`. /// Defines the gaps between rows. Takes precedence over `gutter`.
/// ///

View File

@ -105,6 +105,7 @@ pub fn odd(args: &mut Args) -> SourceResult<Value> {
/// # Parameters /// # Parameters
/// - dividend: ToMod (positional, required) /// - dividend: ToMod (positional, required)
/// The dividend of the modulus. /// The dividend of the modulus.
///
/// - divisor: ToMod (positional, required) /// - divisor: ToMod (positional, required)
/// The divisor of the modulus. /// The divisor of the modulus.
/// ///

View File

@ -77,12 +77,21 @@ pub fn luma(args: &mut Args) -> SourceResult<Value> {
/// ///
/// If this string is given, the individual components should not be given. /// If this string is given, the individual components should not be given.
/// ///
/// # Example
/// ```
/// #let color = rgb("#239dad")
/// #text(16pt, color)[*Typst*]
/// ```
///
/// - red: Component (positional) /// - red: Component (positional)
/// The red component. /// The red component.
///
/// - green: Component (positional) /// - green: Component (positional)
/// The green component. /// The green component.
///
/// - blue: Component (positional) /// - blue: Component (positional)
/// The blue component. /// The blue component.
///
/// - alpha: Component (positional) /// - alpha: Component (positional)
/// The alpha component. /// The alpha component.
/// ///
@ -125,10 +134,13 @@ castable! {
/// # Parameters /// # Parameters
/// - cyan: RatioComponent (positional, required) /// - cyan: RatioComponent (positional, required)
/// The cyan component. /// The cyan component.
///
/// - magenta: RatioComponent (positional, required) /// - magenta: RatioComponent (positional, required)
/// The magenta component. /// The magenta component.
///
/// - yellow: RatioComponent (positional, required) /// - yellow: RatioComponent (positional, required)
/// The yellow component. /// The yellow component.
///
/// - key: RatioComponent (positional, required) /// - key: RatioComponent (positional, required)
/// The key component. /// The key component.
/// ///

View File

@ -22,6 +22,7 @@ pub fn lorem(args: &mut Args) -> SourceResult<Value> {
/// # Parameters /// # Parameters
/// - pattern: NumberingPattern (positional, required) /// - pattern: NumberingPattern (positional, required)
/// A string that defines how the numbering works. /// A string that defines how the numbering works.
///
/// - numbers: NonZeroUsize (positional, variadic) /// - numbers: NonZeroUsize (positional, variadic)
/// The numbers to apply the pattern to. /// The numbers to apply the pattern to.
/// ///

View File

@ -5,6 +5,7 @@ use crate::prelude::*;
/// # Parameters /// # Parameters
/// - body: Content (positional, required) /// - body: Content (positional, required)
/// The content to align. /// The content to align.
///
/// - alignment: Axes<Option<GenAlign>> (positional, settable) /// - alignment: Axes<Option<GenAlign>> (positional, settable)
/// The alignment along both axes. /// The alignment along both axes.
/// ///

View File

@ -6,6 +6,7 @@ use crate::text::TextNode;
/// # Parameters /// # Parameters
/// - count: usize (positional, required) /// - count: usize (positional, required)
/// The number of columns. /// The number of columns.
///
/// - body: Content (positional, required) /// - body: Content (positional, required)
/// The content that should be layouted into the columns. /// The content that should be layouted into the columns.
/// ///

View File

@ -7,8 +7,10 @@ use crate::prelude::*;
/// # Parameters /// # Parameters
/// - body: Content (positional) /// - body: Content (positional)
/// The contents of the box. /// The contents of the box.
///
/// - width: Rel<Length> (named) /// - width: Rel<Length> (named)
/// The width of the box. /// The width of the box.
///
/// - height: Rel<Length> (named) /// - height: Rel<Length> (named)
/// The height of the box. /// The height of the box.
/// ///
@ -78,11 +80,14 @@ impl Inline for BoxNode {}
/// # Parameters /// # Parameters
/// - body: Content (positional) /// - body: Content (positional)
/// The contents of the block. /// The contents of the block.
///
/// - spacing: Spacing (named, settable) /// - spacing: Spacing (named, settable)
/// The spacing around this block. /// The spacing around this block.
///
/// - above: Spacing (named, settable) /// - above: Spacing (named, settable)
/// The spacing between the previous and this block. Takes precedence over /// The spacing between the previous and this block. Takes precedence over
/// `spacing`. /// `spacing`.
///
/// - below: Spacing (named, settable) /// - below: Spacing (named, settable)
/// The spacing between this block and the following one. Takes precedence /// The spacing between this block and the following one. Takes precedence
/// over `spacing`. /// over `spacing`.

View File

@ -7,14 +7,19 @@ use super::Spacing;
/// # Parameters /// # Parameters
/// - cells: Content (positional, variadic) /// - cells: Content (positional, variadic)
/// The contents of the table cells. /// The contents of the table cells.
///
/// - rows: TrackSizings (named) /// - rows: TrackSizings (named)
/// Defines the row sizes. /// Defines the row sizes.
///
/// - columns: TrackSizings (named) /// - columns: TrackSizings (named)
/// Defines the column sizes. /// Defines the column sizes.
///
/// - gutter: TrackSizings (named) /// - gutter: TrackSizings (named)
/// Defines the gaps between rows & columns. /// Defines the gaps between rows & columns.
///
/// - column-gutter: TrackSizings (named) /// - column-gutter: TrackSizings (named)
/// Defines the gaps between columns. Takes precedence over `gutter`. /// Defines the gaps between columns. Takes precedence over `gutter`.
///
/// - row-gutter: TrackSizings (named) /// - row-gutter: TrackSizings (named)
/// Defines the gaps between rows. Takes precedence over `gutter`. /// Defines the gaps between rows. Takes precedence over `gutter`.
/// ///

View File

@ -5,18 +5,25 @@ use crate::prelude::*;
/// # Parameters /// # Parameters
/// - body: Content (positional, required) /// - body: Content (positional, required)
/// The content to pad at the sides. /// The content to pad at the sides.
///
/// - left: Rel<Length> (named) /// - left: Rel<Length> (named)
/// The padding at the left side. /// The padding at the left side.
///
/// - right: Rel<Length> (named) /// - right: Rel<Length> (named)
/// The padding at the right side. /// The padding at the right side.
///
/// - top: Rel<Length> (named) /// - top: Rel<Length> (named)
/// The padding at the top side. /// The padding at the top side.
///
/// - bottom: Rel<Length> (named) /// - bottom: Rel<Length> (named)
/// The padding at the bottom side. /// The padding at the bottom side.
///
/// - x: Rel<Length> (named) /// - x: Rel<Length> (named)
/// The horizontal padding. Both `left` and `right` take precedence over this. /// The horizontal padding. Both `left` and `right` take precedence over this.
///
/// - y: Rel<Length> (named) /// - y: Rel<Length> (named)
/// The vertical padding. Both `top` and `bottom` take precedence over this. /// The vertical padding. Both `top` and `bottom` take precedence over this.
///
/// - rest: Rel<Length> (named) /// - rest: Rel<Length> (named)
/// The padding for all sides. All other parameters take precedence over this. /// The padding for all sides. All other parameters take precedence over this.
/// ///

View File

@ -9,6 +9,7 @@ use crate::text::TextNode;
/// # Parameters /// # Parameters
/// - body: Content (positional, required) /// - body: Content (positional, required)
/// The contents of the page(s). /// The contents of the page(s).
///
/// - paper: Paper (positional, settable) /// - paper: Paper (positional, settable)
/// The paper size. /// The paper size.
/// ///

View File

@ -5,10 +5,13 @@ use crate::prelude::*;
/// # Parameters /// # Parameters
/// - alignment: Axes<Option<GenAlign>> (positional) /// - alignment: Axes<Option<GenAlign>> (positional)
/// Relative to which position in the parent container to place the content. /// Relative to which position in the parent container to place the content.
///
/// - body: Content (positional, required) /// - body: Content (positional, required)
/// The content to place. /// The content to place.
///
/// - dx: Rel<Length> (named) /// - dx: Rel<Length> (named)
/// The horizontal displacement of the placed content. /// The horizontal displacement of the placed content.
///
/// - dy: Rel<Length> (named) /// - dy: Rel<Length> (named)
/// The vertical displacement of the placed content. /// The vertical displacement of the placed content.
/// ///

View File

@ -7,6 +7,7 @@ use crate::prelude::*;
/// # Parameters /// # Parameters
/// - amount: Spacing (positional, required) /// - amount: Spacing (positional, required)
/// How much spacing to insert. /// How much spacing to insert.
///
/// - weak: bool (named) /// - weak: bool (named)
/// If true, the spacing collapses at the start or end of a paragraph. /// If true, the spacing collapses at the start or end of a paragraph.
/// Moreover, from multiple adjacent weak spacings all but the largest one /// Moreover, from multiple adjacent weak spacings all but the largest one
@ -67,6 +68,7 @@ impl Behave for HNode {
/// # Parameters /// # Parameters
/// - amount: Spacing (positional, required) /// - amount: Spacing (positional, required)
/// How much spacing to insert. /// How much spacing to insert.
///
/// - weak: bool (named) /// - weak: bool (named)
/// If true, the spacing collapses at the start or end of a flow. /// If true, the spacing collapses at the start or end of a flow.
/// Moreover, from multiple adjacent weak spacings all but the largest one /// Moreover, from multiple adjacent weak spacings all but the largest one

View File

@ -8,8 +8,10 @@ use crate::prelude::*;
/// # Parameters /// # Parameters
/// - items: StackChild (positional, variadic) /// - items: StackChild (positional, variadic)
/// The items to stack along an axis. /// The items to stack along an axis.
///
/// - dir: Dir (named) /// - dir: Dir (named)
/// The direction along which the items are stacked. /// The direction along which the items are stacked.
///
/// - spacing: Spacing (named) /// - spacing: Spacing (named)
/// Spacing to insert between items where no explicit spacing was provided. /// Spacing to insert between items where no explicit spacing was provided.
/// ///

View File

@ -7,8 +7,10 @@ use crate::prelude::*;
/// # Parameters /// # Parameters
/// - body: Content (positional, required) /// - body: Content (positional, required)
/// The content to move. /// The content to move.
///
/// - dx: Rel<Length> (named) /// - dx: Rel<Length> (named)
/// The horizontal displacement of the content. /// The horizontal displacement of the content.
///
/// - dy: Rel<Length> (named) /// - dy: Rel<Length> (named)
/// The vertical displacement of the content. /// The vertical displacement of the content.
/// ///
@ -61,10 +63,13 @@ impl Inline for MoveNode {}
/// # Parameters /// # Parameters
/// - body: Content (positional, required) /// - body: Content (positional, required)
/// The content to transform. /// The content to transform.
///
/// - angle: Angle (named) /// - angle: Angle (named)
/// The amount of rotation. /// The amount of rotation.
///
/// - x: Ratio (named) /// - x: Ratio (named)
/// The horizontal scaling factor. /// The horizontal scaling factor.
///
/// - y: Ratio (named) /// - y: Ratio (named)
/// The vertical scaling factor. /// The vertical scaling factor.
/// ///

View File

@ -19,6 +19,7 @@ use crate::text::{FontFamily, LinebreakNode, SpaceNode, SymbolNode, TextNode};
/// # Parameters /// # Parameters
/// - items: Content (positional, variadic) /// - items: Content (positional, variadic)
/// The individual parts of the formula. /// The individual parts of the formula.
///
/// - block: bool (named) /// - block: bool (named)
/// Whether the formula is displayed as a separate block. /// Whether the formula is displayed as a separate block.
/// ///
@ -309,6 +310,7 @@ impl Texify for AtomNode {
/// # Parameters /// # Parameters
/// - base: Content (positional, required) /// - base: Content (positional, required)
/// The base to which the accent is applied. /// The base to which the accent is applied.
///
/// - accent: Content (positional, required) /// - accent: Content (positional, required)
/// The accent to apply to the base. /// The accent to apply to the base.
/// ///
@ -393,6 +395,7 @@ impl Texify for AccNode {
/// # Parameters /// # Parameters
/// - num: Content (positional, required) /// - num: Content (positional, required)
/// The fraction's numerator. /// The fraction's numerator.
///
/// - denom: Content (positional, required) /// - denom: Content (positional, required)
/// The fraction's denominator. /// The fraction's denominator.
/// ///
@ -433,6 +436,7 @@ impl Texify for FracNode {
/// # Parameters /// # Parameters
/// - upper: Content (positional, required) /// - upper: Content (positional, required)
/// The binomial's upper index. /// The binomial's upper index.
///
/// - lower: Content (positional, required) /// - lower: Content (positional, required)
/// The binomial's lower index. /// The binomial's lower index.
/// ///
@ -473,8 +477,10 @@ impl Texify for BinomNode {
/// # Parameters /// # Parameters
/// - base: Content (positional, required) /// - base: Content (positional, required)
/// The base to which the applies the sub- and/or superscript. /// The base to which the applies the sub- and/or superscript.
///
/// - sub: Content (named) /// - sub: Content (named)
/// The subscript. /// The subscript.
///
/// - sup: Content (named) /// - sup: Content (named)
/// The superscript. /// The superscript.
/// ///

View File

@ -6,6 +6,7 @@ use crate::text::TextNode;
/// # Parameters /// # Parameters
/// - dest: Destination (positional, required) /// - dest: Destination (positional, required)
/// The destination the link points to. /// The destination the link points to.
///
/// - body: Content (positional) /// - body: Content (positional)
/// How the link is represented. Defaults to the destination if it is a link. /// How the link is represented. Defaults to the destination if it is a link.
/// ///

View File

@ -30,6 +30,7 @@ use crate::prelude::*;
/// # Parameters /// # Parameters
/// - family: EcoString (positional, variadic, settable) /// - family: EcoString (positional, variadic, settable)
/// A prioritized sequence of font families. /// A prioritized sequence of font families.
///
/// - body: Content (positional, required) /// - body: Content (positional, required)
/// Content in which all text is styled according to the other arguments. /// Content in which all text is styled according to the other arguments.
/// ///

View File

@ -11,6 +11,7 @@ use crate::prelude::*;
/// # Parameters /// # Parameters
/// - text: EcoString (positional, required) /// - text: EcoString (positional, required)
/// The raw text. /// The raw text.
///
/// - block: bool (named) /// - block: bool (named)
/// Whether the raw text is displayed as a separate block. /// Whether the raw text is displayed as a separate block.
/// ///

View File

@ -7,10 +7,13 @@ use crate::prelude::*;
/// # Parameters /// # Parameters
/// - origin: Axes<Rel<Length>> (named) /// - origin: Axes<Rel<Length>> (named)
/// The start point of the line. /// The start point of the line.
///
/// - to: Axes<Rel<Length>> (named) /// - to: Axes<Rel<Length>> (named)
/// The end point of the line. /// The end point of the line.
///
/// - length: Rel<Length> (named) /// - length: Rel<Length> (named)
/// The line's length. /// The line's length.
///
/// - angle: Angle (named) /// - angle: Angle (named)
/// The angle at which the line points away from the origin. /// The angle at which the line points away from the origin.
/// ///

View File

@ -7,14 +7,19 @@ use crate::prelude::*;
/// # Parameters /// # Parameters
/// - body: Content (positional) /// - body: Content (positional)
/// The content to place into the shape. /// The content to place into the shape.
///
/// - width: Rel<Length> (named) /// - width: Rel<Length> (named)
/// The shape's width. /// The shape's width.
///
/// - height: Rel<Length> (named) /// - height: Rel<Length> (named)
/// The shape's height. /// The shape's height.
///
/// - size: Length (named) /// - size: Length (named)
/// The square's side length. /// The square's side length.
///
/// - radius: Length (named) /// - radius: Length (named)
/// The circle's radius. /// The circle's radius.
///
/// - stroke: Smart<Sides<Option<PartialStroke>>> (named) /// - stroke: Smart<Sides<Option<PartialStroke>>> (named)
/// How to stroke the shape. /// How to stroke the shape.
/// ///

View File

@ -1,4 +1,3 @@
use proc_macro2::Span;
use unscanny::Scanner; use unscanny::Scanner;
use super::*; use super::*;
@ -14,12 +13,21 @@ pub fn func(item: syn::Item) -> Result<TokenStream> {
let tags = tags(&mut docs); let tags = tags(&mut docs);
let params = params(&mut docs)?; let params = params(&mut docs)?;
let example = quote_option(example(&mut docs));
let syntax = quote_option(section(&mut docs, "Syntax"));
let docs = docs.trim(); let docs = docs.trim();
if docs.contains("# ") {
bail!(item, "Documentation heading not recognized");
}
let info = quote! { let info = quote! {
::typst::model::FuncInfo { ::typst::model::FuncInfo {
name, name,
docs: #docs,
tags: &[#(#tags),*], tags: &[#(#tags),*],
docs: #docs,
example: #example,
syntax: #syntax,
params: ::std::vec![#(#params),*], params: ::std::vec![#(#params),*],
} }
}; };
@ -76,18 +84,18 @@ pub fn func(item: syn::Item) -> Result<TokenStream> {
/// Extract a section. /// Extract a section.
pub fn section(docs: &mut String, title: &str) -> Option<String> { pub fn section(docs: &mut String, title: &str) -> Option<String> {
let needle = format!("# {title}\n"); let needle = format!("\n# {title}\n");
let start = docs.find(&needle)?; let start = docs.find(&needle)?;
let rest = &docs[start..]; let rest = &docs[start..];
let len = rest[1..].find('#').map(|x| 1 + x).unwrap_or(rest.len()); let len = rest[1..].find("\n# ").map(|x| 1 + x).unwrap_or(rest.len());
let end = start + len; let end = start + len;
let section = docs[start + needle.len()..].to_owned(); let section = docs[start + needle.len()..end].trim().to_owned();
docs.replace_range(start..end, ""); docs.replace_range(start..end, "");
Some(section) Some(section)
} }
/// Parse the tag section. /// Parse the tag section.
pub fn tags(docs: &mut String) -> Vec<String> { fn tags(docs: &mut String) -> Vec<String> {
section(docs, "Tags") section(docs, "Tags")
.unwrap_or_default() .unwrap_or_default()
.lines() .lines()
@ -96,24 +104,40 @@ pub fn tags(docs: &mut String) -> Vec<String> {
.collect() .collect()
} }
/// Parse the example section.
pub fn example(docs: &mut String) -> Option<String> {
Some(
section(docs, "Example")?
.lines()
.skip_while(|line| !line.contains("```"))
.skip(1)
.take_while(|line| !line.contains("```"))
.map(|s| s.trim())
.collect::<Vec<_>>()
.join("\n"),
)
}
/// Parse the parameter section. /// Parse the parameter section.
pub fn params(docs: &mut String) -> Result<Vec<TokenStream>> { fn params(docs: &mut String) -> Result<Vec<TokenStream>> {
let Some(section) = section(docs, "Parameters") else { return Ok(vec![]) }; let Some(section) = section(docs, "Parameters") else { return Ok(vec![]) };
let mut s = Scanner::new(&section); let mut s = Scanner::new(&section);
let mut infos = vec![]; let mut infos = vec![];
while s.eat_if('-') { while s.eat_if('-') {
s.eat_whitespace();
let name = s.eat_until(':');
s.expect(": ");
let ty: syn::Type = syn::parse_str(s.eat_until(char::is_whitespace))?;
s.eat_whitespace();
let mut named = false; let mut named = false;
let mut positional = false; let mut positional = false;
let mut required = false; let mut required = false;
let mut variadic = false; let mut variadic = false;
let mut settable = false; let mut settable = false;
s.eat_whitespace();
let name = s.eat_until(':');
s.expect(": ");
let ty: syn::Type = syn::parse_str(s.eat_until(char::is_whitespace))?;
s.eat_whitespace();
s.expect('('); s.expect('(');
for part in s.eat_until(')').split(',').map(str::trim).filter(|s| !s.is_empty()) { for part in s.eat_until(')').split(',').map(str::trim).filter(|s| !s.is_empty()) {
match part { match part {
"named" => named = true, "named" => named = true,
@ -121,12 +145,7 @@ pub fn params(docs: &mut String) -> Result<Vec<TokenStream>> {
"required" => required = true, "required" => required = true,
"variadic" => variadic = true, "variadic" => variadic = true,
"settable" => settable = true, "settable" => settable = true,
_ => { _ => bail!(callsite, "unknown parameter flag {:?}", part),
return Err(syn::Error::new(
Span::call_site(),
format!("unknown parameter flag {:?}", part),
))
}
} }
} }
@ -135,18 +154,20 @@ pub fn params(docs: &mut String) -> Result<Vec<TokenStream>> {
|| (named && variadic) || (named && variadic)
|| (required && variadic) || (required && variadic)
{ {
return Err(syn::Error::new( bail!(callsite, "invalid combination of parameter flags");
Span::call_site(),
"invalid combination of parameter flags",
));
} }
s.expect(')'); s.expect(')');
let docs = dedent(s.eat_until("\n-").trim());
let mut docs = dedent(s.eat_until("\n-").trim());
let example = quote_option(example(&mut docs));
let docs = docs.trim();
infos.push(quote! { infos.push(quote! {
::typst::model::ParamInfo { ::typst::model::ParamInfo {
name: #name, name: #name,
docs: #docs, docs: #docs,
example: #example,
cast: <#ty as ::typst::model::Cast< cast: <#ty as ::typst::model::Cast<
::typst::syntax::Spanned<::typst::model::Value> ::typst::syntax::Spanned<::typst::model::Value>
>>::describe(), >>::describe(),

View File

@ -4,12 +4,18 @@ extern crate proc_macro;
/// Return an error at the given item. /// Return an error at the given item.
macro_rules! bail { macro_rules! bail {
(callsite, $fmt:literal $($tts:tt)*) => {
return Err(syn::Error::new(
proc_macro2::Span::call_site(),
format!(concat!("typst: ", $fmt) $($tts)*)
))
};
($item:expr, $fmt:literal $($tts:tt)*) => { ($item:expr, $fmt:literal $($tts:tt)*) => {
return Err(Error::new_spanned( return Err(syn::Error::new_spanned(
&$item, &$item,
format!(concat!("typst: ", $fmt) $($tts)*) format!(concat!("typst: ", $fmt) $($tts)*)
)) ))
} };
} }
mod capable; mod capable;
@ -19,9 +25,8 @@ mod node;
use proc_macro::TokenStream as BoundaryStream; use proc_macro::TokenStream as BoundaryStream;
use proc_macro2::{TokenStream, TokenTree}; use proc_macro2::{TokenStream, TokenTree};
use quote::{quote, quote_spanned}; use quote::{quote, quote_spanned, ToTokens};
use syn::parse_quote; use syn::{parse_quote, Ident, Result};
use syn::{Error, Ident, Result};
/// Implement `FuncType` for a type or function. /// Implement `FuncType` for a type or function.
#[proc_macro_attribute] #[proc_macro_attribute]
@ -88,3 +93,11 @@ fn documentation(attrs: &[syn::Attribute]) -> String {
fn dedent(text: &str) -> String { fn dedent(text: &str) -> String {
text.lines().map(str::trim).collect::<Vec<_>>().join("\n") text.lines().map(str::trim).collect::<Vec<_>>().join("\n")
} }
/// Quote an optional value.
fn quote_option<T: ToTokens>(option: Option<T>) -> TokenStream {
match option {
Some(value) => quote! { Some(#value) },
None => quote! { None },
}
}

View File

@ -333,13 +333,18 @@ fn create_node_set_func(node: &Node) -> syn::ImplItemMethod {
fn create_node_properties_func(node: &Node) -> syn::ImplItemMethod { fn create_node_properties_func(node: &Node) -> syn::ImplItemMethod {
let infos = node.properties.iter().filter(|p| !p.skip).map(|property| { let infos = node.properties.iter().filter(|p| !p.skip).map(|property| {
let name = property.name.to_string().replace('_', "-").to_lowercase(); let name = property.name.to_string().replace('_', "-").to_lowercase();
let docs = documentation(&property.attrs);
let value_ty = &property.value_ty; let value_ty = &property.value_ty;
let shorthand = matches!(property.shorthand, Some(Shorthand::Positional)); let shorthand = matches!(property.shorthand, Some(Shorthand::Positional));
let mut docs = documentation(&property.attrs);
let example = quote_option(super::func::example(&mut docs));
let docs = docs.trim();
quote! { quote! {
::typst::model::ParamInfo { ::typst::model::ParamInfo {
name: #name, name: #name,
docs: #docs, docs: #docs,
example: #example,
cast: <#value_ty as ::typst::model::Cast< cast: <#value_ty as ::typst::model::Cast<
::typst::syntax::Spanned<::typst::model::Value> ::typst::syntax::Spanned<::typst::model::Value>
>>::describe(), >>::describe(),

View File

@ -170,7 +170,7 @@ bitflags::bitflags! {
impl FontInfo { impl FontInfo {
/// Compute metadata for all fonts in the given data. /// Compute metadata for all fonts in the given data.
pub fn from_data(data: &[u8]) -> impl Iterator<Item = FontInfo> + '_ { pub fn iter(data: &[u8]) -> impl Iterator<Item = FontInfo> + '_ {
let count = ttf_parser::fonts_in_collection(data).unwrap_or(1); let count = ttf_parser::fonts_in_collection(data).unwrap_or(1);
(0..count).filter_map(move |index| { (0..count).filter_map(move |index| {
let ttf = ttf_parser::Face::parse(data, index).ok()?; let ttf = ttf_parser::Face::parse(data, index).ok()?;
@ -179,7 +179,7 @@ impl FontInfo {
} }
/// Compute metadata for a single ttf-parser face. /// Compute metadata for a single ttf-parser face.
pub fn from_ttf(ttf: &ttf_parser::Face) -> Option<Self> { pub(super) fn from_ttf(ttf: &ttf_parser::Face) -> Option<Self> {
// We cannot use Name ID 16 "Typographic Family", because for some // We cannot use Name ID 16 "Typographic Family", because for some
// fonts it groups together more than just Style / Weight / Stretch // fonts it groups together more than just Style / Weight / Stretch
// variants (e.g. Display variants of Noto fonts) and then some // variants (e.g. Display variants of Noto fonts) and then some

View File

@ -69,6 +69,12 @@ impl Font {
}))) })))
} }
/// Parse all fonts in the given data.
pub fn iter(data: Buffer) -> impl Iterator<Item = Self> {
let count = ttf_parser::fonts_in_collection(&data).unwrap_or(1);
(0..count).filter_map(move |index| Self::new(data.clone(), index))
}
/// The underlying buffer. /// The underlying buffer.
pub fn data(&self) -> &Buffer { pub fn data(&self) -> &Buffer {
&self.0.data &self.0.data

View File

@ -214,6 +214,10 @@ pub struct FuncInfo {
pub tags: &'static [&'static str], pub tags: &'static [&'static str],
/// Documentation for the function. /// Documentation for the function.
pub docs: &'static str, pub docs: &'static str,
/// The source code of an example, if any.
pub example: Option<&'static str>,
/// Documentation about this function's syntax, if it has syntax.
pub syntax: Option<&'static str>,
/// Details about the function's parameters. /// Details about the function's parameters.
pub params: Vec<ParamInfo>, pub params: Vec<ParamInfo>,
} }
@ -232,6 +236,8 @@ pub struct ParamInfo {
pub name: &'static str, pub name: &'static str,
/// Documentation for the parameter. /// Documentation for the parameter.
pub docs: &'static str, pub docs: &'static str,
/// The source code of an example, if any.
pub example: Option<&'static str>,
/// Valid values for the parameter. /// Valid values for the parameter.
pub cast: CastInfo, pub cast: CastInfo,
/// Is the parameter positional? /// Is the parameter positional?

View File

@ -122,7 +122,7 @@ pub static LANG_ITEMS: OnceCell<LangItems> = OnceCell::new();
/// break incremental, but only when different sets of lang items are used in /// break incremental, but only when different sets of lang items are used in
/// the same program. For this reason, if this function is called multiple /// the same program. For this reason, if this function is called multiple
/// times, the items must be the same. /// times, the items must be the same.
pub(crate) fn set_lang_items(items: LangItems) { pub fn set_lang_items(items: LangItems) {
if let Err(items) = LANG_ITEMS.set(items) { if let Err(items) = LANG_ITEMS.set(items) {
let first = hash128(LANG_ITEMS.get().unwrap()); let first = hash128(LANG_ITEMS.get().unwrap());
let second = hash128(&items); let second = hash128(&items);

View File

@ -213,10 +213,8 @@ impl TestWorld {
.filter_map(|e| e.ok()) .filter_map(|e| e.ok())
.filter(|entry| entry.file_type().is_file()) .filter(|entry| entry.file_type().is_file())
{ {
let buffer: Buffer = fs::read(entry.path()).unwrap().into(); let data = std::fs::read(entry.path()).unwrap();
for index in 0..ttf_parser::fonts_in_collection(&buffer).unwrap_or(1) { fonts.extend(Font::iter(data.into()));
fonts.push(Font::new(buffer.clone(), index).unwrap())
}
} }
Self { Self {