Document parameters in comment
This commit is contained in:
parent
b6202b646a
commit
35b16e545b
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1221,6 +1221,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"unscanny",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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.
|
||||
///
|
||||
/// Tags: calculate.
|
||||
/// # Parameters
|
||||
/// - dividend: ToMod (positional, required)
|
||||
/// The dividend of the modulus.
|
||||
/// - divisor: ToMod (positional, required)
|
||||
/// The divisor of the modulus.
|
||||
///
|
||||
/// # 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,
|
||||
}
|
||||
|
@ -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")?;
|
||||
|
@ -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 } =
|
||||
|
@ -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")?;
|
||||
|
@ -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.
|
||||
///
|
||||
/// Tags: utility.
|
||||
/// # 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
|
||||
#[func]
|
||||
pub fn numbering(args: &mut Args) -> SourceResult<Value> {
|
||||
let pattern = args.expect::<NumberingPattern>("pattern")?;
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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,
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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 {
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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(),
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -3,7 +3,8 @@ use crate::prelude::*;
|
||||
|
||||
/// The root node that represents a full document.
|
||||
///
|
||||
/// Tags: meta.
|
||||
/// # Tags
|
||||
/// - meta
|
||||
#[func]
|
||||
#[capable(LayoutRoot)]
|
||||
#[derive(Hash)]
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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.
|
||||
///
|
||||
/// Tags: text.
|
||||
/// # Parameters
|
||||
/// - text: ToCase (positional, required)
|
||||
/// The text to convert to uppercase.
|
||||
///
|
||||
/// # 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")?;
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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> {
|
||||
|
@ -14,3 +14,4 @@ bench = false
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "1", features = ["full"] }
|
||||
unscanny = "0.1"
|
||||
|
@ -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) }
|
||||
|
@ -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(§ion);
|
||||
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)
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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, ¶m);
|
||||
ctx.named_param_value_completions(&callee, ¶m);
|
||||
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(¶m.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.");
|
||||
|
@ -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,
|
||||
|
@ -411,7 +411,7 @@ impl Eval for ast::Math {
|
||||
self.children()
|
||||
.map(|node| node.eval(vm))
|
||||
.collect::<SourceResult<_>>()?,
|
||||
self.display(),
|
||||
self.block(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,17 +39,9 @@
|
||||
#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)
|
||||
|
@ -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([])
|
||||
|
||||
---
|
||||
|
Loading…
x
Reference in New Issue
Block a user