Require font to be a named argument

This commit is contained in:
Laurenz 2023-03-08 10:43:03 +01:00
parent 25b5bd1175
commit 1b2b53ecb9
59 changed files with 215 additions and 259 deletions

View File

@ -20,7 +20,7 @@ settable parameters. In the example below, we use two set rules to change the
[heading numbering]($func/heading.numbering) style.
```example
#set text("New Computer Modern")
#set text(font: "New Computer Modern")
#set heading(numbering: "I.")
= Introduction
@ -85,8 +85,8 @@ fantasy encyclopedia.
#set heading(numbering: "(I)")
#show heading: it => block[
#set align(center)
#set text("Inria Serif")
\~ _#it.title;_
#set text(font: "Inria Serif")
#it.numbers \~
]

View File

@ -100,7 +100,7 @@ Let's add a few more styles to our document. We want larger margins and a serif
font. For the purposes of the example, we'll also set another page size.
```example
#set text(10pt, "New Computer Modern")
#set text(font: "New Computer Modern", 10pt)
#set page(
"a6",
margin: (x: 1.8cm, y: 1.5cm),
@ -171,7 +171,7 @@ can do this by setting the `numbering` parameter of the
[`heading`]($func/heading) function.
```example
>>> #set text("New Computer Modern")
>>> #set text(font: "New Computer Modern")
#set heading(numbering: "1.")
= Introduction
@ -190,7 +190,7 @@ each level. We can also use
[letters, roman numerals, and symbols]($func/numbering) for our headings:
```example
>>> #set text("New Computer Modern")
>>> #set text(font: "New Computer Modern")
#set heading(numbering: "1.a")
= Introduction

View File

@ -50,7 +50,7 @@ to learn some new tricks.
Let's start by writing some set rules for the document.
```example
#set text(11pt, "Linux Libertine")
#set text(font: "Linux Libertine", 11pt)
#set par(justify: true)
#set page(
@ -97,7 +97,7 @@ align it and increase its font weight by enclosing it in `[*stars*]`.
```example
>>> #set page(width: 300pt, margin: 30pt)
>>> #set text(11pt, "Linux Libertine")
>>> #set text(font: "Linux Libertine", 11pt)
#align(center, text(17pt)[
*A fluid dynamic model
for glacier flow*
@ -111,7 +111,7 @@ supervisor, we'll add our own and their name.
```example
>>> #set page(width: 300pt, margin: 30pt)
>>> #set text(11pt, "Linux Libertine")
>>> #set text(font: "Linux Libertine", 11pt)
>>>
>>> #align(center, text(17pt)[
>>> *A fluid dynamic model
@ -148,7 +148,7 @@ Now, let's add the abstract. Remember that the conference wants the abstract to
be set ragged and centered.
```example:0,0,612,317.5
>>> #set text(11pt, "Linux Libertine")
>>> #set text(font: "Linux Libertine", 11pt)
>>> #set par(justify: true)
>>> #set page(
>>> "us-letter",
@ -210,7 +210,7 @@ keyword:
<<< ...
>>> #set text(11pt, "Linux Libertine")
>>> #set text(font: "Linux Libertine", 11pt)
>>> #set par(justify: true)
#set page(
>>> "us-letter",
@ -280,7 +280,7 @@ content. In our case, it passes it on to the `columns` function.
>>> for glacier flow
>>> ]
>>>
>>> #set text(11pt, "Linux Libertine")
>>> #set text(font: "Linux Libertine", 11pt)
>>> #set par(justify: true)
>>> #set page(
>>> "us-letter",
@ -342,7 +342,7 @@ a way to set any of that, we need to write our own heading show rule.
>>> for glacier flow
>>> ]
>>>
>>> #set text(11pt, "Linux Libertine")
>>> #set text(font: "Linux Libertine", 11pt)
>>> #set par(justify: true)
>>> #set page(
>>> "us-letter",
@ -421,7 +421,7 @@ differentiate between section and subsection headings:
>>> for glacier flow
>>> ]
>>>
>>> #set text(11pt, "Linux Libertine")
>>> #set text(font: "Linux Libertine", 11pt)
>>> #set par(justify: true)
>>> #set page(
>>> "us-letter",

View File

@ -67,7 +67,7 @@ that content block.
```example
#let template(doc) = [
#set text("Inria Serif")
#set text(font: "Inria Serif")
#show "something cool": [Typst]
#doc
]
@ -86,7 +86,7 @@ previous chapter.
```example
#let conf(title, doc) = {
set text(11pt, "Linux Libertine")
set text(font: "Linux Libertine", 11pt)
set par(justify: true)
set page(
paper: "us-letter",
@ -266,7 +266,7 @@ path of the file after the `{from}` keyword.
>>> abstract: [],
>>> doc,
>>> ) = {
>>> set text(11pt, "Linux Libertine")
>>> set text(font: "Linux Libertine", 11pt)
>>> set par(justify: true)
>>> set page(
>>> "us-letter",

View File

@ -57,6 +57,7 @@ pub struct AlignNode {
/// )
/// ```
#[settable]
#[positional]
#[fold]
#[skip]
#[default(Axes::new(GenAlign::Start, GenAlign::Specific(Align::Top)))]

View File

@ -49,20 +49,6 @@ use super::GridLayouter;
/// becomes part of that item.
///
/// ## Parameters
/// - items: `Content` (positional, variadic)
/// The enumeration's children.
///
/// When using the enum syntax, adjacent items are automatically collected
/// into enumerations, even through constructs like for loops.
///
/// ```example
/// #for phase in (
/// "Launch",
/// "Orbit",
/// "Descent",
/// ) [+ #phase]
/// ```
///
/// - start: `NonZeroUsize` (named)
/// Which number to start the enumeration with.
///
@ -74,31 +60,39 @@ use super::GridLayouter;
/// )
/// ```
///
/// - tight: `bool` (named)
/// If this is `{false}`, the items are spaced apart with
/// [enum spacing]($func/enum.spacing). If it is `{true}`, they use normal
/// [leading]($func/par.leading) instead. This makes the enumeration more
/// compact, which can look better if the items are short.
///
/// ```example
/// + If an enum has a lot of text, and
/// maybe other inline content, it
/// should not be tight anymore.
///
/// + To make an enum wide, simply
/// insert a blank line between the
/// items.
/// ```
///
/// Display: Numbered List
/// Category: layout
#[node(Construct, Layout)]
pub struct EnumNode {
/// The numbered list's items.
///
/// When using the enum syntax, adjacent items are automatically collected
/// into enumerations, even through constructs like for loops.
///
/// ```example
/// #for phase in (
/// "Launch",
/// "Orbit",
/// "Descent",
/// ) [+ #phase]
/// ```
#[variadic]
pub items: Vec<EnumItem>,
/// If true, the items are separated by leading instead of list spacing.
/// If this is `{false}`, the items are spaced apart with
/// [enum spacing]($func/enum.spacing). If it is `{true}`, they use normal
/// [leading]($func/par.leading) instead. This makes the enumeration more
/// compact, which can look better if the items are short.
///
/// ```example
/// + If an enum has a lot of text, and
/// maybe other inline content, it
/// should not be tight anymore.
///
/// + To make an enum wide, simply
/// insert a blank line between the
/// items.
/// ```
#[named]
#[default(true)]
pub tight: bool,
@ -252,6 +246,9 @@ impl Layout for EnumNode {
}
/// An enumeration item.
///
/// Display: Numbered List Item
/// Category: layout
#[node]
pub struct EnumItem {
/// The item's number.

View File

@ -8,6 +8,9 @@ use crate::visualize::{CircleNode, EllipseNode, ImageNode, RectNode, SquareNode}
///
/// This node is responsible for layouting both the top-level content flow and
/// the contents of boxes.
///
/// Display: Flow
/// Category: layout
#[node(Layout)]
pub struct FlowNode {
/// The children that will be arranges into a flow.

View File

@ -167,6 +167,9 @@ impl Layout for ListNode {
}
/// A bullet list item.
///
/// Display: Bullet List Item
/// Category: layout
#[node]
pub struct ListItem {
/// The item's body.

View File

@ -12,11 +12,6 @@ use crate::prelude::*;
/// Pages can be set to use `{auto}` as their width or height. In this case,
/// the pages will grow to fit their content on the respective axis.
///
/// ## Parameters
/// - paper: `Paper` (positional, settable)
/// A standard paper size to set width and height. When this is not specified,
/// Typst defaults to `{"a4"}` paper.
///
/// ## Example
/// ```example
/// >>> #set page(margin: auto)
@ -25,6 +20,11 @@ use crate::prelude::*;
/// There you go, US friends!
/// ```
///
/// ## Parameters
/// - paper: `Paper` (positional, settable)
/// A standard paper size to set width and height. When this is not specified,
/// Typst defaults to `{"a4"}` paper.
///
/// Display: Page
/// Category: layout
#[node]

View File

@ -45,6 +45,7 @@ use crate::text::{
pub struct ParNode {
/// The paragraph's children.
#[variadic]
#[skip]
pub children: Vec<Content>,
/// The indent the first line of a consecutive paragraph should have.

View File

@ -86,9 +86,6 @@ impl Behave for HNode {
/// ```
///
/// ## 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.
@ -108,7 +105,7 @@ impl Behave for HNode {
/// Category: layout
#[node(Construct, Behave)]
pub struct VNode {
/// The amount of vertical spacing.
/// How much spacing to insert.
#[positional]
#[required]
pub amount: Spacing,

View File

@ -127,6 +127,9 @@ impl Layout for TermsNode {
}
/// A term list item.
///
/// Display: Term List Item
/// Category: layout
#[node]
pub struct TermItem {
/// The term described by the list item.

View File

@ -79,12 +79,11 @@ pub struct RotateNode {
/// The amount of rotation.
///
/// ```example
/// #rotate(angle: -1.571rad)[Space!]
/// #rotate(-1.571rad)[Space!]
/// ```
///
#[named]
#[shorthand]
#[default]
#[positional]
#[required]
pub angle: Angle,
/// The content to rotate.
@ -104,9 +103,9 @@ pub struct RotateNode {
/// #let square = square.with(width: 8pt)
///
/// #box(square())
/// #box(rotate(angle: 30deg, origin: center, square()))
/// #box(rotate(angle: 30deg, origin: top + left, square()))
/// #box(rotate(angle: 30deg, origin: bottom + right, square()))
/// #box(rotate(30deg, origin: center, square()))
/// #box(rotate(30deg, origin: top + left, square()))
/// #box(rotate(30deg, origin: bottom + right, square()))
/// ```
#[settable]
#[resolve]

View File

@ -14,15 +14,6 @@ pub(super) const DELIM_SHORT_FALL: Em = Em::new(0.1);
/// $ lr(]sum_(x=1)^n] x, size: #50%) $
/// ```
///
/// ## Parameters
/// - body: `Content` (positional, variadic)
/// The delimited content, including the delimiters.
///
/// - size: `Rel<Length>` (named)
/// The size of the brackets, relative to the height of the wrapped content.
///
/// Defaults to `{100%}`.
///
/// Display: Left/Right
/// Category: math
#[node(Construct, LayoutMath)]
@ -32,7 +23,9 @@ pub struct LrNode {
#[required]
pub body: Content,
/// The size of the brackets.
/// The size of the brackets, relative to the height of the wrapped content.
///
/// Defaults to `{100%}`.
#[named]
#[default]
pub size: Smart<Rel<Length>>,

View File

@ -41,8 +41,7 @@ use self::spacing::*;
use crate::layout::{HNode, ParNode, Spacing};
use crate::prelude::*;
use crate::text::{
families, variant, FallbackList, FontFamily, LinebreakNode, SpaceNode, TextNode,
TextSize,
families, variant, FontFamily, FontList, LinebreakNode, SpaceNode, TextNode, TextSize,
};
/// Create a module with all math definitions.
@ -113,7 +112,7 @@ pub fn module() -> Module {
///
/// ## Example
/// ```example
/// #set text("New Computer Modern")
/// #set text(font: "New Computer Modern")
///
/// Let $a$, $b$, and $c$ be the side
/// lengths of right-angled triangle.
@ -161,8 +160,8 @@ impl Finalize for FormulaNode {
realized
.styled(TextNode::WEIGHT, FontWeight::from_number(450))
.styled(
TextNode::FAMILY,
FallbackList(vec![FontFamily::new("New Computer Modern Math")]),
TextNode::FONT,
FontList(vec![FontFamily::new("New Computer Modern Math")]),
)
}
}
@ -233,7 +232,7 @@ impl LayoutMath for Content {
if let Some(styled) = self.to::<StyledNode>() {
let map = styled.map();
if map.contains(TextNode::FAMILY) {
if map.contains(TextNode::FONT) {
let frame = ctx.layout_content(self)?;
ctx.push(FrameFragment::new(ctx, frame).with_spaced(true));
return Ok(());

View File

@ -183,7 +183,7 @@ impl LayoutMath for FrakNode {
/// $ mono(x + y = z) $
/// ```
///
/// Display: Monospace
/// Display: Monospace
/// Category: math
#[node(LayoutMath)]
pub struct MonoNode {

View File

@ -23,42 +23,38 @@ use crate::text::{Hyphenate, TextNode};
/// This function also has dedicated syntax: Text that starts with `http://` or
/// `https://` is automatically turned into a link.
///
/// ## Parameters
/// - dest: `Destination` (positional, required)
/// The destination the link points to.
///
/// - To link to web pages, `dest` should be a valid URL string. If the URL is
/// in the `mailto:` or `tel:` scheme and the `body` parameter is omitted,
/// the email address or phone number will be the link's body, without the
/// scheme.
///
/// - To link to another part of the document, `dest` must contain a
/// dictionary with a `page` key of type `integer` and `x` and `y`
/// coordinates of type `length`. Pages are counted from one, and the
/// coordinates are relative to the page's top left corner.
///
/// ```example
/// #link("mailto:hello@typst.app") \
/// #link((page: 1, x: 0pt, y: 0pt))[
/// Go to top
/// ]
/// ```
///
/// - body: `Content` (positional)
///
/// The content that should become a link. If `dest` is an URL string, the
/// parameter can be omitted. In this case, the URL will be shown as the link.
///
/// Display: Link
/// Category: meta
#[node(Construct, Show, Finalize)]
pub struct LinkNode {
/// The destination the link points to.
///
/// - To link to web pages, `dest` should be a valid URL string. If the URL is
/// in the `mailto:` or `tel:` scheme and the `body` parameter is omitted,
/// the email address or phone number will be the link's body, without the
/// scheme.
///
/// - To link to another part of the document, `dest` must contain a
/// dictionary with a `page` key of type `integer` and `x` and `y`
/// coordinates of type `length`. Pages are counted from one, and the
/// coordinates are relative to the page's top left corner.
///
/// ```example
/// #link("mailto:hello@typst.app") \
/// #link((page: 1, x: 0pt, y: 0pt))[
/// Go to top
/// ]
/// ```
///
#[positional]
#[required]
pub dest: Destination,
/// How the link is represented.
///
/// The content that should become a link. If `dest` is an URL string, the
/// parameter can be omitted. In this case, the URL will be shown as the
/// link.
#[positional]
#[default]
pub body: Content,

View File

@ -74,10 +74,10 @@ pub trait StyleMapExt {
impl StyleMapExt for StyleMap {
fn set_family(&mut self, preferred: crate::text::FontFamily, existing: StyleChain) {
self.set(
crate::text::TextNode::FAMILY,
crate::text::FallbackList(
crate::text::TextNode::FONT,
crate::text::FontList(
std::iter::once(preferred)
.chain(existing.get(crate::text::TextNode::FAMILY).0.iter().cloned())
.chain(existing.get(crate::text::TextNode::FONT).0.iter().cloned())
.collect(),
),
);

View File

@ -31,7 +31,6 @@ pub struct UnderlineNode {
/// )
/// ```
#[settable]
#[shorthand]
#[resolve]
#[fold]
#[default]
@ -117,7 +116,6 @@ pub struct OverlineNode {
/// )
/// ```
#[settable]
#[shorthand]
#[resolve]
#[fold]
#[default]
@ -207,7 +205,6 @@ pub struct StrikeNode {
/// This is #strike(stroke: 10pt)[redacted].
/// ```
#[settable]
#[shorthand]
#[resolve]
#[fold]
#[default]
@ -219,7 +216,7 @@ pub struct StrikeNode {
/// This is useful if you are unhappy with the offset your font provides.
///
/// ```example
/// #set text(family: "Inria Serif")
/// #set text(font: "Inria Serif")
/// This is #strike(offset: auto)[low-ish]. \
/// This is #strike(offset: -3.5pt)[on-top].
/// ```

View File

@ -39,60 +39,12 @@ use crate::prelude::*;
/// ```
///
/// ## Parameters
/// - family: `FallbackList` (positional, named, variadic, settable)
/// A prioritized sequence of font families.
///
/// When processing text, Typst tries all specified font families in order
/// until it finds a font that has the necessary glyphs. In the example below,
/// the font `Inria Serif` is preferred, but since it does not contain Arabic
/// glyphs, the arabic text uses `Noto Sans Arabic` instead.
///
/// ```example
/// #set text(
/// "Inria Serif",
/// "Noto Sans Arabic",
/// )
///
/// This is Latin. \
/// هذا عربي.
///
/// ```
///
/// - body: `Content` (positional, required)
/// Content in which all text is styled according to the other arguments.
///
/// Display: Text
/// Category: text
#[node(Construct)]
#[set({
if let Some(family) = args.named("family")? {
styles.set(Self::FAMILY, family);
} else {
let mut count = 0;
let mut content = false;
for item in args.items.iter().filter(|item| item.name.is_none()) {
if EcoString::is(&item.value) {
count += 1;
} else if <Content as Cast<Spanned<Value>>>::is(&item.value) {
content = true;
}
}
// Skip the final string if it's needed as the body.
if constructor && !content && count > 0 {
count -= 1;
}
if count > 0 {
let mut list = Vec::with_capacity(count);
for _ in 0..count {
list.push(args.find()?.unwrap());
}
styles.set(Self::FAMILY, FallbackList(list));
}
}
})]
pub struct TextNode {
/// The text.
#[positional]
@ -101,10 +53,25 @@ pub struct TextNode {
pub text: EcoString,
/// A prioritized sequence of font families.
///
/// When processing text, Typst tries all specified font families in order
/// until it finds a font that has the necessary glyphs. In the example
/// below, the font `Inria Serif` is preferred, but since it does not
/// contain Arabic glyphs, the arabic text uses `Noto Sans Arabic` instead.
///
/// ```example
/// #set text(font: (
/// "Inria Serif",
/// "Noto Sans Arabic",
/// ))
///
/// This is Latin. \
/// هذا عربي.
///
/// ```
#[settable]
#[skip]
#[default(FallbackList(vec![FontFamily::new("Linux Libertine")]))]
pub family: FallbackList,
#[default(FontList(vec![FontFamily::new("Linux Libertine")]))]
pub font: FontList,
/// Whether to allow last resort font fallback when the primary font list
/// contains no match. This lets Typst search through all available fonts
@ -117,7 +84,7 @@ pub struct TextNode {
/// something is up.
///
/// ```example
/// #set text(family: "Inria Serif")
/// #set text(font: "Inria Serif")
/// هذا عربي
///
/// #set text(fallback: false)
@ -141,8 +108,8 @@ pub struct TextNode {
/// style later if you change your mind about how to signify the emphasis.
///
/// ```example
/// #text("Linux Libertine", style: "italic")[Italic]
/// #text("DejaVu Sans", style: "oblique")[Oblique]
/// #text(font: "Linux Libertine", style: "italic")[Italic]
/// #text(font: "DejaVu Sans", style: "oblique")[Oblique]
/// ```
#[settable]
#[default(FontStyle::Normal)]
@ -460,7 +427,7 @@ pub struct TextNode {
/// default numbers for the font are used.
///
/// ```example
/// #set text(20pt, "Noto Sans")
/// #set text(font: "Noto Sans", 20pt)
/// #set text(number-type: "lining")
/// Number 9.
///
@ -475,7 +442,7 @@ pub struct TextNode {
/// numbers for the font are used.
///
/// ```example
/// #set text(20pt, "Noto Sans")
/// #set text(font: "Noto Sans", 20pt)
/// #set text(number-width: "proportional")
/// A 12 B 34. \
/// A 56 B 78.
@ -609,16 +576,16 @@ cast_to_value! {
/// Font family fallback list.
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
pub struct FallbackList(pub Vec<FontFamily>);
pub struct FontList(pub Vec<FontFamily>);
cast_from_value! {
FallbackList,
FontList,
family: FontFamily => Self(vec![family]),
values: Array => Self(values.into_iter().map(|v| v.cast()).collect::<StrResult<_>>()?),
}
cast_to_value! {
v: FallbackList => v.0.into()
v: FontList => v.0.into()
}
/// The size of text.

View File

@ -3,8 +3,7 @@ use syntect::highlighting as synt;
use typst::syntax::{self, LinkedNode};
use super::{
FallbackList, FontFamily, Hyphenate, LinebreakNode, SmartQuoteNode, TextNode,
TextSize,
FontFamily, FontList, Hyphenate, LinebreakNode, SmartQuoteNode, TextNode, TextSize,
};
use crate::layout::BlockNode;
use crate::prelude::*;
@ -185,10 +184,7 @@ impl Finalize for RawNode {
map.set(TextNode::OVERHANG, false);
map.set(TextNode::HYPHENATE, Hyphenate(Smart::Custom(false)));
map.set(TextNode::SIZE, TextSize(Em::new(0.8).into()));
map.set(
TextNode::FAMILY,
FallbackList(vec![FontFamily::new("DejaVu Sans Mono")]),
);
map.set(TextNode::FONT, FontList(vec![FontFamily::new("DejaVu Sans Mono")]));
map.set(SmartQuoteNode::ENABLED, false);
realized.styled_with_map(map)
}

View File

@ -560,7 +560,7 @@ pub fn families(styles: StyleChain) -> impl Iterator<Item = FontFamily> + Clone
let tail = if styles.get(TextNode::FALLBACK) { FALLBACKS } else { &[] };
styles
.get(TextNode::FAMILY)
.get(TextNode::FONT)
.0
.into_iter()
.chain(tail.iter().copied().map(FontFamily::new))

View File

@ -174,7 +174,7 @@ fn search_text(content: &Content, sub: bool) -> Option<EcoString> {
/// given string.
fn is_shapable(vt: &Vt, text: &str, styles: StyleChain) -> bool {
let world = vt.world();
for family in styles.get(TextNode::FAMILY).0.iter() {
for family in styles.get(TextNode::FONT).0.iter() {
if let Some(font) = world
.book()
.select(family.as_str(), variant(styles))

View File

@ -10,10 +10,6 @@ use crate::prelude::*;
/// ```
///
/// ## Parameters
/// - start: `Axes<Rel<Length>>` (named)
/// The start point of the line.
/// Must be an array of exactly two relative lengths.
///
/// - end: `Axes<Rel<Length>>` (named)
/// The end point of the line.
/// Must be an array of exactly two relative lengths.
@ -29,7 +25,9 @@ use crate::prelude::*;
/// Category: visualize
#[node(Construct, Layout)]
pub struct LineNode {
/// Where the line starts.
/// The start point of the line.
///
/// Must be an array of exactly two relative lengths.
#[named]
#[default]
pub start: Axes<Rel<Length>>,
@ -37,6 +35,7 @@ pub struct LineNode {
/// The offset from `start` where the line ends.
#[named]
#[default]
#[skip]
pub delta: Axes<Rel<Length>>,
/// How to stroke the line. This can be:

View File

@ -190,6 +190,11 @@ impl Layout for RectNode {
/// ]
/// ```
///
/// ## Parameters
/// - size: `Smart<Length>` (named)
/// The square's side length. This is mutually exclusive with `width` and
/// `height`.
///
/// Display: Square
/// Category: visualize
#[node(Construct, Layout)]
@ -414,26 +419,10 @@ impl Layout for EllipseNode {
/// ```
///
/// ## Parameters
/// - body: `Content` (positional)
/// The content to place into the circle. The circle expands to fit this
/// content, keeping the 1-1 aspect ratio.
///
/// - radius: `Length` (named)
/// The circle's radius. This is mutually exclusive with `width` and
/// `height`.
///
/// - width: `Rel<Length>` (named)
/// The circle's width. This is mutually exclusive with `radius` and `height`.
///
/// In contrast to `size`, this can be relative to the parent container's
/// width.
///
/// - height: `Rel<Length>` (named)
/// The circle's height.This is mutually exclusive with `radius` and `width`.
///
/// In contrast to `size`, this can be relative to the parent container's
/// height.
///
/// Display: Circle
/// Category: visualize
#[node(Construct, Layout)]

View File

@ -1,22 +1,34 @@
use super::*;
/// Expand the `#[func]` macro.
pub fn func(item: syn::Item) -> Result<TokenStream> {
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(),
pub fn func(mut item: syn::Item) -> Result<TokenStream> {
let attrs = match &mut item {
syn::Item::Struct(item) => &mut item.attrs,
syn::Item::Fn(item) => &mut item.attrs,
_ => bail!(item, "expected struct or fn"),
};
let docs = documentation(&attrs);
let mut lines: Vec<_> = docs.lines().collect();
let Some(category) = lines.pop().and_then(|s| s.strip_prefix("Category: ")) else {
bail!(item, "expected category");
};
let Some(display) = lines.pop().and_then(|s| s.strip_prefix("Display: ")) else {
bail!(item, "expected display name");
};
let mut docs = lines.join("\n");
let (params, returns) = params(&mut docs)?;
let docs = docs.trim();
attrs.retain(|attr| !attr.path.is_ident("doc"));
attrs.push(parse_quote! { #[doc = #docs] });
let info = quote! {
::typst::eval::FuncInfo {
name,
display: "TODO",
category: "TODO",
display: #display,
category: #category,
docs: #docs,
params: ::std::vec![#(#params),*],
returns: ::std::vec![#(#returns),*]

View File

@ -598,13 +598,15 @@ pub enum Meta {
}
/// Host for metadata.
///
/// Display: Meta
/// Category: special
#[node]
pub struct MetaNode {
/// Metadata that should be attached to all elements affected by this style
/// property.
#[settable]
#[fold]
#[skip]
#[default]
pub data: Vec<Meta>,
}

View File

@ -326,6 +326,9 @@ impl Sum for Content {
}
/// A node with applied styles.
///
/// Display: Styled
/// Category: special
#[node]
pub struct StyledNode {
/// The styled content.
@ -347,6 +350,9 @@ cast_from_value! {
///
/// Combines other arbitrary content. So, when you write `[Hi] + [you]` in
/// Typst, the two text nodes are combined into a single sequence node.
///
/// Display: Sequence
/// Category: special
#[node]
pub struct SequenceNode {
#[variadic]

View File

@ -146,8 +146,8 @@ impl Args {
}
fn library() -> Library {
/// Category: test
/// Display: Test
/// Category: test
#[func]
fn test(args: &mut typst::eval::Args) -> SourceResult<Value> {
let lhs = args.expect::<Value>("left-hand side")?;
@ -158,8 +158,8 @@ fn library() -> Library {
Ok(Value::None)
}
/// Category: test
/// Display: Print
/// Category: test
#[func]
fn print(args: &mut typst::eval::Args) -> SourceResult<Value> {
print!("> ");

View File

@ -115,7 +115,7 @@
// Should output `Some` in red, `Some` in blue and `Last` in green.
// Everything should be in smallcaps.
#for color in (red, blue, green, yellow) [
#set text("Roboto")
#set text(font: "Roboto")
#show: it => text(fill: color, it)
#smallcaps(if color != green [
Some

View File

@ -44,8 +44,8 @@
}
---
// Error: 28-34 duplicate argument
#set text(family: "Arial", family: "Helvetica")
// Error: 26-30 duplicate argument
#set text(font: "Arial", font: "Helvetica")
---
// Error: 2-6 expected function, found boolean

View File

@ -7,7 +7,7 @@ The non-breaking~space does work.
// Make sure non-breaking and normal space always
// have the same width. Even if the font decided
// differently.
#set text("New Computer Modern")
#set text(font: "New Computer Modern")
a b \
a~b
@ -16,5 +16,5 @@ a~b
- Em dash: ---
---
#set text("Roboto")
#set text(font: "Roboto")
A... vs #"A..."

View File

@ -2,14 +2,14 @@
---
// Test classic example.
#set text("Roboto")
#set text(font: "Roboto")
#show "Der Spiegel": smallcaps
Die Zeitung Der Spiegel existiert.
---
// Another classic example.
#show "TeX": [T#h(-0.145em)#box(move(dy: 0.233em)[E])#h(-0.135em)X]
#show regex("(Lua)?(La)?TeX"): name => box(text("New Computer Modern")[#name])
#show regex("(Lua)?(La)?TeX"): name => box(text(font: "New Computer Modern")[#name])
TeX, LaTeX, LuaTeX and LuaLaTeX!

View File

@ -54,7 +54,7 @@
#eval("let")
---
#show raw: it => text("PT Sans", eval("[" + it.text + "]"))
#show raw: it => text(font: "PT Sans", eval("[" + it.text + "]"))
Interacting
```

View File

@ -3,7 +3,7 @@
---
// Test normal operation and RTL directions.
#set page(height: 3.25cm, width: 7.05cm, columns: 2)
#set text(lang: "ar", "Noto Sans Arabic", "Linux Libertine")
#set text(lang: "ar", font: ("Noto Sans Arabic", "Linux Libertine"))
#set columns(gutter: 30pt)
#box(fill: conifer, height: 8pt, width: 6pt) وتحفيز

View File

@ -37,7 +37,7 @@
---
// Test numbering with closure and nested lists.
#set text("New Computer Modern")
#set text(font: "New Computer Modern")
#set enum(numbering: (..args) => math.mat(args.pos()), full: true)
+ A
+ B

View File

@ -24,5 +24,5 @@
#set page("a4")
#set page("a5")
#set page("a11", flipped: true, fill: eastern)
#set text("Roboto", white)
#set text(font: "Roboto", white)
#smallcaps[Typst]

View File

@ -23,7 +23,7 @@
---
// Test page fill.
#set page(width: 80pt, height: 40pt, fill: eastern)
#text(15pt, "Roboto", fill: white, smallcaps[Typst])
#text(15pt, font: "Roboto", fill: white, smallcaps[Typst])
#page(width: 40pt, fill: none, margin: (top: 10pt, rest: auto))[Hi]
---

View File

@ -10,7 +10,7 @@
// Test that consecutive, embedded LTR runs stay LTR.
// Here, we have two runs: "A" and italic "B".
#let content = par[أنت A#emph[B]مطرC]
#set text("PT Sans", "Noto Sans Arabic")
#set text(font: ("PT Sans", "Noto Sans Arabic"))
#text(lang: "ar", content)
#text(lang: "de", content)
@ -18,7 +18,7 @@
// Test that consecutive, embedded RTL runs stay RTL.
// Here, we have three runs: "גֶ", bold "שֶׁ", and "ם".
#let content = par[Aגֶ#strong[שֶׁ]םB]
#set text("Linux Libertine", "Noto Serif Hebrew")
#set text(font: ("Linux Libertine", "Noto Serif Hebrew"))
#text(lang: "he", content)
#text(lang: "de", content)
@ -29,7 +29,7 @@
---
// Test hard line break (leads to two paragraphs in unicode-bidi).
#set text(lang: "ar", "Noto Sans Arabic", "PT Sans")
#set text(lang: "ar", font: ("Noto Sans Arabic", "PT Sans"))
Life المطر هو الحياة \
الحياة تمطر is rain.

View File

@ -20,7 +20,7 @@ starts a paragraph without indent.
Except if you have another paragraph in them.
#set text(8pt, lang: "ar", "Noto Sans Arabic", "Linux Libertine")
#set text(8pt, lang: "ar", font: ("Noto Sans Arabic", "Linux Libertine"))
#set par(leading: 8pt)
= Arabic

View File

@ -1,6 +1,6 @@
#set page(width: auto, height: auto)
#set par(leading: 4pt, justify: true)
#set text(family: "New Computer Modern")
#set text(font: "New Computer Modern")
#let story = [
In olden times when wishing still helped one, there lived a king whose

View File

@ -23,7 +23,7 @@
[X]
}
#set text("New Computer Modern", size)
#set text(font: "New Computer Modern", size)
Neither #tex, \
nor #xetex!

View File

@ -15,5 +15,5 @@ $ x := #table(columns: 2)[x][y]/mat(1, 2, 3)
---
// Test font switch.
#let here = text.with("Noto Sans")
#let here = text.with(font: "Noto Sans")
$#here[f] := #here[Hi there]$.

View File

@ -11,7 +11,7 @@ $ &sin x + log_2 x \
---
// Test scripts vs limits.
#set text("New Computer Modern")
#set text(font: "New Computer Modern")
Discuss $lim_(n->oo) 1/n$ now.
$ lim_(n->infinity) 1/n = 0 $

View File

@ -26,5 +26,5 @@ $text(#red, "time"^2) + sqrt("place")$
---
// Test different font.
#show math.formula: set text(family: "Fira Math")
#show math.formula: set text(font: "Fira Math")
$ v := vec(1 + 2, 2 - 4, sqrt(3), arrow(x)) + 1 $

View File

@ -38,7 +38,7 @@ multiline.
---
// Test styling.
#show heading.where(level: 5): it => block(
text(family: "Roboto", fill: eastern, it.title + [!])
text(font: "Roboto", fill: eastern, it.title + [!])
)
= Heading

View File

@ -1,7 +1,7 @@
// Test chinese text from Wikipedia.
---
#set text("Noto Serif CJK SC")
#set text(font: "Noto Serif CJK SC")
是美国广播公司电视剧《迷失》第3季的第22和23集也是全剧的第71集和72集
由执行制作人戴蒙·林道夫和卡尔顿·库斯编剧,导演则是另一名执行制作人杰克·本德

View File

@ -10,7 +10,7 @@
#underline(offset: 5pt)[Further below.]
// Different color.
#underline(red, evade: false)[Critical information is conveyed here.]
#underline(stroke: red, evade: false)[Critical information is conveyed here.]
// Inherits font color.
#text(fill: red, underline[Change with the wind.])
@ -19,7 +19,7 @@
#overline(underline[Running amongst the wolves.])
---
#let redact = strike.with(10pt, extent: 0.05em)
#let redact = strike.with(stroke: 10pt, extent: 0.05em)
#let highlight = strike.with(stroke: 10pt + rgb("abcdef88"), extent: 0.05em)
// Abuse thickness and transparency for redacting and highlighting stuff.

View File

@ -5,7 +5,7 @@
#set text(size: 8pt)
#let try(top, bottom) = rect(inset: 0pt, fill: conifer)[
#set text("IBM Plex Mono", top-edge: top, bottom-edge: bottom)
#set text(font: "IBM Plex Mono", top-edge: top, bottom-edge: bottom)
From #top to #bottom
]

View File

@ -11,7 +11,7 @@
---
// Test alternates and stylistic sets.
#set text("IBM Plex Serif")
#set text(font: "IBM Plex Serif")
a vs #text(alternates: true)[a] \
ß vs #text(stylistic-set: 5)[ß]
@ -33,7 +33,7 @@ fi vs. #text(ligatures: false)[No fi]
---
// Test extra number stuff.
#set text("IBM Plex Serif")
#set text(font: "IBM Plex Serif")
0 vs. #text(slashed-zero: true)[0] \
1/2 vs. #text(fractions: true)[1/2]

View File

@ -18,8 +18,8 @@
// Set stretch (not available, matching closest).
#text(stretch: 50%)[Condensed]
// Set family.
#text(family: "IBM Plex Serif")[Serif]
// Set font family.
#text(font: "IBM Plex Serif")[Serif]
// Emoji.
Emoji: 🐪, 🌋, 🏞
@ -32,16 +32,16 @@ Emoji: 🐪, 🌋, 🏞
// Disable font fallback beyond the user-specified list.
// Without disabling, New Computer Modern Math would come to the rescue.
#set text("PT Sans", "Twitter Color Emoji", fallback: false)
#set text(font: ("PT Sans", "Twitter Color Emoji"), fallback: false)
= 𝛼 + 𝛽.
---
// Test string body.
#text("Text") \
#text(red, "Text") \
#text("Ubuntu", blue, "Text") \
#text([Text], teal, "IBM Plex Serif") \
#text(forest, "New Computer Modern", [Text]) \
#text(font: "Ubuntu", blue, "Text") \
#text([Text], teal, font: "IBM Plex Serif") \
#text(forest, font: "New Computer Modern", [Text]) \
---
// Error: 11-16 unexpected argument
@ -55,10 +55,6 @@ Emoji: 🐪, 🌋, 🏞
// Error: 23-27 unexpected argument
#set text(size: 10pt, 12pt)
---
// Error: 32-39 unexpected argument
#set text(family: "Helvetica", "Arial")
---
// Error: 11-31 unexpected argument
#set text(something: "invalid")

View File

@ -12,7 +12,7 @@
---
// Test that the language passed to the shaper has an effect.
#set text("Ubuntu")
#set text(font: "Ubuntu")
// Some lowercase letters are different in Serbian Cyrillic compared to other
// Cyrillic languages. Since there is only one set of Unicode codepoints for

View File

@ -12,7 +12,7 @@
]
// Test hanging punctuation with RTL.
#set text(lang: "he", "PT Sans", "Noto Serif Hebrew")
#set text(lang: "he", font: ("PT Sans", "Noto Serif Hebrew"))
בנייה נכונה של משפטים ארוכים דורשת ידע בשפה. אז בואו נדבר על מזג האוויר.
---

View File

@ -46,7 +46,7 @@ The keyword ```rust let```.
---
// Text show rule
#show raw: set text("Roboto")
#show raw: set text(font: "Roboto")
`Roboto`
---

View File

@ -14,5 +14,5 @@ ABCअपार्टमेंट
---
// Test that RTL safe-to-break doesn't panic even though newline
// doesn't exist in shaping output.
#set text(dir: rtl, "Noto Serif Hebrew")
#set text(dir: rtl, font: "Noto Serif Hebrew")
\ ט

View File

@ -23,11 +23,11 @@ A /**/B/**/ C
---
// Test that a run consisting only of whitespace isn't trimmed.
A#text("IBM Plex Serif")[ ]B
A#text(font: "IBM Plex Serif")[ ]B
---
// Test font change after space.
Left #text("IBM Plex Serif")[Right].
Left #text(font: "IBM Plex Serif")[Right].
---
// Test that linebreak consumed surrounding spaces.

View File

@ -5,7 +5,7 @@
#emoji.woman.old
#emoji.turtle
#set text("New Computer Modern Math")
#set text(font: "New Computer Modern Math")
#sym.arrow
#sym.arrow.l
#sym.arrow.r.squiggly

View File

@ -11,7 +11,7 @@ I'm in#text(tracking: 0.15em + 1.5pt)[ spaace]!
---
// Test that tracking doesn't disrupt mark placement.
#set text("PT Sans", "Noto Serif Hebrew")
#set text(font: ("PT Sans", "Noto Serif Hebrew"))
#set text(tracking: 0.3em)
טֶקסט

View File

@ -42,7 +42,7 @@
---
// Test stroke composition.
#set square(stroke: 4pt)
#set text("Roboto")
#set text(font: "Roboto")
#square(
stroke: (left: red, top: yellow, right: green, bottom: blue),
radius: 100%, align(center+horizon)[*G*],