summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--library/src/basics/heading.rs9
-rw-r--r--library/src/basics/list.rs12
-rw-r--r--library/src/basics/table.rs17
-rw-r--r--library/src/compute/calc.rs88
-rw-r--r--library/src/compute/create.rs176
-rw-r--r--library/src/compute/data.rs21
-rw-r--r--library/src/compute/foundations.rs28
-rw-r--r--library/src/compute/utility.rs20
-rw-r--r--library/src/layout/align.rs9
-rw-r--r--library/src/layout/columns.rs23
-rw-r--r--library/src/layout/container.rs31
-rw-r--r--library/src/layout/grid.rs17
-rw-r--r--library/src/layout/hide.rs7
-rw-r--r--library/src/layout/pad.rs21
-rw-r--r--library/src/layout/page.rs26
-rw-r--r--library/src/layout/par.rs10
-rw-r--r--library/src/layout/place.rs13
-rw-r--r--library/src/layout/repeat.rs7
-rw-r--r--library/src/layout/spacing.rs29
-rw-r--r--library/src/layout/stack.rs11
-rw-r--r--library/src/layout/transform.rs24
-rw-r--r--library/src/lib.rs3
-rw-r--r--library/src/math/matrix.rs14
-rw-r--r--library/src/math/mod.rs115
-rw-r--r--library/src/math/style.rs56
-rw-r--r--library/src/meta/document.rs3
-rw-r--r--library/src/meta/link.rs9
-rw-r--r--library/src/meta/outline.rs3
-rw-r--r--library/src/meta/reference.rs7
-rw-r--r--library/src/text/deco.rs7
-rw-r--r--library/src/text/misc.rs58
-rw-r--r--library/src/text/mod.rs11
-rw-r--r--library/src/text/quotes.rs7
-rw-r--r--library/src/text/raw.rs11
-rw-r--r--library/src/text/shift.rs17
-rw-r--r--library/src/text/symbol.rs7
-rw-r--r--library/src/visualize/image.rs9
-rw-r--r--library/src/visualize/line.rs15
-rw-r--r--library/src/visualize/shape.rs21
-rw-r--r--macros/Cargo.toml1
-rw-r--r--macros/src/castable.rs2
-rw-r--r--macros/src/func.rs122
-rw-r--r--macros/src/lib.rs11
-rw-r--r--macros/src/node.rs47
-rw-r--r--src/ide/complete.rs34
-rw-r--r--src/model/cast.rs2
-rw-r--r--src/model/eval.rs2
-rw-r--r--src/model/func.rs17
-rw-r--r--src/model/library.rs4
-rw-r--r--src/syntax/ast.rs15
-rw-r--r--tests/typ/compute/calc.typ18
-rw-r--r--tests/typ/compute/create.typ2
53 files changed, 980 insertions, 270 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 8d21d48c..d8061d8b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1221,6 +1221,7 @@ dependencies = [
"proc-macro2",
"quote",
"syn",
+ "unscanny",
]
[[package]]
diff --git a/library/src/basics/heading.rs b/library/src/basics/heading.rs
index 60415697..2781034e 100644
--- a/library/src/basics/heading.rs
+++ b/library/src/basics/heading.rs
@@ -7,7 +7,14 @@ use crate::text::{SpaceNode, TextNode, TextSize};
/// A section heading.
///
-/// Tags: basics.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The heading's contents.
+/// - level: NonZeroUsize (named)
+/// The logical nesting depth of the heading, starting from one.
+///
+/// # Tags
+/// - basics
#[func]
#[capable(Prepare, Show, Finalize)]
#[derive(Debug, Hash)]
diff --git a/library/src/basics/list.rs b/library/src/basics/list.rs
index 4c016128..c9215a50 100644
--- a/library/src/basics/list.rs
+++ b/library/src/basics/list.rs
@@ -5,7 +5,17 @@ use crate::text::{SpaceNode, TextNode};
/// An unordered (bulleted) or ordered (numbered) list.
///
-/// Tags: basics.
+/// # Parameters
+/// - items: Content (positional, variadic)
+/// The contents of the list items.
+/// - start: NonZeroUsize (named)
+/// Which number to start the enumeration with.
+/// - tight: bool (named)
+/// Makes the list more compact, if enabled. This looks better if the items
+/// fit into a single line each.
+///
+/// # Tags
+/// - basics
#[func]
#[capable(Layout)]
#[derive(Debug, Hash)]
diff --git a/library/src/basics/table.rs b/library/src/basics/table.rs
index 10a9143f..d7e2e08f 100644
--- a/library/src/basics/table.rs
+++ b/library/src/basics/table.rs
@@ -3,7 +3,22 @@ use crate::prelude::*;
/// A table of items.
///
-/// Tags: basics.
+/// # Parameters
+/// - cells: Content (positional, variadic)
+/// The contents of the table cells.
+/// - rows: TrackSizings (named)
+/// Defines the row sizes.
+/// - columns: TrackSizings (named)
+/// Defines the column sizes.
+/// - gutter: TrackSizings (named)
+/// Defines the gaps between rows & columns.
+/// - column-gutter: TrackSizings (named)
+/// Defines the gaps between columns. Takes precedence over `gutter`.
+/// - row-gutter: TrackSizings (named)
+/// Defines the gaps between rows. Takes precedence over `gutter`.
+///
+/// # Tags
+/// - basics
#[func]
#[capable(Layout)]
#[derive(Debug, Hash)]
diff --git a/library/src/compute/calc.rs b/library/src/compute/calc.rs
index 62d0a419..eccc4531 100644
--- a/library/src/compute/calc.rs
+++ b/library/src/compute/calc.rs
@@ -4,26 +4,37 @@ use crate::prelude::*;
/// The absolute value of a numeric value.
///
-/// Tags: calculate.
+/// # Parameters
+/// - value: ToAbs (positional, required)
+/// The value whose absolute value to calculate.
+///
+/// # Tags
+/// - calculate
#[func]
pub fn abs(args: &mut Args) -> SourceResult<Value> {
- let Spanned { v, span } = args.expect("numeric value")?;
- Ok(match v {
- Value::Int(v) => Value::Int(v.abs()),
- Value::Float(v) => Value::Float(v.abs()),
- Value::Angle(v) => Value::Angle(v.abs()),
- Value::Ratio(v) => Value::Ratio(v.abs()),
- Value::Fraction(v) => Value::Fraction(v.abs()),
- Value::Length(_) | Value::Relative(_) => {
- bail!(span, "cannot take absolute value of a length")
- }
- v => bail!(span, "expected numeric value, found {}", v.type_name()),
- })
+ Ok(args.expect::<ToAbs>("value")?.0)
+}
+
+/// A value of which the absolute value can be taken.
+struct ToAbs(Value);
+
+castable! {
+ ToAbs,
+ v: i64 => Self(Value::Int(v.abs())),
+ v: f64 => Self(Value::Float(v.abs())),
+ v: Angle => Self(Value::Angle(v.abs())),
+ v: Ratio => Self(Value::Ratio(v.abs())),
+ v: Fr => Self(Value::Fraction(v.abs())),
}
/// The minimum of a sequence of values.
///
-/// Tags: calculate.
+/// # Parameters
+/// - values: Value (positional, variadic)
+/// The sequence of values.
+///
+/// # Tags
+/// - calculate
#[func]
pub fn min(args: &mut Args) -> SourceResult<Value> {
minmax(args, Ordering::Less)
@@ -31,7 +42,12 @@ pub fn min(args: &mut Args) -> SourceResult<Value> {
/// The maximum of a sequence of values.
///
-/// Tags: calculate.
+/// # Parameters
+/// - values: Value (positional, variadic)
+/// The sequence of values.
+///
+/// # Tags
+/// - calculate
#[func]
pub fn max(args: &mut Args) -> SourceResult<Value> {
minmax(args, Ordering::Greater)
@@ -60,27 +76,44 @@ fn minmax(args: &mut Args, goal: Ordering) -> SourceResult<Value> {
/// Whether an integer is even.
///
-/// Tags: calculate.
+/// # Parameters
+/// - value: i64 (positional, required)
+/// The number to check for evenness.
+///
+/// # Tags
+/// - calculate
#[func]
pub fn even(args: &mut Args) -> SourceResult<Value> {
- Ok(Value::Bool(args.expect::<i64>("integer")? % 2 == 0))
+ Ok(Value::Bool(args.expect::<i64>("value")? % 2 == 0))
}
/// Whether an integer is odd.
///
-/// Tags: calculate.
+/// # Parameters
+/// - value: i64 (positional, required)
+/// The number to check for oddness.
+///
+/// # Tags
+/// - calculate
#[func]
pub fn odd(args: &mut Args) -> SourceResult<Value> {
- Ok(Value::Bool(args.expect::<i64>("integer")? % 2 != 0))
+ Ok(Value::Bool(args.expect::<i64>("value")? % 2 != 0))
}
-/// The modulo of two numbers.
+/// The modulus of two numbers.
+///
+/// # Parameters
+/// - dividend: ToMod (positional, required)
+/// The dividend of the modulus.
+/// - divisor: ToMod (positional, required)
+/// The divisor of the modulus.
///
-/// Tags: calculate.
+/// # Tags
+/// - calculate
#[func]
pub fn mod_(args: &mut Args) -> SourceResult<Value> {
- let Spanned { v: v1, span: span1 } = args.expect("integer or float")?;
- let Spanned { v: v2, span: span2 } = args.expect("integer or float")?;
+ let Spanned { v: v1, span: span1 } = args.expect("dividend")?;
+ let Spanned { v: v2, span: span2 } = args.expect("divisor")?;
let (a, b) = match (v1, v2) {
(Value::Int(a), Value::Int(b)) => match a.checked_rem(b) {
@@ -104,3 +137,12 @@ pub fn mod_(args: &mut Args) -> SourceResult<Value> {
Ok(Value::Float(a % b))
}
+
+/// A value which can be passed to the `mod` function.
+struct ToMod;
+
+castable! {
+ ToMod,
+ _: i64 => Self,
+ _: f64 => Self,
+}
diff --git a/library/src/compute/create.rs b/library/src/compute/create.rs
index a0eecfb8..e3733d60 100644
--- a/library/src/compute/create.rs
+++ b/library/src/compute/create.rs
@@ -6,42 +6,60 @@ use crate::prelude::*;
/// Convert a value to an integer.
///
-/// Tags: create.
+/// # Parameters
+/// - value: ToInt (positional, required)
+/// The value that should be converted to an integer.
+///
+/// # Tags
+/// - create
#[func]
pub fn int(args: &mut Args) -> SourceResult<Value> {
- let Spanned { v, span } = args.expect("value")?;
- Ok(Value::Int(match v {
- Value::Bool(v) => v as i64,
- Value::Int(v) => v,
- Value::Float(v) => v as i64,
- Value::Str(v) => match v.parse() {
- Ok(v) => v,
- Err(_) => bail!(span, "invalid integer"),
- },
- v => bail!(span, "cannot convert {} to integer", v.type_name()),
- }))
+ Ok(Value::Int(args.expect::<ToInt>("value")?.0))
+}
+
+/// A value that can be cast to an integer.
+struct ToInt(i64);
+
+castable! {
+ ToInt,
+ v: bool => Self(v as i64),
+ v: i64 => Self(v),
+ v: f64 => Self(v as i64),
+ v: EcoString => Self(v.parse().map_err(|_| "not a valid integer")?),
}
/// Convert a value to a float.
///
-/// Tags: create.
+/// # Parameters
+/// - value: ToFloat (positional, required)
+/// The value that should be converted to a float.
+///
+/// # Tags
+/// - create
#[func]
pub fn float(args: &mut Args) -> SourceResult<Value> {
- let Spanned { v, span } = args.expect("value")?;
- Ok(Value::Float(match v {
- Value::Int(v) => v as f64,
- Value::Float(v) => v,
- Value::Str(v) => match v.parse() {
- Ok(v) => v,
- Err(_) => bail!(span, "invalid float"),
- },
- v => bail!(span, "cannot convert {} to float", v.type_name()),
- }))
+ Ok(Value::Float(args.expect::<ToFloat>("value")?.0))
+}
+
+/// A value that can be cast to a float.
+struct ToFloat(f64);
+
+castable! {
+ ToFloat,
+ v: bool => Self(v as i64 as f64),
+ v: i64 => Self(v as f64),
+ v: f64 => Self(v),
+ v: EcoString => Self(v.parse().map_err(|_| "not a valid float")?),
}
/// Create a grayscale color.
///
-/// Tags: create.
+/// # Parameters
+/// - gray: Component (positional, required)
+/// The gray component.
+///
+/// # Tags
+/// - create
#[func]
pub fn luma(args: &mut Args) -> SourceResult<Value> {
let Component(luma) = args.expect("gray component")?;
@@ -50,7 +68,26 @@ pub fn luma(args: &mut Args) -> SourceResult<Value> {
/// Create an RGB(A) color.
///
-/// Tags: create.
+/// # Parameters
+/// - hex: EcoString (positional)
+/// The color in hexademical notation.
+///
+/// Accepts three, four, six or eight hexadecimal digits and optionally
+/// a leading hashtag.
+///
+/// If this string is given, the individual components should not be given.
+///
+/// - red: Component (positional)
+/// The red component.
+/// - green: Component (positional)
+/// The green component.
+/// - blue: Component (positional)
+/// The blue component.
+/// - alpha: Component (positional)
+/// The alpha component.
+///
+/// # Tags
+/// - create
#[func]
pub fn rgb(args: &mut Args) -> SourceResult<Value> {
Ok(Value::Color(if let Some(string) = args.find::<Spanned<EcoString>>()? {
@@ -67,18 +104,6 @@ pub fn rgb(args: &mut Args) -> SourceResult<Value> {
}))
}
-/// Create a CMYK color.
-///
-/// Tags: create.
-#[func]
-pub fn cmyk(args: &mut Args) -> SourceResult<Value> {
- let RatioComponent(c) = args.expect("cyan component")?;
- let RatioComponent(m) = args.expect("magenta component")?;
- let RatioComponent(y) = args.expect("yellow component")?;
- let RatioComponent(k) = args.expect("key component")?;
- Ok(Value::Color(CmykColor::new(c, m, y, k).into()))
-}
-
/// An integer or ratio component.
struct Component(u8);
@@ -95,6 +120,29 @@ castable! {
},
}
+/// Create a CMYK color.
+///
+/// # Parameters
+/// - cyan: RatioComponent (positional, required)
+/// The cyan component.
+/// - magenta: RatioComponent (positional, required)
+/// The magenta component.
+/// - yellow: RatioComponent (positional, required)
+/// The yellow component.
+/// - key: RatioComponent (positional, required)
+/// The key component.
+///
+/// # Tags
+/// - create
+#[func]
+pub fn cmyk(args: &mut Args) -> SourceResult<Value> {
+ let RatioComponent(c) = args.expect("cyan component")?;
+ let RatioComponent(m) = args.expect("magenta component")?;
+ let RatioComponent(y) = args.expect("yellow component")?;
+ let RatioComponent(k) = args.expect("key component")?;
+ Ok(Value::Color(CmykColor::new(c, m, y, k).into()))
+}
+
/// A component that must be a ratio.
struct RatioComponent(u8);
@@ -109,22 +157,36 @@ castable! {
/// Convert a value to a string.
///
-/// Tags: create.
+/// # Parameters
+/// - value: ToStr (positional, required)
+/// The value that should be converted to a string.
+///
+/// # Tags
+/// - create
#[func]
pub fn str(args: &mut Args) -> SourceResult<Value> {
- let Spanned { v, span } = args.expect("value")?;
- Ok(Value::Str(match v {
- Value::Int(v) => format_str!("{}", v),
- Value::Float(v) => format_str!("{}", v),
- Value::Label(label) => label.0.into(),
- Value::Str(v) => v,
- v => bail!(span, "cannot convert {} to string", v.type_name()),
- }))
+ Ok(Value::Str(args.expect::<ToStr>("value")?.0))
+}
+
+/// A value that can be cast to a string.
+struct ToStr(Str);
+
+castable! {
+ ToStr,
+ v: i64 => Self(format_str!("{}", v)),
+ v: f64 => Self(format_str!("{}", v)),
+ v: Label => Self(v.0.into()),
+ v: Str => Self(v),
}
/// Create a label from a string.
///
-/// Tags: create.
+/// # Parameters
+/// - name: EcoString (positional, required)
+/// The name of the label.
+///
+/// # Tags
+/// - create
#[func]
pub fn label(args: &mut Args) -> SourceResult<Value> {
Ok(Value::Label(Label(args.expect("string")?)))
@@ -132,7 +194,12 @@ pub fn label(args: &mut Args) -> SourceResult<Value> {
/// Create a regular expression from a string.
///
-/// Tags: create.
+/// # Parameters
+/// - regex: EcoString (positional, required)
+/// The regular expression.
+///
+/// # Tags
+/// - create
#[func]
pub fn regex(args: &mut Args) -> SourceResult<Value> {
let Spanned { v, span } = args.expect::<Spanned<EcoString>>("regular expression")?;
@@ -141,7 +208,18 @@ pub fn regex(args: &mut Args) -> SourceResult<Value> {
/// Create an array consisting of a sequence of numbers.
///
-/// Tags: create.
+/// # Parameters
+/// - start: i64 (positional)
+/// The start of the range (inclusive).
+///
+/// - end: i64 (positional, required)
+/// The end of the range (exclusive).
+///
+/// - step: i64 (named)
+/// The distance between the generated numbers.
+///
+/// # Tags
+/// - create
#[func]
pub fn range(args: &mut Args) -> SourceResult<Value> {
let first = args.expect::<i64>("end")?;
diff --git a/library/src/compute/data.rs b/library/src/compute/data.rs
index af545304..5de3eb61 100644
--- a/library/src/compute/data.rs
+++ b/library/src/compute/data.rs
@@ -6,7 +6,12 @@ use crate::prelude::*;
/// Read structured data from a CSV file.
///
-/// Tags: data-loading.
+/// # Parameters
+/// - path: EcoString (positional, required)
+/// Path to a CSV file.
+///
+/// # Tags
+/// - data-loading
#[func]
pub fn csv(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v: path, span } =
@@ -49,7 +54,12 @@ fn format_csv_error(error: csv::Error) -> String {
/// Read structured data from a JSON file.
///
-/// Tags: data-loading.
+/// # Parameters
+/// - path: EcoString (positional, required)
+/// Path to a JSON file.
+///
+/// # Tags
+/// - data-loading
#[func]
pub fn json(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v: path, span } =
@@ -92,7 +102,12 @@ fn format_json_error(error: serde_json::Error) -> String {
/// Read structured data from an XML file.
///
-/// Tags: data-loading.
+/// # Parameters
+/// - path: EcoString (positional, required)
+/// Path to an XML file.
+///
+/// # Tags
+/// - data-loading
#[func]
pub fn xml(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v: path, span } =
diff --git a/library/src/compute/foundations.rs b/library/src/compute/foundations.rs
index 22d26553..cb952d81 100644
--- a/library/src/compute/foundations.rs
+++ b/library/src/compute/foundations.rs
@@ -6,7 +6,12 @@ use typst::syntax::Source;
/// The name of a value's type.
///
-/// Tags: foundations.
+/// # Parameters
+/// - value: Value (positional, required)
+/// The value whose type's to determine.
+///
+/// # Tags
+/// - foundations
#[func]
pub fn type_(args: &mut Args) -> SourceResult<Value> {
Ok(args.expect::<Value>("value")?.type_name().into())
@@ -14,7 +19,12 @@ pub fn type_(args: &mut Args) -> SourceResult<Value> {
/// The string representation of a value.
///
-/// Tags: foundations.
+/// # Parameters
+/// - value: Value (positional, required)
+/// The value whose string representation to produce.
+///
+/// # Tags
+/// - foundations
#[func]
pub fn repr(args: &mut Args) -> SourceResult<Value> {
Ok(args.expect::<Value>("value")?.repr().into())
@@ -22,7 +32,12 @@ pub fn repr(args: &mut Args) -> SourceResult<Value> {
/// Ensure that a condition is fulfilled.
///
-/// Tags: foundations.
+/// # Parameters
+/// - condition: bool (positional, required)
+/// The condition that must be true for the assertion to pass.
+///
+/// # Tags
+/// - foundations
#[func]
pub fn assert(args: &mut Args) -> SourceResult<Value> {
let Spanned { v, span } = args.expect::<Spanned<bool>>("condition")?;
@@ -34,7 +49,12 @@ pub fn assert(args: &mut Args) -> SourceResult<Value> {
/// Evaluate a string as Typst markup.
///
-/// Tags: foundations.
+/// # Parameters
+/// - source: String (positional, required)
+/// A string of Typst markup to evaluate.
+///
+/// # Tags
+/// - foundations
#[func]
pub fn eval(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v: text, span } = args.expect::<Spanned<String>>("source")?;
diff --git a/library/src/compute/utility.rs b/library/src/compute/utility.rs
index 5a6534f4..bdad9618 100644
--- a/library/src/compute/utility.rs
+++ b/library/src/compute/utility.rs
@@ -3,18 +3,30 @@ use std::str::FromStr;
use crate::prelude::*;
use crate::text::Case;
-/// Create a blind text string.
+/// Create blind text.
///
-/// Tags: utility.
+/// # Parameters
+/// - words: usize (positional, required)
+/// The length of the blind text in words.
+///
+/// # Tags
+/// - 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()))
}
-/// Apply a numbering pattern to a number.
+/// Apply a numbering pattern to a sequence of numbers.
+///
+/// # 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.
+/// # Tags
+/// - utility
#[func]
pub fn numbering(args: &mut Args) -> SourceResult<Value> {
let pattern = args.expect::<NumberingPattern>("pattern")?;
diff --git a/library/src/layout/align.rs b/library/src/layout/align.rs
index f00aeaf2..7853d84d 100644
--- a/library/src/layout/align.rs
+++ b/library/src/layout/align.rs
@@ -2,7 +2,14 @@ use crate::prelude::*;
/// Align content horizontally and vertically.
///
-/// Tags: layout.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The content to align.
+/// - alignment: Axes<Option<GenAlign>> (positional, settable)
+/// The alignment along both axes.
+///
+/// # Tags
+/// - layout
#[func]
#[capable]
#[derive(Debug, Hash)]
diff --git a/library/src/layout/columns.rs b/library/src/layout/columns.rs
index 3bbf56e4..b31b0a6d 100644
--- a/library/src/layout/columns.rs
+++ b/library/src/layout/columns.rs
@@ -3,13 +3,20 @@ use crate::text::TextNode;
/// Separate a region into multiple equally sized columns.
///
-/// Tags: layout.
+/// # Parameters
+/// - count: usize (positional, required)
+/// The number of columns.
+/// - body: Content (positional, required)
+/// The content that should be layouted into the columns.
+///
+/// # Tags
+/// - layout
#[func]
#[capable(Layout)]
#[derive(Debug, Hash)]
pub struct ColumnsNode {
/// How many columns there should be.
- pub columns: NonZeroUsize,
+ pub count: NonZeroUsize,
/// The child to be layouted into the columns. Most likely, this should be a
/// flow or stack node.
pub body: Content,
@@ -23,7 +30,7 @@ impl ColumnsNode {
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self {
- columns: args.expect("column count")?,
+ count: args.expect("column count")?,
body: args.expect("body")?,
}
.pack())
@@ -44,7 +51,7 @@ impl Layout for ColumnsNode {
}
// Determine the width of the gutter and each column.
- let columns = self.columns.get();
+ let columns = self.count.get();
let gutter = styles.get(Self::GUTTER).relative_to(regions.base.x);
let width = (regions.first.x - gutter * (columns - 1) as f64) / columns as f64;
@@ -106,7 +113,13 @@ impl Layout for ColumnsNode {
/// A column break.
///
-/// Tags: layout.
+/// # Parameters
+/// - weak: bool (named)
+/// If true, the column break is skipped if the current column is already
+/// empty.
+///
+/// # Tags
+/// - layout
#[func]
#[capable(Behave)]
#[derive(Debug, Hash)]
diff --git a/library/src/layout/container.rs b/library/src/layout/container.rs
index d451bccf..4b58434b 100644
--- a/library/src/layout/container.rs
+++ b/library/src/layout/container.rs
@@ -1,9 +1,19 @@
use super::VNode;
+use crate::layout::Spacing;
use crate::prelude::*;
/// An inline-level container that sizes content.
///
-/// Tags: layout.
+/// # Parameters
+/// - body: Content (positional)
+/// The contents of the box.
+/// - width: Rel<Length> (named)
+/// The width of the box.
+/// - height: Rel<Length> (named)
+/// The height of the box.
+///
+/// # Tags
+/// - layout
#[func]
#[capable(Layout, Inline)]
#[derive(Debug, Hash)]
@@ -65,7 +75,20 @@ impl Inline for BoxNode {}
/// A block-level container that places content into a separate flow.
///
-/// Tags: layout.
+/// # Parameters
+/// - body: Content (positional)
+/// The contents of the block.
+/// - spacing: Spacing (named, settable)
+/// The spacing around this block.
+/// - above: Spacing (named, settable)
+/// The spacing between the previous and this block. Takes precedence over
+/// `spacing`.
+/// - below: Spacing (named, settable)
+/// The spacing between this block and the following one. Takes precedence
+/// over `spacing`.
+///
+/// # Tags
+/// - layout
#[func]
#[capable(Layout)]
#[derive(Debug, Hash)]
@@ -74,10 +97,10 @@ pub struct BlockNode(pub Content);
#[node]
impl BlockNode {
/// The spacing between the previous and this block.
- #[property(reflect, skip)]
+ #[property(skip)]
pub const ABOVE: VNode = VNode::block_spacing(Em::new(1.2).into());
/// The spacing between this and the following block.
- #[property(reflect, skip)]
+ #[property(skip)]
pub const BELOW: VNode = VNode::block_spacing(Em::new(1.2).into());
/// Whether this block must stick to the following one.
#[property(skip)]
diff --git a/library/src/layout/grid.rs b/library/src/layout/grid.rs
index e70210c0..8aaffe08 100644
--- a/library/src/layout/grid.rs
+++ b/library/src/layout/grid.rs
@@ -4,7 +4,22 @@ use super::Spacing;
/// Arrange content in a grid.
///
-/// Tags: layout.
+/// # Parameters
+/// - cells: Content (positional, variadic)
+/// The contents of the table cells.
+/// - rows: TrackSizings (named)
+/// Defines the row sizes.
+/// - columns: TrackSizings (named)
+/// Defines the column sizes.
+/// - gutter: TrackSizings (named)
+/// Defines the gaps between rows & columns.
+/// - column-gutter: TrackSizings (named)
+/// Defines the gaps between columns. Takes precedence over `gutter`.
+/// - row-gutter: TrackSizings (named)
+/// Defines the gaps between rows. Takes precedence over `gutter`.
+///
+/// # Tags
+/// - layout
#[func]
#[capable(Layout)]
#[derive(Debug, Hash)]
diff --git a/library/src/layout/hide.rs b/library/src/layout/hide.rs
index 4e70dca9..fc9150c1 100644
--- a/library/src/layout/hide.rs
+++ b/library/src/layout/hide.rs
@@ -2,7 +2,12 @@ use crate::prelude::*;
/// Hide content without affecting layout.
///
-/// Tags: layout.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The content to hide.
+///
+/// # Tags
+/// - layout
#[func]
#[capable(Layout, Inline)]
#[derive(Debug, Hash)]
diff --git a/library/src/layout/pad.rs b/library/src/layout/pad.rs
index 9d882ba4..94492f83 100644
--- a/library/src/layout/pad.rs
+++ b/library/src/layout/pad.rs
@@ -2,7 +2,26 @@ use crate::prelude::*;
/// Pad content at the sides.
///
-/// Tags: layout.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The content to pad at the sides.
+/// - left: Rel<Length> (named)
+/// The padding at the left side.
+/// - right: Rel<Length> (named)
+/// The padding at the right side.
+/// - top: Rel<Length> (named)
+/// The padding at the top side.
+/// - bottom: Rel<Length> (named)
+/// The padding at the bottom side.
+/// - x: Rel<Length> (named)
+/// The horizontal padding. Both `left` and `right` take precedence over this.
+/// - y: Rel<Length> (named)
+/// The vertical padding. Both `top` and `bottom` take precedence over this.
+/// - rest: Rel<Length> (named)
+/// The padding for all sides. All other parameters take precedence over this.
+///
+/// # Tags
+/// - layout
#[func]
#[capable(Layout)]
#[derive(Debug, Hash)]
diff --git a/library/src/layout/page.rs b/library/src/layout/page.rs
index fe83137e..8782ed08 100644
--- a/library/src/layout/page.rs
+++ b/library/src/layout/page.rs
@@ -6,7 +6,14 @@ use crate::text::TextNode;
/// Layouts its child onto one or multiple pages.
///
-/// Tags: layout.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The contents of the page(s).
+/// - paper: Paper (positional, settable)
+/// The paper size.
+///
+/// # Tags
+/// - layout
#[func]
#[capable]
#[derive(Clone, Hash)]
@@ -14,9 +21,6 @@ pub struct PageNode(pub Content);
#[node]
impl PageNode {
- /// The paper size.
- #[property(reflect, skip, shorthand)]
- pub const PAPER: Paper = Paper::A4;
/// The unflipped width of the page.
#[property(resolve)]
pub const WIDTH: Smart<Length> = Smart::Custom(Paper::A4.width().into());
@@ -91,7 +95,7 @@ impl PageNode {
// Realize columns.
let columns = styles.get(Self::COLUMNS);
if columns.get() > 1 {
- child = ColumnsNode { columns, body: self.0.clone() }.pack();
+ child = ColumnsNode { count: columns, body: self.0.clone() }.pack();
}
// Realize margins.
@@ -151,7 +155,12 @@ impl Debug for PageNode {
/// A page break.
///
-/// Tags: layout.
+/// # Parameters
+/// - weak: bool (named)
+/// If true, the page break is skipped if the current page is already empty.
+///
+/// # Tags
+/// - layout
#[func]
#[capable]
#[derive(Debug, Copy, Clone, Hash)]
@@ -266,7 +275,10 @@ macro_rules! papers {
castable! {
Paper,
- $($pat => Self::$var,)*
+ $(
+ /// Produces a paper of the respective size.
+ $pat => Self::$var,
+ )*
}
};
}
diff --git a/library/src/layout/par.rs b/library/src/layout/par.rs
index eed0dbb1..f9731ff2 100644
--- a/library/src/layout/par.rs
+++ b/library/src/layout/par.rs
@@ -13,7 +13,12 @@ use crate::text::{
/// Arrange text, spacing and inline-level nodes into a paragraph.
///
-/// Tags: layout.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The contents of the paragraph.
+///
+/// # Tags
+/// - layout
#[func]
#[capable]
#[derive(Hash)]
@@ -144,7 +149,8 @@ castable! {
/// A paragraph break.
///
-/// Tags: layout.
+/// # Tags
+/// - layout
#[func]
#[capable(Unlabellable)]
#[derive(Debug, Hash)]
diff --git a/library/src/layout/place.rs b/library/src/layout/place.rs
index c3fcd0d5..890480a7 100644
--- a/library/src/layout/place.rs
+++ b/library/src/layout/place.rs
@@ -2,7 +2,18 @@ use crate::prelude::*;
/// Place content at an absolute position.
///
-/// Tags: layout.
+/// # Parameters
+/// - alignment: Axes<Option<GenAlign>> (positional)
+/// Relative to which position in the parent container to place the content.
+/// - body: Content (positional, required)
+/// The content to place.
+/// - dx: Rel<Length> (named)
+/// The horizontal displacement of the placed content.
+/// - dy: Rel<Length> (named)
+/// The vertical displacement of the placed content.
+///
+/// # Tags
+/// - layout
#[func]
#[capable(Layout, Behave)]
#[derive(Debug, Hash)]
diff --git a/library/src/layout/repeat.rs b/library/src/layout/repeat.rs
index a47dbb3e..fa87d922 100644
--- a/library/src/layout/repeat.rs
+++ b/library/src/layout/repeat.rs
@@ -2,7 +2,12 @@ use crate::prelude::*;
/// Repeats content to fill a line.
///
-/// Tags: layout.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The content to repeat.
+///
+/// # Tags
+/// - layout
#[func]
#[capable(Layout, Inline)]
#[derive(Debug, Hash)]
diff --git a/library/src/layout/spacing.rs b/library/src/layout/spacing.rs
index e961c0cf..ef69c070 100644
--- a/library/src/layout/spacing.rs
+++ b/library/src/layout/spacing.rs
@@ -2,9 +2,18 @@ use std::cmp::Ordering;
use crate::prelude::*;
-/// Horizontal spacing.
+/// Horizontal spacing in a paragraph.
///
-/// Tags: layout.
+/// # Parameters
+/// - amount: Spacing (positional, required)
+/// How much spacing to insert.
+/// - weak: bool (named)
+/// If true, the spacing collapses at the start or end of a paragraph.
+/// Moreover, from multiple adjacent weak spacings all but the largest one
+/// collapse.
+///
+/// # Tags
+/// - layout
#[func]
#[capable(Behave)]
#[derive(Debug, Copy, Clone, Hash)]
@@ -55,7 +64,16 @@ impl Behave for HNode {
/// Vertical spacing.
///
-/// Tags: layout.
+/// # Parameters
+/// - amount: Spacing (positional, required)
+/// How much spacing to insert.
+/// - weak: bool (named)
+/// If true, the spacing collapses at the start or end of a flow.
+/// Moreover, from multiple adjacent weak spacings all but the largest one
+/// collapse.
+///
+/// # Tags
+/// - layout
#[func]
#[capable(Behave)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd)]
@@ -123,11 +141,6 @@ impl Behave for VNode {
}
}
-castable! {
- VNode,
- spacing: Spacing => VNode::block_around(spacing),
-}
-
/// Kinds of spacing.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Spacing {
diff --git a/library/src/layout/stack.rs b/library/src/layout/stack.rs
index 111e3433..deb565b6 100644
--- a/library/src/layout/stack.rs
+++ b/library/src/layout/stack.rs
@@ -5,7 +5,16 @@ use crate::prelude::*;
/// Arrange content and spacing along an axis.
///
-/// Tags: layout.
+/// # Parameters
+/// - items: StackChild (positional, variadic)
+/// The items to stack along an axis.
+/// - dir: Dir (named)
+/// The direction along which the items are stacked.
+/// - spacing: Spacing (named)
+/// Spacing to insert between items where no explicit spacing was provided.
+///
+/// # Tags
+/// - layout
#[func]
#[capable(Layout)]
#[derive(Debug, Hash)]
diff --git a/library/src/layout/transform.rs b/library/src/layout/transform.rs
index f1a89d4c..3af44ca0 100644
--- a/library/src/layout/transform.rs
+++ b/library/src/layout/transform.rs
@@ -4,7 +4,16 @@ use crate::prelude::*;
/// Move content without affecting layout.
///
-/// Tags: layout.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The content to move.
+/// - dx: Rel<Length> (named)
+/// The horizontal displacement of the content.
+/// - dy: Rel<Length> (named)
+/// The vertical displacement of the content.
+///
+/// # Tags
+/// - layout
#[func]
#[capable(Layout, Inline)]
#[derive(Debug, Hash)]
@@ -49,7 +58,18 @@ impl Inline for MoveNode {}
/// Transform content without affecting layout.
///
-/// Tags: layout.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The content to transform.
+/// - angle: Angle (named)
+/// The amount of rotation.
+/// - x: Ratio (named)
+/// The horizontal scaling factor.
+/// - y: Ratio (named)
+/// The vertical scaling factor.
+///
+/// # Tags
+/// - layout
#[func]
#[capable(Layout, Inline)]
#[derive(Debug, Hash)]
diff --git a/library/src/lib.rs b/library/src/lib.rs
index 2d03ef7c..7e65ff79 100644
--- a/library/src/lib.rs
+++ b/library/src/lib.rs
@@ -50,7 +50,6 @@ fn scope() -> Scope {
// Math.
std.def_func::<math::MathNode>("math");
- std.def_func::<math::AtomNode>("atom");
std.def_func::<math::AccNode>("acc");
std.def_func::<math::FracNode>("frac");
std.def_func::<math::BinomNode>("binom");
@@ -203,7 +202,7 @@ fn items() -> LangItems {
desc_item: |term, body| {
basics::ListItem::Desc(Box::new(basics::DescItem { term, body })).pack()
},
- math: |children, display| math::MathNode { children, display }.pack(),
+ math: |children, block| math::MathNode { children, block }.pack(),
math_atom: |atom| math::AtomNode(atom).pack(),
math_script: |base, sub, sup| math::ScriptNode { base, sub, sup }.pack(),
math_frac: |num, denom| math::FracNode { num, denom }.pack(),
diff --git a/library/src/math/matrix.rs b/library/src/math/matrix.rs
index 21294b71..a32750ff 100644
--- a/library/src/math/matrix.rs
+++ b/library/src/math/matrix.rs
@@ -2,7 +2,12 @@ use super::*;
/// A column vector.
///
-/// Tags: math.
+/// # Parameters
+/// - elements: Content (positional, variadic)
+/// The elements of the vector.
+///
+/// # Tags
+/// - math
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
@@ -66,7 +71,12 @@ castable! {
/// A case distinction.
///
-/// Tags: math.
+/// # Parameters
+/// - branches: Content (positional, variadic)
+/// The branches of the case distinction.
+///
+/// # Tags
+/// - math
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs
index 41cdb9f5..3bd7b2e3 100644
--- a/library/src/math/mod.rs
+++ b/library/src/math/mod.rs
@@ -16,22 +16,35 @@ use crate::text::{FontFamily, LinebreakNode, SpaceNode, SymbolNode, TextNode};
/// A piece of a mathematical formula.
///
-/// Tags: math.
+/// # 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
#[func]
#[capable(Show, Layout, Inline, Texify)]
#[derive(Debug, Clone, Hash)]
pub struct MathNode {
- /// Whether the formula is display-level.
- pub display: bool,
+ /// Whether the formula is displayed as a separate block.
+ pub block: bool,
/// The pieces of the formula.
pub children: Vec<Content>,
}
#[node]
impl MathNode {
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ let block = args.named("block")?.unwrap_or(false);
+ let children = args.all()?;
+ Ok(Self { block, children }.pack())
+ }
+
fn field(&self, name: &str) -> Option<Value> {
match name {
- "display" => Some(Value::Bool(self.display)),
+ "block" => Some(Value::Bool(self.block)),
_ => None,
}
}
@@ -48,7 +61,7 @@ impl Show for MathNode {
.guarded(Guard::Base(NodeId::of::<Self>()))
.styled_with_map(map);
- if self.display {
+ if self.block {
realized = realized.aligned(Axes::with_x(Some(Align::Center.into())))
}
@@ -65,7 +78,7 @@ impl Layout for MathNode {
) -> SourceResult<Fragment> {
let mut t = Texifier::new(styles);
self.texify(&mut t)?;
- Ok(layout_tex(vt, &t.finish(), self.display, styles)
+ Ok(layout_tex(vt, &t.finish(), self.block, styles)
.unwrap_or(Fragment::frame(Frame::new(Size::zero()))))
}
}
@@ -247,7 +260,12 @@ impl Texify for Content {
/// An atom in a math formula: `x`, `+`, `12`.
///
-/// Tags: math.
+/// # Parameters
+/// - text: EcoString (positional, required)
+/// The atom's text.
+///
+/// # Tags
+/// - math
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
@@ -288,7 +306,14 @@ impl Texify for AtomNode {
/// An accented node.
///
-/// Tags: math.
+/// # 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
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
@@ -365,7 +390,14 @@ impl Texify for AccNode {
/// A fraction.
///
-/// Tags: math.
+/// # Parameters
+/// - num: Content (positional, required)
+/// The fraction's numerator.
+/// - denom: Content (positional, required)
+/// The fraction's denominator.
+///
+/// # Tags
+/// - math
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
@@ -398,7 +430,14 @@ impl Texify for FracNode {
/// A binomial.
///
-/// Tags: math.
+/// # Parameters
+/// - upper: Content (positional, required)
+/// The binomial's upper index.
+/// - lower: Content (positional, required)
+/// The binomial's lower index.
+///
+/// # Tags
+/// - math
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
@@ -431,7 +470,16 @@ impl Texify for BinomNode {
/// A sub- and/or superscript.
///
-/// Tags: math.
+/// # Parameters
+/// - base: Content (positional, required)
+/// The base to which the applies the sub- and/or superscript.
+/// - sub: Content (named)
+/// The subscript.
+/// - sup: Content (named)
+/// The superscript.
+///
+/// # Tags
+/// - math
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
@@ -445,7 +493,14 @@ pub struct ScriptNode {
}
#[node]
-impl ScriptNode {}
+impl ScriptNode {
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ let base = args.expect("base")?;
+ let sub = args.named("sub")?;
+ let sup = args.named("sup")?;
+ Ok(Self { base, sub, sup }.pack())
+ }
+}
impl Texify for ScriptNode {
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
@@ -469,14 +524,23 @@ impl Texify for ScriptNode {
/// A math alignment point: `&`, `&&`.
///
-/// Tags: math.
+/// # Parameters
+/// - index: usize (positional, required)
+/// The alignment point's index.
+///
+/// # Tags
+/// - math
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
-pub struct AlignPointNode(pub usize);
+pub struct AlignPointNode(pub NonZeroUsize);
#[node]
-impl AlignPointNode {}
+impl AlignPointNode {
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ Ok(Self(args.expect("index")?).pack())
+ }
+}
impl Texify for AlignPointNode {
fn texify(&self, _: &mut Texifier) -> SourceResult<()> {
@@ -486,7 +550,12 @@ impl Texify for AlignPointNode {
/// A square root.
///
-/// Tags: math.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The expression to take the square root of.
+///
+/// # Tags
+/// - math
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
@@ -510,7 +579,12 @@ impl Texify for SqrtNode {
/// A floored expression.
///
-/// Tags: math.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The expression to floor.
+///
+/// # Tags
+/// - math
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
@@ -534,7 +608,12 @@ impl Texify for FloorNode {
/// A ceiled expression.
///
-/// Tags: math.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The expression to ceil.
+///
+/// # Tags
+/// - math
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
diff --git a/library/src/math/style.rs b/library/src/math/style.rs
index 444b1fb4..344aa071 100644
--- a/library/src/math/style.rs
+++ b/library/src/math/style.rs
@@ -2,7 +2,12 @@ use super::*;
/// Serif (roman) font style.
///
-/// Tags: math.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The piece of formula to style.
+///
+/// # Tags
+/// - math
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
@@ -26,7 +31,12 @@ impl Texify for SerifNode {
/// Sans-serif font style.
///
-/// Tags: math.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The piece of formula to style.
+///
+/// # Tags
+/// - math
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
@@ -50,7 +60,12 @@ impl Texify for SansNode {
/// Bold font style.
///
-/// Tags: math.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The piece of formula to style.
+///
+/// # Tags
+/// - math
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
@@ -74,7 +89,12 @@ impl Texify for BoldNode {
/// Italic font style.
///
-/// Tags: math.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The piece of formula to style.
+///
+/// # Tags
+/// - math
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
@@ -98,7 +118,12 @@ impl Texify for ItalNode {
/// Calligraphic font style.
///
-/// Tags: math.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The piece of formula to style.
+///
+/// # Tags
+/// - math
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
@@ -122,7 +147,12 @@ impl Texify for CalNode {
/// Fraktur font style.
///
-/// Tags: math.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The piece of formula to style.
+///
+/// # Tags
+/// - math
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
@@ -146,7 +176,12 @@ impl Texify for FrakNode {
/// Monospace font style.
///
-/// Tags: math.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The piece of formula to style.
+///
+/// # Tags
+/// - math
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
@@ -170,7 +205,12 @@ impl Texify for MonoNode {
/// Blackboard bold (double-struck) font style.
///
-/// Tags: math.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The piece of formula to style.
+///
+/// # Tags
+/// - math
#[func]
#[capable(Texify)]
#[derive(Debug, Hash)]
diff --git a/library/src/meta/document.rs b/library/src/meta/document.rs
index 8c664df3..e20821c5 100644
--- a/library/src/meta/document.rs
+++ b/library/src/meta/document.rs
@@ -3,7 +3,8 @@ use crate::prelude::*;
/// The root node that represents a full document.
///
-/// Tags: meta.
+/// # Tags
+/// - meta
#[func]
#[capable(LayoutRoot)]
#[derive(Hash)]
diff --git a/library/src/meta/link.rs b/library/src/meta/link.rs
index 6f5d8af1..27294a8d 100644
--- a/library/src/meta/link.rs
+++ b/library/src/meta/link.rs
@@ -3,7 +3,14 @@ use crate::text::TextNode;
/// Link text and other elements to a destination.
///
-/// Tags: meta.
+/// # Parameters
+/// - dest: Destination (positional, required)
+/// The destination the link points to.
+/// - body: Content (positional)
+/// How the link is represented. Defaults to the destination if it is a link.
+///
+/// # Tags
+/// - meta
#[func]
#[capable(Show, Finalize)]
#[derive(Debug, Hash)]
diff --git a/library/src/meta/outline.rs b/library/src/meta/outline.rs
index 27ca5944..6ea65391 100644
--- a/library/src/meta/outline.rs
+++ b/library/src/meta/outline.rs
@@ -5,7 +5,8 @@ use crate::text::{LinebreakNode, SpaceNode, TextNode};
/// A section outline (table of contents).
///
-/// Tags: meta.
+/// # Tags
+/// - meta
#[func]
#[capable(Prepare, Show)]
#[derive(Debug, Hash)]
diff --git a/library/src/meta/reference.rs b/library/src/meta/reference.rs
index 378d19d2..c49ff970 100644
--- a/library/src/meta/reference.rs
+++ b/library/src/meta/reference.rs
@@ -3,7 +3,12 @@ use crate::text::TextNode;
/// A reference to a label.
///
-/// Tags: meta.
+/// # Parameters
+/// - target: Label (positional, required)
+/// The label that should be referenced.
+///
+/// # Tags
+/// - meta
#[func]
#[capable(Show)]
#[derive(Debug, Hash)]
diff --git a/library/src/text/deco.rs b/library/src/text/deco.rs
index 86866715..d725697e 100644
--- a/library/src/text/deco.rs
+++ b/library/src/text/deco.rs
@@ -6,7 +6,12 @@ use crate::prelude::*;
/// Typeset underline, stricken-through or overlined text.
///
-/// Tags: text.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The content to decorate.
+///
+/// # Tags
+/// - text
#[func]
#[capable(Show)]
#[derive(Debug, Hash)]
diff --git a/library/src/text/misc.rs b/library/src/text/misc.rs
index fc4f7d73..20be156a 100644
--- a/library/src/text/misc.rs
+++ b/library/src/text/misc.rs
@@ -3,7 +3,8 @@ use crate::prelude::*;
/// A text space.
///
-/// Tags: text.
+/// # Tags
+/// - text
#[func]
#[capable(Unlabellable, Behave)]
#[derive(Debug, Hash)]
@@ -26,7 +27,12 @@ impl Behave for SpaceNode {
/// A line break.
///
-/// Tags: text.
+/// # Parameters
+/// - justify: bool (named)
+/// Whether to justify the line before the break.
+///
+/// # Tags
+/// - text
#[func]
#[capable(Behave)]
#[derive(Debug, Hash)]
@@ -50,7 +56,12 @@ impl Behave for LinebreakNode {
/// Strongly emphasizes content by increasing the font weight.
///
-/// Tags: text.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The content to strongly emphasize.
+///
+/// # Tags
+/// - text
#[func]
#[capable(Show)]
#[derive(Debug, Hash)]
@@ -98,7 +109,12 @@ impl Fold for Delta {
/// Emphasizes content by flipping the italicness.
///
-/// Tags: text.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The content to emphasize.
+///
+/// # Tags
+/// - text
#[func]
#[capable(Show)]
#[derive(Debug, Hash)]
@@ -136,17 +152,27 @@ impl Fold for Toggle {
}
}
-/// Convert a string or content to lowercase.
+/// Convert text or content to lowercase.
///
-/// Tags: text.
+/// # Parameters
+/// - text: ToCase (positional, required)
+/// The text to convert to lowercase.
+///
+/// # Tags
+/// - text
#[func]
pub fn lower(args: &mut Args) -> SourceResult<Value> {
case(Case::Lower, args)
}
-/// Convert a string or content to uppercase.
+/// Convert text or content to uppercase.
+///
+/// # Parameters
+/// - text: ToCase (positional, required)
+/// The text to convert to uppercase.
///
-/// Tags: text.
+/// # Tags
+/// - text
#[func]
pub fn upper(args: &mut Args) -> SourceResult<Value> {
case(Case::Upper, args)
@@ -162,6 +188,15 @@ fn case(case: Case, args: &mut Args) -> SourceResult<Value> {
})
}
+/// A value whose case can be changed.
+struct ToCase;
+
+castable! {
+ ToCase,
+ _: Str => Self,
+ _: Content => Self,
+}
+
/// A case transformation on text.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Case {
@@ -183,7 +218,12 @@ impl Case {
/// Display text in small capitals.
///
-/// Tags: text.
+/// # Parameters
+/// - text: Content (positional, required)
+/// The text to display to small capitals.
+///
+/// # Tags
+/// - text
#[func]
pub fn smallcaps(args: &mut Args) -> SourceResult<Value> {
let body: Content = args.expect("content")?;
diff --git a/library/src/text/mod.rs b/library/src/text/mod.rs
index 7340e5de..21dea8af 100644
--- a/library/src/text/mod.rs
+++ b/library/src/text/mod.rs
@@ -25,9 +25,16 @@ use typst::util::EcoString;
use crate::layout::ParNode;
use crate::prelude::*;
-/// A single run of text with the same style.
+/// Stylable text.
///
-/// Tags: text.
+/// # 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
#[func]
#[capable]
#[derive(Clone, Hash)]
diff --git a/library/src/text/quotes.rs b/library/src/text/quotes.rs
index ab6f166c..4f65c7fd 100644
--- a/library/src/text/quotes.rs
+++ b/library/src/text/quotes.rs
@@ -4,7 +4,12 @@ use crate::prelude::*;
/// A smart quote.
///
-/// Tags: text.
+/// # Parameters
+/// - double: bool (named)
+/// Whether to produce a smart double quote.
+///
+/// # Tags
+/// - text
#[func]
#[capable]
#[derive(Debug, Hash)]
diff --git a/library/src/text/raw.rs b/library/src/text/raw.rs
index 125a5da1..74626588 100644
--- a/library/src/text/raw.rs
+++ b/library/src/text/raw.rs
@@ -8,14 +8,21 @@ use crate::prelude::*;
/// Raw text with optional syntax highlighting.
///
-/// Tags: text.
+/// # Parameters
+/// - text: EcoString (positional, required)
+/// The raw text.
+/// - block: bool (named)
+/// Whether the raw text is displayed as a separate block.
+///
+/// # Tags
+/// - text
#[func]
#[capable(Show)]
#[derive(Debug, Hash)]
pub struct RawNode {
/// The raw text.
pub text: EcoString,
- /// Whether the node is block-level.
+ /// Whether the raw text is displayed as a separate block.
pub block: bool,
}
diff --git a/library/src/text/shift.rs b/library/src/text/shift.rs
index ad4a6cd9..969dce68 100644
--- a/library/src/text/shift.rs
+++ b/library/src/text/shift.rs
@@ -4,14 +4,19 @@ use typst::util::EcoString;
use super::{variant, SpaceNode, TextNode, TextSize};
use crate::prelude::*;
-/// Sub or superscript text.
+/// Sub- or superscript text.
///
-/// The text is rendered smaller and its baseline is raised. To provide the best
-/// typography possible, we first try to transform the text to superscript
-/// codepoints. If that fails, we fall back to rendering shrunk normal letters
-/// in a raised way.
+/// The text is rendered smaller and its baseline is raised/lowered. To provide
+/// the best typography possible, we first try to transform the text to
+/// superscript codepoints. If that fails, we fall back to rendering shrunk
+/// normal letters in a raised way.
///
-/// Tags: text.
+/// # Parameters
+/// - body: Content (positional, required)
+/// The text to display in sub- or superscript.
+///
+/// # Tags
+/// - text
#[func]
#[capable(Show)]
#[derive(Debug, Hash)]
diff --git a/library/src/text/symbol.rs b/library/src/text/symbol.rs
index eece81ab..ccf1a55e 100644
--- a/library/src/text/symbol.rs
+++ b/library/src/text/symbol.rs
@@ -3,7 +3,12 @@ use crate::text::TextNode;
/// A symbol identified by symmie notation.
///
-/// Tags: text.
+/// # Parameters
+/// - notation: EcoString (positional, required)
+/// The symbols symmie notation.
+///
+/// # Tags
+/// - text
#[func]
#[capable(Show)]
#[derive(Debug, Hash)]
diff --git a/library/src/visualize/image.rs b/library/src/visualize/image.rs
index 936ec3bf..0ce3f20b 100644
--- a/library/src/visualize/image.rs
+++ b/library/src/visualize/image.rs
@@ -6,7 +6,14 @@ use crate::prelude::*;
/// Show a raster or vector graphic.
///
-/// Tags: visualize.
+/// Supported formats are PNG, JPEG, GIF and SVG.
+///
+/// # Parameters
+/// - path: EcoString (positional, required)
+/// Path to an image file.
+///
+/// # Tags
+/// - visualize
#[func]
#[capable(Layout, Inline)]
#[derive(Debug, Hash)]
diff --git a/library/src/visualize/line.rs b/library/src/visualize/line.rs
index 9c9b8b00..5c8c9644 100644
--- a/library/src/visualize/line.rs
+++ b/library/src/visualize/line.rs
@@ -2,7 +2,20 @@ use crate::prelude::*;
/// Display a line without affecting the layout.
///
-/// Tags: visualize.
+/// You should only provide either an endpoint or an angle and a length.
+///
+/// # Parameters
+/// - origin: Axes<Rel<Length>> (named)
+/// The start point of the line.
+/// - to: Axes<Rel<Length>> (named)
+/// The end point of the line.
+/// - length: Rel<Length> (named)
+/// The line's length.
+/// - angle: Angle (named)
+/// The angle at which the line points away from the origin.
+///
+/// # Tags
+/// - visualize
#[func]
#[capable(Layout, Inline)]
#[derive(Debug, Hash)]
diff --git a/library/src/visualize/shape.rs b/library/src/visualize/shape.rs
index a3443189..be80b4fb 100644
--- a/library/src/visualize/shape.rs
+++ b/library/src/visualize/shape.rs
@@ -4,7 +4,22 @@ use crate::prelude::*;
/// A sizable and fillable shape with optional content.
///
-/// Tags: visualize.
+/// # Parameters
+/// - body: Content (positional)
+/// The content to place into the shape.
+/// - width: Rel<Length> (named)
+/// The shape's width.
+/// - height: Rel<Length> (named)
+/// The shape's height.
+/// - size: Length (named)
+/// The square's side length.
+/// - radius: Length (named)
+/// The circle's radius.
+/// - stroke: Smart<Sides<Option<PartialStroke>>> (named)
+/// How to stroke the shape.
+///
+/// # Tags
+/// - visualize
#[func]
#[capable(Layout, Inline)]
#[derive(Debug, Hash)]
@@ -27,7 +42,7 @@ impl<const S: ShapeKind> ShapeNode<S> {
/// How to fill the shape.
pub const FILL: Option<Paint> = None;
/// How to stroke the shape.
- #[property(reflect, skip, resolve, fold)]
+ #[property(skip, resolve, fold)]
pub const STROKE: Smart<Sides<Option<PartialStroke>>> = Smart::Auto;
/// How much to pad the shape's content.
@@ -38,7 +53,7 @@ impl<const S: ShapeKind> ShapeNode<S> {
pub const OUTSET: Sides<Option<Rel<Length>>> = Sides::splat(Rel::zero());
/// How much to round the shape's corners.
- #[property(reflect, skip, resolve, fold)]
+ #[property(skip, resolve, fold)]
pub const RADIUS: Corners<Option<Rel<Length>>> = Corners::splat(Rel::zero());
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
diff --git a/macros/Cargo.toml b/macros/Cargo.toml
index 20ca0262..679a5bec 100644
--- a/macros/Cargo.toml
+++ b/macros/Cargo.toml
@@ -14,3 +14,4 @@ bench = false
proc-macro2 = "1"
quote = "1"
syn = { version = "1", features = ["full"] }
+unscanny = "0.1"
diff --git a/macros/src/castable.rs b/macros/src/castable.rs
index 48cdf9e1..6738ee1d 100644
--- a/macros/src/castable.rs
+++ b/macros/src/castable.rs
@@ -150,7 +150,7 @@ fn create_describe_func(castable: &Castable) -> TokenStream {
let mut infos = vec![];
for cast in &castable.casts {
- let docs = doc_comment(&cast.attrs);
+ let docs = documentation(&cast.attrs);
infos.push(match &cast.pattern {
Pattern::Str(lit) => {
quote! { ::typst::model::CastInfo::Value(#lit.into(), #docs) }
diff --git a/macros/src/func.rs b/macros/src/func.rs
index 4523d48a..98fca6a4 100644
--- a/macros/src/func.rs
+++ b/macros/src/func.rs
@@ -1,36 +1,26 @@
+use proc_macro2::Span;
+use unscanny::Scanner;
+
use super::*;
/// Expand the `#[func]` macro.
pub fn func(item: syn::Item) -> Result<TokenStream> {
- let doc_comment = match &item {
- syn::Item::Struct(item) => doc_comment(&item.attrs),
- syn::Item::Enum(item) => doc_comment(&item.attrs),
- syn::Item::Fn(item) => doc_comment(&item.attrs),
+ let mut 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 mut tags = vec![];
- let mut kept = vec![];
- for line in doc_comment.lines() {
- let line = line.trim();
- if let Some(suffix) = line.trim_end_matches(".").strip_prefix("Tags: ") {
- tags.extend(suffix.split(", "));
- } else {
- kept.push(line);
- }
- }
-
- while kept.last().map_or(false, |line| line.is_empty()) {
- kept.pop();
- }
-
- let docs = kept.join("\n");
+ let tags = tags(&mut docs);
+ let params = params(&mut docs)?;
+ let docs = docs.trim();
let info = quote! {
::typst::model::FuncInfo {
name,
docs: #docs,
tags: &[#(#tags),*],
- params: ::std::vec![],
+ params: ::std::vec![#(#params),*],
}
};
@@ -83,3 +73,93 @@ pub fn func(item: syn::Item) -> Result<TokenStream> {
})
}
}
+
+/// Extract a section.
+pub fn section(docs: &mut String, title: &str) -> Option<String> {
+ let needle = format!("# {title}\n");
+ let start = docs.find(&needle)?;
+ let rest = &docs[start..];
+ let len = rest[1..].find('#').map(|x| 1 + x).unwrap_or(rest.len());
+ let end = start + len;
+ let section = docs[start + needle.len()..].to_owned();
+ docs.replace_range(start..end, "");
+ Some(section)
+}
+
+/// Parse the tag section.
+pub 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 parameter section.
+pub fn params(docs: &mut String) -> Result<Vec<TokenStream>> {
+ let Some(section) = section(docs, "Parameters") else { return Ok(vec![]) };
+ let mut s = Scanner::new(&section);
+ let mut infos = vec![];
+
+ while s.eat_if('-') {
+ s.eat_whitespace();
+ let name = s.eat_until(':');
+ s.expect(": ");
+ let ty: syn::Type = syn::parse_str(s.eat_until(char::is_whitespace))?;
+ s.eat_whitespace();
+ let mut named = false;
+ let mut positional = false;
+ let mut required = false;
+ let mut variadic = false;
+ let mut settable = false;
+ s.expect('(');
+ for part in s.eat_until(')').split(',').map(str::trim).filter(|s| !s.is_empty()) {
+ match part {
+ "named" => named = true,
+ "positional" => positional = true,
+ "required" => required = true,
+ "variadic" => variadic = true,
+ "settable" => settable = true,
+ _ => {
+ return Err(syn::Error::new(
+ Span::call_site(),
+ format!("unknown parameter flag {:?}", part),
+ ))
+ }
+ }
+ }
+
+ if (!named && !positional)
+ || (variadic && !positional)
+ || (named && variadic)
+ || (required && variadic)
+ {
+ return Err(syn::Error::new(
+ Span::call_site(),
+ "invalid combination of parameter flags",
+ ));
+ }
+
+ s.expect(')');
+ let docs = dedent(s.eat_until("\n-").trim());
+ infos.push(quote! {
+ ::typst::model::ParamInfo {
+ name: #name,
+ docs: #docs,
+ cast: <#ty as ::typst::model::Cast<
+ ::typst::syntax::Spanned<::typst::model::Value>
+ >>::describe(),
+ named: #named,
+ positional: #positional,
+ required: #required,
+ variadic: #variadic,
+ settable: #settable,
+ }
+ });
+
+ s.eat_whitespace();
+ }
+
+ Ok(infos)
+}
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index 15dc3ee7..a03dc30e 100644
--- a/macros/src/lib.rs
+++ b/macros/src/lib.rs
@@ -64,7 +64,7 @@ pub fn castable(stream: BoundaryStream) -> BoundaryStream {
}
/// Extract documentation comments from an attribute list.
-fn doc_comment(attrs: &[syn::Attribute]) -> String {
+fn documentation(attrs: &[syn::Attribute]) -> String {
let mut doc = String::new();
// Parse doc comments.
@@ -72,7 +72,9 @@ fn doc_comment(attrs: &[syn::Attribute]) -> String {
if let Ok(syn::Meta::NameValue(meta)) = attr.parse_meta() {
if meta.path.is_ident("doc") {
if let syn::Lit::Str(string) = &meta.lit {
- doc.push_str(&string.value());
+ let full = string.value();
+ let line = full.strip_prefix(' ').unwrap_or(&full);
+ doc.push_str(line);
doc.push('\n');
}
}
@@ -81,3 +83,8 @@ fn doc_comment(attrs: &[syn::Attribute]) -> String {
doc.trim().into()
}
+
+/// Dedent documentation text.
+fn dedent(text: &str) -> String {
+ text.lines().map(str::trim).collect::<Vec<_>>().join("\n")
+}
diff --git a/macros/src/node.rs b/macros/src/node.rs
index ad079c0e..1e03c441 100644
--- a/macros/src/node.rs
+++ b/macros/src/node.rs
@@ -36,7 +36,6 @@ struct Property {
shorthand: Option<Shorthand>,
resolve: bool,
fold: bool,
- reflect: bool,
}
/// The shorthand form of a style property.
@@ -118,7 +117,6 @@ fn prepare_property(item: &syn::ImplItemConst) -> Result<Property> {
let mut referenced = false;
let mut resolve = false;
let mut fold = false;
- let mut reflect = false;
// Parse the `#[property(..)]` attribute.
let mut stream = tokens.into_iter().peekable();
@@ -152,7 +150,6 @@ fn prepare_property(item: &syn::ImplItemConst) -> Result<Property> {
"referenced" => referenced = true,
"resolve" => resolve = true,
"fold" => fold = true,
- "reflect" => reflect = true,
_ => bail!(ident, "invalid attribute"),
}
}
@@ -192,7 +189,6 @@ fn prepare_property(item: &syn::ImplItemConst) -> Result<Property> {
referenced,
resolve,
fold,
- reflect,
})
}
@@ -275,9 +271,9 @@ fn create_node_construct_func(node: &Node) -> syn::ImplItemMethod {
parse_quote! {
fn construct(
_: &::typst::model::Vm,
- _: &mut ::typst::model::Args,
+ args: &mut ::typst::model::Args,
) -> ::typst::diag::SourceResult<::typst::model::Content> {
- unimplemented!()
+ ::typst::diag::bail!(args.span, "cannot be constructed manually");
}
}
})
@@ -335,27 +331,26 @@ fn create_node_set_func(node: &Node) -> syn::ImplItemMethod {
/// Create the node's `properties` function.
fn create_node_properties_func(node: &Node) -> syn::ImplItemMethod {
- let infos = node
- .properties
- .iter()
- .filter(|p| !p.skip || p.reflect)
- .map(|property| {
- let name = property.name.to_string().replace('_', "-").to_lowercase();
- let docs = doc_comment(&property.attrs);
- let value_ty = &property.value_ty;
- let shorthand = matches!(property.shorthand, Some(Shorthand::Positional));
- quote! {
- ::typst::model::ParamInfo {
- name: #name,
- docs: #docs,
- settable: true,
- shorthand: #shorthand,
- cast: <#value_ty as ::typst::model::Cast<
- ::typst::syntax::Spanned<::typst::model::Value>
- >>::describe(),
- }
+ let infos = node.properties.iter().filter(|p| !p.skip).map(|property| {
+ let name = property.name.to_string().replace('_', "-").to_lowercase();
+ let docs = documentation(&property.attrs);
+ let value_ty = &property.value_ty;
+ let shorthand = matches!(property.shorthand, Some(Shorthand::Positional));
+ quote! {
+ ::typst::model::ParamInfo {
+ name: #name,
+ docs: #docs,
+ cast: <#value_ty as ::typst::model::Cast<
+ ::typst::syntax::Spanned<::typst::model::Value>
+ >>::describe(),
+ named: true,
+ positional: #shorthand,
+ required: false,
+ variadic: false,
+ settable: true,
}
- });
+ }
+ });
parse_quote! {
fn properties() -> ::std::vec::Vec<::typst::model::ParamInfo>
diff --git a/src/ide/complete.rs b/src/ide/complete.rs
index d4e72b3d..153a9ccb 100644
--- a/src/ide/complete.rs
+++ b/src/ide/complete.rs
@@ -1,3 +1,5 @@
+use std::collections::HashSet;
+
use if_chain::if_chain;
use super::summarize_font_family;
@@ -141,7 +143,7 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
SyntaxKind::Colon | SyntaxKind::Space { .. } => ctx.cursor,
_ => ctx.leaf.offset(),
};
- ctx.param_value_completions(&callee, &param);
+ ctx.named_param_value_completions(&callee, &param);
return true;
}
}
@@ -360,6 +362,7 @@ struct CompletionContext<'a> {
explicit: bool,
from: usize,
completions: Vec<Completion>,
+ seen_casts: HashSet<u128>,
}
impl<'a> CompletionContext<'a> {
@@ -382,6 +385,7 @@ impl<'a> CompletionContext<'a> {
explicit,
from: cursor,
completions: vec![],
+ seen_casts: HashSet::new(),
})
}
@@ -440,14 +444,16 @@ impl<'a> CompletionContext<'a> {
continue;
}
- self.completions.push(Completion {
- kind: CompletionKind::Param,
- label: param.name.into(),
- apply: Some(format_eco!("{}: ${{}}", param.name)),
- detail: Some(param.docs.into()),
- });
+ if param.named {
+ self.completions.push(Completion {
+ kind: CompletionKind::Param,
+ label: param.name.into(),
+ apply: Some(format_eco!("{}: ${{}}", param.name)),
+ detail: Some(param.docs.into()),
+ });
+ }
- if param.shorthand {
+ if param.positional {
self.cast_completions(&param.cast);
}
}
@@ -458,11 +464,12 @@ impl<'a> CompletionContext<'a> {
}
/// Add completions for the values of a function parameter.
- fn param_value_completions(&mut self, callee: &ast::Ident, name: &str) {
+ fn named_param_value_completions(&mut self, callee: &ast::Ident, name: &str) {
let param = if_chain! {
if let Some(Value::Func(func)) = self.scope.get(callee);
if let Some(info) = func.info();
if let Some(param) = info.param(name);
+ if param.named;
then { param }
else { return; }
};
@@ -475,7 +482,12 @@ impl<'a> CompletionContext<'a> {
}
/// Add completions for a castable.
- fn cast_completions(&mut self, cast: &CastInfo) {
+ fn cast_completions(&mut self, cast: &'a CastInfo) {
+ // Prevent duplicate completions from appearing.
+ if !self.seen_casts.insert(crate::util::hash128(cast)) {
+ return;
+ }
+
match cast {
CastInfo::Any => {}
CastInfo::Value(value, docs) => {
@@ -485,7 +497,7 @@ impl<'a> CompletionContext<'a> {
self.snippet_completion("none", "none", "Nonexistent.")
}
CastInfo::Type("auto") => {
- self.snippet_completion("auto", "auto", "A smart default");
+ self.snippet_completion("auto", "auto", "A smart default.");
}
CastInfo::Type("boolean") => {
self.snippet_completion("false", "false", "Yes / Enabled.");
diff --git a/src/model/cast.rs b/src/model/cast.rs
index 833b9e9e..5c95fe4b 100644
--- a/src/model/cast.rs
+++ b/src/model/cast.rs
@@ -33,7 +33,7 @@ pub trait Cast<V = Value>: Sized {
}
/// Describes a possible value for a cast.
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Hash)]
pub enum CastInfo {
/// Any value is okay.
Any,
diff --git a/src/model/eval.rs b/src/model/eval.rs
index d54acf0a..a9fa2e14 100644
--- a/src/model/eval.rs
+++ b/src/model/eval.rs
@@ -411,7 +411,7 @@ impl Eval for ast::Math {
self.children()
.map(|node| node.eval(vm))
.collect::<SourceResult<_>>()?,
- self.display(),
+ self.block(),
))
}
}
diff --git a/src/model/func.rs b/src/model/func.rs
index 5b38b700..46befd77 100644
--- a/src/model/func.rs
+++ b/src/model/func.rs
@@ -232,12 +232,21 @@ pub struct ParamInfo {
pub name: &'static str,
/// Documentation for the parameter.
pub docs: &'static str,
- /// Is the parameter settable with a set rule?
- pub settable: bool,
- /// Can the name be omitted?
- pub shorthand: bool,
/// Valid values for the parameter.
pub cast: CastInfo,
+ /// Is the parameter positional?
+ pub positional: bool,
+ /// Is the parameter named?
+ ///
+ /// Can be true even if `positional` is true if the parameter can be given
+ /// in both variants.
+ pub named: bool,
+ /// Is the parameter required?
+ pub required: bool,
+ /// Can the parameter be given any number of times?
+ pub variadic: bool,
+ /// Is the parameter settable with a set rule?
+ pub settable: bool,
}
/// A user-defined closure.
diff --git a/src/model/library.rs b/src/model/library.rs
index 02eb9179..83310610 100644
--- a/src/model/library.rs
+++ b/src/model/library.rs
@@ -66,7 +66,7 @@ pub struct LangItems {
/// An item in a description list: `/ Term: Details`.
pub desc_item: fn(term: Content, body: Content) -> Content,
/// A mathematical formula: `$x$`, `$ x^2 $`.
- pub math: fn(children: Vec<Content>, display: bool) -> Content,
+ pub math: fn(children: Vec<Content>, block: bool) -> Content,
/// An atom in a formula: `x`, `+`, `12`.
pub math_atom: fn(atom: EcoString) -> Content,
/// A base with optional sub- and superscripts in a formula: `a_1^2`.
@@ -75,7 +75,7 @@ pub struct LangItems {
/// A fraction in a formula: `x/2`.
pub math_frac: fn(num: Content, denom: Content) -> Content,
/// An alignment point in a formula: `&`, `&&`.
- pub math_align_point: fn(count: usize) -> Content,
+ pub math_align_point: fn(count: NonZeroUsize) -> Content,
}
impl Debug for LangItems {
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs
index 56d4415e..abbca5ec 100644
--- a/src/syntax/ast.rs
+++ b/src/syntax/ast.rs
@@ -295,7 +295,7 @@ impl Raw {
self.get().lang.as_ref()
}
- /// Whether the raw block is block-level.
+ /// Whether the raw text should be displayed in a separate block.
pub fn block(&self) -> bool {
self.get().block
}
@@ -425,8 +425,8 @@ impl Math {
self.0.children().filter_map(SyntaxNode::cast)
}
- /// Whether this is a display-level math formula.
- pub fn display(&self) -> bool {
+ /// Whether the formula should be displayed as a separate block.
+ pub fn block(&self) -> bool {
matches!(self.children().next(), Some(MathNode::Space(_)))
&& matches!(self.children().last(), Some(MathNode::Space(_)))
}
@@ -564,8 +564,13 @@ node! {
impl AlignPoint {
/// The number of ampersands.
- pub fn count(&self) -> usize {
- self.0.children().filter(|n| n.kind() == &SyntaxKind::Amp).count()
+ pub fn count(&self) -> NonZeroUsize {
+ self.0
+ .children()
+ .filter(|n| n.kind() == &SyntaxKind::Amp)
+ .count()
+ .try_into()
+ .expect("alignment point is missing ampersand sign")
}
}
diff --git a/tests/typ/compute/calc.typ b/tests/typ/compute/calc.typ
index 4ccefa22..bc6ef7f6 100644
--- a/tests/typ/compute/calc.typ
+++ b/tests/typ/compute/calc.typ
@@ -13,19 +13,19 @@
#test(type(float(10)), "float")
---
-// Error: 6-10 cannot convert length to integer
+// Error: 6-10 expected boolean, integer, float, or string, found length
#int(10pt)
---
-// Error: 8-13 cannot convert function to float
+// Error: 8-13 expected boolean, integer, float, or string, found function
#float(float)
---
-// Error: 6-12 invalid integer
+// Error: 6-12 not a valid integer
#int("nope")
---
-// Error: 8-15 invalid float
+// Error: 8-15 not a valid float
#float("1.2.3")
---
@@ -39,18 +39,10 @@
#test(abs(-25%), 25%)
---
-// Error: 6-17 expected numeric value, found string
+// Error: 6-17 expected integer, float, angle, ratio, or fraction, found string
#abs("no number")
---
-// Error: 6-11 cannot take absolute value of a length
-#abs(-12pt)
-
----
-// Error: 6-16 cannot take absolute value of a length
-#abs(50% - 12pt)
-
----
// Test the `even` and `odd` functions.
#test(even(2), true)
#test(odd(2), false)
diff --git a/tests/typ/compute/create.typ b/tests/typ/compute/create.typ
index 462f06e9..ccb7bd2e 100644
--- a/tests/typ/compute/create.typ
+++ b/tests/typ/compute/create.typ
@@ -48,7 +48,7 @@
#test(str(10 / 3).len() > 10, true)
---
-// Error: 6-8 cannot convert content to string
+// Error: 6-8 expected integer, float, label, or string, found content
#str([])
---