diff --git a/library/src/basics/heading.rs b/library/src/basics/heading.rs index b7d8c72c3..12a6ac665 100644 --- a/library/src/basics/heading.rs +++ b/library/src/basics/heading.rs @@ -5,30 +5,48 @@ use crate::layout::{BlockNode, VNode}; use crate::prelude::*; use crate::text::{SpaceNode, TextNode, TextSize}; +/// # Heading /// A section heading. /// -/// # Example +/// With headings, you can structure your document into sections. Each heading +/// has a _level,_ which starts at one and is unbounded upwards. This level +/// indicates the logical role of the following content (section, subsection, +/// etc.) Top-level heading indicates a top-level section of the document (not +/// the document's title). +/// +/// Typst can automatically number your headings for you. To enable numbering, +/// specify how you want your headings to be numbered with a [numbering +/// pattern](@numbering). +/// +/// Independently from the numbering, Typst can also automatically generate an +/// [outline](@outline) of all headings for you. To exclude one or more headings +/// from this outline, you can set the `outlined` parameter to `{false}`. +/// +/// ## Example /// ``` -/// #set heading(numbering: "I.") +/// #set heading(numbering: "1.a)") /// /// = Introduction /// In recent years, ... +/// +/// == Preliminaries +/// To start, ... /// ``` /// -/// # 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. +/// ## Syntax +/// Headings have dedicated syntax: They can be created by starting a line with +/// one or multiple equals signs, followed by a space. The number of equals +/// signs determines the heading's logical nesting depth. /// -/// # Parameters -/// - body: Content (positional, required) -/// The heading's contents. +/// ## Parameters +/// - title: Content (positional, required) +/// The heading's title. /// /// - level: NonZeroUsize (named) /// The logical nesting depth of the heading, starting from one. /// -/// # Tags -/// - basics +/// ## Category +/// basics #[func] #[capable(Prepare, Show, Finalize)] #[derive(Debug, Hash)] @@ -37,12 +55,12 @@ pub struct HeadingNode { /// default style, this controls the text size of the heading. pub level: NonZeroUsize, /// The heading's contents. - pub body: Content, + pub title: Content, } #[node] impl HeadingNode { - /// How to number the heading. + /// How to number the heading. Accepts a [numbering pattern](@numbering). /// /// # Example /// ``` @@ -72,7 +90,7 @@ impl HeadingNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult { Ok(Self { - body: args.expect("body")?, + title: args.expect("title")?, level: args.named("level")?.unwrap_or(NonZeroUsize::new(1).unwrap()), } .pack()) @@ -81,7 +99,7 @@ impl HeadingNode { fn field(&self, name: &str) -> Option { match name { "level" => Some(Value::Int(self.level.get() as i64)), - "body" => Some(Value::Content(self.body.clone())), + "title" => Some(Value::Content(self.title.clone())), _ => None, } } @@ -118,7 +136,7 @@ impl Prepare for HeadingNode { impl Show for HeadingNode { fn show(&self, _: &mut Vt, this: &Content, _: StyleChain) -> SourceResult { - let mut realized = self.body.clone(); + let mut realized = self.title.clone(); if let Some(Value::Str(numbering)) = this.field("numbers") { realized = TextNode::packed(numbering) + SpaceNode.pack() + realized; } diff --git a/library/src/basics/list.rs b/library/src/basics/list.rs index 15b26169d..ca60576c7 100644 --- a/library/src/basics/list.rs +++ b/library/src/basics/list.rs @@ -3,9 +3,10 @@ use crate::layout::{BlockNode, GridNode, HNode, ParNode, Spacing, TrackSizing}; use crate::prelude::*; use crate::text::{SpaceNode, TextNode}; +/// # List /// An unordered (bulleted) or ordered (numbered) list. /// -/// # Parameters +/// ## Parameters /// - items: Content (positional, variadic) /// The contents of the list items. /// @@ -16,7 +17,7 @@ use crate::text::{SpaceNode, TextNode}; /// Makes the list more compact, if enabled. This looks better if the items /// fit into a single line each. /// -/// # Example +/// ### Example /// ``` /// #show columns.with(2) /// #list(tight: true)[Tight][List] @@ -24,8 +25,8 @@ use crate::text::{SpaceNode, TextNode}; /// #list(tight: false)[Wide][List] /// ``` /// -/// # Tags -/// - basics +/// ## Category +/// basics #[func] #[capable(Layout)] #[derive(Debug, Hash)] diff --git a/library/src/basics/table.rs b/library/src/basics/table.rs index 05f2ffff8..dbb3d58e0 100644 --- a/library/src/basics/table.rs +++ b/library/src/basics/table.rs @@ -1,9 +1,10 @@ use crate::layout::{GridNode, TrackSizing, TrackSizings}; use crate::prelude::*; +/// # Table /// A table of items. /// -/// # Parameters +/// ## Parameters /// - cells: Content (positional, variadic) /// The contents of the table cells. /// @@ -22,8 +23,8 @@ use crate::prelude::*; /// - row-gutter: TrackSizings (named) /// Defines the gaps between rows. Takes precedence over `gutter`. /// -/// # Tags -/// - basics +/// ## Category +/// basics #[func] #[capable(Layout)] #[derive(Debug, Hash)] diff --git a/library/src/compute/calc.rs b/library/src/compute/calc.rs index 1984d5ef6..873eff15d 100644 --- a/library/src/compute/calc.rs +++ b/library/src/compute/calc.rs @@ -2,14 +2,22 @@ use std::cmp::Ordering; use crate::prelude::*; +/// # Absolute /// The absolute value of a numeric value. /// -/// # Parameters +/// ## Example +/// ``` +/// #abs(-5) \ +/// #abs(5pt - 2cm) \ +/// #abs(2fr) +/// ``` +/// +/// ## Parameters /// - value: ToAbs (positional, required) /// The value whose absolute value to calculate. /// -/// # Tags -/// - calculate +/// ## Category +/// calculate #[func] pub fn abs(args: &mut Args) -> SourceResult { Ok(args.expect::("value")?.0) @@ -22,32 +30,50 @@ castable! { ToAbs, v: i64 => Self(Value::Int(v.abs())), v: f64 => Self(Value::Float(v.abs())), + v: Length => Self(Value::Length(v.try_abs() + .ok_or_else(|| "cannot take absolute value of this length")?)), v: Angle => Self(Value::Angle(v.abs())), v: Ratio => Self(Value::Ratio(v.abs())), v: Fr => Self(Value::Fraction(v.abs())), } +/// # Minimum /// The minimum of a sequence of values. /// -/// # Parameters -/// - values: Value (positional, variadic) -/// The sequence of values. +/// ## Example +/// ``` +/// #min(1, -3, -5, 20, 3, 6) \ +/// #min("Typst", "in", "beta") +/// ``` /// -/// # Tags -/// - calculate +/// ## Parameters +/// - values: Value (positional, variadic) +/// The sequence of values from which to extract the minimum. +/// Must not be empty. +/// +/// ## Category +/// calculate #[func] pub fn min(args: &mut Args) -> SourceResult { minmax(args, Ordering::Less) } +/// # Maximum /// The maximum of a sequence of values. /// -/// # Parameters -/// - values: Value (positional, variadic) -/// The sequence of values. +/// ## Example +/// ``` +/// #max(1, -3, -5, 20, 3, 6) \ +/// #max("Typst", "in", "beta") +/// ``` /// -/// # Tags -/// - calculate +/// ## Parameters +/// - values: Value (positional, variadic) +/// The sequence of values from which to extract the maximum. +/// Must not be empty. +/// +/// ## Category +/// calculate #[func] pub fn max(args: &mut Args) -> SourceResult { minmax(args, Ordering::Greater) @@ -74,43 +100,67 @@ fn minmax(args: &mut Args, goal: Ordering) -> SourceResult { Ok(extremum) } +/// # Even /// Whether an integer is even. /// -/// # Parameters +/// ## Example +/// ``` +/// #even(4) \ +/// #even(5) \ +/// { range(10).filter(even) } +/// ``` +/// +/// ## Parameters /// - value: i64 (positional, required) /// The number to check for evenness. /// -/// # Tags -/// - calculate +/// ## Category +/// calculate #[func] pub fn even(args: &mut Args) -> SourceResult { Ok(Value::Bool(args.expect::("value")? % 2 == 0)) } +/// # Odd /// Whether an integer is odd. /// -/// # Parameters +/// ## Example +/// ``` +/// #odd(4) \ +/// #odd(5) \ +/// { range(10).filter(odd) } +/// ``` +/// +/// +/// ## Parameters /// - value: i64 (positional, required) /// The number to check for oddness. /// -/// # Tags -/// - calculate +/// ## Category +/// calculate #[func] pub fn odd(args: &mut Args) -> SourceResult { Ok(Value::Bool(args.expect::("value")? % 2 != 0)) } +/// # Modulus /// The modulus of two numbers. /// -/// # Parameters +/// ## Example +/// ``` +/// #mod(20, 6) \ +/// #mod(1.75, 0.5) +/// ``` +/// +/// ## Parameters /// - dividend: ToMod (positional, required) /// The dividend of the modulus. /// /// - divisor: ToMod (positional, required) /// The divisor of the modulus. /// -/// # Tags -/// - calculate +/// ## Category +/// calculate #[func] pub fn mod_(args: &mut Args) -> SourceResult { let Spanned { v: v1, span: span1 } = args.expect("dividend")?; diff --git a/library/src/compute/create.rs b/library/src/compute/create.rs index cb693a1c3..a34c0ba61 100644 --- a/library/src/compute/create.rs +++ b/library/src/compute/create.rs @@ -4,14 +4,27 @@ use typst::model::Regex; use crate::prelude::*; +/// # Integer /// Convert a value to an integer. /// -/// # Parameters +/// - Booleans are converted to `0` or `1`. +/// - Floats are floored to the next 64-bit integer. +/// - Strings are parsed in base 10. +/// +/// ## Example +/// ``` +/// #int(false) \ +/// #int(true) \ +/// #int(2.7) \ +/// { int("27") + int("4") } +/// ``` +/// +/// ## Parameters /// - value: ToInt (positional, required) /// The value that should be converted to an integer. /// -/// # Tags -/// - create +/// ## Category +/// create #[func] pub fn int(args: &mut Args) -> SourceResult { Ok(Value::Int(args.expect::("value")?.0)) @@ -28,14 +41,29 @@ castable! { v: EcoString => Self(v.parse().map_err(|_| "not a valid integer")?), } +/// # Float /// Convert a value to a float. /// -/// # Parameters +/// - Booleans are converted to `0.0` or `1.0`. +/// - Integers are converted to the closest 64-bit float. +/// - Strings are parsed in base 10 to the closest 64-bit float. +/// Exponential notation is supported. +/// +/// ## Example +/// ``` +/// #float(false) \ +/// #float(true) \ +/// #float(4) \ +/// #float("2.7") \ +/// #float("1e5") +/// ``` +/// +/// ## Parameters /// - value: ToFloat (positional, required) /// The value that should be converted to a float. /// -/// # Tags -/// - create +/// ## Category +/// create #[func] pub fn float(args: &mut Args) -> SourceResult { Ok(Value::Float(args.expect::("value")?.0)) @@ -52,23 +80,45 @@ castable! { v: EcoString => Self(v.parse().map_err(|_| "not a valid float")?), } +/// # Luma /// Create a grayscale color. /// -/// # Parameters +/// ## Example +/// ``` +/// #for x in range(250, step: 50) { +/// square(fill: luma(x)) +/// } +/// ``` +/// +/// ## Parameters /// - gray: Component (positional, required) /// The gray component. /// -/// # Tags -/// - create +/// ## Category +/// create #[func] pub fn luma(args: &mut Args) -> SourceResult { let Component(luma) = args.expect("gray component")?; Ok(Value::Color(LumaColor::new(luma).into())) } +/// # RGBA /// Create an RGB(A) color. /// -/// # Parameters +/// The color is specified in the sRGB color space. +/// +/// _Note:_ While you can specify transparent colors and Typst's preview will +/// render them correctly, the PDF export does not handle them properly at the +/// moment. This will be fixed in the future. +/// +/// ## Example +/// ``` +/// #square(fill: rgb("#b1f2eb")) +/// #square(fill: rgb(87, 127, 230)) +/// #square(fill: rgb(25%, 13%, 65%)) +/// ``` +/// +/// ## Parameters /// - hex: EcoString (positional) /// The color in hexademical notation. /// @@ -77,10 +127,11 @@ pub fn luma(args: &mut Args) -> SourceResult { /// /// If this string is given, the individual components should not be given. /// -/// # Example +/// ### Example /// ``` -/// #let color = rgb("#239dad") -/// #text(16pt, color)[*Typst*] +/// #text(16pt, rgb("#239dad"))[ +/// *Typst* +/// ] /// ``` /// /// - red: Component (positional) @@ -95,8 +146,8 @@ pub fn luma(args: &mut Args) -> SourceResult { /// - alpha: Component (positional) /// The alpha component. /// -/// # Tags -/// - create +/// ## Category +/// create #[func] pub fn rgb(args: &mut Args) -> SourceResult { Ok(Value::Color(if let Some(string) = args.find::>()? { @@ -129,9 +180,21 @@ castable! { }, } +/// # CMYK /// Create a CMYK color. /// -/// # Parameters +/// This is useful if you want to target a specific printer. The conversion +/// to RGB for display preview might differ from how your printer reproduces +/// the color. +/// +/// ## Example +/// ``` +/// #square( +/// fill: cmyk(27%, 0%, 3%, 5%) +/// ) +/// ```` +/// +/// ## Parameters /// - cyan: RatioComponent (positional, required) /// The cyan component. /// @@ -144,8 +207,8 @@ castable! { /// - key: RatioComponent (positional, required) /// The key component. /// -/// # Tags -/// - create +/// ## Category +/// create #[func] pub fn cmyk(args: &mut Args) -> SourceResult { let RatioComponent(c) = args.expect("cyan component")?; @@ -167,14 +230,27 @@ castable! { }, } +/// # String /// Convert a value to a string. /// -/// # Parameters +/// - Integers are formatted in base 10. +/// - Floats are formatted in base 10 and never in exponential notation. +/// - From labels the name is extracted. +/// +/// ## Example +/// ``` +/// #str(10) \ +/// #str(2.7) \ +/// #str(1e8) \ +/// #str() +/// ``` +/// +/// ## Parameters /// - value: ToStr (positional, required) /// The value that should be converted to a string. /// -/// # Tags -/// - create +/// ## Category +/// create #[func] pub fn str(args: &mut Args) -> SourceResult { Ok(Value::Str(args.expect::("value")?.0)) @@ -191,36 +267,94 @@ castable! { v: Str => Self(v), } +/// # Label /// Create a label from a string. /// -/// # Parameters +/// Inserting a label into content attaches it to the closest previous element +/// that is not a space. Then, the element can be [referenced](@ref) and styled +/// through the label. +/// +/// ## Example +/// ``` +/// #show : set text(blue) +/// #show label("b"): set text(red) +/// +/// = Heading +/// *Strong* #label("b") +/// ``` +/// +/// ## Syntax +/// This function also has dedicated syntax: You can create a label by enclosing +/// its name in angle brackets. This works both in markup and code. +/// +/// ## Parameters /// - name: EcoString (positional, required) /// The name of the label. /// -/// # Tags -/// - create +/// ## Category +/// create #[func] pub fn label(args: &mut Args) -> SourceResult { Ok(Value::Label(Label(args.expect("string")?))) } +/// # Regex /// Create a regular expression from a string. /// -/// # Parameters -/// - regex: EcoString (positional, required) -/// The regular expression. +/// The result can be used as a show rule +/// [selector](/docs/reference/concepts/#selector) and with +/// [string methods](/docs/reference/concepts/#methods) like `find`, `split`, +/// and `replace`. /// -/// # Tags -/// - create +/// [See here](https://docs.rs/regex/latest/regex/#syntax) for a specification +/// of the supported syntax. +/// +/// ## Example +/// ``` +/// // Works with show rules. +/// #show regex("\d+"): set text(red) +/// +/// The numbers 1 to 10. +/// +/// // Works with string methods. +/// { "a,b;c" +/// .split(regex("[,;]")) } +/// ``` +/// +/// ## Parameters +/// - regex: EcoString (positional, required) +/// The regular expression as a string. +/// +/// Most regex escape sequences just work because they are not valid Typst +/// escape sequences. To produce regex escape sequences that are also valid in +/// Typst (e.g. `[\\]`), you need to escape twice. Thus, to match a verbatim +/// backslash, you would need to write `{regex("\\\\")}`. +/// +/// ## Category +/// create #[func] pub fn regex(args: &mut Args) -> SourceResult { let Spanned { v, span } = args.expect::>("regular expression")?; Ok(Regex::new(&v).at(span)?.into()) } +/// # Range /// Create an array consisting of a sequence of numbers. /// -/// # Parameters +/// If you pass just one positional parameter, it is interpreted as the `end` of +/// the range. If you pass two, they describe the `start` and `end` of the +/// range. +/// +/// ## Example +/// ``` +/// #range(5) \ +/// #range(2, 5) \ +/// #range(20, step: 4) \ +/// #range(21, step: 4) \ +/// #range(5, 2, step: -1) +/// ``` +/// +/// ## Parameters /// - start: i64 (positional) /// The start of the range (inclusive). /// @@ -230,8 +364,8 @@ pub fn regex(args: &mut Args) -> SourceResult { /// - step: i64 (named) /// The distance between the generated numbers. /// -/// # Tags -/// - create +/// ## Category +/// create #[func] pub fn range(args: &mut Args) -> SourceResult { let first = args.expect::("end")?; diff --git a/library/src/compute/data.rs b/library/src/compute/data.rs index ed87f78d7..e947d6175 100644 --- a/library/src/compute/data.rs +++ b/library/src/compute/data.rs @@ -4,6 +4,7 @@ use typst::diag::{format_xml_like_error, FileError}; use crate::prelude::*; +/// # CSV /// Read structured data from a CSV file. /// /// The CSV file will be read and parsed into a 2-dimensional array of strings: @@ -11,7 +12,7 @@ use crate::prelude::*; /// rows will be collected into a single array. Header rows will not be /// stripped. /// -/// # Example +/// ## Example /// ``` /// #let results = csv("/data.csv") /// @@ -21,7 +22,8 @@ use crate::prelude::*; /// ..results.flatten(), /// ) /// ``` -/// # Parameters +/// +/// ## Parameters /// - path: EcoString (positional, required) /// Path to a CSV file. /// - delimiter: Delimiter (named) @@ -29,8 +31,8 @@ use crate::prelude::*; /// Must be a single ASCII character. /// Defaults to a comma. /// -/// # Tags -/// - data-loading +/// ## Category +/// data-loading #[func] pub fn csv(vm: &Vm, args: &mut Args) -> SourceResult { let Spanned { v: path, span } = @@ -95,6 +97,7 @@ fn format_csv_error(error: csv::Error) -> String { } } +/// # JSON /// Read structured data from a JSON file. /// /// The file must contain a valid JSON object or array. JSON objects will be @@ -108,7 +111,7 @@ fn format_csv_error(error: csv::Error) -> String { /// The JSON files in the example contain a object with the keys `temperature`, /// `unit`, and `weather`. /// -/// # Example +/// ## Example /// ``` /// #let forecast(day) = block[ /// #square( @@ -134,12 +137,12 @@ fn format_csv_error(error: csv::Error) -> String { /// #forecast(json("/tuesday.json")) /// ``` /// -/// # Parameters +/// ## Parameters /// - path: EcoString (positional, required) /// Path to a JSON file. /// -/// # Tags -/// - data-loading +/// ## Category +/// data-loading #[func] pub fn json(vm: &Vm, args: &mut Args) -> SourceResult { let Spanned { v: path, span } = @@ -177,12 +180,10 @@ fn convert_json(value: serde_json::Value) -> Value { /// Format the user-facing JSON error message. fn format_json_error(error: serde_json::Error) -> String { assert!(error.is_syntax() || error.is_eof()); - format!( - "failed to parse json file: syntax error in line {}", - error.line() - ) + format!("failed to parse json file: syntax error in line {}", error.line()) } +/// # XML /// Read structured data from an XML file. /// /// The XML file is parsed into an array of dictionaries and strings. XML nodes @@ -198,7 +199,7 @@ fn format_json_error(error: serde_json::Error) -> String { /// `content` tag contains one or more paragraphs, which are represented as `p` /// tags. /// -/// # Example +/// ## Example /// ``` /// #let findChild(elem, tag) = { /// elem.children @@ -232,12 +233,12 @@ fn format_json_error(error: serde_json::Error) -> String { /// } /// ``` /// -/// # Parameters +/// ## Parameters /// - path: EcoString (positional, required) /// Path to an XML file. /// -/// # Tags -/// - data-loading +/// ## Category +/// data-loading #[func] pub fn xml(vm: &Vm, args: &mut Args) -> SourceResult { let Spanned { v: path, span } = diff --git a/library/src/compute/foundations.rs b/library/src/compute/foundations.rs index cb952d812..0ee0a628d 100644 --- a/library/src/compute/foundations.rs +++ b/library/src/compute/foundations.rs @@ -4,40 +4,43 @@ use comemo::Track; use typst::model; use typst::syntax::Source; +/// # Type /// The name of a value's type. /// -/// # Parameters +/// ## Parameters /// - value: Value (positional, required) /// The value whose type's to determine. /// -/// # Tags -/// - foundations +/// ## Category +/// foundations #[func] pub fn type_(args: &mut Args) -> SourceResult { Ok(args.expect::("value")?.type_name().into()) } +/// # Representation /// The string representation of a value. /// -/// # Parameters +/// ## Parameters /// - value: Value (positional, required) /// The value whose string representation to produce. /// -/// # Tags -/// - foundations +/// ## Category +/// foundations #[func] pub fn repr(args: &mut Args) -> SourceResult { Ok(args.expect::("value")?.repr().into()) } +/// # Assert /// Ensure that a condition is fulfilled. /// -/// # Parameters +/// ## Parameters /// - condition: bool (positional, required) /// The condition that must be true for the assertion to pass. /// -/// # Tags -/// - foundations +/// ## Category +/// foundations #[func] pub fn assert(args: &mut Args) -> SourceResult { let Spanned { v, span } = args.expect::>("condition")?; @@ -47,14 +50,15 @@ pub fn assert(args: &mut Args) -> SourceResult { Ok(Value::None) } +/// # Evaluate /// Evaluate a string as Typst markup. /// -/// # Parameters +/// ## Parameters /// - source: String (positional, required) /// A string of Typst markup to evaluate. /// -/// # Tags -/// - foundations +/// ## Category +/// foundations #[func] pub fn eval(vm: &Vm, args: &mut Args) -> SourceResult { let Spanned { v: text, span } = args.expect::>("source")?; diff --git a/library/src/compute/utility.rs b/library/src/compute/utility.rs index ffad740e8..1a3511d01 100644 --- a/library/src/compute/utility.rs +++ b/library/src/compute/utility.rs @@ -3,31 +3,33 @@ use std::str::FromStr; use crate::prelude::*; use crate::text::Case; +/// # Blind Text /// Create blind text. /// -/// # Parameters +/// ## Parameters /// - words: usize (positional, required) /// The length of the blind text in words. /// -/// # Tags -/// - utility +/// ## Category +/// utility #[func] pub fn lorem(args: &mut Args) -> SourceResult { let words: usize = args.expect("number of words")?; Ok(Value::Str(lipsum::lipsum(words).into())) } +/// # Numbering /// Apply a numbering pattern to a sequence of numbers. /// -/// # Parameters +/// ## Parameters /// - pattern: NumberingPattern (positional, required) /// A string that defines how the numbering works. /// /// - numbers: NonZeroUsize (positional, variadic) /// The numbers to apply the pattern to. /// -/// # Tags -/// - utility +/// ## Category +/// utility #[func] pub fn numbering(args: &mut Args) -> SourceResult { let pattern = args.expect::("pattern")?; diff --git a/library/src/layout/align.rs b/library/src/layout/align.rs index 5426f5e28..9fcb216f5 100644 --- a/library/src/layout/align.rs +++ b/library/src/layout/align.rs @@ -1,16 +1,17 @@ use crate::prelude::*; +/// # Align /// Align content horizontally and vertically. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The content to align. /// /// - alignment: Axes> (positional, settable) /// The alignment along both axes. /// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable] #[derive(Debug, Hash)] diff --git a/library/src/layout/columns.rs b/library/src/layout/columns.rs index 6154cd142..e8ae5f35c 100644 --- a/library/src/layout/columns.rs +++ b/library/src/layout/columns.rs @@ -1,17 +1,18 @@ use crate::prelude::*; use crate::text::TextNode; +/// # Columns /// Separate a region into multiple equally sized columns. /// -/// # Parameters +/// ## Parameters /// - count: usize (positional, required) /// The number of columns. /// /// - body: Content (positional, required) /// The content that should be layouted into the columns. /// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable(Layout)] #[derive(Debug, Hash)] @@ -112,15 +113,16 @@ impl Layout for ColumnsNode { } } +/// # Column Break /// A column break. /// -/// # Parameters +/// ## Parameters /// - weak: bool (named) /// If true, the column break is skipped if the current column is already /// empty. /// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable(Behave)] #[derive(Debug, Hash)] diff --git a/library/src/layout/container.rs b/library/src/layout/container.rs index 5df6b2dc3..933deebac 100644 --- a/library/src/layout/container.rs +++ b/library/src/layout/container.rs @@ -2,9 +2,10 @@ use super::VNode; use crate::layout::Spacing; use crate::prelude::*; +/// # Box /// An inline-level container that sizes content. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional) /// The contents of the box. /// @@ -14,8 +15,8 @@ use crate::prelude::*; /// - height: Rel (named) /// The height of the box. /// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable(Layout, Inline)] #[derive(Debug, Hash)] @@ -75,9 +76,10 @@ impl Layout for BoxNode { impl Inline for BoxNode {} +/// # Block /// A block-level container that places content into a separate flow. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional) /// The contents of the block. /// @@ -92,8 +94,8 @@ impl Inline for BoxNode {} /// The spacing between this block and the following one. Takes precedence /// over `spacing`. /// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable(Layout)] #[derive(Debug, Hash)] diff --git a/library/src/layout/grid.rs b/library/src/layout/grid.rs index 1cf950011..eb45cfbea 100644 --- a/library/src/layout/grid.rs +++ b/library/src/layout/grid.rs @@ -2,9 +2,10 @@ use crate::prelude::*; use super::Spacing; +/// # Grid /// Arrange content in a grid. /// -/// # Parameters +/// ## Parameters /// - cells: Content (positional, variadic) /// The contents of the table cells. /// @@ -23,8 +24,8 @@ use super::Spacing; /// - row-gutter: TrackSizings (named) /// Defines the gaps between rows. Takes precedence over `gutter`. /// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable(Layout)] #[derive(Debug, Hash)] diff --git a/library/src/layout/hide.rs b/library/src/layout/hide.rs index fc9150c15..f7f195492 100644 --- a/library/src/layout/hide.rs +++ b/library/src/layout/hide.rs @@ -1,13 +1,14 @@ use crate::prelude::*; +/// # Hide /// Hide content without affecting layout. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The content to hide. /// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable(Layout, Inline)] #[derive(Debug, Hash)] diff --git a/library/src/layout/pad.rs b/library/src/layout/pad.rs index 1326ae22a..7825d133e 100644 --- a/library/src/layout/pad.rs +++ b/library/src/layout/pad.rs @@ -1,8 +1,9 @@ use crate::prelude::*; +/// # Padding /// Pad content at the sides. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The content to pad at the sides. /// @@ -27,8 +28,8 @@ use crate::prelude::*; /// - rest: Rel (named) /// The padding for all sides. All other parameters take precedence over this. /// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable(Layout)] #[derive(Debug, Hash)] diff --git a/library/src/layout/page.rs b/library/src/layout/page.rs index a2548c025..dfaaae23a 100644 --- a/library/src/layout/page.rs +++ b/library/src/layout/page.rs @@ -4,17 +4,18 @@ use super::ColumnsNode; use crate::prelude::*; use crate::text::TextNode; +/// # Page /// Layouts its child onto one or multiple pages. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The contents of the page(s). /// /// - paper: Paper (positional, settable) /// The paper size. /// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable] #[derive(Clone, Hash)] @@ -154,14 +155,15 @@ impl Debug for PageNode { } } +/// # Page Break /// A page break. /// -/// # Parameters +/// ## Parameters /// - weak: bool (named) /// If true, the page break is skipped if the current page is already empty. /// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable] #[derive(Debug, Copy, Clone, Hash)] diff --git a/library/src/layout/par.rs b/library/src/layout/par.rs index f9731ff2a..bdd8c9d5b 100644 --- a/library/src/layout/par.rs +++ b/library/src/layout/par.rs @@ -11,14 +11,15 @@ use crate::text::{ shape, LinebreakNode, Quoter, Quotes, ShapedText, SmartQuoteNode, SpaceNode, TextNode, }; +/// # Paragraph /// Arrange text, spacing and inline-level nodes into a paragraph. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The contents of the paragraph. /// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable] #[derive(Hash)] @@ -147,10 +148,11 @@ castable! { "optimized" => Self::Optimized, } +/// # Paragraph Break /// A paragraph break. /// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable(Unlabellable)] #[derive(Debug, Hash)] diff --git a/library/src/layout/place.rs b/library/src/layout/place.rs index 8baf69afc..ed3d71bf3 100644 --- a/library/src/layout/place.rs +++ b/library/src/layout/place.rs @@ -1,8 +1,9 @@ use crate::prelude::*; +/// # Place /// Place content at an absolute position. /// -/// # Parameters +/// ## Parameters /// - alignment: Axes> (positional) /// Relative to which position in the parent container to place the content. /// @@ -15,8 +16,8 @@ use crate::prelude::*; /// - dy: Rel (named) /// The vertical displacement of the placed content. /// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable(Layout, Behave)] #[derive(Debug, Hash)] diff --git a/library/src/layout/repeat.rs b/library/src/layout/repeat.rs index fa87d922d..864454c42 100644 --- a/library/src/layout/repeat.rs +++ b/library/src/layout/repeat.rs @@ -1,13 +1,14 @@ use crate::prelude::*; +/// # Repeat /// Repeats content to fill a line. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The content to repeat. /// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable(Layout, Inline)] #[derive(Debug, Hash)] diff --git a/library/src/layout/spacing.rs b/library/src/layout/spacing.rs index 092d2c0ab..f306d47c2 100644 --- a/library/src/layout/spacing.rs +++ b/library/src/layout/spacing.rs @@ -2,9 +2,10 @@ use std::cmp::Ordering; use crate::prelude::*; +/// # Spacing (H) /// Horizontal spacing in a paragraph. /// -/// # Parameters +/// ## Parameters /// - amount: Spacing (positional, required) /// How much spacing to insert. /// @@ -13,8 +14,8 @@ use crate::prelude::*; /// Moreover, from multiple adjacent weak spacings all but the largest one /// collapse. /// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable(Behave)] #[derive(Debug, Copy, Clone, Hash)] @@ -63,9 +64,10 @@ impl Behave for HNode { } } +/// # Spacing (V) /// Vertical spacing. /// -/// # Parameters +/// ## Parameters /// - amount: Spacing (positional, required) /// How much spacing to insert. /// @@ -74,8 +76,8 @@ impl Behave for HNode { /// Moreover, from multiple adjacent weak spacings all but the largest one /// collapse. /// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable(Behave)] #[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd)] diff --git a/library/src/layout/stack.rs b/library/src/layout/stack.rs index 2683175a3..f83f4e416 100644 --- a/library/src/layout/stack.rs +++ b/library/src/layout/stack.rs @@ -3,9 +3,10 @@ use typst::model::StyledNode; use super::{AlignNode, Spacing}; use crate::prelude::*; +/// # Stack /// Arrange content and spacing along an axis. /// -/// # Parameters +/// ## Parameters /// - items: StackChild (positional, variadic) /// The items to stack along an axis. /// @@ -15,8 +16,8 @@ use crate::prelude::*; /// - spacing: Spacing (named) /// Spacing to insert between items where no explicit spacing was provided. /// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable(Layout)] #[derive(Debug, Hash)] diff --git a/library/src/layout/transform.rs b/library/src/layout/transform.rs index b509607d4..6d2caf1d2 100644 --- a/library/src/layout/transform.rs +++ b/library/src/layout/transform.rs @@ -2,9 +2,10 @@ use typst::geom::Transform; use crate::prelude::*; +/// # Move /// Move content without affecting layout. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The content to move. /// @@ -14,8 +15,8 @@ use crate::prelude::*; /// - dy: Rel (named) /// The vertical displacement of the content. /// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable(Layout, Inline)] #[derive(Debug, Hash)] @@ -58,64 +59,44 @@ impl Layout for MoveNode { impl Inline for MoveNode {} -/// Transform content without affecting layout. +/// # Rotate +/// Rotate content with affecting layout. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) -/// The content to transform. +/// The content to rotate. /// /// - angle: Angle (named) /// The amount of rotation. /// -/// - x: Ratio (named) -/// The horizontal scaling factor. -/// -/// - y: Ratio (named) -/// The vertical scaling factor. -/// -/// # Tags -/// - layout +/// ## Category +/// layout #[func] #[capable(Layout, Inline)] #[derive(Debug, Hash)] -pub struct TransformNode { - /// Transformation to apply to the content. - pub transform: Transform, - /// The content that should be transformed. +pub struct RotateNode { + /// The angle by which to rotate the node. + pub angle: Angle, + /// The content that should be rotated. pub body: Content, } -/// Rotate content without affecting layout. -pub type RotateNode = TransformNode; - -/// Scale content without affecting layout. -pub type ScaleNode = TransformNode; - #[node] -impl TransformNode { - /// The origin of the transformation. +impl RotateNode { + /// The origin of the rotation. #[property(resolve)] pub const ORIGIN: Axes> = Axes::default(); fn construct(_: &Vm, args: &mut Args) -> SourceResult { - let transform = match T { - ROTATE => { - let angle = args.named_or_find("angle")?.unwrap_or_default(); - Transform::rotate(angle) - } - SCALE | _ => { - let all = args.find()?; - let sx = args.named("x")?.or(all).unwrap_or(Ratio::one()); - let sy = args.named("y")?.or(all).unwrap_or(Ratio::one()); - Transform::scale(sx, sy) - } - }; - - Ok(Self { transform, body: args.expect("body")? }.pack()) + Ok(Self { + angle: args.named_or_find("angle")?.unwrap_or_default(), + body: args.expect("body")?, + } + .pack()) } } -impl Layout for TransformNode { +impl Layout for RotateNode { fn layout( &self, vt: &mut Vt, @@ -127,7 +108,7 @@ impl Layout for TransformNode { let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON); let Axes { x, y } = origin.zip(frame.size()).map(|(o, s)| o.position(s)); let transform = Transform::translate(x, y) - .pre_concat(self.transform) + .pre_concat(Transform::rotate(self.angle)) .pre_concat(Transform::translate(-x, -y)); frame.transform(transform); } @@ -135,15 +116,69 @@ impl Layout for TransformNode { } } -impl Inline for TransformNode {} +impl Inline for RotateNode {} -/// Kinds of transformations. +/// # Scale +/// Scale content without affecting layout. /// -/// The move transformation is handled separately. -pub type TransformKind = usize; +/// ## Parameters +/// - body: Content (positional, required) +/// The content to scale. +/// +/// - x: Ratio (named) +/// The horizontal scaling factor. +/// +/// - y: Ratio (named) +/// The vertical scaling factor. +/// +/// ## Category +/// layout +#[func] +#[capable(Layout, Inline)] +#[derive(Debug, Hash)] +pub struct ScaleNode { + /// Scaling factor. + pub factor: Axes, + /// The content that should be scaled. + pub body: Content, +} -/// A rotational transformation. -const ROTATE: TransformKind = 1; +#[node] +impl ScaleNode { + /// The origin of the transformation. + #[property(resolve)] + pub const ORIGIN: Axes> = Axes::default(); -/// A scale transformation. -const SCALE: TransformKind = 2; + fn construct(_: &Vm, args: &mut Args) -> SourceResult { + let all = args.find()?; + let x = args.named("x")?.or(all).unwrap_or(Ratio::one()); + let y = args.named("y")?.or(all).unwrap_or(Ratio::one()); + Ok(Self { + factor: Axes::new(x, y), + body: args.expect("body")?, + } + .pack()) + } +} + +impl Layout for ScaleNode { + fn layout( + &self, + vt: &mut Vt, + styles: StyleChain, + regions: Regions, + ) -> SourceResult { + let mut fragment = self.body.layout(vt, styles, regions)?; + for frame in &mut fragment { + let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON); + let Axes { x, y } = origin.zip(frame.size()).map(|(o, s)| o.position(s)); + let transform = Transform::translate(x, y) + .pre_concat(Transform::scale(self.factor.x, self.factor.y)) + .pre_concat(Transform::translate(-x, -y)); + frame.transform(transform); + } + Ok(fragment) + } +} + +impl Inline for ScaleNode {} diff --git a/library/src/lib.rs b/library/src/lib.rs index 7e65ff791..da05a8de6 100644 --- a/library/src/lib.rs +++ b/library/src/lib.rs @@ -196,7 +196,7 @@ fn items() -> LangItems { }, link: |url| meta::LinkNode::from_url(url).pack(), ref_: |target| meta::RefNode(target).pack(), - heading: |level, body| basics::HeadingNode { level, body }.pack(), + heading: |level, body| basics::HeadingNode { level, title: body }.pack(), list_item: |body| basics::ListItem::List(Box::new(body)).pack(), enum_item: |number, body| basics::ListItem::Enum(number, Box::new(body)).pack(), desc_item: |term, body| { diff --git a/library/src/math/matrix.rs b/library/src/math/matrix.rs index a32750ff1..92199774d 100644 --- a/library/src/math/matrix.rs +++ b/library/src/math/matrix.rs @@ -1,13 +1,14 @@ use super::*; +/// # Vector /// A column vector. /// -/// # Parameters +/// ## Parameters /// - elements: Content (positional, variadic) /// The elements of the vector. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] @@ -59,24 +60,25 @@ pub enum Delimiter { castable! { Delimiter, - /// Delimit matrices with parentheses. + /// Delimit vector with parentheses. "(" => Self::Paren, - /// Delimit matrices with brackets. + /// Delimit vector with brackets. "[" => Self::Bracket, - /// Delimit matrices with curly braces. + /// Delimit vector with curly braces. "{" => Self::Brace, - /// Delimit matrices with vertical bars. + /// Delimit vector with vertical bars. "|" => Self::Bar, } +/// # Cases /// A case distinction. /// -/// # Parameters +/// ## Parameters /// - branches: Content (positional, variadic) /// The branches of the case distinction. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs index 5342a954f..c8d15796c 100644 --- a/library/src/math/mod.rs +++ b/library/src/math/mod.rs @@ -14,17 +14,18 @@ use self::tex::layout_tex; use crate::prelude::*; use crate::text::{FontFamily, LinebreakNode, SpaceNode, SymbolNode, TextNode}; +/// # Math /// A piece of a mathematical formula. /// -/// # Parameters +/// ## Parameters /// - items: Content (positional, variadic) /// The individual parts of the formula. /// /// - block: bool (named) /// Whether the formula is displayed as a separate block. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Show, Layout, Inline, Texify)] #[derive(Debug, Clone, Hash)] @@ -259,14 +260,15 @@ impl Texify for Content { } } +/// # Atom /// An atom in a math formula: `x`, `+`, `12`. /// -/// # Parameters +/// ## Parameters /// - text: EcoString (positional, required) /// The atom's text. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] @@ -305,17 +307,18 @@ impl Texify for AtomNode { } } +/// # Accent /// An accented node. /// -/// # Parameters +/// ## Parameters /// - base: Content (positional, required) /// The base to which the accent is applied. /// /// - accent: Content (positional, required) /// The accent to apply to the base. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] @@ -390,17 +393,18 @@ impl Texify for AccNode { } } +/// # Fraction /// A fraction. /// -/// # Parameters +/// ## Parameters /// - num: Content (positional, required) /// The fraction's numerator. /// /// - denom: Content (positional, required) /// The fraction's denominator. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] @@ -431,17 +435,18 @@ impl Texify for FracNode { } } +/// # Binomial /// A binomial. /// -/// # Parameters +/// ## Parameters /// - upper: Content (positional, required) /// The binomial's upper index. /// /// - lower: Content (positional, required) /// The binomial's lower index. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] @@ -472,9 +477,10 @@ impl Texify for BinomNode { } } +/// # Script /// A sub- and/or superscript. /// -/// # Parameters +/// ## Parameters /// - base: Content (positional, required) /// The base to which the applies the sub- and/or superscript. /// @@ -484,8 +490,8 @@ impl Texify for BinomNode { /// - sup: Content (named) /// The superscript. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] @@ -528,14 +534,15 @@ impl Texify for ScriptNode { } } +/// # Alignment Point /// A math alignment point: `&`, `&&`. /// -/// # Parameters +/// ## Parameters /// - index: usize (positional, required) /// The alignment point's index. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] @@ -554,14 +561,15 @@ impl Texify for AlignPointNode { } } +/// # Square Root /// A square root. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The expression to take the square root of. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] @@ -583,14 +591,15 @@ impl Texify for SqrtNode { } } +/// # Floor /// A floored expression. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The expression to floor. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] @@ -612,14 +621,15 @@ impl Texify for FloorNode { } } +/// # Ceil /// A ceiled expression. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The expression to ceil. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] diff --git a/library/src/math/style.rs b/library/src/math/style.rs index 344aa0718..4a9c43b88 100644 --- a/library/src/math/style.rs +++ b/library/src/math/style.rs @@ -1,13 +1,14 @@ use super::*; +/// # Serif /// Serif (roman) font style. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The piece of formula to style. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] @@ -29,14 +30,15 @@ impl Texify for SerifNode { } } +/// # Sans-serif /// Sans-serif font style. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The piece of formula to style. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] @@ -58,14 +60,15 @@ impl Texify for SansNode { } } +/// # Bold /// Bold font style. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The piece of formula to style. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] @@ -87,14 +90,15 @@ impl Texify for BoldNode { } } +/// # Italic /// Italic font style. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The piece of formula to style. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] @@ -116,14 +120,15 @@ impl Texify for ItalNode { } } +/// # Calligraphic /// Calligraphic font style. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The piece of formula to style. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] @@ -145,14 +150,15 @@ impl Texify for CalNode { } } +/// # Fraktur /// Fraktur font style. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The piece of formula to style. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] @@ -174,14 +180,15 @@ impl Texify for FrakNode { } } +/// # Monospace /// Monospace font style. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The piece of formula to style. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] @@ -203,14 +210,15 @@ impl Texify for MonoNode { } } +/// # Doublestruck /// Blackboard bold (double-struck) font style. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The piece of formula to style. /// -/// # Tags -/// - math +/// ## Category +/// math #[func] #[capable(Texify)] #[derive(Debug, Hash)] diff --git a/library/src/meta/document.rs b/library/src/meta/document.rs index c5d4e0503..32e944dc1 100644 --- a/library/src/meta/document.rs +++ b/library/src/meta/document.rs @@ -1,6 +1,7 @@ use crate::layout::{LayoutRoot, PageNode}; use crate::prelude::*; +/// # Document /// The root element of a document and its metadata. /// /// All documents are automatically wrapped in a `document` element. The main @@ -10,8 +11,8 @@ use crate::prelude::*; /// The metadata set with this function is not rendered within the document. /// Instead, it is embedded in the compiled PDF file. /// -/// # Tags -/// - meta +/// ## Category +/// meta #[func] #[capable(LayoutRoot)] #[derive(Hash)] diff --git a/library/src/meta/link.rs b/library/src/meta/link.rs index 3b816083d..501ccddb7 100644 --- a/library/src/meta/link.rs +++ b/library/src/meta/link.rs @@ -1,12 +1,13 @@ use crate::prelude::*; use crate::text::TextNode; +/// # Link /// Link to a URL or another location in the document. /// /// The link function makes its positional `body` argument clickable and links /// it to the destination specified by the `dest` argument. /// -/// # Example +/// ## Example /// ``` /// #show link: underline /// @@ -16,10 +17,10 @@ use crate::text::TextNode; /// ] /// ``` /// -/// # Parameters +/// ## Parameters /// - dest: Destination (positional, required) /// The destination the link points to. -/// +/// /// - To link to web pages, `dest` should be a valid URL string. If the URL is /// in the `mailto:` or `tel:` scheme and the `body` parameter is omitted, /// the email address or phone number will be the link's body, without the @@ -30,7 +31,7 @@ use crate::text::TextNode; /// coordinates of type `length`. Pages are counted from one, and the /// coordinates are relative to the page's top left corner. /// -/// # Example +/// ### Example /// ``` /// #link("mailto:hello@typst.app") \ /// #link((page: 1, x: 0pt, y: 0pt))[ @@ -43,8 +44,8 @@ use crate::text::TextNode; /// The content that should become a link. If `dest` is an URL string, the /// parameter can be omitted. In this case, the URL will be shown as the link. /// -/// # Tags -/// - meta +/// ## Category +/// meta #[func] #[capable(Show, Finalize)] #[derive(Debug, Hash)] diff --git a/library/src/meta/outline.rs b/library/src/meta/outline.rs index 474535266..da73ad5af 100644 --- a/library/src/meta/outline.rs +++ b/library/src/meta/outline.rs @@ -3,13 +3,14 @@ use crate::layout::{BlockNode, HNode, HideNode, RepeatNode, Spacing}; use crate::prelude::*; use crate::text::{LinebreakNode, SpaceNode, TextNode}; -/// Generate a section outline / table of contents. +/// # Outline +/// A section outline / table of contents. /// -/// This function generates a list of all headings in the -/// document, up to a given depth. The [@heading] numbering will be reproduced -/// within the outline. +/// This function generates a list of all headings in the document, up to a +/// given depth. The [heading](@heading) numbering will be reproduced within the +/// outline. /// -/// # Example +/// ## Example /// ``` /// #outline() /// @@ -20,8 +21,8 @@ use crate::text::{LinebreakNode, SpaceNode, TextNode}; /// #lorem(10) /// ``` /// -/// # Tags -/// - meta +/// ## Category +/// meta #[func] #[capable(Prepare, Show)] #[derive(Debug, Hash)] @@ -31,8 +32,8 @@ pub struct OutlineNode; impl OutlineNode { /// The title of the outline. /// - /// - When set to `{auto}`, an appropriate title for the [@text] language will - /// be used. This is the default. + /// - When set to `{auto}`, an appropriate title for the [text](@text) + /// language will be used. This is the default. /// - When set to `{none}`, the outline will not have a title. /// - A custom title can be set by passing content. #[property(referenced)] @@ -44,7 +45,7 @@ impl OutlineNode { /// Whether to indent the subheadings to align the start of their numbering /// with the title of their parents. This will only have an effect if a - /// [@heading] numbering is set. + /// [heading](@heading) numbering is set. /// /// # Example /// ``` @@ -114,7 +115,7 @@ impl Show for OutlineNode { }); seq.push( - HeadingNode { body, level: NonZeroUsize::new(1).unwrap() } + HeadingNode { title: body, level: NonZeroUsize::new(1).unwrap() } .pack() .styled(HeadingNode::NUMBERING, None) .styled(HeadingNode::OUTLINED, false), @@ -175,7 +176,7 @@ impl Show for OutlineNode { }; // Add the numbering and section name. - let start = numbering + heading.body.clone(); + let start = numbering + heading.title.clone(); seq.push(start.linked(Destination::Internal(loc))); // Add filler symbols between the section name and page number. diff --git a/library/src/meta/reference.rs b/library/src/meta/reference.rs index 1a4b22e41..2165ed972 100644 --- a/library/src/meta/reference.rs +++ b/library/src/meta/reference.rs @@ -1,6 +1,7 @@ use crate::prelude::*; use crate::text::TextNode; +/// # Reference /// A reference to a label. /// /// *Note: This function is currently unimplemented.* @@ -10,17 +11,17 @@ use crate::text::TextNode; /// 1" for a reference to the first heading's label. The references are also /// links to the respective labels. /// -/// # Syntax +/// ## Syntax /// This function also has dedicated syntax: A reference to a label can be /// created by typing an `@` followed by the name of the label (e.g. `[= /// Introduction ]` can be referenced by typing `[@intro]`). /// -/// # Parameters +/// ## Parameters /// - target: Label (positional, required) /// The label that should be referenced. /// -/// # Tags -/// - meta +/// ## Category +/// meta #[func] #[capable(Show)] #[derive(Debug, Hash)] diff --git a/library/src/text/deco.rs b/library/src/text/deco.rs index d725697e8..81157bfb7 100644 --- a/library/src/text/deco.rs +++ b/library/src/text/deco.rs @@ -4,14 +4,15 @@ use ttf_parser::{GlyphId, OutlineBuilder}; use super::TextNode; use crate::prelude::*; +/// # Underline /// Typeset underline, stricken-through or overlined text. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The content to decorate. /// -/// # Tags -/// - text +/// ## Category +/// text #[func] #[capable(Show)] #[derive(Debug, Hash)] diff --git a/library/src/text/misc.rs b/library/src/text/misc.rs index 20be156af..df40e52df 100644 --- a/library/src/text/misc.rs +++ b/library/src/text/misc.rs @@ -1,10 +1,11 @@ use super::TextNode; use crate::prelude::*; +/// # Space /// A text space. /// -/// # Tags -/// - text +/// ## Category +/// text #[func] #[capable(Unlabellable, Behave)] #[derive(Debug, Hash)] @@ -25,14 +26,15 @@ impl Behave for SpaceNode { } } +/// # Line Break /// A line break. /// -/// # Parameters +/// ## Parameters /// - justify: bool (named) /// Whether to justify the line before the break. /// -/// # Tags -/// - text +/// ## Category +/// text #[func] #[capable(Behave)] #[derive(Debug, Hash)] @@ -54,14 +56,15 @@ impl Behave for LinebreakNode { } } +/// # Strong Emphasis /// Strongly emphasizes content by increasing the font weight. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The content to strongly emphasize. /// -/// # Tags -/// - text +/// ## Category +/// text #[func] #[capable(Show)] #[derive(Debug, Hash)] @@ -107,14 +110,15 @@ impl Fold for Delta { } } +/// # Emphasis /// Emphasizes content by flipping the italicness. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The content to emphasize. /// -/// # Tags -/// - text +/// ## Category +/// text #[func] #[capable(Show)] #[derive(Debug, Hash)] @@ -152,27 +156,29 @@ impl Fold for Toggle { } } +/// # Lowercase /// Convert text or content to lowercase. /// -/// # Parameters +/// ## Parameters /// - text: ToCase (positional, required) /// The text to convert to lowercase. /// -/// # Tags -/// - text +/// ## Category +/// text #[func] pub fn lower(args: &mut Args) -> SourceResult { case(Case::Lower, args) } +/// # Uppercase /// Convert text or content to uppercase. /// -/// # Parameters +/// ## Parameters /// - text: ToCase (positional, required) /// The text to convert to uppercase. /// -/// # Tags -/// - text +/// ## Category +/// text #[func] pub fn upper(args: &mut Args) -> SourceResult { case(Case::Upper, args) @@ -216,14 +222,15 @@ impl Case { } } +/// # Small Capitals /// Display text in small capitals. /// -/// # Parameters +/// ## Parameters /// - text: Content (positional, required) /// The text to display to small capitals. /// -/// # Tags -/// - text +/// ## Category +/// text #[func] pub fn smallcaps(args: &mut Args) -> SourceResult { let body: Content = args.expect("content")?; diff --git a/library/src/text/mod.rs b/library/src/text/mod.rs index 315de95db..e962685d2 100644 --- a/library/src/text/mod.rs +++ b/library/src/text/mod.rs @@ -25,17 +25,18 @@ use typst::util::EcoString; use crate::layout::ParNode; use crate::prelude::*; +/// # Text /// Stylable text. /// -/// # Parameters +/// ## Parameters /// - family: EcoString (positional, variadic, settable) /// A prioritized sequence of font families. /// /// - body: Content (positional, required) /// Content in which all text is styled according to the other arguments. /// -/// # Tags -/// - text +/// ## Category +/// text #[func] #[capable] #[derive(Clone, Hash)] diff --git a/library/src/text/quotes.rs b/library/src/text/quotes.rs index 4f65c7fd0..5965df561 100644 --- a/library/src/text/quotes.rs +++ b/library/src/text/quotes.rs @@ -2,14 +2,15 @@ use typst::syntax::is_newline; use crate::prelude::*; +/// # Smart Quote /// A smart quote. /// -/// # Parameters +/// ## Parameters /// - double: bool (named) /// Whether to produce a smart double quote. /// -/// # Tags -/// - text +/// ## Category +/// text #[func] #[capable] #[derive(Debug, Hash)] diff --git a/library/src/text/raw.rs b/library/src/text/raw.rs index 55c292989..240f82e8a 100644 --- a/library/src/text/raw.rs +++ b/library/src/text/raw.rs @@ -6,17 +6,18 @@ use super::{FontFamily, Hyphenate, LinebreakNode, TextNode}; use crate::layout::BlockNode; use crate::prelude::*; +/// # Raw Text /// Raw text with optional syntax highlighting. /// -/// # Parameters +/// ## Parameters /// - text: EcoString (positional, required) /// The raw text. /// /// - block: bool (named) /// Whether the raw text is displayed as a separate block. /// -/// # Tags -/// - text +/// ## Category +/// text #[func] #[capable(Show)] #[derive(Debug, Hash)] diff --git a/library/src/text/shift.rs b/library/src/text/shift.rs index 969dce682..5ea3b5eac 100644 --- a/library/src/text/shift.rs +++ b/library/src/text/shift.rs @@ -4,6 +4,7 @@ use typst::util::EcoString; use super::{variant, SpaceNode, TextNode, TextSize}; use crate::prelude::*; +/// # Subscript /// Sub- or superscript text. /// /// The text is rendered smaller and its baseline is raised/lowered. To provide @@ -11,12 +12,12 @@ use crate::prelude::*; /// superscript codepoints. If that fails, we fall back to rendering shrunk /// normal letters in a raised way. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional, required) /// The text to display in sub- or superscript. /// -/// # Tags -/// - text +/// ## Category +/// text #[func] #[capable(Show)] #[derive(Debug, Hash)] diff --git a/library/src/text/symbol.rs b/library/src/text/symbol.rs index ccf1a55e5..ec2653dff 100644 --- a/library/src/text/symbol.rs +++ b/library/src/text/symbol.rs @@ -1,14 +1,15 @@ use crate::prelude::*; use crate::text::TextNode; +/// # Symbol /// A symbol identified by symmie notation. /// -/// # Parameters +/// ## Parameters /// - notation: EcoString (positional, required) /// The symbols symmie notation. /// -/// # Tags -/// - text +/// ## Category +/// text #[func] #[capable(Show)] #[derive(Debug, Hash)] diff --git a/library/src/visualize/image.rs b/library/src/visualize/image.rs index 0ce3f20bd..a3eb32a59 100644 --- a/library/src/visualize/image.rs +++ b/library/src/visualize/image.rs @@ -4,16 +4,17 @@ use typst::image::{Image, ImageFormat, RasterFormat, VectorFormat}; use crate::prelude::*; +/// # Image /// Show a raster or vector graphic. /// /// Supported formats are PNG, JPEG, GIF and SVG. /// -/// # Parameters +/// ## Parameters /// - path: EcoString (positional, required) /// Path to an image file. /// -/// # Tags -/// - visualize +/// ## Category +/// visualize #[func] #[capable(Layout, Inline)] #[derive(Debug, Hash)] diff --git a/library/src/visualize/line.rs b/library/src/visualize/line.rs index 6c60ef4e5..7fed90ce7 100644 --- a/library/src/visualize/line.rs +++ b/library/src/visualize/line.rs @@ -1,10 +1,11 @@ use crate::prelude::*; +/// # Line /// Display a line without affecting the layout. /// /// You should only provide either an endpoint or an angle and a length. /// -/// # Parameters +/// ## Parameters /// - origin: Axes> (named) /// The start point of the line. /// @@ -17,8 +18,8 @@ use crate::prelude::*; /// - angle: Angle (named) /// The angle at which the line points away from the origin. /// -/// # Tags -/// - visualize +/// ## Category +/// visualize #[func] #[capable(Layout, Inline)] #[derive(Debug, Hash)] diff --git a/library/src/visualize/shape.rs b/library/src/visualize/shape.rs index 9c66e8671..5edab70b9 100644 --- a/library/src/visualize/shape.rs +++ b/library/src/visualize/shape.rs @@ -2,9 +2,10 @@ use std::f64::consts::SQRT_2; use crate::prelude::*; +/// # Rectangle /// A sizable and fillable shape with optional content. /// -/// # Parameters +/// ## Parameters /// - body: Content (positional) /// The content to place into the shape. /// @@ -23,8 +24,8 @@ use crate::prelude::*; /// - stroke: Smart>> (named) /// How to stroke the shape. /// -/// # Tags -/// - visualize +/// ## Category +/// visualize #[func] #[capable(Layout, Inline)] #[derive(Debug, Hash)] diff --git a/macros/src/func.rs b/macros/src/func.rs index c830a32f4..73522f0e4 100644 --- a/macros/src/func.rs +++ b/macros/src/func.rs @@ -4,27 +4,34 @@ use super::*; /// Expand the `#[func]` macro. pub fn func(item: syn::Item) -> Result { - let mut docs = match &item { + let docs = match &item { syn::Item::Struct(item) => documentation(&item.attrs), syn::Item::Enum(item) => documentation(&item.attrs), syn::Item::Fn(item) => documentation(&item.attrs), _ => String::new(), }; - let tags = tags(&mut docs); + let first = docs.lines().next().unwrap(); + let display = first.strip_prefix("# ").unwrap(); + let display = display.trim(); + + let mut docs = docs[first.len()..].to_string(); + let example = example(&mut docs, 2); let params = params(&mut docs)?; - let example = quote_option(example(&mut docs)); - let syntax = quote_option(section(&mut docs, "Syntax")); + let syntax = quote_option(section(&mut docs, "Syntax", 2)); + let category = section(&mut docs, "Category", 2).expect("missing category"); + let example = quote_option(example); let docs = docs.trim(); if docs.contains("# ") { - bail!(item, "Documentation heading not recognized"); + bail!(item, "unrecognized heading"); } let info = quote! { ::typst::model::FuncInfo { name, - tags: &[#(#tags),*], + display: #display, + category: #category, docs: #docs, example: #example, syntax: #syntax, @@ -57,7 +64,7 @@ pub fn func(item: syn::Item) -> Result { impl::typst::model::FuncType for #ty { fn create_func(name: &'static str) -> ::typst::model::Func { - ::typst::model::Func::from_fn(name, #full, #info) + ::typst::model::Func::from_fn(#full, #info) } } }) @@ -75,7 +82,7 @@ pub fn func(item: syn::Item) -> Result { impl #params ::typst::model::FuncType for #ident #args #clause { fn create_func(name: &'static str) -> ::typst::model::Func { - ::typst::model::Func::from_node::(name, #info) + ::typst::model::Func::from_node::(#info) } } }) @@ -83,31 +90,27 @@ pub fn func(item: syn::Item) -> Result { } /// Extract a section. -pub fn section(docs: &mut String, title: &str) -> Option { - let needle = format!("\n# {title}\n"); +pub fn section(docs: &mut String, title: &str, level: usize) -> Option { + let hashtags = "#".repeat(level); + let needle = format!("\n{hashtags} {title}\n"); let start = docs.find(&needle)?; let rest = &docs[start..]; - let len = rest[1..].find("\n# ").map(|x| 1 + x).unwrap_or(rest.len()); + let len = rest[1..] + .find("\n# ") + .or(rest[1..].find("\n## ")) + .or(rest[1..].find("\n### ")) + .map(|x| 1 + x) + .unwrap_or(rest.len()); let end = start + len; let section = docs[start + needle.len()..end].trim().to_owned(); docs.replace_range(start..end, ""); Some(section) } -/// Parse the tag section. -fn tags(docs: &mut String) -> Vec { - section(docs, "Tags") - .unwrap_or_default() - .lines() - .filter_map(|line| line.strip_prefix('-')) - .map(|s| s.trim().into()) - .collect() -} - /// Parse the example section. -pub fn example(docs: &mut String) -> Option { +pub fn example(docs: &mut String, level: usize) -> Option { Some( - section(docs, "Example")? + section(docs, "Example", level)? .lines() .skip_while(|line| !line.contains("```")) .skip(1) @@ -119,7 +122,7 @@ pub fn example(docs: &mut String) -> Option { /// Parse the parameter section. fn params(docs: &mut String) -> Result> { - let Some(section) = section(docs, "Parameters") else { return Ok(vec![]) }; + let Some(section) = section(docs, "Parameters", 2) else { return Ok(vec![]) }; let mut s = Scanner::new(§ion); let mut infos = vec![]; @@ -159,7 +162,7 @@ fn params(docs: &mut String) -> Result> { s.expect(')'); let mut docs = dedent(s.eat_until("\n-").trim()); - let example = quote_option(example(&mut docs)); + let example = quote_option(example(&mut docs, 3)); let docs = docs.trim(); infos.push(quote! { diff --git a/macros/src/node.rs b/macros/src/node.rs index 5f9573f95..e8324594a 100644 --- a/macros/src/node.rs +++ b/macros/src/node.rs @@ -337,7 +337,7 @@ fn create_node_properties_func(node: &Node) -> syn::ImplItemMethod { let shorthand = matches!(property.shorthand, Some(Shorthand::Positional)); let mut docs = documentation(&property.attrs); - let example = quote_option(super::func::example(&mut docs)); + let example = quote_option(super::func::example(&mut docs, 1)); let docs = docs.trim(); quote! { diff --git a/src/geom/em.rs b/src/geom/em.rs index 93dc80e4d..9f5aff398 100644 --- a/src/geom/em.rs +++ b/src/geom/em.rs @@ -42,6 +42,11 @@ impl Em { (self.0).0 } + /// The absolute value of this em length. + pub fn abs(self) -> Self { + Self::new(self.get().abs()) + } + /// Convert to an absolute length at the given font size. pub fn at(self, font_size: Abs) -> Abs { let resolved = font_size * self.get(); diff --git a/src/geom/length.rs b/src/geom/length.rs index 230ea48b2..ae615f14e 100644 --- a/src/geom/length.rs +++ b/src/geom/length.rs @@ -18,6 +18,12 @@ impl Length { Self { abs: Abs::zero(), em: Em::zero() } } + /// Try to compute the absolute value of the length. + pub fn try_abs(self) -> Option { + (self.abs.is_zero() || self.em.is_zero()) + .then(|| Self { abs: self.abs.abs(), em: self.em.abs() }) + } + /// Try to divide two lengths. pub fn try_div(self, other: Self) -> Option { if self.abs.is_zero() && other.abs.is_zero() { diff --git a/src/ide/complete.rs b/src/ide/complete.rs index 53d4851f4..27b81fb2a 100644 --- a/src/ide/complete.rs +++ b/src/ide/complete.rs @@ -774,7 +774,7 @@ impl<'a> CompletionContext<'a> { matches!( value, Value::Func(func) if func.info().map_or(false, |info| { - info.tags.contains(&"math") + info.category == "math" }), ) }); @@ -805,7 +805,7 @@ impl<'a> CompletionContext<'a> { !short_form || matches!( value, Value::Func(func) if func.info().map_or(true, |info| { - !info.tags.contains(&"math") + info.category != "math" }), ) }); diff --git a/src/model/eval.rs b/src/model/eval.rs index 91fe61bb8..a9fa2e145 100644 --- a/src/model/eval.rs +++ b/src/model/eval.rs @@ -476,10 +476,7 @@ impl Eval for ast::Frac { type Output = Content; fn eval(&self, vm: &mut Vm) -> SourceResult { - Ok((vm.items.math_frac)( - self.num().eval(vm)?, - self.denom().eval(vm)?, - )) + Ok((vm.items.math_frac)(self.num().eval(vm)?, self.denom().eval(vm)?)) } } @@ -781,11 +778,7 @@ impl Eval for ast::FieldAccess { .field(&field) .ok_or_else(|| format!("unknown field {field:?}")) .at(span)?, - v => bail!( - self.target().span(), - "cannot access field on {}", - v.type_name() - ), + v => bail!(self.target().span(), "cannot access field on {}", v.type_name()), }) } } diff --git a/src/model/func.rs b/src/model/func.rs index fb8b3dd0e..e47594a19 100644 --- a/src/model/func.rs +++ b/src/model/func.rs @@ -37,18 +37,16 @@ impl Func { /// Create a new function from a native rust function. pub fn from_fn( - name: &'static str, func: fn(&Vm, &mut Args) -> SourceResult, info: FuncInfo, ) -> Self { - Self(Arc::new(Repr::Native(Native { name, func, set: None, node: None, info }))) + Self(Arc::new(Repr::Native(Native { func, set: None, node: None, info }))) } /// Create a new function from a native rust node. - pub fn from_node(name: &'static str, mut info: FuncInfo) -> Self { + pub fn from_node(mut info: FuncInfo) -> Self { info.params.extend(T::properties()); Self(Arc::new(Repr::Native(Native { - name, func: |ctx, args| { let styles = T::set(args, true)?; let content = T::construct(ctx, args)?; @@ -68,7 +66,7 @@ impl Func { /// The name of the function. pub fn name(&self) -> Option<&str> { match self.0.as_ref() { - Repr::Native(native) => Some(native.name), + Repr::Native(native) => Some(native.info.name), Repr::Closure(closure) => closure.name.as_deref(), Repr::With(func, _) => func.name(), } @@ -184,8 +182,6 @@ pub trait FuncType { /// A function defined by a native rust function or node. struct Native { - /// The name of the function. - name: &'static str, /// The function pointer. func: fn(&Vm, &mut Args) -> SourceResult, /// The set rule. @@ -198,7 +194,6 @@ struct Native { impl Hash for Native { fn hash(&self, state: &mut H) { - self.name.hash(state); (self.func as usize).hash(state); self.set.map(|set| set as usize).hash(state); self.node.hash(state); @@ -210,8 +205,10 @@ impl Hash for Native { pub struct FuncInfo { /// The function's name. pub name: &'static str, - /// Tags that categorize the function. - pub tags: &'static [&'static str], + /// The display name of the function. + pub display: &'static str, + /// Which category the function is part of. + pub category: &'static str, /// Documentation for the function. pub docs: &'static str, /// The source code of an example, if any. diff --git a/tests/src/tests.rs b/tests/src/tests.rs index 7316ed115..9dc7aa6dd 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -145,6 +145,9 @@ impl Args { } fn library() -> Library { + /// # Test + /// ## Category + /// test #[func] fn test(args: &mut typst::model::Args) -> SourceResult { let lhs = args.expect::("left-hand side")?; @@ -155,6 +158,9 @@ fn library() -> Library { Ok(Value::None) } + /// # Print + /// ## Category + /// test #[func] fn print(args: &mut typst::model::Args) -> SourceResult { print!("> ");