Documentation

This commit is contained in:
Laurenz 2022-12-20 16:08:16 +01:00
parent b8ffd3ad3d
commit f5f7df7247
47 changed files with 707 additions and 394 deletions

View File

@ -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<Content> {
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<Value> {
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<Content> {
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;
}

View File

@ -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)]

View File

@ -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)]

View File

@ -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<Value> {
Ok(args.expect::<ToAbs>("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<Value> {
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<Value> {
minmax(args, Ordering::Greater)
@ -74,43 +100,67 @@ fn minmax(args: &mut Args, goal: Ordering) -> SourceResult<Value> {
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<Value> {
Ok(Value::Bool(args.expect::<i64>("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<Value> {
Ok(Value::Bool(args.expect::<i64>("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<Value> {
let Spanned { v: v1, span: span1 } = args.expect("dividend")?;

View File

@ -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<Value> {
Ok(Value::Int(args.expect::<ToInt>("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<Value> {
Ok(Value::Float(args.expect::<ToFloat>("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<Value> {
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<Value> {
///
/// 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<Value> {
/// - alpha: Component (positional)
/// The alpha component.
///
/// # Tags
/// - create
/// ## Category
/// create
#[func]
pub fn rgb(args: &mut Args) -> SourceResult<Value> {
Ok(Value::Color(if let Some(string) = args.find::<Spanned<EcoString>>()? {
@ -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<Value> {
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(<intro>)
/// ```
///
/// ## 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<Value> {
Ok(Value::Str(args.expect::<ToStr>("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 <a>: set text(blue)
/// #show label("b"): set text(red)
///
/// = Heading <a>
/// *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<Value> {
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<Value> {
let Spanned { v, span } = args.expect::<Spanned<EcoString>>("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<Value> {
/// - step: i64 (named)
/// The distance between the generated numbers.
///
/// # Tags
/// - create
/// ## Category
/// create
#[func]
pub fn range(args: &mut Args) -> SourceResult<Value> {
let first = args.expect::<i64>("end")?;

View File

@ -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<Value> {
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<Value> {
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<Value> {
let Spanned { v: path, span } =

View File

@ -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<Value> {
Ok(args.expect::<Value>("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<Value> {
Ok(args.expect::<Value>("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<Value> {
let Spanned { v, span } = args.expect::<Spanned<bool>>("condition")?;
@ -47,14 +50,15 @@ pub fn assert(args: &mut Args) -> SourceResult<Value> {
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<Value> {
let Spanned { v: text, span } = args.expect::<Spanned<String>>("source")?;

View File

@ -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<Value> {
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<Value> {
let pattern = args.expect::<NumberingPattern>("pattern")?;

View File

@ -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<Option<GenAlign>> (positional, settable)
/// The alignment along both axes.
///
/// # Tags
/// - layout
/// ## Category
/// layout
#[func]
#[capable]
#[derive(Debug, Hash)]

View File

@ -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)]

View File

@ -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<Length> (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)]

View File

@ -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)]

View File

@ -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)]

View File

@ -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<Length> (named)
/// The padding for all sides. All other parameters take precedence over this.
///
/// # Tags
/// - layout
/// ## Category
/// layout
#[func]
#[capable(Layout)]
#[derive(Debug, Hash)]

View File

@ -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)]

View File

@ -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)]

View File

@ -1,8 +1,9 @@
use crate::prelude::*;
/// # Place
/// Place content at an absolute position.
///
/// # Parameters
/// ## Parameters
/// - alignment: Axes<Option<GenAlign>> (positional)
/// Relative to which position in the parent container to place the content.
///
@ -15,8 +16,8 @@ use crate::prelude::*;
/// - dy: Rel<Length> (named)
/// The vertical displacement of the placed content.
///
/// # Tags
/// - layout
/// ## Category
/// layout
#[func]
#[capable(Layout, Behave)]
#[derive(Debug, Hash)]

View File

@ -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)]

View File

@ -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)]

View File

@ -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)]

View File

@ -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<Length> (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<const T: TransformKind> {
/// 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<ROTATE>;
/// Scale content without affecting layout.
pub type ScaleNode = TransformNode<SCALE>;
#[node]
impl<const T: TransformKind> TransformNode<T> {
/// The origin of the transformation.
impl RotateNode {
/// The origin of the rotation.
#[property(resolve)]
pub const ORIGIN: Axes<Option<GenAlign>> = Axes::default();
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
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<const T: TransformKind> Layout for TransformNode<T> {
impl Layout for RotateNode {
fn layout(
&self,
vt: &mut Vt,
@ -127,7 +108,7 @@ impl<const T: TransformKind> Layout for TransformNode<T> {
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<const T: TransformKind> Layout for TransformNode<T> {
}
}
impl<const T: TransformKind> Inline for TransformNode<T> {}
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<Ratio>,
/// 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<Option<GenAlign>> = Axes::default();
/// A scale transformation.
const SCALE: TransformKind = 2;
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
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<Fragment> {
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 {}

View File

@ -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| {

View File

@ -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)]

View File

@ -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)]

View File

@ -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)]

View File

@ -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)]

View File

@ -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)]

View File

@ -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.

View File

@ -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 <intro>]` 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)]

View File

@ -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)]

View File

@ -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<Value> {
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<Value> {
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<Value> {
let body: Content = args.expect("content")?;

View File

@ -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)]

View File

@ -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)]

View File

@ -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)]

View File

@ -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)]

View File

@ -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)]

View File

@ -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)]

View File

@ -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<Rel<Length>> (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)]

View File

@ -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<Sides<Option<PartialStroke>>> (named)
/// How to stroke the shape.
///
/// # Tags
/// - visualize
/// ## Category
/// visualize
#[func]
#[capable(Layout, Inline)]
#[derive(Debug, Hash)]

View File

@ -4,27 +4,34 @@ use super::*;
/// Expand the `#[func]` macro.
pub fn func(item: syn::Item) -> Result<TokenStream> {
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<TokenStream> {
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<TokenStream> {
impl #params ::typst::model::FuncType for #ident #args #clause {
fn create_func(name: &'static str) -> ::typst::model::Func {
::typst::model::Func::from_node::<Self>(name, #info)
::typst::model::Func::from_node::<Self>(#info)
}
}
})
@ -83,31 +90,27 @@ pub fn func(item: syn::Item) -> Result<TokenStream> {
}
/// Extract a section.
pub fn section(docs: &mut String, title: &str) -> Option<String> {
let needle = format!("\n# {title}\n");
pub fn section(docs: &mut String, title: &str, level: usize) -> Option<String> {
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<String> {
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<String> {
pub fn example(docs: &mut String, level: usize) -> Option<String> {
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<String> {
/// Parse the parameter section.
fn params(docs: &mut String) -> Result<Vec<TokenStream>> {
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(&section);
let mut infos = vec![];
@ -159,7 +162,7 @@ fn params(docs: &mut String) -> Result<Vec<TokenStream>> {
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! {

View File

@ -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! {

View File

@ -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();

View File

@ -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> {
(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<f64> {
if self.abs.is_zero() && other.abs.is_zero() {

View File

@ -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"
}),
)
});

View File

@ -476,10 +476,7 @@ impl Eval for ast::Frac {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
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()),
})
}
}

View File

@ -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<Value>,
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<T: Node>(name: &'static str, mut info: FuncInfo) -> Self {
pub fn from_node<T: 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<Value>,
/// The set rule.
@ -198,7 +194,6 @@ struct Native {
impl Hash for Native {
fn hash<H: Hasher>(&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.

View File

@ -145,6 +145,9 @@ impl Args {
}
fn library() -> Library {
/// # Test
/// ## Category
/// test
#[func]
fn test(args: &mut typst::model::Args) -> SourceResult<Value> {
let lhs = args.expect::<Value>("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<Value> {
print!("> ");