diff --git a/Cargo.lock b/Cargo.lock index feb932b01..da9700708 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2962,6 +2962,7 @@ name = "typst-docs" version = "0.7.0" dependencies = [ "comemo", + "ecow", "heck", "include_dir", "once_cell", diff --git a/crates/typst-docs/Cargo.toml b/crates/typst-docs/Cargo.toml index 625c67da3..b9158a38f 100644 --- a/crates/typst-docs/Cargo.toml +++ b/crates/typst-docs/Cargo.toml @@ -14,6 +14,7 @@ bench = false typst = { path = "../typst" } typst-library = { path = "../typst-library" } comemo = "0.3" +ecow = { version = "0.1.2", features = ["serde"] } heck = "0.4" include_dir = "0.7" once_cell = "1" diff --git a/crates/typst-docs/src/html.rs b/crates/typst-docs/src/html.rs index bde69d89d..c64649e3d 100644 --- a/crates/typst-docs/src/html.rs +++ b/crates/typst-docs/src/html.rs @@ -1,17 +1,22 @@ +use std::fmt::{self, Debug, Formatter}; use std::ops::Range; use comemo::Prehashed; +use ecow::EcoString; +use heck::{ToKebabCase, ToTitleCase}; use pulldown_cmark as md; +use serde::{Deserialize, Serialize}; use typed_arena::Arena; -use typst::diag::FileResult; -use typst::eval::{Bytes, Datetime, Tracer}; +use typst::diag::{FileResult, StrResult}; +use typst::eval::{Bytes, Datetime, Library, Tracer}; use typst::font::{Font, FontBook}; -use typst::geom::{Point, Size}; +use typst::geom::{Abs, Point, Size}; use typst::syntax::{FileId, Source, VirtualPath}; use typst::World; +use unscanny::Scanner; use yaml_front_matter::YamlFrontMatter; -use super::*; +use super::{contributors, OutlineItem, Resolver, FILE_DIR, FONTS, LIBRARY}; /// HTML documentation. #[derive(Serialize)] @@ -21,7 +26,7 @@ pub struct Html { #[serde(skip)] md: String, #[serde(skip)] - description: Option, + description: Option, #[serde(skip)] outline: Vec, } @@ -39,18 +44,7 @@ impl Html { /// Convert markdown to HTML. #[track_caller] - pub fn markdown(resolver: &dyn Resolver, md: &str) -> Self { - Self::markdown_with_id_base(resolver, md, "") - } - - /// Convert markdown to HTML, preceding all fragment identifiers with the - /// `id_base`. - #[track_caller] - pub fn markdown_with_id_base( - resolver: &dyn Resolver, - md: &str, - id_base: &str, - ) -> Self { + pub fn markdown(resolver: &dyn Resolver, md: &str, nesting: Option) -> Self { let mut text = md; let mut description = None; let document = YamlFrontMatter::parse::(md); @@ -62,9 +56,18 @@ impl Html { let options = md::Options::ENABLE_TABLES | md::Options::ENABLE_HEADING_ATTRIBUTES; let ids = Arena::new(); - let mut handler = Handler::new(resolver, id_base.into(), &ids); - let iter = md::Parser::new_ext(text, options) - .filter_map(|mut event| handler.handle(&mut event).then_some(event)); + let mut handler = Handler::new(text, resolver, nesting, &ids); + let mut events = md::Parser::new_ext(text, options).peekable(); + let iter = std::iter::from_fn(|| loop { + let mut event = events.next()?; + handler.peeked = events.peek().and_then(|event| match event { + md::Event::Text(text) => Some(text.clone()), + _ => None, + }); + if handler.handle(&mut event) { + return Some(event); + } + }); let mut raw = String::new(); md::html::push_html(&mut raw, iter); @@ -93,7 +96,11 @@ impl Html { /// Returns `None` if the HTML doesn't start with an `h1` tag. pub fn title(&self) -> Option<&str> { let mut s = Scanner::new(&self.raw); - s.eat_if("

").then(|| s.eat_until("

")) + s.eat_if("'); + s.eat_if('>'); + s.eat_until("") + }) } /// The outline of the HTML. @@ -102,7 +109,7 @@ impl Html { } /// The description from the front matter. - pub fn description(&self) -> Option { + pub fn description(&self) -> Option { self.description.clone() } } @@ -116,26 +123,35 @@ impl Debug for Html { /// Front matter metadata. #[derive(Deserialize)] struct Metadata { - description: String, + description: EcoString, } struct Handler<'a> { + text: &'a str, resolver: &'a dyn Resolver, - lang: Option, - code: String, + peeked: Option>, + lang: Option, + code: EcoString, outline: Vec, - id_base: String, + nesting: Option, ids: &'a Arena, } impl<'a> Handler<'a> { - fn new(resolver: &'a dyn Resolver, id_base: String, ids: &'a Arena) -> Self { + fn new( + text: &'a str, + resolver: &'a dyn Resolver, + nesting: Option, + ids: &'a Arena, + ) -> Self { Self { + text, resolver, + peeked: None, lang: None, - code: String::new(), + code: EcoString::new(), outline: vec![], - id_base, + nesting, ids, } } @@ -157,15 +173,13 @@ impl<'a> Handler<'a> { } // Register HTML headings for the outline. - md::Event::Start(md::Tag::Heading(level, Some(id), _)) => { + md::Event::Start(md::Tag::Heading(level, id, _)) => { self.handle_heading(id, level); } // Also handle heading closings. - md::Event::End(md::Tag::Heading(level, Some(_), _)) => { - if *level > md::HeadingLevel::H1 && !self.id_base.is_empty() { - nest_heading(level); - } + md::Event::End(md::Tag::Heading(level, _, _)) => { + nest_heading(level, self.nesting()); } // Rewrite contributor sections. @@ -185,10 +199,10 @@ impl<'a> Handler<'a> { "unsupported link type: {ty:?}", ); - *dest = self - .handle_link(dest) - .unwrap_or_else(|| panic!("invalid link: {dest}")) - .into(); + *dest = match self.handle_link(dest) { + Ok(link) => link.into(), + Err(err) => panic!("invalid link: {dest} ({err})"), + }; } // Inline raw. @@ -208,7 +222,7 @@ impl<'a> Handler<'a> { // Code blocks. md::Event::Start(md::Tag::CodeBlock(md::CodeBlockKind::Fenced(lang))) => { self.lang = Some(lang.as_ref().into()); - self.code = String::new(); + self.code = EcoString::new(); return false; } md::Event::End(md::Tag::CodeBlock(md::CodeBlockKind::Fenced(_))) => { @@ -232,7 +246,7 @@ impl<'a> Handler<'a> { } fn handle_image(&self, link: &str) -> String { - if let Some(file) = FILES.get_file(link) { + if let Some(file) = FILE_DIR.get_file(link) { self.resolver.image(link, file.contents()) } else if let Some(url) = self.resolver.link(link) { url @@ -241,16 +255,35 @@ impl<'a> Handler<'a> { } } - fn handle_heading(&mut self, id: &mut &'a str, level: &mut md::HeadingLevel) { + fn handle_heading( + &mut self, + id_slot: &mut Option<&'a str>, + level: &mut md::HeadingLevel, + ) { + nest_heading(level, self.nesting()); if *level == md::HeadingLevel::H1 { return; } + let default = self.peeked.as_ref().map(|text| text.to_kebab_case()); + let id: &'a str = match (&id_slot, default) { + (Some(id), default) => { + if Some(*id) == default.as_deref() { + eprintln!("heading id #{id} was specified unnecessarily"); + } + id + } + (None, Some(default)) => self.ids.alloc(default).as_str(), + (None, None) => panic!("missing heading id {}", self.text), + }; + + *id_slot = (!id.is_empty()).then_some(id); + // Special case for things like "v0.3.0". let name = if id.starts_with('v') && id.contains('.') { - id.to_string() + id.into() } else { - id.to_title_case() + id.to_title_case().into() }; let mut children = &mut self.outline; @@ -262,106 +295,22 @@ impl<'a> Handler<'a> { depth -= 1; } - // Put base before id. - if !self.id_base.is_empty() { - nest_heading(level); - *id = self.ids.alloc(format!("{}-{id}", self.id_base)).as_str(); - } - - children.push(OutlineItem { id: id.to_string(), name, children: vec![] }); + children.push(OutlineItem { id: id.into(), name, children: vec![] }); } - fn handle_link(&self, link: &str) -> Option { - if link.starts_with('#') || link.starts_with("http") { - return Some(link.into()); + fn handle_link(&self, link: &str) -> StrResult { + if let Some(link) = self.resolver.link(link) { + return Ok(link); } - if !link.starts_with('$') { - return self.resolver.link(link); + crate::link::resolve(link) + } + + fn nesting(&self) -> usize { + match self.nesting { + Some(nesting) => nesting, + None => panic!("headings are not allowed here:\n{}", self.text), } - - let root = link.split('/').next()?; - let rest = &link[root.len()..].trim_matches('/'); - let base = match root { - "$tutorial" => "/docs/tutorial/", - "$reference" => "/docs/reference/", - "$category" => "/docs/reference/", - "$syntax" => "/docs/reference/syntax/", - "$styling" => "/docs/reference/styling/", - "$scripting" => "/docs/reference/scripting/", - "$types" => "/docs/reference/types/", - "$type" => "/docs/reference/types/", - "$func" => "/docs/reference/", - "$guides" => "/docs/guides/", - "$packages" => "/docs/packages/", - "$changelog" => "/docs/changelog/", - "$community" => "/docs/community/", - _ => panic!("unknown link root: {root}"), - }; - - let mut route = base.to_string(); - if root == "$type" && rest.contains('.') { - let mut parts = rest.split('.'); - let ty = parts.next()?; - let method = parts.next()?; - route.push_str(ty); - route.push_str("/#methods-"); - route.push_str(method); - } else if root == "$func" { - let mut parts = rest.split('.').peekable(); - let first = parts.peek().copied(); - let mut focus = &LIBRARY.global; - while let Some(m) = first.and_then(|name| module(focus, name).ok()) { - focus = m; - parts.next(); - } - - let name = parts.next()?; - - let value = focus.get(name).ok()?; - let Value::Func(func) = value else { return None }; - let info = func.info()?; - route.push_str(info.category); - route.push('/'); - - if let Some(group) = GROUPS - .iter() - .filter(|_| first == Some("math")) - .find(|group| group.functions.iter().any(|func| func == info.name)) - { - route.push_str(&group.name); - route.push_str("/#"); - route.push_str(info.name); - if let Some(param) = parts.next() { - route.push_str("-parameters-"); - route.push_str(param); - } - } else { - route.push_str(name); - route.push('/'); - if let Some(next) = parts.next() { - if info.params.iter().any(|param| param.name == next) { - route.push_str("#parameters-"); - route.push_str(next); - } else if info.scope.iter().any(|(name, _)| name == next) { - route.push('#'); - route.push_str(info.name); - route.push('-'); - route.push_str(next); - } else { - return None; - } - } - } - } else { - route.push_str(rest); - } - - if !route.contains('#') && !route.ends_with('/') { - route.push('/'); - } - - Some(route) } } @@ -466,15 +415,10 @@ fn html_attr_range(html: &str, attr: &str) -> Option> { } /// Increase the nesting level of a Markdown heading. -fn nest_heading(level: &mut md::HeadingLevel) { - *level = match &level { - md::HeadingLevel::H1 => md::HeadingLevel::H2, - md::HeadingLevel::H2 => md::HeadingLevel::H3, - md::HeadingLevel::H3 => md::HeadingLevel::H4, - md::HeadingLevel::H4 => md::HeadingLevel::H5, - md::HeadingLevel::H5 => md::HeadingLevel::H6, - v => **v, - }; +fn nest_heading(level: &mut md::HeadingLevel, nesting: usize) { + *level = ((*level as usize) + nesting) + .try_into() + .unwrap_or(md::HeadingLevel::H6); } /// A world for example compilations. @@ -499,7 +443,7 @@ impl World for DocWorld { fn file(&self, id: FileId) -> FileResult { assert!(id.package().is_none()); - Ok(FILES + Ok(FILE_DIR .get_file(id.vpath().as_rootless_path()) .unwrap_or_else(|| panic!("failed to load {:?}", id.vpath())) .contents() diff --git a/crates/typst-docs/src/lib.rs b/crates/typst-docs/src/lib.rs index 96ae460c8..3018c244f 100644 --- a/crates/typst-docs/src/lib.rs +++ b/crates/typst-docs/src/lib.rs @@ -2,49 +2,39 @@ mod contribs; mod html; +mod link; +mod model; pub use contribs::{contributors, Author, Commit}; pub use html::Html; +pub use model::*; -use std::fmt::{self, Debug, Formatter}; use std::path::Path; use comemo::Prehashed; +use ecow::{eco_format, EcoString}; use heck::ToTitleCase; use include_dir::{include_dir, Dir}; use once_cell::sync::Lazy; use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use serde_yaml as yaml; +use typst::diag::{bail, StrResult}; use typst::doc::Frame; -use typst::eval::{CastInfo, Func, FuncInfo, Library, Module, ParamInfo, Value}; +use typst::eval::{CastInfo, Func, Library, Module, ParamInfo, Scope, Type, Value}; use typst::font::{Font, FontBook}; use typst::geom::{Abs, Smart}; use typst_library::layout::{Margin, PageElem}; -use unscanny::Scanner; -static DOCS: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/../../docs"); -static FILES: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/../../assets/files"); -static DETAILS: Lazy = Lazy::new(|| yaml("reference/details.yml")); +static DOCS_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/../../docs"); +static FILE_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/../../assets/files"); +static FONT_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/../../assets/fonts"); + +static CATEGORIES: Lazy = Lazy::new(|| yaml("reference/categories.yml")); static GROUPS: Lazy> = Lazy::new(|| yaml("reference/groups.yml")); -static FONTS: Lazy<(Prehashed, Vec)> = Lazy::new(|| { - static DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/../../assets/fonts"); - let fonts: Vec<_> = DIR - .files() - .flat_map(|file| Font::iter(file.contents().into())) - .collect(); - let book = FontBook::from_fonts(&fonts); - (Prehashed::new(book), fonts) -}); - static LIBRARY: Lazy> = Lazy::new(|| { let mut lib = typst_library::build(); - // Hack for documenting the `mix` function in the color module. - // Will be superseded by proper associated functions. - lib.global - .scope_mut() - .define("mix", typst_library::compute::mix_func()); lib.styles .set(PageElem::set_width(Smart::Custom(Abs::pt(240.0).into()))); lib.styles.set(PageElem::set_height(Smart::Auto)); @@ -55,13 +45,22 @@ static LIBRARY: Lazy> = Lazy::new(|| { Prehashed::new(lib) }); +static FONTS: Lazy<(Prehashed, Vec)> = Lazy::new(|| { + let fonts: Vec<_> = FONT_DIR + .files() + .flat_map(|file| Font::iter(file.contents().into())) + .collect(); + let book = FontBook::from_fonts(&fonts); + (Prehashed::new(book), fonts) +}); + /// Build documentation pages. pub fn provide(resolver: &dyn Resolver) -> Vec { vec![ markdown_page(resolver, "/docs/", "overview.md").with_route("/docs/"), tutorial_pages(resolver), reference_pages(resolver), - guides_pages(resolver), + guide_pages(resolver), packages_page(resolver), markdown_page(resolver, "/docs/", "changelog.md"), markdown_page(resolver, "/docs/", "roadmap.md"), @@ -84,54 +83,32 @@ pub trait Resolver { fn commits(&self, from: &str, to: &str) -> Vec; } -/// Details about a documentation page and its children. -#[derive(Debug, Serialize)] -pub struct PageModel { - pub route: String, - pub title: String, - pub description: String, - pub part: Option<&'static str>, - pub outline: Vec, - pub body: BodyModel, - pub children: Vec, -} - -impl PageModel { - fn with_route(self, route: &str) -> Self { - Self { route: route.into(), ..self } +/// Create a page from a markdown file. +#[track_caller] +fn markdown_page( + resolver: &dyn Resolver, + parent: &str, + path: impl AsRef, +) -> PageModel { + assert!(parent.starts_with('/') && parent.ends_with('/')); + let md = DOCS_DIR.get_file(path).unwrap().contents_utf8().unwrap(); + let html = Html::markdown(resolver, md, Some(0)); + let title: EcoString = html.title().expect("chapter lacks a title").into(); + PageModel { + route: eco_format!("{parent}{}/", urlify(&title)), + title, + description: html.description().unwrap(), + part: None, + outline: html.outline(), + body: BodyModel::Html(html), + children: vec![], } - - fn with_part(self, part: &'static str) -> Self { - Self { part: Some(part), ..self } - } -} - -/// An element in the "On This Page" outline. -#[derive(Debug, Clone, Serialize)] -pub struct OutlineItem { - id: String, - name: String, - children: Vec, -} - -/// Details about the body of a documentation page. -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -#[serde(tag = "kind", content = "content")] -pub enum BodyModel { - Html(Html), - Category(CategoryModel), - Func(FuncModel), - Funcs(FuncsModel), - Type(TypeModel), - Symbols(SymbolsModel), - Packages(Html), } /// Build the tutorial. fn tutorial_pages(resolver: &dyn Resolver) -> PageModel { let mut page = markdown_page(resolver, "/docs/", "tutorial/welcome.md"); - page.children = DOCS + page.children = DOCS_DIR .get_dir("tutorial") .unwrap() .files() @@ -149,23 +126,20 @@ fn reference_pages(resolver: &dyn Resolver) -> PageModel { .with_part("Language"), markdown_page(resolver, "/docs/reference/", "reference/styling.md"), markdown_page(resolver, "/docs/reference/", "reference/scripting.md"), - types_page(resolver, "/docs/reference/"), - category_page(resolver, "text").with_part("Content"), + category_page(resolver, "foundations").with_part("Library"), + category_page(resolver, "text"), category_page(resolver, "math"), category_page(resolver, "layout"), category_page(resolver, "visualize"), category_page(resolver, "meta"), category_page(resolver, "symbols"), - category_page(resolver, "foundations").with_part("Compute"), - category_page(resolver, "calculate"), - category_page(resolver, "construct"), category_page(resolver, "data-loading"), ]; page } /// Build the guides section. -fn guides_pages(resolver: &dyn Resolver) -> PageModel { +fn guide_pages(resolver: &dyn Resolver) -> PageModel { let mut page = markdown_page(resolver, "/docs/", "guides/welcome.md"); page.children = vec![ markdown_page(resolver, "/docs/guides/", "guides/guide-for-latex-users.md"), @@ -182,257 +156,162 @@ fn packages_page(resolver: &dyn Resolver) -> PageModel { description: "Packages for Typst.".into(), part: None, outline: vec![], - body: BodyModel::Packages(Html::markdown(resolver, details("packages"))), + body: BodyModel::Packages(Html::markdown( + resolver, + category_details("packages"), + Some(1), + )), children: vec![], } } -/// Create a page from a markdown file. -#[track_caller] -fn markdown_page( - resolver: &dyn Resolver, - parent: &str, - path: impl AsRef, -) -> PageModel { - assert!(parent.starts_with('/') && parent.ends_with('/')); - let md = DOCS.get_file(path).unwrap().contents_utf8().unwrap(); - let html = Html::markdown(resolver, md); - let title = html.title().expect("chapter lacks a title").to_string(); - PageModel { - route: format!("{parent}{}/", urlify(&title)), - title, - description: html.description().unwrap(), - part: None, - outline: html.outline(), - body: BodyModel::Html(html), - children: vec![], - } -} - -/// Details about a category. -#[derive(Debug, Serialize)] -pub struct CategoryModel { - pub name: String, - pub details: Html, - pub kind: &'static str, - pub items: Vec, - pub markup_shorthands: Option>, - pub math_shorthands: Option>, -} - -/// Details about a category item. -#[derive(Debug, Serialize)] -pub struct CategoryItem { - pub name: String, - pub route: String, - pub oneliner: String, - pub code: bool, -} - /// Create a page for a category. #[track_caller] fn category_page(resolver: &dyn Resolver, category: &str) -> PageModel { - let route = format!("/docs/reference/{category}/"); + let route = eco_format!("/docs/reference/{category}/"); let mut children = vec![]; let mut items = vec![]; - let focus = match category { - "math" => &LIBRARY.math, - "calculate" => module(&LIBRARY.global, "calc").unwrap(), - _ => &LIBRARY.global, + let (module, path): (&Module, &[&str]) = match category { + "math" => (&LIBRARY.math, &["math"]), + _ => (&LIBRARY.global, &[]), }; - let parents: &[&str] = match category { - "math" => &[], - "calculate" => &["calc"], - _ => &[], - }; - - let grouped = match category { - "math" => GROUPS.as_slice(), - _ => &[], - }; - - // Add functions. - for (_, value) in focus.scope().iter() { - let Value::Func(func) = value else { continue }; - let Some(info) = func.info() else { continue }; - if info.category != category { - continue; + // Add groups. + for mut group in GROUPS.iter().filter(|g| g.category == category).cloned() { + let mut focus = module; + if group.name == "calc" { + focus = get_module(focus, "calc").unwrap(); + group.functions = focus + .scope() + .iter() + .filter(|(_, v)| matches!(v, Value::Func(_))) + .map(|(k, _)| k.clone()) + .collect(); } - - // Skip grouped functions. - if grouped - .iter() - .flat_map(|group| &group.functions) - .any(|f| f == info.name) - { - continue; - } - - let subpage = function_page(resolver, &route, func, info, parents); - items.push(CategoryItem { - name: info.name.into(), - route: subpage.route.clone(), - oneliner: oneliner(info.docs).into(), - code: true, - }); - children.push(subpage); + let (child, item) = group_page(resolver, &route, &group, focus.scope()); + children.push(child); + items.push(item); } - // Add grouped functions. - for group in grouped { - let mut functions = vec![]; - let mut outline = vec![OutlineItem { - id: "summary".into(), - name: "Summary".into(), - children: vec![], - }]; - - for name in &group.functions { - let value = focus.get(name).unwrap(); - let Value::Func(func) = value else { panic!("not a function") }; - let info = func.info().unwrap(); - let func = func_model(resolver, func, info, &[], info.name); - let id = urlify(&func.path.join("-")); - let children = func_outline(&func, &id, false); - outline.push(OutlineItem { id, name: func.display.into(), children }); - functions.push(func); + // Add functions. + let scope = module.scope(); + for (name, value) in scope.iter() { + if scope.get_category(name) != Some(category) { + continue; } - let route = format!("{}{}/", route, group.name); - items.push(CategoryItem { - name: group.name.clone(), - route: route.clone(), - oneliner: oneliner(&group.description).into(), - code: false, - }); + if category == "math" { + // Skip grouped functions. + if GROUPS.iter().flat_map(|group| &group.functions).any(|f| f == name) { + continue; + } - children.push(PageModel { - route, - title: group.display.clone(), - description: format!("Documentation for {} group of functions.", group.name), - part: None, - outline, - body: BodyModel::Funcs(FuncsModel { - name: group.name.clone(), - display: group.display.clone(), - details: Html::markdown(resolver, &group.description), - functions, - }), - children: vec![], - }); + // Already documented in the text category. + if name == "text" { + continue; + } + } + + match value { + Value::Func(func) => { + let name = func.name().unwrap(); + + let subpage = func_page(resolver, &route, func, path); + items.push(CategoryItem { + name: name.into(), + route: subpage.route.clone(), + oneliner: oneliner(func.docs().unwrap_or_default()).into(), + code: true, + }); + children.push(subpage); + } + Value::Type(ty) => { + let subpage = type_page(resolver, &route, ty); + items.push(CategoryItem { + name: ty.short_name().into(), + route: subpage.route.clone(), + oneliner: oneliner(ty.docs()).into(), + code: true, + }); + children.push(subpage); + } + _ => {} + } } children.sort_by_cached_key(|child| child.title.clone()); items.sort_by_cached_key(|item| item.name.clone()); // Add symbol pages. These are ordered manually. - let mut markup_shorthands = vec![]; - let mut math_shorthands = vec![]; + let mut shorthands = None; if category == "symbols" { + let mut markup = vec![]; + let mut math = vec![]; for module in ["sym", "emoji"] { - let subpage = symbol_page(resolver, &route, module); + let subpage = symbols_page(resolver, &route, module); let BodyModel::Symbols(model) = &subpage.body else { continue }; let list = &model.list; - markup_shorthands.extend( + markup.extend( list.iter() .filter(|symbol| symbol.markup_shorthand.is_some()) .cloned(), ); - math_shorthands.extend( + math.extend( list.iter().filter(|symbol| symbol.math_shorthand.is_some()).cloned(), ); items.push(CategoryItem { name: module.into(), route: subpage.route.clone(), - oneliner: oneliner(details(module)).into(), + oneliner: oneliner(category_details(module)).into(), code: true, }); children.push(subpage); } + shorthands = Some(ShorthandsModel { markup, math }); } - let name = category.to_title_case(); - let kind = match category { - "symbols" => "Modules", - _ => "Functions", - }; + let name: EcoString = category.to_title_case().into(); PageModel { route, title: name.clone(), - description: format!("Documentation for functions related to {name} in Typst."), + description: eco_format!( + "Documentation for functions related to {name} in Typst." + ), part: None, - outline: category_outline(kind), + outline: category_outline(), body: BodyModel::Category(CategoryModel { name, - details: Html::markdown(resolver, details(category)), - kind, + details: Html::markdown(resolver, category_details(category), Some(1)), items, - markup_shorthands: Some(markup_shorthands), - math_shorthands: Some(math_shorthands), + shorthands, }), children, } } /// Produce an outline for a category page. -fn category_outline(kind: &str) -> Vec { - vec![ - OutlineItem { - id: "summary".into(), - name: "Summary".into(), - children: vec![], - }, - OutlineItem { - id: urlify(kind), - name: kind.into(), - children: vec![], - }, - ] -} - -/// Details about a function. -#[derive(Debug, Serialize)] -pub struct FuncModel { - pub path: Vec<&'static str>, - pub display: &'static str, - pub keywords: Option<&'static str>, - pub oneliner: &'static str, - pub element: bool, - pub details: Html, - pub params: Vec, - pub returns: Vec<&'static str>, - pub methods: Vec, - pub scope: Vec, -} - -/// Details about a group of functions. -#[derive(Debug, Serialize)] -pub struct FuncsModel { - pub name: String, - pub display: String, - pub details: Html, - pub functions: Vec, +fn category_outline() -> Vec { + vec![OutlineItem::from_name("Summary"), OutlineItem::from_name("Definitions")] } /// Create a page for a function. -fn function_page( +fn func_page( resolver: &dyn Resolver, parent: &str, func: &Func, - info: &FuncInfo, - parents: &[&'static str], + path: &[&str], ) -> PageModel { - let model = func_model(resolver, func, info, parents, ""); + let model = func_model(resolver, func, path, false); + let name = func.name().unwrap(); PageModel { - route: format!("{parent}{}/", urlify(info.name)), - title: info.display.to_string(), - description: format!("Documentation for the `{}` function.", info.name), + route: eco_format!("{parent}{}/", urlify(name)), + title: func.title().unwrap().into(), + description: eco_format!("Documentation for the `{name}` function."), part: None, - outline: func_outline(&model, "", true), + outline: func_outline(&model, ""), body: BodyModel::Func(model), children: vec![], } @@ -442,143 +321,63 @@ fn function_page( fn func_model( resolver: &dyn Resolver, func: &Func, - info: &FuncInfo, - parents: &[&'static str], - id_base: &str, + path: &[&str], + nested: bool, ) -> FuncModel { - let mut s = unscanny::Scanner::new(info.docs); - let docs = s.eat_until("\n## Methods").trim(); + let name = func.name().unwrap(); + let scope = func.scope().unwrap(); + let docs = func.docs().unwrap(); - let mut path = parents.to_vec(); - let mut name = info.name; - for parent in parents.iter().rev() { - name = name - .strip_prefix(parent) - .or(name.strip_prefix(parent.strip_suffix('s').unwrap_or(parent))) - .unwrap_or(name) - .trim_matches('-'); + let mut self_ = false; + let mut params = func.params().unwrap(); + if params.first().map_or(false, |first| first.name == "self") { + self_ = true; + params = ¶ms[1..]; } - path.push(name); - - let scope = info - .scope - .iter() - .filter_map(|(_, value)| { - let Value::Func(func) = value else { return None }; - let info = func.info().unwrap(); - Some(func_model(resolver, func, info, &path, id_base)) - }) - .collect(); let mut returns = vec![]; - casts(resolver, &mut returns, &mut vec![], &info.returns); + casts(resolver, &mut returns, &mut vec![], func.returns().unwrap()); returns.sort_by_key(|ty| type_index(ty)); if returns == ["none"] { returns.clear(); } + let nesting = if nested { None } else { Some(1) }; + let (details, example) = + if nested { split_details_and_example(docs) } else { (docs, None) }; + FuncModel { - path, - display: info.display, - keywords: info.keywords, - oneliner: oneliner(docs), + path: path.iter().copied().map(Into::into).collect(), + name: name.into(), + title: func.title().unwrap(), + keywords: func.keywords(), + oneliner: oneliner(details), element: func.element().is_some(), - details: Html::markdown_with_id_base(resolver, docs, id_base), - params: info.params.iter().map(|param| param_model(resolver, param)).collect(), + details: Html::markdown(resolver, details, nesting), + example: example.map(|md| Html::markdown(resolver, md, None)), + self_, + params: params.iter().map(|param| param_model(resolver, param)).collect(), returns, - methods: method_models(resolver, info.docs), - scope, + scope: scope_models(resolver, name, scope), } } -/// Produce an outline for a function page. -fn func_outline(model: &FuncModel, base: &str, summary: bool) -> Vec { - let mut outline = vec![]; - - if summary { - outline.push(OutlineItem { - id: "summary".into(), - name: "Summary".into(), - children: vec![], - }); - } - - outline.extend(model.details.outline()); - - if !model.params.is_empty() { - let join = if base.is_empty() { "" } else { "-" }; - outline.push(OutlineItem { - id: format!("{base}{join}parameters"), - name: "Parameters".into(), - children: model - .params - .iter() - .map(|param| OutlineItem { - id: format!("{base}{join}parameters-{}", urlify(param.name)), - name: param.name.into(), - children: vec![], - }) - .collect(), - }); - } - - for func in &model.scope { - let id = urlify(&func.path.join("-")); - let children = func_outline(func, &id, false); - outline.push(OutlineItem { id, name: func.display.into(), children }) - } - - outline.extend(methods_outline(&model.methods)); - outline -} - -/// Details about a function parameter. -#[derive(Debug, Serialize)] -pub struct ParamModel { - pub name: &'static str, - pub details: Html, - pub example: Option, - pub types: Vec<&'static str>, - pub strings: Vec, - pub default: Option, - pub positional: bool, - pub named: bool, - pub required: bool, - pub variadic: bool, - pub settable: bool, -} - -/// A specific string that can be passed as an argument. -#[derive(Debug, Serialize)] -pub struct StrParam { - pub string: String, - pub details: Html, -} - /// Produce a parameter's model. fn param_model(resolver: &dyn Resolver, info: &ParamInfo) -> ParamModel { + let (details, example) = split_details_and_example(info.docs); + let mut types = vec![]; let mut strings = vec![]; - casts(resolver, &mut types, &mut strings, &info.cast); + casts(resolver, &mut types, &mut strings, &info.input); if !strings.is_empty() && !types.contains(&"string") { types.push("string"); } types.sort_by_key(|ty| type_index(ty)); - let mut details = info.docs; - let mut example = None; - if let Some(mut i) = info.docs.find("```example") { - while info.docs[..i].ends_with('`') { - i -= 1; - } - details = &info.docs[..i]; - example = Some(&info.docs[i..]); - } - ParamModel { name: info.name, - details: Html::markdown(resolver, details), - example: example.map(|md| Html::markdown(resolver, md)), + details: Html::markdown(resolver, details, None), + example: example.map(|md| Html::markdown(resolver, md, None)), types, strings, default: info.default.map(|default| { @@ -593,6 +392,20 @@ fn param_model(resolver: &dyn Resolver, info: &ParamInfo) -> ParamModel { } } +/// Split up documentation into details and an example. +fn split_details_and_example(docs: &str) -> (&str, Option<&str>) { + let mut details = docs; + let mut example = None; + if let Some(mut i) = docs.find("```") { + while docs[..i].ends_with('`') { + i -= 1; + } + details = &docs[..i]; + example = Some(&docs[i..]); + } + (details, example) +} + /// Process cast information into types and strings. fn casts( resolver: &dyn Resolver, @@ -603,11 +416,11 @@ fn casts( match info { CastInfo::Any => types.push("any"), CastInfo::Value(Value::Str(string), docs) => strings.push(StrParam { - string: string.to_string(), - details: Html::markdown(resolver, docs), + string: string.clone().into(), + details: Html::markdown(resolver, docs, None), }), CastInfo::Value(..) => {} - CastInfo::Type(ty) => types.push(ty), + CastInfo::Type(ty) => types.push(ty.short_name()), CastInfo::Union(options) => { for option in options { casts(resolver, types, strings, option); @@ -616,258 +429,216 @@ fn casts( } } -/// A collection of symbols. -#[derive(Debug, Serialize)] -pub struct TypeModel { - pub name: String, - pub oneliner: &'static str, - pub details: Html, - pub methods: Vec, +/// Produce models for a function's scope. +fn scope_models(resolver: &dyn Resolver, name: &str, scope: &Scope) -> Vec { + scope + .iter() + .filter_map(|(_, value)| { + let Value::Func(func) = value else { return None }; + Some(func_model(resolver, func, &[name], true)) + }) + .collect() } -/// Details about a built-in method on a type. -#[derive(Debug, Serialize)] -pub struct MethodModel { - pub name: &'static str, - pub details: Html, - pub params: Vec, - pub returns: Vec<&'static str>, -} +/// Produce an outline for a function page. +fn func_outline(model: &FuncModel, id_base: &str) -> Vec { + let mut outline = vec![]; -/// Create a page for the types. -fn types_page(resolver: &dyn Resolver, parent: &str) -> PageModel { - let route = format!("{parent}types/"); - let mut children = vec![]; - let mut items = vec![]; + if id_base.is_empty() { + outline.push(OutlineItem::from_name("Summary")); + outline.extend(model.details.outline()); - for model in type_models(resolver) { - let route = format!("{route}{}/", urlify(&model.name)); - items.push(CategoryItem { - name: model.name.clone(), - route: route.clone(), - oneliner: model.oneliner.into(), - code: true, - }); - children.push(PageModel { - route, - title: model.name.to_title_case(), - description: format!("Documentation for the `{}` type.", model.name), - part: None, - outline: type_outline(&model), - body: BodyModel::Type(model), + if !model.params.is_empty() { + outline.push(OutlineItem { + id: "parameters".into(), + name: "Parameters".into(), + children: model + .params + .iter() + .map(|param| OutlineItem { + id: eco_format!("parameters-{}", urlify(param.name)), + name: param.name.into(), + children: vec![], + }) + .collect(), + }); + } + + outline.extend(scope_outline(&model.scope)); + } else { + outline.extend(model.params.iter().map(|param| OutlineItem { + id: eco_format!("{id_base}-{}", urlify(param.name)), + name: param.name.into(), children: vec![], - }); + })); } - PageModel { - route, - title: "Types".into(), - description: "Documentation for Typst's built-in types.".into(), - part: None, - outline: category_outline("Types"), - body: BodyModel::Category(CategoryModel { - name: "Types".into(), - details: Html::markdown(resolver, details("types")), - kind: "Types", - items, - markup_shorthands: None, - math_shorthands: None, - }), - children, - } + outline } -/// Produce the types' models. -fn type_models(resolver: &dyn Resolver) -> Vec { - let file = DOCS.get_file("reference/types.md").unwrap(); - let text = file.contents_utf8().unwrap(); - - let mut s = unscanny::Scanner::new(text); - let mut types = vec![]; - - while s.eat_if("# ") { - let part = s.eat_until("\n# "); - types.push(type_model(resolver, part)); - s.eat_if('\n'); +/// Produce an outline for a function scope. +fn scope_outline(scope: &[FuncModel]) -> Option { + if scope.is_empty() { + return None; } - types + Some(OutlineItem { + id: "definitions".into(), + name: "Definitions".into(), + children: scope + .iter() + .map(|func| { + let id = urlify(&eco_format!("definitions-{}", func.name)); + let children = func_outline(func, &id); + OutlineItem { id, name: func.title.into(), children } + }) + .collect(), + }) +} + +/// Create a page for a group of functions. +fn group_page( + resolver: &dyn Resolver, + parent: &str, + group: &GroupData, + scope: &Scope, +) -> (PageModel, CategoryItem) { + let mut functions = vec![]; + let mut outline = vec![OutlineItem::from_name("Summary")]; + + let path: Vec<_> = group.path.iter().map(|s| s.as_str()).collect(); + let details = Html::markdown(resolver, &group.description, Some(1)); + outline.extend(details.outline()); + + let mut outline_items = vec![]; + for name in &group.functions { + let value = scope.get(name).unwrap(); + let Value::Func(func) = value else { panic!("not a function") }; + let func = func_model(resolver, func, &path, true); + let id_base = urlify(&eco_format!("functions-{}", func.name)); + let children = func_outline(&func, &id_base); + outline_items.push(OutlineItem { + id: id_base, + name: func.title.into(), + children, + }); + functions.push(func); + } + + outline.push(OutlineItem { + id: "functions".into(), + name: "Functions".into(), + children: outline_items, + }); + + let model = PageModel { + route: eco_format!("{parent}{}", group.name), + title: group.display.clone(), + description: eco_format!("Documentation for the {} functions.", group.name), + part: None, + outline, + body: BodyModel::Group(GroupModel { + name: group.name.clone(), + title: group.display.clone(), + details, + functions, + }), + children: vec![], + }; + + let item = CategoryItem { + name: group.name.clone(), + route: model.route.clone(), + oneliner: oneliner(&group.description).into(), + code: false, + }; + + (model, item) +} + +/// Create a page for a type. +fn type_page(resolver: &dyn Resolver, parent: &str, ty: &Type) -> PageModel { + let model = type_model(resolver, ty); + PageModel { + route: eco_format!("{parent}{}/", urlify(ty.short_name())), + title: ty.title().into(), + description: eco_format!("Documentation for the {} type.", ty.title()), + part: None, + outline: type_outline(&model), + body: BodyModel::Type(model), + children: vec![], + } } /// Produce a type's model. -fn type_model(resolver: &dyn Resolver, part: &'static str) -> TypeModel { - let mut s = unscanny::Scanner::new(part); - let display = s.eat_until('\n').trim(); - let docs = s.eat_until("\n## Methods").trim(); +fn type_model(resolver: &dyn Resolver, ty: &Type) -> TypeModel { TypeModel { - name: display.to_lowercase(), - oneliner: oneliner(docs), - details: Html::markdown(resolver, docs), - methods: method_models(resolver, part), - } -} - -/// Produce multiple methods' models. -fn method_models(resolver: &dyn Resolver, docs: &'static str) -> Vec { - let mut s = unscanny::Scanner::new(docs); - s.eat_until("\n## Methods"); - s.eat_whitespace(); - - let mut methods = vec![]; - if s.eat_if("## Methods") { - s.eat_until("\n### "); - while s.eat_if("\n### ") { - methods.push(method_model(resolver, s.eat_until("\n### "))); - } - } - - methods -} - -/// Produce a method's model. -fn method_model(resolver: &dyn Resolver, part: &'static str) -> MethodModel { - let mut s = unscanny::Scanner::new(part); - let mut params = vec![]; - let mut returns = vec![]; - - let name = s.eat_until('(').trim(); - s.expect("()"); - let docs = s.eat_until("\n- ").trim(); - - while s.eat_if("\n- ") { - let name = s.eat_until(':'); - s.expect(": "); - let types: Vec<_> = - s.eat_until(['(', '\n']).split(" or ").map(str::trim).collect(); - if !types.iter().all(|ty| type_index(ty) != usize::MAX) { - panic!( - "unknown type in method {} parameter {}", - name, - types.iter().find(|ty| type_index(ty) == usize::MAX).unwrap() - ) - } - - if name == "returns" { - returns = types; - continue; - } - - s.expect('('); - - let mut named = false; - let mut positional = false; - let mut required = false; - let mut variadic = false; - for part in s.eat_until(')').split(',').map(str::trim) { - match part { - "named" => named = true, - "positional" => positional = true, - "required" => required = true, - "variadic" => variadic = true, - _ => panic!("unknown parameter flag {:?}", part), - } - } - - s.expect(')'); - - params.push(ParamModel { - name, - details: Html::markdown(resolver, s.eat_until("\n- ").trim()), - example: None, - types, - strings: vec![], - default: None, - positional, - named, - required, - variadic, - settable: false, - }); - } - - MethodModel { - name, - details: Html::markdown(resolver, docs), - params, - returns, + name: ty.short_name(), + title: ty.title(), + keywords: ty.keywords(), + oneliner: oneliner(ty.docs()), + details: Html::markdown(resolver, ty.docs(), Some(1)), + constructor: ty + .constructor() + .ok() + .map(|func| func_model(resolver, &func, &[], true)), + scope: scope_models(resolver, ty.short_name(), ty.scope()), } } /// Produce an outline for a type page. fn type_outline(model: &TypeModel) -> Vec { - let mut outline = vec![OutlineItem { - id: "summary".into(), - name: "Summary".into(), - children: vec![], - }]; + let mut outline = vec![OutlineItem::from_name("Summary")]; + outline.extend(model.details.outline()); - outline.extend(methods_outline(&model.methods)); + if let Some(func) = &model.constructor { + outline.push(OutlineItem { + id: "constructor".into(), + name: "Constructor".into(), + children: func_outline(func, "constructor"), + }); + } + + outline.extend(scope_outline(&model.scope)); outline } -/// Produce an outline for a type's method. -fn methods_outline(methods: &[MethodModel]) -> Option { - (!methods.is_empty()).then(|| OutlineItem { - id: "methods".into(), - name: "Methods".into(), - children: methods.iter().map(method_outline).collect(), - }) -} +/// Create a page for symbols. +fn symbols_page(resolver: &dyn Resolver, parent: &str, name: &str) -> PageModel { + let module = get_module(&LIBRARY.global, name).unwrap(); + let title = match name { + "sym" => "General", + "emoji" => "Emoji", + _ => unreachable!(), + }; -/// Produce an outline for a type's method. -fn method_outline(model: &MethodModel) -> OutlineItem { - OutlineItem { - id: format!("methods-{}", urlify(model.name)), - name: model.name.into(), - children: model - .params - .iter() - .map(|param| OutlineItem { - id: format!( - "methods-{}-parameters-{}", - urlify(model.name), - urlify(param.name) - ), - name: param.name.into(), - children: vec![], - }) - .collect(), + let model = symbols_model(resolver, name, title, module.scope()); + PageModel { + route: eco_format!("{parent}{name}/"), + title: title.into(), + description: eco_format!("Documentation for the `{name}` module."), + part: None, + outline: vec![], + body: BodyModel::Symbols(model), + children: vec![], } } -/// A collection of symbols. -#[derive(Debug, Serialize)] -pub struct SymbolsModel { - pub name: &'static str, - pub details: Html, - pub list: Vec, -} - -/// Details about a symbol. -#[derive(Debug, Clone, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct SymbolModel { - pub name: String, - pub markup_shorthand: Option<&'static str>, - pub math_shorthand: Option<&'static str>, - pub codepoint: u32, - pub accent: bool, - pub unicode_name: Option, - pub alternates: Vec, -} - -/// Create a page for symbols. -fn symbol_page(resolver: &dyn Resolver, parent: &str, name: &str) -> PageModel { - let module = module(&LIBRARY.global, name).unwrap(); - +/// Produce a symbol list's model. +fn symbols_model( + resolver: &dyn Resolver, + name: &str, + title: &'static str, + scope: &Scope, +) -> SymbolsModel { let mut list = vec![]; - for (name, value) in module.scope().iter() { + for (name, value) in scope.iter() { let Value::Symbol(symbol) = value else { continue }; let complete = |variant: &str| { if variant.is_empty() { - name.into() + name.clone() } else { - format!("{}.{}", name, variant) + eco_format!("{}.{}", name, variant) } }; @@ -883,7 +654,7 @@ fn symbol_page(resolver: &dyn Resolver, parent: &str, name: &str) -> PageModel { codepoint: c as u32, accent: typst::eval::Symbol::combining_accent(c).is_some(), unicode_name: unicode_names2::name(c) - .map(|s| s.to_string().to_title_case()), + .map(|s| s.to_string().to_title_case().into()), alternates: symbol .variants() .filter(|(other, _)| other != &variant) @@ -893,63 +664,40 @@ fn symbol_page(resolver: &dyn Resolver, parent: &str, name: &str) -> PageModel { } } - let title = match name { - "sym" => "General", - "emoji" => "Emoji", - _ => unreachable!(), - }; - - PageModel { - route: format!("{parent}{name}/"), - title: title.into(), - description: format!("Documentation for the `{name}` module."), - part: None, - outline: vec![], - body: BodyModel::Symbols(SymbolsModel { - name: title, - details: Html::markdown(resolver, details(name)), - list, - }), - children: vec![], + SymbolsModel { + name: title, + details: Html::markdown(resolver, category_details(name), Some(1)), + list, } } -/// Data about a collection of functions. -#[derive(Debug, Deserialize)] -struct GroupData { - name: String, - display: String, - functions: Vec, - description: String, -} - /// Extract a module from another module. #[track_caller] -fn module<'a>(parent: &'a Module, name: &str) -> Result<&'a Module, String> { +fn get_module<'a>(parent: &'a Module, name: &str) -> StrResult<&'a Module> { match parent.scope().get(name) { Some(Value::Module(module)) => Ok(module), - _ => Err(format!("module doesn't contain module `{name}`")), + _ => bail!("module doesn't contain module `{name}`"), } } /// Load YAML from a path. #[track_caller] fn yaml(path: &str) -> T { - let file = DOCS.get_file(path).unwrap(); + let file = DOCS_DIR.get_file(path).unwrap(); yaml::from_slice(file.contents()).unwrap() } /// Load details for an identifying key. #[track_caller] -fn details(key: &str) -> &str { - DETAILS +fn category_details(key: &str) -> &str { + CATEGORIES .get(&yaml::Value::String(key.into())) .and_then(|value| value.as_str()) .unwrap_or_else(|| panic!("missing details for {key}")) } /// Turn a title into an URL fragment. -pub fn urlify(title: &str) -> String { +pub fn urlify(title: &str) -> EcoString { title .chars() .map(|c| c.to_ascii_lowercase()) @@ -974,33 +722,47 @@ const TYPE_ORDER: &[&str] = &[ "any", "none", "auto", - "boolean", - "integer", + "bool", + "int", "float", "length", "angle", "ratio", - "relative length", + "relative", "fraction", "color", "datetime", - "string", + "duration", + "str", "bytes", "regex", "label", "content", "array", - "dictionary", - "function", - "arguments", - "location", - "dir", - "alignment", - "2d alignment", + "dict", + "func", + "args", "selector", + "location", + "direction", + "alignment", + "alignment2d", "stroke", ]; +/// Data about a collection of functions. +#[derive(Debug, Clone, Deserialize)] +struct GroupData { + name: EcoString, + category: EcoString, + display: EcoString, + #[serde(default)] + path: Vec, + #[serde(default)] + functions: Vec, + description: EcoString, +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/typst-docs/src/link.rs b/crates/typst-docs/src/link.rs new file mode 100644 index 000000000..5d7a5485a --- /dev/null +++ b/crates/typst-docs/src/link.rs @@ -0,0 +1,102 @@ +use typst::diag::{bail, StrResult}; +use typst::eval::Func; + +use super::{get_module, GROUPS, LIBRARY}; + +/// Resolve an intra-doc link. +pub fn resolve(link: &str) -> StrResult { + if link.starts_with('#') || link.starts_with("http") { + return Ok(link.to_string()); + } + + let (head, tail) = split_link(link)?; + let mut route = match resolve_known(head) { + Some(route) => route.into(), + None => resolve_definition(head)?, + }; + + if !tail.is_empty() { + route.push('/'); + route.push_str(tail); + } + + if !route.contains('#') && !route.ends_with('/') { + route.push('/'); + } + + Ok(route) +} + +/// Split a link at the first slash. +fn split_link(link: &str) -> StrResult<(&str, &str)> { + let first = link.split('/').next().unwrap_or(link); + let rest = link[first.len()..].trim_start_matches('/'); + Ok((first, rest)) +} + +/// Resolve a `$` link head to a known destination. +fn resolve_known(head: &str) -> Option<&'static str> { + Some(match head { + "$tutorial" => "/docs/tutorial/", + "$reference" => "/docs/reference/", + "$category" => "/docs/reference/", + "$syntax" => "/docs/reference/syntax/", + "$styling" => "/docs/reference/styling/", + "$scripting" => "/docs/reference/scripting/", + "$guides" => "/docs/guides/", + "$packages" => "/docs/packages/", + "$changelog" => "/docs/changelog/", + "$community" => "/docs/community/", + _ => return None, + }) +} + +/// Resolve a `$` link to a global definition. +fn resolve_definition(head: &str) -> StrResult { + let mut parts = head.trim_start_matches('$').split('.').peekable(); + let mut focus = &LIBRARY.global; + while let Some(m) = parts.peek().and_then(|&name| get_module(focus, name).ok()) { + focus = m; + parts.next(); + } + + let name = parts.next().ok_or("link is missing first part")?; + let value = focus.field(name)?; + let Some(category) = focus.scope().get_category(name) else { + bail!("{name} has no category"); + }; + + // Handle grouped functions. + if let Some(group) = GROUPS + .iter() + .filter(|_| category == "math") + .find(|group| group.functions.iter().any(|func| func == name)) + { + let mut route = + format!("/docs/reference/math/{}/#functions-{}", group.name, name); + if let Some(param) = parts.next() { + route.push('-'); + route.push_str(param); + } + return Ok(route); + } + + let mut route = format!("/docs/reference/{category}/{name}/"); + if let Some(next) = parts.next() { + if value.field(next).is_ok() { + route.push_str("#definitions-"); + route.push_str(next); + } else if value + .clone() + .cast::() + .map_or(false, |func| func.param(next).is_some()) + { + route.push_str("#parameters-"); + route.push_str(next); + } else { + bail!("field {next} not found"); + } + } + + Ok(route) +} diff --git a/crates/typst-docs/src/model.rs b/crates/typst-docs/src/model.rs new file mode 100644 index 000000000..46d979a27 --- /dev/null +++ b/crates/typst-docs/src/model.rs @@ -0,0 +1,170 @@ +use ecow::EcoString; +use heck::ToKebabCase; +use serde::Serialize; + +use crate::html::Html; + +/// Details about a documentation page and its children. +#[derive(Debug, Serialize)] +pub struct PageModel { + pub route: EcoString, + pub title: EcoString, + pub description: EcoString, + pub part: Option<&'static str>, + pub outline: Vec, + pub body: BodyModel, + pub children: Vec, +} + +impl PageModel { + pub fn with_route(self, route: &str) -> Self { + Self { route: route.into(), ..self } + } + + pub fn with_part(self, part: &'static str) -> Self { + Self { part: Some(part), ..self } + } +} + +/// An element in the "On This Page" outline. +#[derive(Debug, Clone, Serialize)] +pub struct OutlineItem { + pub id: EcoString, + pub name: EcoString, + pub children: Vec, +} + +impl OutlineItem { + /// Create an outline item from a name with auto-generated id. + pub fn from_name(name: &str) -> Self { + Self { + id: name.to_kebab_case().into(), + name: name.into(), + children: vec![], + } + } +} + +/// Details about the body of a documentation page. +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +#[serde(tag = "kind", content = "content")] +pub enum BodyModel { + Html(Html), + Category(CategoryModel), + Func(FuncModel), + Group(GroupModel), + Type(TypeModel), + Symbols(SymbolsModel), + Packages(Html), +} + +/// Details about a category. +#[derive(Debug, Serialize)] +pub struct CategoryModel { + pub name: EcoString, + pub details: Html, + pub items: Vec, + pub shorthands: Option, +} + +/// Details about a category item. +#[derive(Debug, Serialize)] +pub struct CategoryItem { + pub name: EcoString, + pub route: EcoString, + pub oneliner: EcoString, + pub code: bool, +} + +/// Details about a function. +#[derive(Debug, Serialize)] +pub struct FuncModel { + pub path: Vec, + pub name: EcoString, + pub title: &'static str, + pub keywords: &'static [&'static str], + pub oneliner: &'static str, + pub element: bool, + pub details: Html, + /// This example is only for nested function models. Others can have + /// their example directly in their detals. + pub example: Option, + #[serde(rename = "self")] + pub self_: bool, + pub params: Vec, + pub returns: Vec<&'static str>, + pub scope: Vec, +} + +/// Details about a function parameter. +#[derive(Debug, Serialize)] +pub struct ParamModel { + pub name: &'static str, + pub details: Html, + pub example: Option, + pub types: Vec<&'static str>, + pub strings: Vec, + pub default: Option, + pub positional: bool, + pub named: bool, + pub required: bool, + pub variadic: bool, + pub settable: bool, +} + +/// A specific string that can be passed as an argument. +#[derive(Debug, Serialize)] +pub struct StrParam { + pub string: EcoString, + pub details: Html, +} + +/// Details about a group of functions. +#[derive(Debug, Serialize)] +pub struct GroupModel { + pub name: EcoString, + pub title: EcoString, + pub details: Html, + pub functions: Vec, +} + +/// Details about a type. +#[derive(Debug, Serialize)] +pub struct TypeModel { + pub name: &'static str, + pub title: &'static str, + pub keywords: &'static [&'static str], + pub oneliner: &'static str, + pub details: Html, + pub constructor: Option, + pub scope: Vec, +} + +/// A collection of symbols. +#[derive(Debug, Serialize)] +pub struct SymbolsModel { + pub name: &'static str, + pub details: Html, + pub list: Vec, +} + +/// Details about a symbol. +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SymbolModel { + pub name: EcoString, + pub codepoint: u32, + pub accent: bool, + pub unicode_name: Option, + pub alternates: Vec, + pub markup_shorthand: Option<&'static str>, + pub math_shorthand: Option<&'static str>, +} + +/// Shorthands listed on a category page. +#[derive(Debug, Serialize)] +pub struct ShorthandsModel { + pub markup: Vec, + pub math: Vec, +} diff --git a/crates/typst/src/eval/array.rs b/crates/typst/src/eval/array.rs index 34bfc80e5..cb9234b37 100644 --- a/crates/typst/src/eval/array.rs +++ b/crates/typst/src/eval/array.rs @@ -207,7 +207,7 @@ impl Array { .ok_or_else(|| out_of_bounds_no_default(index, self.len())) } - /// Add a value to the end of the array. + /// Adds a value to the end of the array. #[func] pub fn push( &mut self, @@ -217,14 +217,14 @@ impl Array { self.0.push(value); } - /// Remove the last item from the array and return it. Fails with an error + /// Removes the last item from the array and returns it. Fails with an error /// if the array is empty. #[func] pub fn pop(&mut self) -> StrResult { self.0.pop().ok_or_else(array_is_empty) } - /// Insert a value into the array at the specified index. Fails with an + /// Inserts a value into the array at the specified index. Fails with an /// error if the index is out of bounds. #[func] pub fn insert( @@ -240,7 +240,7 @@ impl Array { Ok(()) } - /// Remove the value at the specified index from the array and return it. + /// Removes the value at the specified index from the array and return it. #[func] pub fn remove( &mut self, @@ -252,7 +252,7 @@ impl Array { Ok(self.0.remove(i)) } - /// Extract a subslice of the array. Fails with an error if the start or + /// Extracts a subslice of the array. Fails with an error if the start or /// index is out of bounds. #[func] pub fn slice( diff --git a/crates/typst/src/eval/dict.rs b/crates/typst/src/eval/dict.rs index 5f17a6915..2b2b8f9e2 100644 --- a/crates/typst/src/eval/dict.rs +++ b/crates/typst/src/eval/dict.rs @@ -154,7 +154,7 @@ impl Dict { .ok_or_else(|| missing_key_no_default(&key)) } - /// Insert a new pair into the dictionary and return the value. If the + /// Inserts a new pair into the dictionary and return the value. If the /// dictionary already contains this key, the value is updated. #[func] pub fn insert( @@ -167,7 +167,7 @@ impl Dict { Arc::make_mut(&mut self.0).insert(key, value); } - /// Remove a pair from the dictionary by key and return the value. + /// Removes a pair from the dictionary by key and return the value. #[func] pub fn remove(&mut self, key: Str) -> StrResult { match Arc::make_mut(&mut self.0).shift_remove(&key) { diff --git a/docs/changelog.md b/docs/changelog.md index c497d006a..591d2df44 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -8,25 +8,25 @@ description: | ## Version 0.7.0 (August 7, 2023) { #v0.7.0 } - Text and Layout - Added support for floating figures through the - [`placement`]($func/figure.placement) argument on the figure function + [`placement`]($figure.placement) argument on the figure function - Added support for arbitrary floating content through the - [`float`]($func/place.float) argument on the place function + [`float`]($place.float) argument on the place function - Added support for loading `.sublime-syntax` files as highlighting - [syntaxes]($func/raw.syntaxes) for raw blocks + [syntaxes]($raw.syntaxes) for raw blocks - Added support for loading `.tmTheme` files as highlighting - [themes]($func/raw.theme) for raw blocks - - Added _bounds_ option to [`top-edge`]($func/text.top-edge) and - [`bottom-edge`]($func/text.bottom-edge) arguments of text function for tight + [themes]($raw.theme) for raw blocks + - Added _bounds_ option to [`top-edge`]($text.top-edge) and + [`bottom-edge`]($text.bottom-edge) arguments of text function for tight bounding boxes - Removed nonsensical top- and bottom-edge options, e.g. _ascender_ for the bottom edge (**Breaking change**) - - Added [`script`]($func/text.script) argument to text function - - Added [`alternative`]($func/smartquote.alternative) argument to smart quote + - Added [`script`]($text.script) argument to text function + - Added [`alternative`]($smartquote.alternative) argument to smart quote function - Added basic i18n for Japanese - Added hyphenation support for `nb` and `nn` language codes in addition to `no` - - Fixed positioning of [placed elements]($func/place) in containers + - Fixed positioning of [placed elements]($place) in containers - Fixed overflowing containers due to optimized line breaks - Export @@ -38,21 +38,21 @@ description: | - Math - Improved layout of primes (e.g. in `[$a'_1$]`) - Improved display of multi-primes (e.g. in `[$a''$]`) - - Improved layout of [roots]($func/math.root) - - Changed relations to show attachments as [limits]($func/math.limits) by - default (e.g. in `[$a ->^x b$]`) + - Improved layout of [roots]($math.root) + - Changed relations to show attachments as [limits]($math.limits) by default + (e.g. in `[$a ->^x b$]`) - Large operators and delimiters are now always vertically centered - - [Boxes]($func/box) in equations now sit on the baseline instead of being + - [Boxes]($box) in equations now sit on the baseline instead of being vertically centered by default. Notably, this does not affect - [blocks]($func/block) because they are not inline elements. - - Added support for [weak spacing]($func/h.weak) + [blocks]($block) because they are not inline elements. + - Added support for [weak spacing]($h.weak) - Added support for OpenType character variants - - Added support for customizing the [math class]($func/math.class) of content + - Added support for customizing the [math class]($math.class) of content - Fixed spacing around `.`, `\/`, and `...` - Fixed spacing between closing delimiters and large operators - Fixed a bug with math font weight selection - Symbols and Operators (**Breaking changes**) - - Added `id`, `im`, and `tr` text [operators]($func/math.op) + - Added `id`, `im`, and `tr` text [operators]($math.op) - Renamed `ident` to `equiv` with alias `eq.triple` and removed `ident.strict` in favor of `eq.quad` - Renamed `ast.sq` to `ast.square` and `integral.sq` to `integral.square` @@ -65,36 +65,32 @@ description: | - Scripting - Fields - - Added `abs` and `em` field to [lengths]($type/length) - - Added `ratio` and `length` field to - [relative lengths]($type/relative-length) - - Added `x` and `y` field to [2d alignments]($func/align.alignment) + - Added `abs` and `em` field to [lengths]($length) + - Added `ratio` and `length` field to [relative lengths]($relative) + - Added `x` and `y` field to [2d alignments]($align.alignment) - Added `paint`, `thickness`, `cap`, `join`, `dash`, and `miter-limit` field - to [strokes]($func/line.stroke) + to [strokes]($stroke) - Accessor and utility methods - - Added [`dedup`]($type/array.dedup) method to arrays - - Added `pt`, `mm`, `cm`, and `inches` method to [lengths]($type/length) - - Added `deg` and `rad` method to [angles]($type/angle) - - Added `kind`, `hex`, `rgba`, `cmyk`, and `luma` method to - [colors]($type/color) - - Added `axis`, `start`, `end`, and `inv` method to - [directions]($func/stack.dir) - - Added `axis` and `inv` method to [alignments]($func/align.alignment) - - Added `inv` method to [2d alignments]($func/align.alignment) - - Added `start` argument to [`enumerate`]($type/array.enumerate) method on - arrays - - Added [`color.mix`]($func/color.mix) function - - Added `mode` and `scope` arguments to [`eval`]($func/eval) function - - Added [`bytes`]($type/bytes) type for holding large byte buffers - - Added [`encoding`]($func/read.encoding) argument to read function to read - a file as bytes instead of a string - - Added [`image.decode`]($func/image.decode) function for decoding an image + - Added [`dedup`]($array.dedup) method to arrays + - Added `pt`, `mm`, `cm`, and `inches` method to [lengths]($length) + - Added `deg` and `rad` method to [angles]($angle) + - Added `kind`, `hex`, `rgba`, `cmyk`, and `luma` method to [colors]($color) + - Added `axis`, `start`, `end`, and `inv` method to [directions]($stack.dir) + - Added `axis` and `inv` method to [alignments]($align.alignment) + - Added `inv` method to [2d alignments]($align.alignment) + - Added `start` argument to [`enumerate`]($array.enumerate) method on arrays + - Added [`color.mix`]($color.mix) function + - Added `mode` and `scope` arguments to [`eval`]($eval) function + - Added [`bytes`]($bytes) type for holding large byte buffers + - Added [`encoding`]($read.encoding) argument to read function to read a + file as bytes instead of a string + - Added [`image.decode`]($image.decode) function for decoding an image directly from a string or bytes - - Added [`bytes`]($func/bytes) function for converting a string or an array - of integers to bytes - - Added [`array`]($func/array) function for converting bytes to an array of + - Added [`bytes`]($bytes) function for converting a string or an array of + integers to bytes + - Added [`array`]($array) function for converting bytes to an array of integers - - Added support for converting bytes to a string with the [`str`]($func/str) + - Added support for converting bytes to a string with the [`str`]($str) function - Tooling and Diagnostics @@ -126,19 +122,19 @@ description: | - Fixed displayed compilation time (now includes export) - Miscellaneous Improvements - - Added [`bookmarked`]($func/heading.bookmarked) argument to heading to - control whether a heading becomes part of the PDF outline - - Added [`caption-pos`]($func/figure.caption-pos) argument to control the - position of a figure's caption - - Added [`metadata`]($func/metadata) function for exposing an arbitrary value - to the introspection system - - Fixed that a [`state`]($func/state) was identified by the pair `(key, init)` + - Added [`bookmarked`]($heading.bookmarked) argument to heading to control + whether a heading becomes part of the PDF outline + - Added [`caption-pos`]($figure.caption-pos) argument to control the position + of a figure's caption + - Added [`metadata`]($metadata) function for exposing an arbitrary value to + the introspection system + - Fixed that a [`state`]($state) was identified by the pair `(key, init)` instead of just its `key` - - Improved indent logic of [enumerations]($func/enum). Instead of requiring at + - Improved indent logic of [enumerations]($enum). Instead of requiring at least as much indent as the end of the marker, they now require only one more space indent than the start of the marker. As a result, even long markers like `12.` work with just 2 spaces of indent. - - Fixed bug with indent logic of [`raw`]($func/raw) blocks + - Fixed bug with indent logic of [`raw`]($raw) blocks - Fixed a parsing bug with dictionaries - Development @@ -158,26 +154,26 @@ description: | - Math - Added support for optical size variants of glyphs in math mode - - Added argument to enable [`limits`]($func/math.limits) conditionally - depending on whether the equation is set in [`display`]($func/math.display) - or [`inline`]($func/math.inline) style + - Added argument to enable [`limits`]($math.limits) conditionally depending on + whether the equation is set in [`display`]($math.display) or + [`inline`]($math.inline) style - Added `gt.eq.slant` and `lt.eq.slant` symbols - Increased precedence of factorials in math mode (`[$1/n!$]` works correctly now) - - Improved [underlines]($func/math.underline) and - [overlines]($func/math.overline) in math mode - - Fixed usage of [`limits`]($func/math.limits) function in show rules + - Improved [underlines]($math.underline) and [overlines]($math.overline) in + math mode + - Fixed usage of [`limits`]($math.limits) function in show rules - Fixed bugs with line breaks in equations - Text and Layout - - Added support for alternating page [margins]($func/page.margin) with the - `inside` and `outside` keys - - Added support for specifying the page [`binding`]($func/page.binding) - - Added [`to`]($func/pagebreak.to) argument to pagebreak function to skip to - the next even or odd page + - Added support for alternating page [margins]($page.margin) with the `inside` + and `outside` keys + - Added support for specifying the page [`binding`]($page.binding) + - Added [`to`]($pagebreak.to) argument to pagebreak function to skip to the + next even or odd page - Added basic i18n for a few more languages (TR, SQ, TL) - Fixed bug with missing table row at page break - - Fixed bug with [underlines]($func/underline) + - Fixed bug with [underlines]($underline) - Fixed bug superfluous table lines - Fixed smart quotes after line breaks - Fixed a crash related to text layout @@ -193,13 +189,13 @@ description: | - Now displays compilation time - Miscellaneous Improvements - - Added [`outline.entry`]($func/outline.entry) to customize outline entries - with show rules + - Added [`outline.entry`]($outline.entry) to customize outline entries with + show rules - Added some hints for error messages - - Added some missing syntaxes for [`raw`]($func/raw) highlighting + - Added some missing syntaxes for [`raw`]($raw) highlighting - Improved rendering of rotated images in PNG export and web app - - Made [footnotes]($func/footnote) reusable and referenceable - - Fixed bug with citations and bibliographies in [`locate`]($func/locate) + - Made [footnotes]($footnote) reusable and referenceable + - Fixed bug with citations and bibliographies in [`locate`]($locate) - Fixed inconsistent tense in documentation - Development @@ -211,45 +207,44 @@ description: | ## Version 0.5.0 (June 9, 2023) { #v0.5.0 } - Text and Layout - - Added [`raw`]($func/raw) syntax highlighting for many more languages - - Added support for Korean [numbering]($func/numbering) + - Added [`raw`]($raw) syntax highlighting for many more languages + - Added support for Korean [numbering]($numbering) - Added basic i18n for a few more languages (NL, SV, DA) - Improved line breaking for East Asian languages - - Expanded functionality of outline [`indent`]($func/outline.indent) property + - Expanded functionality of outline [`indent`]($outline.indent) property - Fixed footnotes in columns - - Fixed page breaking bugs with [footnotes]($func/footnote) + - Fixed page breaking bugs with [footnotes]($footnote) - Fixed bug with handling of footnotes in lists, tables, and figures - Fixed a bug with CJK punctuation adjustment - Fixed a crash with rounded rectangles - - Fixed alignment of [`line`]($func/line) elements + - Fixed alignment of [`line`]($line) elements - Math - **Breaking change:** The syntax rules for mathematical - [attachments]($func/math.attach) were improved: `[$f^abs(3)$]` now parses as + [attachments]($math.attach) were improved: `[$f^abs(3)$]` now parses as `[$f^(abs(3))$]` instead of `[$(f^abs)(3)$]`. To disambiguate, add a space: `[$f^zeta (3)$]`. - - Added [forced size]($category/math/sizes) commands for math - (e.g., [`display`]($func/math.display)) - - Added [`supplement`]($func/math.equation.supplement) parameter to - [`equation`]($func/math.equation), used by [references]($func/ref) + - Added [forced size]($category/math/sizes) commands for math (e.g., + [`display`]($math.display)) + - Added [`supplement`]($math.equation.supplement) parameter to + [`equation`]($math.equation), used by [references]($ref) - New [symbols]($category/symbols/sym): `bullet`, `xor`, `slash.big`, `sigma.alt`, `tack.r.not`, `tack.r.short`, `tack.r.double.not` - Fixed a bug with symbols in matrices - - Fixed a crash in the [`attach`]($func/math.attach) function + - Fixed a crash in the [`attach`]($math.attach) function - Scripting - - Added new [`datetime`]($type/datetime) type and - [`datetime.today`]($func/datetime.today) to retrieve the current date - - Added [`str.from-unicode`]($func/str.from-unicode) and - [`str.to-unicode`]($func/str.to-unicode) functions - - Added [`fields`]($type/content.fields) method on content - - Added `base` parameter to [`str`]($func/str) function - - Added [`calc.exp`]($func/calc.exp) and [`calc.ln`]($func/calc.ln) - - Improved accuracy of [`calc.pow`]($func/calc.pow) and - [`calc.log`]($func/calc.log) for specific bases - - Fixed [removal]($type/dictionary.remove) order for dictionary - - Fixed `.at(default: ..)` for [strings]($type/string.at) and - [content]($type/content.at) + - Added new [`datetime`]($datetime) type and + [`datetime.today`]($datetime.today) to retrieve the current date + - Added [`str.from-unicode`]($str.from-unicode) and + [`str.to-unicode`]($str.to-unicode) functions + - Added [`fields`]($content.fields) method on content + - Added `base` parameter to [`str`]($str) function + - Added [`calc.exp`]($calc.exp) and [`calc.ln`]($calc.ln) + - Improved accuracy of [`calc.pow`]($calc.pow) and [`calc.log`]($calc.log) for + specific bases + - Fixed [removal]($dictionary.remove) order for dictionary + - Fixed `.at(default: ..)` for [strings]($str.at) and [content]($content.at) - Fixed field access on styled elements - Removed deprecated `calc.mod` function @@ -266,8 +261,8 @@ description: | - Improved error message for failed length comparisons - Fixed a bug with images not showing up in Apple Preview - Fixed multiple bugs with the PDF outline - - Fixed citations and other searchable elements in [`hide`]($func/hide) - - Fixed bugs with [reference supplements]($func/ref.supplement) + - Fixed citations and other searchable elements in [`hide`]($hide) + - Fixed bugs with [reference supplements]($ref.supplement) - Fixed Nix flake @@ -275,29 +270,28 @@ description: | ## Version 0.4.0 (May 20, 2023) { #v0.4.0 } - Footnotes - Implemented support for footnotes - - The [`footnote`]($func/footnote) function inserts a footnote - - The [`footnote.entry`]($func/footnote.entry) function can be used to - customize the footnote listing - - The `{"chicago-notes"}` [citation style]($func/cite.style) is now available + - The [`footnote`]($footnote) function inserts a footnote + - The [`footnote.entry`]($footnote.entry) function can be used to customize + the footnote listing + - The `{"chicago-notes"}` [citation style]($cite.style) is now available - Documentation - Added a [Guide for LaTeX users]($guides/guide-for-latex-users) - Now shows default values for optional arguments - Added richer outlines in "On this Page" - Added initial support for search keywords: "Table of Contents" will now find - the [outline]($func/outline) function. Suggestions for more keywords are - welcome! + the [outline]($outline) function. Suggestions for more keywords are welcome! - Fixed issue with search result ranking - Fixed many more small issues - Math - **Breaking change**: Alignment points (`&`) in equations now alternate between left and right alignment - - Added support for writing roots with Unicode: - For example, `[$root(x+y)$]` can now also be written as `[$√(x+y)$]` - - Fixed uneven vertical [`attachment`]($func/math.attach) alignment - - Fixed spacing on decorated elements - (e.g., spacing around a [canceled]($func/math.cancel) operator) + - Added support for writing roots with Unicode: For example, `[$root(x+y)$]` + can now also be written as `[$√(x+y)$]` + - Fixed uneven vertical [`attachment`]($math.attach) alignment + - Fixed spacing on decorated elements (e.g., spacing around a + [canceled]($math.cancel) operator) - Fixed styling for stretchable symbols - Added `tack.r.double`, `tack.l.double`, `dotless.i` and `dotless.j` [symbols]($category/symbols/sym) @@ -307,40 +301,37 @@ description: | - Scripting - Added function scopes: A function can now hold related definitions in its - own scope, similar to a module. The new [`assert.eq`]($func/assert.eq) - function, for instance, is part of the [`assert`]($func/assert) function's - scope. Note that function scopes are currently only available for built-in - functions. - - Added [`assert.eq`]($func/assert.eq) and [`assert.ne`]($func/assert.ne) - functions for simpler equality and inequality assertions with more helpful - error messages - - Exposed [list]($func/list.item), [enum]($func/enum.item), and - [term list]($func/terms.item) items in their respective functions' scope - - The `at` methods on [strings]($type/string.at), [arrays]($type/array.at), - [dictionaries]($type/dictionary.at), and [content]($type/content.at) now support + own scope, similar to a module. The new [`assert.eq`]($assert.eq) function, + for instance, is part of the [`assert`]($assert) function's scope. Note that + function scopes are currently only available for built-in functions. + - Added [`assert.eq`]($assert.eq) and [`assert.ne`]($assert.ne) functions for + simpler equality and inequality assertions with more helpful error messages + - Exposed [list]($list.item), [enum]($enum.item), and [term list]($terms.item) + items in their respective functions' scope + - The `at` methods on [strings]($str.at), [arrays]($array.at), + [dictionaries]($dictionary.at), and [content]($content.at) now support specifying a default value - - Added support for passing a function to [`replace`]($type/string.replace) - that is called with each match. - - Fixed [replacement]($type/string.replace) strings: They are now inserted - completely verbatim instead of supporting the previous (unintended) magic - dollar syntax for capture groups + - Added support for passing a function to [`replace`]($str.replace) that is + called with each match. + - Fixed [replacement]($str.replace) strings: They are now inserted completely + verbatim instead of supporting the previous (unintended) magic dollar syntax + for capture groups - Fixed bug with trailing placeholders in destructuring patterns - Fixed bug with underscore in parameter destructuring - Fixed crash with nested patterns and when hovering over an invalid pattern - - Better error messages when casting to an [integer]($func/int) or - [float]($func/float) fails + - Better error messages when casting to an [integer]($int) or [float]($float) + fails - Text and Layout - Implemented sophisticated CJK punctuation adjustment - - Disabled [overhang]($func/text.overhang) for CJK punctuation + - Disabled [overhang]($text.overhang) for CJK punctuation - Added basic translations for Traditional Chinese - - Fixed [alignment]($func/raw.align) of text inside raw blocks (centering a - raw block, e.g. through a figure, will now keep the text itself - left-aligned) + - Fixed [alignment]($raw.align) of text inside raw blocks (centering a raw + block, e.g. through a figure, will now keep the text itself left-aligned) - Added support for passing a array instead of a function to configure table - cell [alignment]($func/table.align) and [fill]($func/table.fill) per column - - Fixed automatic figure [`kind`]($func/figure.kind) detection - - Made alignment of [enum numbers]($func/enum.number-align) configurable, + cell [alignment]($table.align) and [fill]($table.fill) per column + - Fixed automatic figure [`kind`]($figure.kind) detection + - Made alignment of [enum numbers]($enum.number-align) configurable, defaulting to `end` - Figures can now be made breakable with a show-set rule for blocks in figure - Initial fix for smart quotes in RTL languages @@ -372,8 +363,8 @@ description: | - Improved incremental parsing for more efficient recompilations - Added support for `.yaml` extension in addition to `.yml` for bibliographies - The CLI now emits escape codes only if the output is a TTY - - For users of the `typst` crate: The `Document` is now `Sync` again and - the `World` doesn't have to be `'static` anymore + - For users of the `typst` crate: The `Document` is now `Sync` again and the + `World` doesn't have to be `'static` anymore @@ -381,41 +372,41 @@ description: | - **Breaking changes:** - Renamed a few symbols: What was previous `dot.op` is now just `dot` and the basic dot is `dot.basic`. The same applies to `ast` and `tilde`. - - Renamed `mod` to [`rem`]($func/calc.rem) to more accurately reflect - the behaviour. It will remain available as `mod` until the next update as a + - Renamed `mod` to [`rem`]($calc.rem) to more accurately reflect the + behaviour. It will remain available as `mod` until the next update as a grace period. - A lone underscore is not a valid identifier anymore, it can now only be used in patterns - - Removed `before` and `after` arguments from [`query`]($func/query). This is - now handled through flexible [selectors]($type/selector) combinator methods - - Added support for [attachments]($func/math.attach) (sub-, superscripts) that + - Removed `before` and `after` arguments from [`query`]($query). This is now + handled through flexible [selectors]($selector) combinator methods + - Added support for [attachments]($math.attach) (sub-, superscripts) that precede the base symbol. The `top` and `bottom` arguments have been renamed to `t` and `b`. - New features - - Added support for more complex [strokes]($func/line.stroke) - (configurable caps, joins, and dash patterns) - - Added [`cancel`]($func/math.cancel) function for equations + - Added support for more complex [strokes]($stroke) (configurable caps, joins, + and dash patterns) + - Added [`cancel`]($math.cancel) function for equations - Added support for [destructuring]($scripting/#bindings) in argument lists and assignments - - Added [`alt`]($func/image.alt) text argument to image function - - Added [`toml`]($func/toml) function for loading data from a TOML file - - Added [`zip`]($type/array.zip), [`sum`]($type/array.sum), and - [`product`]($type/array.product) methods for arrays + - Added [`alt`]($image.alt) text argument to image function + - Added [`toml`]($toml) function for loading data from a TOML file + - Added [`zip`]($array.zip), [`sum`]($array.sum), and + [`product`]($array.product) methods for arrays - Added `fact`, `perm`, `binom`, `gcd`, `lcm`, `atan2`, `quo`, `trunc`, and `fract` [calculation]($category/calculate) - Improvements - Text in SVGs now displays properly - Typst now generates a PDF heading outline - - [References]($func/ref) now provides the referenced element as a field in - show rules + - [References]($ref) now provides the referenced element as a field in show + rules - Refined linebreak algorithm for better Chinese justification - Locations are now a valid kind of selector - Added a few symbols for algebra - Added Spanish smart quote support - - Added [`selector`]($func/selector) function to turn a selector-like value - into a selector on which combinator methods can be called + - Added [`selector`]($selector) function to turn a selector-like value into a + selector on which combinator methods can be called - Improved some error messages - The outline and bibliography headings can now be styled with show-set rules - Operations on numbers now produce an error instead of overflowing @@ -423,7 +414,7 @@ description: | - Bug fixes - Fixed wrong linebreak before punctuation that follows inline equations, citations, and other elements - - Fixed a bug with [argument sinks]($type/arguments) + - Fixed a bug with [argument sinks]($arguments) - Fixed strokes with thickness zero - Fixed hiding and show rules in math - Fixed alignment in matrices @@ -431,13 +422,13 @@ description: | - Fixed grid cell alignment - Fixed alignment of list marker and enum markers in presence of global alignment settings - - Fixed [path]($func/path) closing + - Fixed [path]($path) closing - Fixed compiler crash with figure references - A single trailing line breaks is now ignored in math, just like in text - Command line interface - - Font path and compilation root can now be set with the environment - variables `TYPST_FONT_PATHS` and `TYPST_ROOT` + - Font path and compilation root can now be set with the environment variables + `TYPST_FONT_PATHS` and `TYPST_ROOT` - The output of `typst fonts` now includes the embedded fonts - Development @@ -453,18 +444,18 @@ description: | - **Breaking changes:** - Removed support for iterating over index and value in [for loops]($scripting/#loops). This is now handled via unpacking and - enumerating. Same goes for the [`map`]($type/array.map) method. - - [Dictionaries]($type/dictionary) now iterate in insertion order instead of + enumerating. Same goes for the [`map`]($array.map) method. + - [Dictionaries]($dictionary) now iterate in insertion order instead of alphabetical order. - New features - Added [unpacking syntax]($scripting/#bindings) for let bindings, which allows things like `{let (1, 2) = array}` - - Added [`enumerate`]($type/array.enumerate) method - - Added [`path`]($func/path) function for drawing Bézier paths - - Added [`layout`]($func/layout) function to access the size of the - surrounding page or container - - Added `key` parameter to [`sorted`]($type/array.sorted) method + - Added [`enumerate`]($array.enumerate) method + - Added [`path`]($path) function for drawing Bézier paths + - Added [`layout`]($layout) function to access the size of the surrounding + page or container + - Added `key` parameter to [`sorted`]($array.sorted) method - Command line interface - Fixed `--open` flag blocking the program @@ -478,17 +469,16 @@ description: | - Added basic i18n for a few more languages (AR, NB, CS, NN, PL, SL, ES, UA, VI) - Added a few numbering patterns (Ihora, Chinese) - - Added `sinc` [operator]($func/math.op) - - Fixed bug where math could not be hidden with [`hide`]($func/hide) + - Added `sinc` [operator]($math.op) + - Fixed bug where math could not be hidden with [`hide`]($hide) - Fixed sizing issues with box, block, and shapes - Fixed some translations - - Fixed inversion of "R" in [`cal`]($func/math.cal) and - [`frak`]($func/math.frak) styles + - Fixed inversion of "R" in [`cal`]($math.cal) and [`frak`]($math.frak) styles - Fixed some styling issues in math - Fixed supplements of references to headings - Fixed syntax highlighting of identifiers in certain scenarios - - [Ratios]($type/ratio) can now be multiplied with more types and be converted - to [floats]($type/float) with the [`float`]($func/float) function + - [Ratios]($ratio) can now be multiplied with more types and be converted to + [floats]($float) with the [`float`]($float) function @@ -499,22 +489,22 @@ description: | - `typst watch file.typ` or `typst w file.typ` to compile and watch - `typst fonts` to list all fonts - Manual counters now start at zero. Read the "How to step" section - [here]($func/counter) for more details - - The [bibliography styles]($func/bibliography.style) - `{"author-date"}` and `{"author-title"}` were renamed to - `{"chicago-author-date"}` and `{"chicago-author-title"}` + [here]($counter) for more details + - The [bibliography styles]($bibliography.style) `{"author-date"}` and + `{"author-title"}` were renamed to `{"chicago-author-date"}` and + `{"chicago-author-title"}` - Figure improvements - - Figures now automatically detect their content and adapt their - behaviour. Figures containing tables, for instance, are automatically - prefixed with "Table X" and have a separate counter + - Figures now automatically detect their content and adapt their behaviour. + Figures containing tables, for instance, are automatically prefixed with + "Table X" and have a separate counter - The figure's supplement (e.g. "Figure" or "Table") can now be customized - In addition, figures can now be completely customized because the show rule gives access to the automatically resolved kind, supplement, and counter - Bibliography improvements - - The [`bibliography`]($func/bibliography) now also accepts multiple - bibliography paths (as an array) + - The [`bibliography`]($bibliography) now also accepts multiple bibliography + paths (as an array) - Parsing of BibLaTeX files is now more permissive (accepts non-numeric edition, pages, volumes, dates, and Jabref-style comments; fixed abbreviation parsing) @@ -522,9 +512,8 @@ description: | - Fixed APA bibliography ordering - Drawing additions - - Added [`polygon`]($func/polygon) function for drawing polygons - - Added support for clipping in [boxes]($func/box.clip) and - [blocks]($func/block.clip) + - Added [`polygon`]($polygon) function for drawing polygons + - Added support for clipping in [boxes]($box.clip) and [blocks]($block.clip) - Command line interface - Now returns with non-zero status code if there is an error @@ -534,27 +523,26 @@ description: | - Added `--open` flag to directly open the PDF - Miscellaneous improvements - - Added [`yaml`]($func/yaml) function to load data from YAML files + - Added [`yaml`]($yaml) function to load data from YAML files - Added basic i18n for a few more languages (IT, RU, ZH, FR, PT) - Added numbering support for Hebrew - - Added support for [integers]($type/integer) with base 2, 8, and 16 + - Added support for [integers]($int) with base 2, 8, and 16 - Added symbols for double bracket and laplace operator - - The [`link`]($func/link) function now accepts [labels]($func/label) + - The [`link`]($link) function now accepts [labels]($label) - The link syntax now allows more characters - Improved justification of Japanese and Chinese text - Calculation functions behave more consistently w.r.t to non-real results - Replaced deprecated angle brackets - Reduced maximum function call depth from 256 to 64 - - Fixed [`first-line-indent`]($func/par.first-line-indent) being not applied - when a paragraph starts with styled text + - Fixed [`first-line-indent`]($par.first-line-indent) being not applied when a + paragraph starts with styled text - Fixed extraneous spacing in unary operators in equations - Fixed block spacing, e.g. in `{block(above: 1cm, below: 1cm, ..)}` - Fixed styling of text operators in math - Fixed invalid parsing of language tag in raw block with a single backtick - Fixed bugs with displaying counters and state - Fixed crash related to page counter - - Fixed crash when [`symbol`]($func/symbol) function was called without - arguments + - Fixed crash when [`symbol`]($symbol) function was called without arguments - Fixed crash in bibliography generation - Fixed access to label of certain content elements - Fixed line number in error message for CSV parsing @@ -562,12 +550,12 @@ description: | -## March 28, 2023 +## March 28, 2023 { #_ } - **Breaking changes:** - Enumerations now require a space after their marker, that is, `[1.ok]` must now be written as `[1. ok]` - - Changed default style for [term lists]($func/terms): Does not include a - colon anymore and has a bit more indent + - Changed default style for [term lists]($terms): Does not include a colon + anymore and has a bit more indent - Command line interface - Added `--font-path` argument for CLI @@ -575,16 +563,15 @@ description: | - Fixed build of CLI if `git` is not installed - Miscellaneous improvements - - Added support for disabling [matrix]($func/math.mat) and - [vector]($func/math.vec) delimiters. Generally with - `[#set math.mat(delim: none)]` or one-off with + - Added support for disabling [matrix]($math.mat) and [vector]($math.vec) + delimiters. Generally with `[#set math.mat(delim: none)]` or one-off with `[$mat(delim: #none, 1, 2; 3, 4)$]`. - - Added [`separator`]($func/terms.separator) argument to term lists - - Added [`round`]($func/math.round) function for equations + - Added [`separator`]($terms.separator) argument to term lists + - Added [`round`]($math.round) function for equations - Numberings now allow zeros. To reset a counter, you can write `[#counter(..).update(0)]` - Added documentation for `{page()}` and `{position()}` methods on - [`location`]($func/locate) type + [`location`]($location) type - Added symbols for double, triple, and quadruple dot accent - Added smart quotes for Norwegian BokmÃ¥l - Added Nix flake @@ -593,92 +580,90 @@ description: | - Fixed parsing of unbalanced delimiters in fractions: `[$1/(2 (x)$]` - Fixed unexpected parsing of numbers as enumerations, e.g. in `[1.2]` - Fixed combination of page fill and header - - Fixed compiler crash if [`repeat`]($func/repeat) is used in page with - automatic width - - Fixed [matrices]($func/math.mat) with explicit delimiter - - Fixed [`indent`]($func/terms.indent) property of term lists + - Fixed compiler crash if [`repeat`]($repeat) is used in page with automatic + width + - Fixed [matrices]($math.mat) with explicit delimiter + - Fixed [`indent`]($terms.indent) property of term lists - Numerous documentation fixes - Links in bibliographies are now affected by link styling - Fixed hovering over comments in web app -## March 21, 2023 +## March 21, 2023 { #_ } - Reference and bibliography management - - [Bibliographies]($func/bibliography) and [citations]($func/cite) (currently - supported styles are APA, Chicago Author Date, IEEE, and MLA) - - You can now [reference]($func/ref) sections, figures, formulas, and works - from the bibliography with `[@label]` + - [Bibliographies]($bibliography) and [citations]($cite) (currently supported + styles are APA, Chicago Author Date, IEEE, and MLA) + - You can now [reference]($ref) sections, figures, formulas, and works from + the bibliography with `[@label]` - You can make an element referenceable with a label: - `[= Introduction ]` - `[$ A = pi r^2 $ ]` - Introspection system for interactions between different parts of the document - - [`counter`]($func/counter) function + - [`counter`]($counter) function - Access and modify counters for pages, headings, figures, and equations - Define and use your own custom counters - Time travel: Find out what the counter value was or will be at some other point in the document (e.g. when you're building a list of figures, you can determine the value of the figure counter at any given figure). - Counters count in layout order and not in code order - - [`state`]($func/state) function + - [`state`]($state) function - Manage arbitrary state across your document - Time travel: Find out the value of your state at any position in the document - State is modified in layout order and not in code order - - [`query`]($func/query) function - - Find all occurrences of an element or a label, either in the whole document - or before/after some location + - [`query`]($query) function + - Find all occurrences of an element or a label, either in the whole + document or before/after some location - Link to elements, find out their position on the pages and access their fields - Example use cases: Custom list of figures or page header with current chapter title - - [`locate`]($func/locate) function + - [`locate`]($locate) function - Determines the location of itself in the final layout - Can be accessed to get the `page` and `x`, `y` coordinates - Can be used with counters and state to find out their values at that location - Can be used with queries to find elements before or after its location -- New [`measure`]($func/measure) function +- New [`measure`]($measure) function - Measure the layouted size of elements - - To be used in combination with the new [`style`]($func/style) function that - lets you generate different content based on the style context something is + - To be used in combination with the new [`style`]($style) function that lets + you generate different content based on the style context something is inserted into (because that affects the measured size of content) - Exposed content representation - Content is not opaque anymore - Content can be compared for equality - The tree of content elements can be traversed with code - - Can be observed in hover tooltips or with [`repr`]($func/repr) - - New [methods]($type/content) on content: `func`, `has`, `at`, and `location` + - Can be observed in hover tooltips or with [`repr`]($repr) + - New [methods]($content) on content: `func`, `has`, `at`, and `location` - All optional fields on elements are now settable - More uniform field names (`heading.title` becomes `heading.body`, `list.items` becomes `list.children`, and a few more changes) - Further improvements - - Added [`figure`]($func/figure) function - - Added [`numbering`]($func/math.equation.numbering) parameter on equation function - - Added [`numbering`]($func/page.numbering) and - [`number-align`]($func/page.number-align) parameters on page function - - The page function's [`header`]($func/page.header) and - [`footer`]($func/page.footer) parameters do not take functions anymore. If - you want to customize them based on the page number, use the new - [`numbering`]($func/page.numbering) parameter or [`counter`]($func/counter) - function instead. - - Added [`footer-descent`]($func/page.footer-descent) and - [`header-ascent`]($func/page.header-ascent) parameters + - Added [`figure`]($figure) function + - Added [`numbering`]($math.equation.numbering) parameter on equation function + - Added [`numbering`]($page.numbering) and + [`number-align`]($page.number-align) parameters on page function + - The page function's [`header`]($page.header) and [`footer`]($page.footer) + parameters do not take functions anymore. If you want to customize them + based on the page number, use the new [`numbering`]($page.numbering) + parameter or [`counter`]($counter) function instead. + - Added [`footer-descent`]($page.footer-descent) and + [`header-ascent`]($page.header-ascent) parameters - Better default alignment in header and footer - Fixed Arabic vowel placement - Fixed PDF font embedding issues - - Renamed `math.formula` to [`math.equation`]($func/math.equation) + - Renamed `math.formula` to [`math.equation`]($math.equation) - Font family must be a named argument now: `[#set text(font: "..")]` - - Added support for [hanging indent]($func/par.hanging-indent) - - Renamed paragraph `indent` to - [`first-line-indent`]($func/par.first-line-indent) - - More accurate [logarithm]($func/calc.log) when base is `2` or `10` + - Added support for [hanging indent]($par.hanging-indent) + - Renamed paragraph `indent` to [`first-line-indent`]($par.first-line-indent) + - More accurate [logarithm]($calc.log) when base is `2` or `10` - Improved some error messages - - Fixed layout of [`terms`]($func/terms) list + - Fixed layout of [`terms`]($terms) list - Web app improvements - Added template gallery @@ -697,76 +682,75 @@ description: | - New back button instead of four-dots button - Lots of bug fixes -## February 25, 2023 +## February 25, 2023 { #_ } - Font changes - New default font: Linux Libertine - New default font for raw blocks: DejaVu Sans Mono - New default font for math: Book weight of New Computer Modern Math - Lots of new math fonts available - Removed Latin Modern fonts in favor of New Computer Modern family - - Removed unnecessary smallcaps fonts which are already accessible through - the corresponding main font and the [`smallcaps`]($func/smallcaps) function + - Removed unnecessary smallcaps fonts which are already accessible through the + corresponding main font and the [`smallcaps`]($smallcaps) function - Improved default spacing for headings -- Added [`panic`]($func/panic) function -- Added [`clusters`]($type/string.clusters) and - [`codepoints`]($type/string.codepoints) - methods for strings -- Support for multiple authors in [`set document`]($func/document.author) +- Added [`panic`]($panic) function +- Added [`clusters`]($str.clusters) and [`codepoints`]($str.codepoints) methods + for strings +- Support for multiple authors in [`set document`]($document.author) - Fixed crash when string is accessed at a position that is not a char boundary - Fixed semicolon parsing in `[#var ;]` - Fixed incremental parsing when inserting backslash at end of `[#"abc"]` -- Fixed names of a few font families - (including Noto Sans Symbols and New Computer Modern families) +- Fixed names of a few font families (including Noto Sans Symbols and New + Computer Modern families) - Fixed autocompletion for font families - Improved incremental compilation for user-defined functions -## February 15, 2023 -- [Box]($func/box) and [block]($func/block) have gained `fill`, `stroke`, - `radius`, and `inset` properties -- Blocks may now be explicitly sized, fixed-height blocks can still break - across pages -- Blocks can now be configured to be [`breakable`]($func/block.breakable) or not -- [Numbering style]($func/enum.numbering) can now be configured for nested enums -- [Markers]($func/list.marker) can now be configured for nested lists -- The [`eval`]($func/eval) function now expects code instead of markup and - returns an arbitrary value. Markup can still be evaluated by surrounding the - string with brackets. +## February 15, 2023 { #_ } +- [Box]($box) and [block]($block) have gained `fill`, `stroke`, `radius`, and + `inset` properties +- Blocks may now be explicitly sized, fixed-height blocks can still break across + pages +- Blocks can now be configured to be [`breakable`]($block.breakable) or not +- [Numbering style]($enum.numbering) can now be configured for nested enums +- [Markers]($list.marker) can now be configured for nested lists +- The [`eval`]($eval) function now expects code instead of markup and returns an + arbitrary value. Markup can still be evaluated by surrounding the string with + brackets. - PDFs generated by Typst now contain XMP metadata - Link boxes are now disabled in PDF output - Tables don't produce small empty cells before a pagebreak anymore - Fixed raw block highlighting bug -## February 12, 2023 +## February 12, 2023 { #_ } - Shapes, images, and transformations (move/rotate/scale/repeat) are now - block-level. To integrate them into a paragraph, use a [`box`]($func/box) as - with other elements. + block-level. To integrate them into a paragraph, use a [`box`]($box) as with + other elements. - A colon is now required in an "everything" show rule: Write `{show: it => ..}` - instead of `{show it => ..}`. This prevents intermediate states that ruin - your whole document. + instead of `{show it => ..}`. This prevents intermediate states that ruin your + whole document. - Non-math content like a shape or table in a math formula is now centered vertically - Support for widow and orphan prevention within containers -- Support for [RTL]($func/text.dir) in lists, grids, and tables +- Support for [RTL]($text.dir) in lists, grids, and tables - Support for explicit `{auto}` sizing for boxes and shapes - Support for fractional (i.e. `{1fr}`) widths for boxes - Fixed bug where columns jump to next page - Fixed bug where list items have no leading - Fixed relative sizing in lists, squares and grid auto columns -- Fixed relative displacement in [`place`]($func/place) function +- Fixed relative displacement in [`place`]($place) function - Fixed that lines don't have a size - Fixed bug where `{set document(..)}` complains about being after content - Fixed parsing of `{not in}` operation - Fixed hover tooltips in math - Fixed bug where a heading show rule may not contain a pagebreak when an outline is present -- Added [`baseline`]($func/box.baseline) property on [`box`]($func/box) -- Added [`tg`]($func/math.op) and [`ctg`]($func/math.op) operators in math -- Added delimiter setting for [`cases`]($func/math.cases) function +- Added [`baseline`]($box.baseline) property on [`box`]($box) +- Added [`tg`]($math.op) and [`ctg`]($math.op) operators in math +- Added delimiter setting for [`cases`]($math.cases) function - Parentheses are now included when accepting a function autocompletion -## February 2, 2023 -- Merged text and math symbols, renamed a few symbols - (including `infty` to `infinity` with the alias `oo`) +## February 2, 2023 { #_ } +- Merged text and math symbols, renamed a few symbols (including `infty` to + `infinity` with the alias `oo`) - Fixed missing italic mappings - Math italics correction is now applied properly - Parentheses now scale in `[$zeta(x/2)$]` @@ -779,17 +763,17 @@ description: | - Heading and list markers now parse consistently - Allow arbitrary math directly in content -## January 30, 2023 +## January 30, 2023 { #_ } [Go to the announcement blog post.](https://typst.app/blog/2023/january-update) - New expression syntax in markup/math - Blocks cannot be directly embedded in markup anymore - Like other expressions, they now require a leading hashtag - - More expressions available with hashtag, including literals - (`[#"string"]`) as well as field access and method call - without space: `[#emoji.face]` + - More expressions available with hashtag, including literals (`[#"string"]`) + as well as field access and method call without space: `[#emoji.face]` - New import syntax - `[#import "module.typ"]` creates binding named `module` - - `[#import "module.typ": a, b]` or `[#import "module.typ": *]` to import items + - `[#import "module.typ": a, b]` or `[#import "module.typ": *]` to import + items - `[#import emoji: face, turtle]` to import from already bound module - New symbol handling - Removed symbol notation @@ -811,8 +795,8 @@ description: | - To forcibly match two delimiters, use `lr` function - Line breaks may occur between matched delimiters - Delimiters may also be unbalanced - - You can also use the `lr` function to scale the brackets - (or just one bracket) to a specific size manually + - You can also use the `lr` function to scale the brackets (or just one + bracket) to a specific size manually - Multi-line math with alignment - The `\` character inserts a line break - The `&` character defines an alignment point @@ -835,10 +819,11 @@ description: | - New shorthands: `[|`, `|]`, and `||` - New `attach` function, overridable attachments with `script` and `limit` - Manual spacing in math, with `h`, `thin`, `med`, `thick` and `quad` - - Symbols and other content may now be used like a function, e.g. `[$zeta(x)$]` + - Symbols and other content may now be used like a function, e.g. + `[$zeta(x)$]` - Added Fira Math font, removed Noto Sans Math font - - Support for alternative math fonts through - `[#show math.formula: set text("Fira Math")]` + - Support for alternative math fonts through `[#show math.formula: set + text("Fira Math")]` - More library improvements - New `calc` module, `abs`, `min`, `max`, `even`, `odd` and `mod` moved there - New `message` argument on `{assert}` function diff --git a/docs/community.md b/docs/community.md index 0ac7b50d8..bdb835c47 100644 --- a/docs/community.md +++ b/docs/community.md @@ -6,8 +6,8 @@ description: | # Community Hey and welcome to the Community page! We're so glad you're here. Typst is -developed by an early-stage startup and is still in its infancy, but it -would be pointless without people like you who are interested in it. +developed by an early-stage startup and is still in its infancy, but it would be +pointless without people like you who are interested in it. We would love to not only hear from you but to also provide spaces where you can discuss any topic around Typst, typesetting, writing, the sciences, and @@ -22,9 +22,9 @@ Of course, you are also very welcome to connect with us on social media [GitHub](https://github.com/typst)). ## What to share? { #want-to-share } -For our community, we want to foster versatility and inclusivity. -You are welcome to post about any topic that you think would interest other -community members, but if you need a little inspiration, here are a few ideas: +For our community, we want to foster versatility and inclusivity. You are +welcome to post about any topic that you think would interest other community +members, but if you need a little inspiration, here are a few ideas: - Share and discuss your thoughts and ideas for new features or improvements you'd like to see in Typst @@ -33,16 +33,15 @@ community members, but if you need a little inspiration, here are a few ideas: - Share importable files or templates that you use to style your documents - Alert us of bugs you encounter while using Typst -## Beta test { #beta-test } -We are starting a public beta test of our product on March 21st, 2023. -The Typst compiler is still under active development and breaking changes can -occur at any point. The compiler is developed in the open on +## Beta test +We are starting a public beta test of our product on March 21st, 2023. The Typst +compiler is still under active development and breaking changes can occur at any +point. The compiler is developed in the open on [GitHub](https://github.com/typst/typst). We will update the members of our Discord server and our social media followers -when new features become available. We'll also update you on the -development progress of large features. A development tracker will become -available on the documentation pages soon. +when new features become available. We'll also update you on the development +progress of large features. ## How to support Typst { #support-typst } If you want to support Typst, there are multiple ways to do that! You can diff --git a/docs/guides/guide-for-latex-users.md b/docs/guides/guide-for-latex-users.md index b51e8cf13..736735233 100644 --- a/docs/guides/guide-for-latex-users.md +++ b/docs/guides/guide-for-latex-users.md @@ -4,7 +4,7 @@ description: | similarities between Typst and LaTeX so you can get started quickly. --- -# Guide for LaTeX users +# Guide for LaTeX users { # } This page is a good starting point if you have used LaTeX before and want to try out Typst. We will explore the main differences between these two systems from a user perspective. Although Typst is not built upon LaTeX and has a different @@ -32,8 +32,8 @@ That's easy. You just create a new, empty text file (the file extension is `.typ`). No boilerplate is needed to get started. Simply start by writing your text. It will be set on an empty A4-sized page. If you are using the web app, click "+ Empty document" to create a new project with a file and enter the -editor. [Paragraph breaks]($func/parbreak) work just as they do in LaTeX, just -use a blank line. +editor. [Paragraph breaks]($parbreak) work just as they do in LaTeX, just use a +blank line. ```example Hey there! @@ -47,8 +47,8 @@ LaTeX uses the command `\section` to create a section heading. Nested headings are indicated with `\subsection`, `\subsubsection`, etc. Depending on your document class, there is also `\part` or `\chapter`. -In Typst, [headings]($func/heading) are less verbose: You prefix the line with -the heading on it with an equals sign and a space to get a first-order heading: +In Typst, [headings]($heading) are less verbose: You prefix the line with the +heading on it with an equals sign and a space to get a first-order heading: `[= Introduction]`. If you need a second-order heading, you use two equals signs: `[== In this paper]`. You can nest headings as deeply as you'd like by adding more equals signs. @@ -60,23 +60,23 @@ Emphasis (usually rendered as italic text) is expressed by enclosing text in Here is a list of common markup commands used in LaTeX and their Typst equivalents. You can also check out the [full syntax cheat sheet]($syntax). -| Element | LaTeX | Typst | See | -|:-----------------|:--------------------------|:-----------------------|:-------------------------| -| Strong emphasis | `\textbf{strong}` | `[*strong*]` | [`strong`]($func/strong) | -| Emphasis | `\emph{emphasis}` | `[_emphasis_]` | [`emph`]($func/emph) | -| Monospace / code | `\texttt{print(1)}` | ``[`print(1)`]`` | [`raw`]($func/raw) | -| Link | `\url{https://typst.app}` | `[https://typst.app/]` | [`link`]($func/link) | -| Label | `\label{intro}` | `[]` | [`label`]($func/label) | -| Reference | `\ref{intro}` | `[@intro]` | [`ref`]($func/ref) | -| Citation | `\cite{humphrey97}` | `[@humphrey97]` | [`cite`]($func/cite) | -| Bullet list | `itemize` environment | `[- List]` | [`list`]($func/list) | -| Numbered list | `enumerate` environment | `[+ List]` | [`enum`]($func/enum) | -| Term list | `description` environment | `[/ Term: List]` | [`terms`]($func/terms) | -| Figure | `figure` environment | `figure` function | [`figure`]($func/figure) | -| Table | `table` environment | `table` function | [`table`]($func/table) | -| Equation | `$x$`, `align` / `equation` environments | `[$x$]`, `[$ x = y $]` | [`equation`]($func/math.equation) | +| Element | LaTeX | Typst | See +|:-----------------|:--------------------------|:-----------------------|:-------------------- +| Strong emphasis | `\textbf{strong}` | `[*strong*]` | [`strong`]($strong) | +| Emphasis | `\emph{emphasis}` | `[_emphasis_]` | [`emph`]($emph) | +| Monospace / code | `\texttt{print(1)}` | ``[`print(1)`]`` | [`raw`]($raw) | +| Link | `\url{https://typst.app}` | `[https://typst.app/]` | [`link`]($link) | +| Label | `\label{intro}` | `[]` | [`label`]($label) | +| Reference | `\ref{intro}` | `[@intro]` | [`ref`]($ref) | +| Citation | `\cite{humphrey97}` | `[@humphrey97]` | [`cite`]($cite) | +| Bullet list | `itemize` environment | `[- List]` | [`list`]($list) | +| Numbered list | `enumerate` environment | `[+ List]` | [`enum`]($enum) | +| Term list | `description` environment | `[/ Term: List]` | [`terms`]($terms) | +| Figure | `figure` environment | `figure` function | [`figure`]($figure) | +| Table | `table` environment | `table` function | [`table`]($table) | +| Equation | `$x$`, `align` / `equation` environments | `[$x$]`, `[$ x = y $]` | [`equation`]($math.equation) | -[Lists]($func/list) do not rely on environments in Typst. Instead, they have +[Lists]($list) do not rely on environments in Typst. Instead, they have lightweight syntax like headings. To create an unordered list (`itemize`), prefix each line of an item with a hyphen: @@ -100,11 +100,11 @@ To write this list in Typst... ```` Nesting lists works just by using proper indentation. Adding a blank line in -between items results in a more [widely]($func/list.tight) spaced list. +between items results in a more [widely]($list.tight) spaced list. -To get a [numbered list]($func/enum) (`enumerate`) instead, use a `+` instead of -the hyphen. For a [term list]($func/terms) (`description`), write -`[/ Term: Description]` instead. +To get a [numbered list]($enum) (`enumerate`) instead, use a `+` instead of the +hyphen. For a [term list]($terms) (`description`), write `[/ Term: Description]` +instead. ## How do I use a command? { #commands } LaTeX heavily relies on commands (prefixed by backslashes). It uses these @@ -115,16 +115,16 @@ braces: `\cite{rasmus}`. Typst differentiates between [markup mode and code mode]($scripting/#blocks). The default is markup mode, where you compose text and apply syntactic constructs such as `[*stars for bold text*]`. Code mode, on the other hand, -parallels programming languages like Python, providing the option to input -and execute segments of code. +parallels programming languages like Python, providing the option to input and +execute segments of code. Within Typst's markup, you can switch to code mode for a single command (or rather, _expression_) using a hashtag (`#`). This is how you call functions to, -for example, split your project into different [files]($scripting/#modules) -or render text based on some [condition]($scripting/#conditionals). -Within code mode, it is possible to include normal markup -[_content_]($type/content) by using square brackets. Within code mode, this -content is treated just as any other normal value for a variable. +for example, split your project into different [files]($scripting/#modules) or +render text based on some [condition]($scripting/#conditionals). Within code +mode, it is possible to include normal markup [_content_]($content) by using +square brackets. Within code mode, this content is treated just as any other +normal value for a variable. ```example First, a rectangle: @@ -142,15 +142,14 @@ And finally a little loop: ] ``` -A function call always involves the name of the function ([`rect`]($func/rect), -[`underline`]($func/underline), [`calc.max`]($func/calc.max), -[`range`]($func/range)) followed by parentheses (as opposed to LaTeX where the -square brackets and curly braces are optional if the macro requires no -arguments). The expected list of arguments passed within those parentheses -depends on the concrete function and is specified in the -[reference]($reference). +A function call always involves the name of the function ([`rect`]($rect), +[`underline`]($underline), [`calc.max`]($calc.max), [`range`]($range)) followed +by parentheses (as opposed to LaTeX where the square brackets and curly braces +are optional if the macro requires no arguments). The expected list of arguments +passed within those parentheses depends on the concrete function and is +specified in the [reference]($reference). -### Arguments { #arguments } +### Arguments A function can have multiple arguments. Some arguments are positional, i.e., you just provide the value: The function `[#lower("SCREAM")]` returns its argument in all-lowercase. Many functions use named arguments instead of positional @@ -173,13 +172,13 @@ Named arguments are similar to how some LaTeX environments are configured, for example, you would type `\begin{enumerate}[label={\alph*)}]` to start a list with the labels `a)`, `b)`, and so on. -Often, you want to provide some [content]($type/content) to a function. For -example, the LaTeX command `\underline{Alternative A}` would translate to +Often, you want to provide some [content]($content) to a function. For example, +the LaTeX command `\underline{Alternative A}` would translate to `[#underline([Alternative A])]` in Typst. The square brackets indicate that a -value is [content]($type/content). Within these brackets, you can use normal -markup. However, that's a lot of parentheses for a pretty simple construct. -This is why you can also move trailing content arguments after the parentheses -(and omit the parentheses if they would end up empty). +value is [content]($content). Within these brackets, you can use normal markup. +However, that's a lot of parentheses for a pretty simple construct. This is why +you can also move trailing content arguments after the parentheses (and omit the +parentheses if they would end up empty). ```example Typst is an #underline[alternative] @@ -188,20 +187,20 @@ to LaTeX. #rect(fill: aqua)[Get started here!] ``` -### Data types { #data-types } +### Data types You likely already noticed that the arguments have distinctive data types. Typst -supports [many data types]($type). Below, there is a table with some of the most +supports many data types. Below, there is a table with some of the most important ones and how to write them. In order to specify values of any of these types, you have to be in code mode! -| Data type | Example | -|:-------------------------------------|:----------------------------------| -| [Content]($type/content) | `{[*fast* typesetting]}` | -| [String]($type/string) | `{"Pietro S. Author"}` | -| [Integer]($type/integer) | `{23}` | -| [Floating point number]($type/float) | `{1.459}` | -| [Absolute length]($type/length) | `{12pt}`, `{5in}`, `{0.3cm}`, ... | -| [Relative length]($type/ratio) | `{65%}` | +| Data type | Example | +|:--------------------------------|:----------------------------------| +| [Content]($content) | `{[*fast* typesetting]}` | +| [String]($str) | `{"Pietro S. Author"}` | +| [Integer]($int) | `{23}` | +| [Floating point number]($float) | `{1.459}` | +| [Absolute length]($length) | `{12pt}`, `{5in}`, `{0.3cm}`, ... | +| [Relative length]($ratio) | `{65%}` | The difference between content and string is that content can contain markup, including function calls, while a string really is just a plain sequence of @@ -219,9 +218,9 @@ braces and only affect that argument. Other commands such as content within the document or current scope. In Typst, the same function can be used both to affect the appearance for the -remainder of the document, a block (or scope), or just its arguments. For example, -`[#text(weight: "bold")[bold text]]` will only embolden its argument, while -`[#set text(weight: "bold")]` will embolden any text until the end of the +remainder of the document, a block (or scope), or just its arguments. For +example, `[#text(weight: "bold")[bold text]]` will only embolden its argument, +while `[#set text(weight: "bold")]` will embolden any text until the end of the current block, or, if there is none, document. The effects of a function are immediately obvious based on whether it is used in a call or a [set rule.]($styling/#set-rules) @@ -249,9 +248,9 @@ Good results can only be obtained by ``` The `+` is syntactic sugar (think of it as an abbreviation) for a call to the -[`{enum}`]($func/enum) function, to which we apply a set rule above. [Most -syntax is linked to a function in this way.]($syntax) If you need to style an -element beyond what its arguments enable, you can completely redefine its +[`{enum}`]($enum) function, to which we apply a set rule above. +[Most syntax is linked to a function in this way.]($syntax) If you need to style +an element beyond what its arguments enable, you can completely redefine its appearance with a [show rule]($styling/#show-rules) (somewhat comparable to `\renewcommand`). @@ -261,7 +260,7 @@ command to define how your document is supposed to look. In that command, you may have replaced `article` with another value such as `report` and `amsart` to select a different look. -When using Typst, you style your documents with [functions]($type/function). +When using Typst, you style your documents with [functions]($function). Typically, you use a template that provides a function that styles your whole document. First, you import the function from a template file. Then, you apply it to your whole document. This is accomplished with a @@ -357,7 +356,7 @@ paragraphs right here! ``` The [`{import}`]($scripting/#modules) statement makes -[functions]($type/function) (and other definitions) from another file available. +[functions]($function) (and other definitions) from another file available. In this example, it imports the `conf` function from the `conf.typ` file. This function formats a document as a conference article. We use a show rule to apply it to the document and also configure some metadata of the article. After @@ -393,23 +392,23 @@ Typst is "batteries included," so the equivalent of many popular LaTeX packages is built right-in. Below, we compiled a table with frequently loaded packages and their corresponding Typst functions. -| LaTeX Package | Typst Alternative | -|:--------------------------------|:---------------------------------------------------------------------| -| graphicx, svg | [`image`]($func/image) function | -| tabularx | [`table`]($func/table), [`grid`]($func/grid) functions | -| fontenc, inputenc, unicode-math | Just start writing! | -| babel, polyglossia | [`text`]($func/text.lang) function: `[#set text(lang: "zh")]` | -| amsmath | [Math mode]($category/math) | -| amsfonts, amssymb | [`sym`]($category/symbols) module and [syntax]($syntax/#math) | -| geometry, fancyhdr | [`page`]($func/page) function | -| xcolor | [`text`]($func/text.fill) function: `[#set text(fill: rgb("#0178A4"))]` | -| hyperref | [`link`]($func/link) function | -| bibtex, biblatex, natbib | [`cite`]($func/cite), [`bibliography`]($func/bibliography) functions | -| lstlisting, minted | [`raw`]($func/raw) function and syntax | -| parskip | [`block`]($func/block.spacing) and [`par`]($func/par.first-line-indent) functions | -| csquotes | Set the [`text`]($func/text.lang) language and type `["]` or `[']` | -| caption | [`figure`]($func/figure) function | -| enumitem | [`list`]($func/list), [`enum`]($func/enum), [`terms`]($func/terms) functions | +| LaTeX Package | Typst Alternative | +|:--------------------------------|:------------------------------------------------------------- | +| graphicx, svg | [`image`]($image) function | +| tabularx | [`table`]($table), [`grid`]($grid) functions | +| fontenc, inputenc, unicode-math | Just start writing! | +| babel, polyglossia | [`text`]($text.lang) function: `[#set text(lang: "zh")]` | +| amsmath | [Math mode]($category/math) | +| amsfonts, amssymb | [`sym`]($category/symbols) module and [syntax]($syntax/#math) | +| geometry, fancyhdr | [`page`]($page) function | +| xcolor | [`text`]($text.fill) function: `[#set text(fill: rgb("#0178A4"))]` | +| hyperref | [`link`]($link) function | +| bibtex, biblatex, natbib | [`cite`]($cite), [`bibliography`]($bibliography) functions | +| lstlisting, minted | [`raw`]($raw) function and syntax | +| parskip | [`block`]($block.spacing) and [`par`]($par.first-line-indent) functions | +| csquotes | Set the [`text`]($text.lang) language and type `["]` or `[']` | +| caption | [`figure`]($figure) function | +| enumitem | [`list`]($list), [`enum`]($enum), [`terms`]($terms) functions | If you need to load functions and variables from another file, for example, to use a template, you can use an [`import`]($scripting/#modules) statement. If you @@ -444,12 +443,12 @@ Typst pre-defines a lot of useful variables in math mode. All Greek (`alpha`, their name. Some symbols are additionally available through shorthands, such as `<=`, `>=`, and `->`. -Refer to the [symbol page]($func/symbol) for a full list of the symbols. +Refer to the [symbol pages]($reference/symbols) for a full list of the symbols. If a symbol is missing, you can also access it through a [Unicode escape sequence]($syntax/#escapes). Alternate and related forms of symbols can often be selected by -[appending a modifier]($type/symbol) after a period. For example, +[appending a modifier]($symbol) after a period. For example, `arrow.l.squiggly` inserts a squiggly left-pointing arrow. If you want to insert multiletter text in your expression instead, enclose it in double quotes: @@ -459,7 +458,7 @@ $ delta "if" x <= 5 $ In Typst, delimiters will scale automatically for their expressions, just as if `\left` and `\right` commands were implicitly inserted in LaTeX. You can -customize delimiter behavior using the [`lr` function]($func/math.lr). To +customize delimiter behavior using the [`lr` function]($math.lr). To prevent a pair of delimiters from scaling, you can escape them with backslashes. Typst will automatically set terms around a slash `/` as a fraction while @@ -470,7 +469,7 @@ fraction will appear in the output. $ f(x) = (x + 1) / x $ ``` -[Sub- and superscripts]($func/math.attach) work similarly in Typst and LaTeX. +[Sub- and superscripts]($math.attach) work similarly in Typst and LaTeX. `{$x^2$}` will produce a superscript, `{$x_2$}` yields a subscript. If you want to include more than one value in a sub- or superscript, enclose their contents in parentheses: `{$x_(a -> epsilon)$}`. @@ -487,7 +486,7 @@ $ f(x, y) := cases( ) $ ``` -The above example uses the [`cases` function]($func/math.cases) to describe f. Within +The above example uses the [`cases` function]($math.cases) to describe f. Within the cases function, arguments are delimited using commas and the arguments are also interpreted as math. If you need to interpret arguments as Typst values instead, prefix them with a `#`: @@ -514,7 +513,7 @@ If you'd like to enter your mathematical symbols directly as Unicode, that is possible, too! Math calls can have two-dimensional argument lists using `;` as a delimiter. The -most common use for this is the [`mat` function]($func/math.mat) that creates +most common use for this is the [`mat` function]($math.mat) that creates matrices: ```example @@ -531,13 +530,13 @@ Papers set in LaTeX have an unmistakeable look. This is mostly due to their font, Computer Modern, justification, narrow line spacing, and wide margins. The example below -- sets wide [margins]($func/page.margin) -- enables [justification]($func/par.justify), [tighter lines]($func/par.leading) - and [first-line-indent]($func/par.first-line-indent) -- [sets the font]($func/text.font) to "New Computer Modern", an OpenType - derivate of Computer Modern for both text and [code blocks]($func/raw) -- disables paragraph [spacing]($func/block.spacing) -- increases [spacing]($func/block.spacing) around [headings]($func/heading) +- sets wide [margins]($page.margin) +- enables [justification]($par.justify), [tighter lines]($par.leading) and + [first-line-indent]($par.first-line-indent) +- [sets the font]($text.font) to "New Computer Modern", an OpenType derivate of + Computer Modern for both text and [code blocks]($raw) +- disables paragraph [spacing]($block.spacing) +- increases [spacing]($block.spacing) around [headings]($heading) ```typ #set page(margin: 1.75in) @@ -566,9 +565,9 @@ applicable, contains possible workarounds. - **Change page margins without a pagebreak.** In LaTeX, margins can always be adjusted, even without a pagebreak. To change margins in Typst, you use the - [`page` function]($func/page) which will force a page break. If you just want - a few paragraphs to stretch into the margins, then reverting to the old - margins, you can use the [`pad` function]($func/pad) with negative padding. + [`page` function]($page) which will force a page break. If you just want a few + paragraphs to stretch into the margins, then reverting to the old margins, you + can use the [`pad` function]($pad) with negative padding. - **Floating figures.** The figure command of LaTeX will smartly choose where to place a figure, be it on the top or bottom of the page, or a dedicated figure @@ -589,9 +588,9 @@ applicable, contains possible workarounds. orphans, it uses less sophisticated algorithms to determine page breaks. You can insert custom page breaks in Typst using `[#pagebreak(weak: true)]` before submitting your document. The argument `weak` ensures that no double page - break will be created if this spot would be a natural page break - anyways. You can also use `[#v(1fr)]` to distribute space on your page. It - works quite similar to LaTeX's `\vfill`. + break will be created if this spot would be a natural page break anyways. You + can also use `[#v(1fr)]` to distribute space on your page. It works quite + similar to LaTeX's `\vfill`. - **Bibliographies are not customizable.** In LaTeX, the packages `bibtex`, `biblatex`, and `natbib` provide a wide range of reference and bibliography diff --git a/docs/guides/page-setup.md b/docs/guides/page-setup.md index b396a383b..b3a2f801e 100644 --- a/docs/guides/page-setup.md +++ b/docs/guides/page-setup.md @@ -16,28 +16,29 @@ you can get started with writing. In Typst, each page has a width, a height, and margins on all four sides. The top and bottom margins may contain a header and footer. The set rule of the -[`{page}`]($func/page) element is where you control all of the page setup. If -you make changes with this set rule, Typst will ensure that there is a new and -conforming empty page afterward, so it may insert a page break. Therefore, it -is best to specify your [`{page}`]($func/page) set rule at the start of your -document or in your template. +[`{page}`]($page) element is where you control all of the page setup. If you +make changes with this set rule, Typst will ensure that there is a new and +conforming empty page afterward, so it may insert a page break. Therefore, it is +best to specify your [`{page}`]($page) set rule at the start of your document or +in your template. ```example #set rect( width: 100%, height: 100%, - inset: 4pt + inset: 4pt, ) >>> #set text(6pt) +>>> #set page(margin: auto) #set page( - "iso-b7", - header: rect[Header], - footer: rect[Footer], + paper: "iso-b7", + header: rect(fill: aqua)[Header], + footer: rect(fill: aqua)[Footer], number-align: center, ) -#rect(fill: rgb("#565565")) +#rect(fill: aqua) ``` This example visualizes the dimensions for page content, headers, and footers. @@ -45,45 +46,46 @@ The page content is the page size (ISO B7) minus each side's default margin. In the top and the bottom margin, there are stroked rectangles visualizing the header and footer. They do not touch the main content, instead, they are offset by 30% of the respective margin. You can control this offset by specifying the -[`header-ascent`]($func/page.header-ascent) and -[`footer-descent`]($func/page.footer-descent) arguments. +[`header-ascent`]($page.header-ascent) and +[`footer-descent`]($page.footer-descent) arguments. Below, the guide will go more into detail on how to accomplish common page setup requirements with examples. ## Customize page size and margins { #customize-margins } -Typst's default page size is A4 paper. Depending on your region and your -use case, you will want to change this. You can do this by using the -[`{page}`]($func/page) set rule and passing it a string argument to use a common -page size. Options include the complete ISO 216 series (e.g. `"iso-a4"`, -`"iso-c2"`), customary US formats like `"us-legal"` or `"us-letter"`, and more. -Check out the reference for the [page's paper argument]($func/page.paper) to -learn about all available options. +Typst's default page size is A4 paper. Depending on your region and your use +case, you will want to change this. You can do this by using the +[`{page}`]($page) set rule and passing it a string argument to use a common page +size. Options include the complete ISO 216 series (e.g. `"iso-a4"`, `"iso-c2"`), +customary US formats like `"us-legal"` or `"us-letter"`, and more. Check out the +reference for the [page's paper argument]($page.paper) to learn about all +available options. ```example +>>> #set page(margin: auto) #set page("us-letter") This page likes freedom. ``` If you need to customize your page size to some dimensions, you can specify the -named arguments [`width`]($func/page.width) and [`height`]($func/page.height) -instead. +named arguments [`width`]($page.width) and [`height`]($page.height) instead. ```example +>>> #set page(margin: auto) #set page(width: 12cm, height: 12cm) This page is a square. ``` ### Change the page's margins { #change-margins } -Margins are a vital ingredient for good typography: [Typographers consider lines -that fit between 45 and 75 characters best length for -legibility](http://webtypography.net/2.1.2) and your margins and +Margins are a vital ingredient for good typography: +[Typographers consider lines that fit between 45 and 75 characters best length +for legibility](http://webtypography.net/2.1.2) and your margins and [columns](#columns) help define line widths. By default, Typst will create margins proportional to the page size of your document. To set custom margins, -you will use the [`margin`]($func/page.margin) argument in the -[`{page}`]($func/page) set rule. +you will use the [`margin`]($page.margin) argument in the [`{page}`]($page) set +rule. The `margin` argument will accept a length if you want to set all margins to the same width. However, you often want to set different margins on each side. To do @@ -119,11 +121,7 @@ the margin dictionary. The `inside` margin points towards the spine, and the `outside` margin points towards the edge of the bound book. ```typ -#set page(margin: ( - inside: 2.5cm, - outside: 2cm, - y: 1.75cm, -)) +#set page(margin: (inside: 2.5cm, outside: 2cm, y: 1.75cm)) ``` Typst will assume that documents written in Left-to-Right scripts are bound on @@ -133,13 +131,12 @@ output by a different app, the binding is reversed from Typst's perspective. Also, some books, like English-language Mangas are customarily bound on the right, despite English using Left-to-Right script. To change the binding side and explicitly set where the `inside` and `outside` are, set the -[`binding`]($func/page.binding) argument in the [`{page}`]($func/page) set rule. +[`binding`]($page.binding) argument in the [`{page}`]($page) set rule. ```typ -#set text(lang: "es") - // Produce a book bound on the right, // even though it is set in Spanish. +#set text(lang: "es") #set page(binding: right) ``` @@ -151,9 +148,9 @@ Headers and footers are inserted in the top and bottom margins of every page. You can add custom headers and footers or just insert a page number. In case you need more than just a page number, the best way to insert a header -and a footer are the [`header`]($func/page.header) and -[`footer`]($func/page.footer) arguments of the [`{page}`]($func/page) set rule. -You can pass any content as their values: +and a footer are the [`header`]($page.header) and [`footer`]($page.footer) +arguments of the [`{page}`]($page) set rule. You can pass any content as their +values: ```example >>> #set page("a5", margin: (x: 2.5cm, y: 3cm)) @@ -168,7 +165,7 @@ You can pass any content as their values: Headers are bottom-aligned by default so that they do not collide with the top edge of the page. You can change this by wrapping your header in the -[`{align}`]($func/align) function. +[`{align}`]($align) function. ### Different header and footer on specific pages { #specific-pages } You'll need different headers and footers on some pages. For example, you may @@ -191,13 +188,13 @@ conditionally remove the header on the first page: ``` This example may look intimidating, but let's break it down: We are telling -Typst that the header depends on the current [location]($func/locate). The `loc` +Typst that the header depends on the current [location]($locate). The `loc` value allows other functions to find out where on the page we currently are. We -then ask Typst if the page [counter]($func/counter) is larger than one at our -current position. The page counter starts at one, so we are skipping the header -on a single page. Counters may have multiple levels. This feature is used for -items like headings, but the page counter will always have a single level, so we -can just look at the first one. +then ask Typst if the page [counter]($counter) is larger than one at our current +position. The page counter starts at one, so we are skipping the header on a +single page. Counters may have multiple levels. This feature is used for items +like headings, but the page counter will always have a single level, so we can +just look at the first one. You can, of course, add an `else` to this example to add a different header to the first page instead. @@ -206,8 +203,8 @@ the first page instead. The technique described in the previous section can be adapted to perform more advanced tasks using Typst's labels. For example, pages with big tables could omit their headers to help keep clutter down. We will mark our tables with a -`` [label]($type/label) and use the [query system]($func/query) to -find out if such a label exists on the current page: +`` [label]($label) and use the [query system]($query) to find out if +such a label exists on the current page: ```typ >>> #set page("a5", margin: (x: 2.5cm, y: 3cm)) @@ -232,8 +229,10 @@ find out if such a label exists on the current page: #pagebreak() #table( - columns: 2 * (1fr, ) -)[A][B][C][D] + columns: 2 * (1fr,), + [A], [B], + [C], [D], +) ``` Here, we query for all instances of the `` label. We then check that @@ -243,10 +242,10 @@ could add an `else` to add another header instead of deleting it. ## Add and customize page numbers { #page-numbers } Page numbers help readers keep track of and reference your document more easily. -The simplest way to insert footnotes is the [`numbering`]($func/page.numbering) -argument of the [`{page}`]($func/page) set rule. You can pass a [_numbering -pattern_]($func/numbering.numbering) string that shows how you want your pages -to be numbered. +The simplest way to insert footnotes is the [`numbering`]($page.numbering) +argument of the [`{page}`]($page) set rule. You can pass a [_numbering +pattern_]($numbering.numbering) string that shows how you want your pages to be +numbered. ```example >>> #set page("iso-b6", margin: 1.75cm) @@ -278,21 +277,21 @@ the string. This is one of many numbered pages. ``` -Go to the [`{numbering}` function reference]($func/numbering.numbering) to learn -more about the arguments you can pass here. +Go to the [`{numbering}` function reference]($numbering.numbering) to learn more +about the arguments you can pass here. In case you need to right- or left-align the page number, use the -[`number-align`]($func/page.number-align) argument of the [`{page}`]($func/page) -set rule. Alternating alignment between even and odd pages is not currently -supported using this property. To do this, you'll need to specify a custom -footer with your footnote and query the page counter as described in the section -on conditionally omitting headers and footers. +[`number-align`]($page.number-align) argument of the [`{page}`]($page) set rule. +Alternating alignment between even and odd pages is not currently supported +using this property. To do this, you'll need to specify a custom footer with +your footnote and query the page counter as described in the section on +conditionally omitting headers and footers. -### Custom footer with page numbers { #custom-footer-with-page-numbers } +### Custom footer with page numbers Sometimes, you need to add other content than a page number to your footer. -However, once a footer is specified, the [`numbering`]($func/page.numbering) -argument of the [`{page}`]($func/page) set rule is ignored. This section shows -you how to add a custom footer with page numbers and more. +However, once a footer is specified, the [`numbering`]($page.numbering) argument +of the [`{page}`]($page) set rule is ignored. This section shows you how to add +a custom footer with page numbers and more. ```example >>> #set page("iso-b6", margin: 1.75cm) @@ -319,7 +318,7 @@ This page has a custom footer. The example above shows how to add a custom footer with page numbers. First, we need to recover the page number using the page counter. For this, we are using -the [`{locate}` function]($func/locate) to check the page counter, just like in +the [`{locate}` function]($locate) to check the page counter, just like in the conditional header section. We then store the current and final page number in variables. @@ -328,8 +327,8 @@ insert all the free space on the line, and finally display the current page number and the page total. This would work just the same in the header and with any content. -We can, of course, use the [`{numbering}` function]($func/numbering) to use -numbering pattern strings like before: +We can, of course, use the [`{numbering}` function]($numbering) to use numbering +pattern strings like before: ```example >>> #set page("iso-b6", margin: 1.75cm) @@ -395,10 +394,10 @@ This page has a custom footer. ``` In this example, we use the number of pages to create an array of -[circles]($func/circle). The circles are wrapped in a [box]($func/box) so they -can all appear on the same line because they are blocks and would otherwise -create paragraph breaks. The length of this [array]($type/array) depends on the -current page number. +[circles]($circle). The circles are wrapped in a [box]($box) so they can all +appear on the same line because they are blocks and would otherwise create +paragraph breaks. The length of this [array]($array) depends on the current page +number. We then insert the circles at the right side of the footer, with 1pt of space between them. The join method of an array will attempt to @@ -406,8 +405,8 @@ between them. The join method of an array will attempt to value, interspersed with its argument. In our case, we get a single content value with circles and spaces between them that we can use with the align function. Finally, we use another box to ensure that the text and the circles -can share a line and use the [`inset` argument]($func/box.inset) to raise the -circles a bit so they line up nicely with the text. +can share a line and use the [`inset` argument]($box.inset) to raise the circles +a bit so they line up nicely with the text. ### Reset the page number and skip pages { #skip-pages } Do you, at some point in your document, need to reset the page number? Maybe you @@ -416,8 +415,7 @@ to skip a few page numbers because you will insert pages into the final printed product. The right way to modify the page number is to manipulate the page -[counter]($func/counter). The simplest manipulation is to set the counter back -to 1. +[counter]($counter). The simplest manipulation is to set the counter back to 1. ```typ #counter(page).update(1) @@ -435,8 +433,8 @@ In this example, we skip five pages. `i` is the current value of the page counter and `i + 5` is the return value of our function. In case you need to retrieve the actual page number instead of the value of the -page counter, you can use the [`page`]($func/locate) method on the argument of -the `{locate}` closure: +page counter, you can use the [`page`]($locate) method on the argument of the +`{locate}` closure: ```example #counter(page).update(i => i + 5) @@ -447,7 +445,7 @@ the `{locate}` closure: ``` You can also obtain the page numbering pattern from the `{locate}` closure -parameter with the [`page-numbering`]($func/locate) method. +parameter with the [`page-numbering`]($locate) method. ## Add columns { #columns } Add columns to your document to fit more on a page while maintaining legible @@ -455,7 +453,7 @@ line lengths. Columns are vertical blocks of text which are separated by some whitespace. This space is called the gutter. If all of your content needs to be laid out in columns, you can just specify the -desired number of columns in the [`{page}`]($func/page.columns) set rule: +desired number of columns in the [`{page}`]($page.columns) set rule: ```example >>> #set page(height: 120pt) @@ -469,8 +467,8 @@ in the next section. ### Use columns anywhere in your document { #columns-anywhere } Very commonly, scientific papers have a single-column title and abstract, while the main body is set in two-columns. To achieve this effect, Typst includes a -standalone [`{columns}` function]($func/columns) that can be used to insert -columns anywhere on a page. +standalone [`{columns}` function]($columns) that can be used to insert columns +anywhere on a page. Conceptually, the `columns` function must wrap the content of the columns: @@ -502,8 +500,8 @@ nesting and write more legible Typst markup: ``` The show rule will wrap everything that comes after it in its function. The -[`with` mehtod]($type/function.with) allows us to pass arguments, in this case, -the column count, to a function without calling it. +[`with` mehtod]($function.with) allows us to pass arguments, in this case, the +column count, to a function without calling it. Another use of the `columns` function is to create columns inside of a container like a rectangle or to customize gutter size: @@ -523,24 +521,23 @@ like a rectangle or to customize gutter size: ) ``` -### Balanced columns { #balanced-columns } +### Balanced columns If the columns on the last page of a document differ greatly in length, they may create a lopsided and unappealing layout. That's why typographers will often equalize the length of columns on the last page. This effect is called balancing columns. Typst cannot yet balance columns automatically. However, you can -balance columns manually by placing [`[#colbreak()]`]($func/colbreak) at an +balance columns manually by placing [`[#colbreak()]`]($colbreak) at an appropriate spot in your markup, creating the desired column break manually. -## One-off modifications { #one-off-modifications } +## One-off modifications You do not need to override your page settings if you need to insert a single page with a different setup. For example, you may want to insert a page that's flipped to landscape to insert a big table or change the margin and columns for -your title page. In this case, you can call [`{page}`]($func/page) as a function -with your content as an argument and the overrides as the other arguments. This -will insert enough new pages with your overridden settings to place your content -on them. Typst will revert to the page settings from the set rule after the -call. +your title page. In this case, you can call [`{page}`]($page) as a function with +your content as an argument and the overrides as the other arguments. This will +insert enough new pages with your overridden settings to place your content on +them. Typst will revert to the page settings from the set rule after the call. ```example >>> #set page("a6") diff --git a/docs/guides/welcome.md b/docs/guides/welcome.md index f48fe1e5e..f648c1c00 100644 --- a/docs/guides/welcome.md +++ b/docs/guides/welcome.md @@ -7,6 +7,6 @@ Welcome to the Guides section! Here, you'll find helpful material for specific user groups or use cases. Currently, one guide is available: An introduction to Typst for LaTeX users. Feel free to propose other topics for guides! -## List of Guides { #list-of-guides } +## List of Guides - [Guide for LaTeX users]($guides/guide-for-latex-users) - [Page setup guide]($guides/page-setup-guide) diff --git a/docs/overview.md b/docs/overview.md index e3fa3b3cd..bc7f14fc1 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -10,6 +10,9 @@ for the sciences. It is designed to be an alternative both to advanced tools like LaTeX and simpler tools like Word and Google Docs. Our goal with Typst is to build a typesetting tool that is highly capable _and_ a pleasure to use. -This documentation is split into two parts: A beginner-friendly tutorial that introduces Typst through a practical use case and a comprehensive reference that explains all of Typst's concepts and features. +This documentation is split into two parts: A beginner-friendly tutorial that +introduces Typst through a practical use case and a comprehensive reference that +explains all of Typst's concepts and features. -We also invite you to join the community we're building around Typst. Typst is still a very young project, so your feedback is more than valuable. +We also invite you to join the community we're building around Typst. Typst is +still a very young project, so your feedback is more than valuable. diff --git a/docs/reference/details.yml b/docs/reference/categories.yml similarity index 71% rename from docs/reference/details.yml rename to docs/reference/categories.yml index 5b6b9ed0c..fa2958f85 100644 --- a/docs/reference/details.yml +++ b/docs/reference/categories.yml @@ -1,33 +1,26 @@ -types: | - To style your document, you need to work with values of different kinds: Lengths - specifying the size of your elements, colors for your text and shapes, and more. - Typst categorizes these into clearly defined _types_ and tells you where it - expects which type of value. +# Descriptions of the documentation categories. - Apart from very basic types for numeric values and typical types known from - programming languages, Typst provides a special type for _content._ A value of - this type can hold anything that you can enter into your document: Text, - elements like headings and shapes, and style information. +foundations: | + Foundational types and functions. - In some places of Typst more specialized data types are used. Instead of listing - all of them here, they are explained where they are relevant. + Here, you'll find documentation for basic data types like [integers]($int) and + [strings]($int) as well as details about core computational functions. text: | Text styling. - The [text function]($func/text) is of particular interest. + The [text function]($text) is of particular interest. math: | - Typst has special [syntax]($syntax/#math) and library functions - to typeset mathematical formulas. Math formulas can be displayed inline with - text or as separate blocks. They will be typeset into their own block if they - start and end with at least one space (e.g. `[$ x^2 $]`). + Typst has special [syntax]($syntax/#math) and library functions to typeset + mathematical formulas. Math formulas can be displayed inline with text or as + separate blocks. They will be typeset into their own block if they start and + end with at least one space (e.g. `[$ x^2 $]`). In math, single letters are always displayed as is. Multiple letters, however, are interpreted as variables and functions. To display multiple letters verbatim, you can place them into quotes and to access single letter - variables, you can use the - [hashtag syntax]($scripting/#expressions). + variables, you can use the [hashtag syntax]($scripting/#expressions). ```example $ A = pi r^2 $ @@ -41,7 +34,7 @@ math: | Math mode makes a wide selection of [symbols]($category/symbols/sym) like `pi`, `dot`, or `RR` available. Many mathematical symbols are available in different variants. You can select between different variants by applying - [modifiers]($type/symbol) to the symbol. Typst further recognizes a number of + [modifiers]($symbol) to the symbol. Typst further recognizes a number of shorthand sequences like `=>` that approximate a symbol. When such a shorthand exists, the symbol's documentation lists it. @@ -51,6 +44,7 @@ math: | Formulas can also contain line breaks. Each line can contain one or multiple _alignment points_ (`&`) which are then aligned. + ```example $ sum_(k=0)^n k &= 1 + ... + n \ @@ -60,11 +54,11 @@ math: | Math mode supports special function calls without the hashtag prefix. In these "math calls", the argument list works a little differently than in code: - - Within them, Typst is still in "math mode". Thus, you can write math directly - into them, but need to use hashtag syntax to pass code expressions (except - for strings, which are available in the math syntax). - - They support positional and named arguments, but don't support - trailing content blocks and argument spreading. + - Within them, Typst is still in "math mode". Thus, you can write math + directly into them, but need to use hashtag syntax to pass code expressions + (except for strings, which are available in the math syntax). + - They support positional and named arguments, but don't support trailing + content blocks and argument spreading. - They provide additional syntax for 2-dimensional argument lists. The semicolon (`;`) merges preceding arguments separated by commas into an array argument. @@ -143,27 +137,11 @@ emoji: | certain emojis, you can also import them from the `emoji` module (`[#import emoji: face]`) to use them without the `#emoji.` prefix. -foundations: | - Foundational functions for computation. - -calculate: | - Calculations and processing of numeric values. - - These functions are part of the `calc` module and not imported by default. In - addition to the functions listed below, the `calc` module also defines the - constants `pi`, `tau`, `e`, `inf`, and `nan`. - -construct: | - Construction of and conversions between values of different types. - data-loading: | Data loading from external files. - These functions help you with embedding data from experiments in your - documents. - -utility: | - Useful utility functions. + These functions help you with loading and embedding data, for example from + the results of an experiment. packages: | Typst [packages]($scripting/#packages) encapsulate reusable building blocks diff --git a/docs/reference/groups.yml b/docs/reference/groups.yml index 3bc80339e..cbf9cfec9 100644 --- a/docs/reference/groups.yml +++ b/docs/reference/groups.yml @@ -3,24 +3,27 @@ - name: variants display: Variants + category: math functions: ["serif", "sans", "frak", "mono", "bb", "cal"] description: | Alternate typefaces within formulas. - These functions are distinct from the [`text`]($func/text) function because - math fonts contain multiple variants of each letter. + These functions are distinct from the [`text`]($text) function because math + fonts contain multiple variants of each letter. - name: styles display: Styles + category: math functions: ["upright", "italic", "bold"] description: | Alternate letterforms within formulas. - These functions are distinct from the [`text`]($func/text) function because - math fonts contain multiple variants of each letter. + These functions are distinct from the [`text`]($text) function because math + fonts contain multiple variants of each letter. - name: sizes display: Sizes + category: math functions: ["display", "inline", "script", "sscript"] description: | Forced size styles for expressions within formulas. @@ -31,6 +34,7 @@ - name: underover display: Under/Over + category: math functions: [ "underline", "overline", @@ -47,23 +51,41 @@ - name: roots display: Roots + category: math functions: ["root", "sqrt"] - description: Square and non-square roots. + description: | + Square and non-square roots. + + # Example + ```example + $ sqrt(3 - 2 sqrt(2)) = sqrt(2) - 1 $ + $ root(3, x) $ + ``` - name: attach display: Attach + category: math functions: ["attach", "scripts", "limits"] description: | Subscript, superscripts, and limits. - The `attach` function backs the `[$a_b^c$]` syntax that adds top and bottom - attachments to a part of an equation. Attachments can be displayed either as - sub/superscripts, or limits. Typst automatically decides which is more - suitable depending on the base, but you can also control this manually with - the `scripts` and `limits` functions. + Attachments can be displayed either as sub/superscripts, or limits. Typst + automatically decides which is more suitable depending on the base, but you + can also control this manually with the `scripts` and `limits` functions. + + # Example + ```example + $ sum_(i=0)^n a_i = 2^(1+i) $ + ``` + + # Syntax + This function also has dedicated syntax for attachments after the base: Use + the underscore (`_`) to indicate a subscript i.e. bottom attachment and the + hat (`^`) to indicate a superscript i.e. top attachment. - name: lr display: Left/Right + category: math functions: ["lr", "abs", "norm", "floor", "ceil", "round"] description: | Delimiter matching. @@ -74,3 +96,21 @@ and control their size exactly. Apart from the `lr` function, Typst provides a few more functions that create delimiter pairings for absolute, ceiled, and floored values as well as norms. + + # Example + ```example + $ [a, b/2] $ + $ lr(]sum_(x=1)^n] x, size: #50%) $ + $ abs((x + y) / 2) $ + ``` + +- name: calc + display: Calculation + category: foundations + path: ["calc"] + description: | + Calculations and processing of numeric values. + + These functions are part of the `calc` module and not imported by default. + In addition to the functions listed below, the `calc` module also defines + the constants `pi`, `tau`, `e`, `inf`, and `nan`. diff --git a/docs/reference/scripting.md b/docs/reference/scripting.md index 72d2bb9a6..29448c721 100644 --- a/docs/reference/scripting.md +++ b/docs/reference/scripting.md @@ -7,7 +7,7 @@ Typst embeds a powerful scripting language. You can automate your documents and create more sophisticated styles with code. Below is an overview over the scripting concepts. -## Expressions { #expressions } +## Expressions In Typst, markup and code are fused into one. All but the most common elements are created with _functions._ To make this as convenient as possible, Typst provides compact syntax to embed a code expression into markup: An expression is @@ -23,14 +23,13 @@ be interpreted as text, the expression can forcibly be ended with a semicolon ``` The example above shows a few of the available expressions, including -[function calls]($type/function), -[field accesses]($scripting/#fields), and +[function calls]($function), [field accesses]($scripting/#fields), and [method calls]($scripting/#methods). More kinds of expressions are discussed in the remainder of this chapter. A few kinds of expressions are not compatible with the hashtag syntax (e.g. binary operator expressions). To embed these into markup, you can use parentheses, as in `[#(1 + 2)]`. -## Blocks { #blocks } +## Blocks To structure your code and embed markup into it, Typst provides two kinds of _blocks:_ @@ -45,9 +44,9 @@ _blocks:_ - **Content block:** `{[*Hey* there!]}` \ With content blocks, you can handle markup/content as a programmatic value, - store it in variables and pass it to [functions]($type/function). Content + store it in variables and pass it to [functions]($function). Content blocks are delimited by square brackets and can contain arbitrary markup. A - content block results in a value of type [content]($type/content). An + content block results in a value of type [content]($content). An arbitrary number of content blocks can be passed as trailing arguments to functions. That is, `{list([A], [B])}` is equivalent to `{list[A][B]}`. @@ -69,7 +68,7 @@ As already demonstrated above, variables can be defined with `{let}` bindings. The variable is assigned the value of the expression that follows the `=` sign. The assignment of a value is optional, if no value is assigned, the variable will be initialized as `{none}`. The `{let}` keyword can also be used to create -a [custom named function]($type/function/#definitions). Let bindings can be +a [custom named function]($function/#defining-functions). Let bindings can be accessed for the rest of the containing block or document. ```example @@ -81,8 +80,8 @@ It explains #name. Sum is #add(2, 3). ``` -Let bindings can also be used to destructure [arrays]($type/array) and -[dictionaries]($type/dictionary). In this case, the left-hand side of the +Let bindings can also be used to destructure [arrays]($array) and +[dictionaries]($dictionary). In this case, the left-hand side of the assignment should mirror an array or dictionary. The `..` operator can be used once in the pattern to collect the remainder of the array's or dictionary's items. @@ -142,7 +141,7 @@ swap variables among other things. } ``` -## Conditionals { #conditionals } +## Conditionals With a conditional, you can display or compute different things depending on whether some condition is fulfilled. Typst supports `{if}`, `{else if}` and `{else}` expression. When the condition evaluates to `{true}`, the conditional @@ -164,7 +163,7 @@ Each branch can have a code or content block as its body. - `{if condition [..] else {..}}` - `{if condition [..] else if condition {..} else [..]}` -## Loops { #loops } +## Loops With loops, you can repeat content or compute something iteratively. Typst supports two types of loops: `{for}` and `{while}` loops. The former iterate over a specified collection whereas the latter iterate as long as a condition @@ -190,18 +189,18 @@ together into one larger array. For loops can iterate over a variety of collections: - `{for letter in "abc" {..}}` \ - Iterates over the characters of the [string]($type/string). + Iterates over the characters of the [string]($str). (Technically, iterates over the grapheme clusters of the string. Most of the time, a grapheme cluster is just a single character/codepoint. However, some constructs like flag emojis that consist of multiple codepoints are still only one cluster.) - `{for value in array {..}}` \ - Iterates over the items in the [array]($type/array). The destructuring syntax + Iterates over the items in the [array]($array). The destructuring syntax described in [Let binding]($scripting/#bindings) can also be used here. - `{for pair in dict {..}}` \ - Iterates over the key-value pairs of the [dictionary]($type/dictionary). + Iterates over the key-value pairs of the [dictionary]($dictionary). The pairs can also be destructured by using `{for (key, value) in dict {..}}`. To control the execution of the loop, Typst provides the `{break}` and @@ -225,16 +224,16 @@ The body of a loop can be a code or content block: - `{while condition {..}}` - `{while condition [..]}` -## Fields { #fields } +## Fields You can use _dot notation_ to access fields on a value. The value in question can be either: -- a [dictionary]($type/dictionary) that has the specified key, -- a [symbol]($type/symbol) that has the specified modifier, -- a [module]($type/module) containing the specified definition, -- [content]($type/content) consisting of an element that has the specified - field. The available fields match the arguments of the - [element function]($type/function/#element-functions) that were given when - the element was constructed. +- a [dictionary]($dictionary) that has the specified key, +- a [symbol]($symbol) that has the specified modifier, +- a [module]($module) containing the specified definition, +- [content]($content) consisting of an element that has the specified field. The + available fields match the arguments of the + [element function]($function/#element-functions) that were given when the + element was constructed. ```example #let dict = (greet: "Hello") @@ -246,12 +245,20 @@ can be either: #it.level ``` -## Methods { #methods } -A method is a kind of a [function]($type/function) that is tightly coupled with -a specific type. It is called on a value of its type using the same dot notation -that is also used for fields: `{value.method(..)}`. The -[type documentation]($type) lists the available methods for each of the built-in -types. You cannot define your own methods. +## Methods +A _method call_ is a convenient way to call a function that is scoped to a +value's [type]($type). For example, we can call the [`str.len`]($str.len) +function in the following two equivalent ways: + +```example +#str.len("abc") is the same as +#"abc".len() +``` + +The structure of a method call is `{value.method(..args)}` and its equivalent +full function call is `{type(value).method(value, ..args)}`. The documentation +of each type lists it's scoped functions. You cannot currently define your own +methods. ```example #let array = (1, 2, 3, 4) @@ -261,26 +268,29 @@ types. You cannot define your own methods. #("a, b, c" .split(", ") .join[ --- ]) + +#"abc".len() is the same as +#str.len("abc") ``` -Methods are the only functions in Typst that can modify the value they are -called on. In some cases, this means that a method is only called for its side -effect and its return value should be ignored (and not participate in joining). -The canonical way to discard a value is with a let binding: -`{let _ = array.remove(1)}`. +There are a few special functions that modify the value they are called on (e.g. +[`array.push`]($array.push)). These functions _must_ be called in method form. +In some cases, when the method is only called for its side effect, its return +value should be ignored (and not participate in joining). The canonical way to +discard a value is with a let binding: `{let _ = array.remove(1)}`. -## Modules { #modules } +## Modules You can split up your Typst projects into multiple files called _modules._ A module can refer to the content and definitions of another module in multiple ways: - **Including:** `{include "bar.typ"}` \ Evaluates the file at the path `bar.typ` and returns the resulting - [content]($type/content). + [content]($content). - **Import:** `{import "bar.typ"}` \ Evaluates the file at the path `bar.typ` and inserts the resulting - [module]($type/module) into the current scope as `bar` (filename without + [module]($module) into the current scope as `bar` (filename without extension). - **Import items:** `{import "bar.typ": a, b}` \ @@ -289,15 +299,15 @@ ways: bindings) and defines them in the current file. Replacing `a, b` with `*` loads all variables defined in a module. -Instead of a path, you can also use a [module value]($type/module), as shown in -the following example: +Instead of a path, you can also use a [module value]($module), as shown in the +following example: ```example #import emoji: face #face.grin ``` -## Packages { #packages } +## Packages To reuse building blocks across projects, you can also create and import Typst _packages._ A package import is specified as a triple of a namespace, a name, and a version. @@ -316,7 +326,7 @@ If you are using Typst locally, you can also create your own system-local packages. For more details on this, see the [package repository](https://github.com/typst/packages). -## Operators { #operators } +## Operators The following table lists all available unary and binary operators with effect, arity (unary, binary) and precedence level (higher binds stronger). diff --git a/docs/reference/styling.md b/docs/reference/styling.md index 31e04b244..687fed790 100644 --- a/docs/reference/styling.md +++ b/docs/reference/styling.md @@ -10,15 +10,14 @@ might not be a built-in property for everything you wish to do. For this reason, Typst further supports _show rules_ that can completely redefine the appearance of elements. -## Set rules { #set-rules } +## Set rules With set rules, you can customize the appearance of elements. They are written -as a [function call]($type/function) to an -[element function]($type/function/#element-functions) preceded by the `{set}` -keyword (or `[#set]` in markup). Only optional parameters of that function can -be provided to the set rule. Refer to each function's documentation to see which -parameters are optional. In the example below, we use two set rules to change -the [font family]($func/text.font) and -[heading numbering]($func/heading.numbering). +as a [function call]($function) to an [element +function]($function/#element-functions) preceded by the `{set}` keyword (or +`[#set]` in markup). Only optional parameters of that function can be provided +to the set rule. Refer to each function's documentation to see which parameters +are optional. In the example below, we use two set rules to change the +[font family]($text.font) and [heading numbering]($heading.numbering). ```example #set heading(numbering: "I.") @@ -60,13 +59,14 @@ a _set-if_ rule. #task(critical: false)[Work deadline] ``` -## Show rules { #show-rules } +## Show rules With show rules, you can deeply customize the look of a type of element. The most basic form of show rule is a _show-set rule._ Such a rule is written as the -`{show}` keyword followed by a [selector]($type/selector), a colon and then a set rule. The most basic form of selector is an -[element function]($type/function/#element-functions). This lets the set rule -only apply to the selected element. In the example below, headings become dark -blue while all other text stays black. +`{show}` keyword followed by a [selector]($selector), a colon and then a set +rule. The most basic form of selector is an +[element function]($function/#element-functions). This lets the set rule only +apply to the selected element. In the example below, headings become dark blue +while all other text stays black. ```example #show heading: set text(navy) @@ -79,12 +79,11 @@ With show-set rules you can mix and match properties from different functions to achieve many different effects. But they still limit you to what is predefined in Typst. For maximum flexibility, you can instead write a show rule that defines how to format an element from scratch. To write such a show rule, -replace the set rule after the colon with an arbitrary -[function]($type/function). This function receives the element in question and -can return arbitrary content. Different -[fields]($scripting/#fields) are available on the element passed -to the function. Below, we define a show rule that formats headings for a -fantasy encyclopedia. +replace the set rule after the colon with an arbitrary [function]($function). +This function receives the element in question and can return arbitrary content. +Different [fields]($scripting/#fields) are available on the element passed to +the function. Below, we define a show rule that formats headings for a fantasy +encyclopedia. ```example #set heading(numbering: "(I)") @@ -125,15 +124,15 @@ to: - **Regex:** `{show regex("\w+"): ..}` \ Select and transform text with a regular expression for even more flexibility. - See the documentation of the [`regex` function]($func/regex) for details. + See the documentation of the [`regex` type]($regex) for details. - **Function with fields:** `{show heading.where(level: 1): ..}` \ Transform only elements that have the specified fields. For example, you might want to only change the style of level-1 headings. - **Label:** `{show : ..}` \ - Select and transform elements that have the specified label. - See the documentation of the [`label` function]($func/label) for more details. + Select and transform elements that have the specified label. See the + documentation of the [`label` type]($label) for more details. ```example #show "Project": smallcaps diff --git a/docs/reference/syntax.md b/docs/reference/syntax.md index 3209e8e54..c03d6ab31 100644 --- a/docs/reference/syntax.md +++ b/docs/reference/syntax.md @@ -11,7 +11,7 @@ set and show rules, which let you style your document easily and automatically. All this is backed by a tightly integrated scripting language with built-in and user-defined functions. -## Markup { #markup } +## Markup Typst provides built-in markup for the most common document elements. Most of the syntax elements are just shortcuts for a corresponding function. The table below lists all markup that is available and links to the best place to learn @@ -19,20 +19,20 @@ more about their syntax and usage. | Name | Example | See | | ------------------ | ------------------------ | ---------------------------- | -| Paragraph break | Blank line | [`parbreak`]($func/parbreak) | -| Strong emphasis | `[*strong*]` | [`strong`]($func/strong) | -| Emphasis | `[_emphasis_]` | [`emph`]($func/emph) | -| Raw text | ``[`print(1)`]`` | [`raw`]($func/raw) | -| Link | `[https://typst.app/]` | [`link`]($func/link) | -| Label | `[]` | [`label`]($func/label) | -| Reference | `[@intro]` | [`ref`]($func/ref) | -| Heading | `[= Heading]` | [`heading`]($func/heading) | -| Bullet list | `[- item]` | [`list`]($func/list) | -| Numbered list | `[+ item]` | [`enum`]($func/enum) | -| Term list | `[/ Term: description]` | [`terms`]($func/terms) | +| Paragraph break | Blank line | [`parbreak`]($parbreak) | +| Strong emphasis | `[*strong*]` | [`strong`]($strong) | +| Emphasis | `[_emphasis_]` | [`emph`]($emph) | +| Raw text | ``[`print(1)`]`` | [`raw`]($raw) | +| Link | `[https://typst.app/]` | [`link`]($link) | +| Label | `[]` | [`label`]($label) | +| Reference | `[@intro]` | [`ref`]($ref) | +| Heading | `[= Heading]` | [`heading`]($heading) | +| Bullet list | `[- item]` | [`list`]($list) | +| Numbered list | `[+ item]` | [`enum`]($enum) | +| Term list | `[/ Term: description]` | [`terms`]($terms) | | Math | `[$x^2$]` | [Math]($category/math) | -| Line break | `[\]` | [`linebreak`]($func/linebreak) | -| Smart quote | `['single' or "double"]` | [`smartquote`]($func/smartquote) | +| Line break | `[\]` | [`linebreak`]($linebreak) | +| Smart quote | `['single' or "double"]` | [`smartquote`]($smartquote) | | Symbol shorthand | `[~, ---]` | [Symbols]($category/symbols/sym) | | Code expression | `[#rect(width: 1cm)]` | [Scripting]($scripting/#expressions) | | Character escape | `[Tweet at us \#ad]` | [Below](#escapes) | @@ -52,8 +52,8 @@ follows: | Block-level math | `[$ x^2 $]` | [Math]($category/math) | | Bottom attachment | `[$x_1$]` | [`attach`]($category/math/attach) | | Top attachment | `[$x^2$]` | [`attach`]($category/math/attach) | -| Fraction | `[$1 + (a+b)/5$]` | [`frac`]($func/math.frac) | -| Line break | `[$x \ y$]` | [`linebreak`]($func/linebreak) | +| Fraction | `[$1 + (a+b)/5$]` | [`frac`]($math.frac) | +| Line break | `[$x \ y$]` | [`linebreak`]($linebreak) | | Alignment point | `[$x &= 2 \ &= 3$]` | [Math]($category/math) | | Variable access | `[$#x$, $pi$]` | [Math]($category/math) | | Field access | `[$arrow.r.long$]` | [Scripting]($scripting/#fields) | @@ -73,22 +73,22 @@ a table listing all syntax that is available in code mode: | Name | Example | See | | ------------------------ | ----------------------------- | ---------------------------------- | | Variable access | `{x}` | [Scripting]($scripting/#blocks) | -| Any literal | `{1pt, "hey"}` | [Types]($types) | +| Any literal | `{1pt, "hey"}` | [Scripting]($scripting/#expressions) | | Code block | `{{ let x = 1; x + 2 }}` | [Scripting]($scripting/#blocks) | | Content block | `{[*Hello*]}` | [Scripting]($scripting/#blocks) | | Parenthesized expression | `{(1 + 2)}` | [Scripting]($scripting/#blocks) | -| Array | `{(1, 2, 3)}` | [Array]($type/array) | -| Dictionary | `{(a: "hi", b: 2)}` | [Dictionary]($type/dictionary) | +| Array | `{(1, 2, 3)}` | [Array]($array) | +| Dictionary | `{(a: "hi", b: 2)}` | [Dictionary]($dictionary) | | Unary operator | `{-x}` | [Scripting]($scripting/#operators) | | Binary operator | `{x + y}` | [Scripting]($scripting/#operators) | | Assignment | `{x = 1}` | [Scripting]($scripting/#operators) | | Field access | `{x.y}` | [Scripting]($scripting/#fields) | | Method call | `{x.flatten()}` | [Scripting]($scripting/#methods) | -| Function call | `{min(x, y)}` | [Function]($type/function) | -| Argument spreading | `{min(..nums)}` | [Arguments]($type/arguments) | -| Unnamed function | `{(x, y) => x + y}` | [Function]($type/function) | +| Function call | `{min(x, y)}` | [Function]($function) | +| Argument spreading | `{min(..nums)}` | [Arguments]($arguments) | +| Unnamed function | `{(x, y) => x + y}` | [Function]($function) | | Let binding | `{let x = 1}` | [Scripting]($scripting/#bindings) | -| Named function | `{let f(x) = 2 * x}` | [Function]($type/function) | +| Named function | `{let f(x) = 2 * x}` | [Function]($function) | | Set rule | `{set text(14pt)}` | [Styling]($styling/#set-rules) | | Set-if rule | `{set text(..) if .. }` | [Styling]($styling/#set-rules) | | Show-set rule | `{show par: set block(..)}` | [Styling]($styling/#show-rules) | @@ -98,16 +98,16 @@ a table listing all syntax that is available in code mode: | For loop | `{for x in (1, 2, 3) {..}}` | [Scripting]($scripting/#loops) | | While loop | `{while x < 10 {..}}` | [Scripting]($scripting/#loops) | | Loop control flow | `{break, continue}` | [Scripting]($scripting/#loops) | -| Return from function | `{return x}` | [Function]($type/function) | +| Return from function | `{return x}` | [Function]($function) | | Include module | `{include "bar.typ"}` | [Scripting]($scripting/#modules) | | Import module | `{import "bar.typ"}` | [Scripting]($scripting/#modules) | | Import items from module | `{import "bar.typ": a, b, c}` | [Scripting]($scripting/#modules) | | Comment | `[/* block */, // line]` | [Below](#comments) | -## Comments { #comments } -Comments are ignored by Typst and will not be included in the output. This -is useful to exclude old versions or to add annotations. -To comment out a single line, start it with `//`: +## Comments +Comments are ignored by Typst and will not be included in the output. This is +useful to exclude old versions or to add annotations. To comment out a single +line, start it with `//`: ```example // our data barely supports // this claim @@ -129,9 +129,9 @@ Our study design is as follows: ## Escape sequences { #escapes } Escape sequences are used to insert special characters that are hard to type or otherwise have special meaning in Typst. To escape a character, precede it with -a backslash. To insert any Unicode codepoint, you can write a hexadecimal -escape sequence: `[\u{1f600}]`. The same kind of escape sequences also work in -[strings]($type/string). +a backslash. To insert any Unicode codepoint, you can write a hexadecimal escape +sequence: `[\u{1f600}]`. The same kind of escape sequences also work in +[strings]($str). ```example I got an ice cream for diff --git a/docs/reference/types.md b/docs/reference/types.md deleted file mode 100644 index 5de4fb0c8..000000000 --- a/docs/reference/types.md +++ /dev/null @@ -1,1471 +0,0 @@ -# None -A value that indicates the absence of any other value. - -The none type has exactly one value: `{none}`. - -When inserted into the document, it is not visible. -This is also the value that is produced by empty code blocks. -It can be [joined]($scripting/#blocks) with any value, yielding -the other value. - -## Example -```example -Not visible: #none -``` - -# Auto -A value that indicates a smart default. - -The auto type has exactly one value: `{auto}`. - -Parameters that support the `{auto}` value have some smart default or contextual -behaviour. A good example is the [text direction]($func/text.dir) parameter. -Setting it to `{auto}` lets Typst automatically determine the direction from the -[text language]($func/text.lang). - -# Boolean -Either `{true}` or `{false}`. - -The boolean type has two values: `{true}` and `{false}`. It denotes whether -something is active or enabled. - -## Example -```example -#false \ -#true \ -#(1 < 2) -``` - -# Integer -A whole number. - -The number can be negative, zero, or positive. As Typst uses 64 bits to store -integers, integers cannot be smaller than `{-9223372036854775808}` or larger than -`{9223372036854775807}`. - -The number can also be specified as hexadecimal, octal, or binary by starting it -with a zero followed by either `x`, `o`, or `b`. - -You can convert a value to an integer with the [`int`]($func/int) function. - -## Example -```example -#(1 + 2) \ -#(2 - 5) \ -#(3 + 4 < 8) - -#0xff \ -#0o10 \ -#0b1001 -``` - -# Float -A floating-point number. - -A limited-precision representation of a real number. Typst uses 64 bits to -store floats. Wherever a float is expected, you can also pass an -[integer]($type/integer). - -You can convert a value to a float with the [`float`]($func/float) function. - -## Example -```example -#3.14 \ -#1e4 \ -#(10 / 4) -``` - -# Length -A size or distance, possibly expressed with contextual units. -Typst supports the following length units: - -- Points: `{72pt}` -- Millimeters: `{254mm}` -- Centimeters: `{2.54cm}` -- Inches: `{1in}` -- Relative to font size: `{2.5em}` - -A length has the following fields: - -- `abs`: A length with just the absolute component of the current length -(that is, excluding the `em` component). -- `em`: The amount of `em` units in this length, as a [float]($type/float). - -You can multiply lengths with and divide them by integers and floats. - -## Example -```example -#rect(width: 20pt) -#rect(width: 2em) -#rect(width: 1in) - -#(3em + 5pt).em -#(20pt).em - -#(40em + 2pt).abs -#(5em).abs -``` - -## Methods -### pt() -Converts this length to points. - -Fails with an error if this length has non-zero `em` units (such as `5em + 2pt` -instead of just `2pt`). Use the `abs` field (such as in `(5em + 2pt).abs.pt()`) -to ignore the `em` component of the length (thus converting only its absolute -component). - -- returns: float - -### mm() -Converts this length to millimeters. - -Fails with an error if this length has non-zero `em` units (such as `5em + 2pt` -instead of just `2pt`). See the [`pt`]($type/float.pt) method for more info. - -- returns: float - -### cm() -Converts this length to centimeters. - -Fails with an error if this length has non-zero `em` units (such as `5em + 2pt` -instead of just `2pt`). See the [`pt`]($type/float.pt) method for more info. - -- returns: float - -### inches() -Converts this length to inches. - -Fails with an error if this length has non-zero `em` units (such as `5em + 2pt` -instead of just `2pt`). See the [`pt`]($type/float.pt) method for more info. - -- returns: float - -# Angle -An angle describing a rotation. -Typst supports the following angular units: - -- Degrees: `{180deg}` -- Radians: `{3.14rad}` - -## Example -```example -#rotate(10deg)[Hello there!] -``` - -## Methods -### deg() -Converts this angle to degrees. - -- returns: float - -### rad() -Converts this angle to radians. - -- returns: float - -# Ratio -A ratio of a whole. - -Written as a number, followed by a percent sign. - -## Example -```example -#set align(center) -#scale(x: 150%)[ - Scaled apart. -] -``` - -# Relative Length -A length in relation to some known length. - -This type is a combination of a [length]($type/length) with a -[ratio]($type/ratio). It results from addition and subtraction -of a length and a ratio. Wherever a relative length is expected, you can also -use a bare length or ratio. - -A relative length has the following fields: -- `length`: Its length component. -- `ratio`: Its ratio component. - -## Example -```example -#rect(width: 100% - 50pt) - -#(100% - 50pt).length -#(100% - 50pt).ratio -``` - -# Fraction -Defines how the the remaining space in a layout is distributed. - -Each fractionally sized element gets space based on the ratio of its fraction to -the sum of all fractions. - -For more details, also see the [h]($func/h) and [v]($func/v) functions and the -[grid function]($func/grid). - -## Example -```example -Left #h(1fr) Left-ish #h(2fr) Right -``` - -# Color -A color in a specific color space. - -Typst supports: -- sRGB through the [`rgb` function]($func/rgb) -- Device CMYK through [`cmyk` function]($func/cmyk) -- D65 Gray through the [`luma` function]($func/luma) - -Furthermore, Typst provides the following built-in colors: - -`black`, `gray`, `silver`, `white`, `navy`, `blue`, `aqua`, `teal`, `eastern`, -`purple`, `fuchsia`, `maroon`, `red`, `orange`, `yellow`, `olive`, `green`, and -`lime`. - -## Methods -### kind() -Returns the constructor function for this color's kind -([`rgb`]($func/rgb), [`cmyk`]($func/cmyk) or [`luma`]($func/luma)). - -```example -#let color = cmyk(1%, 2%, 3%, 4%) -#(color.kind() == cmyk) -``` - -- returns: function - -### lighten() -Lightens a color. - -- amount: ratio (positional, required) - The factor to lighten the color by. -- returns: color - -### darken() -Darkens a color. - -- amount: ratio (positional, required) - The factor to darken the color by. -- returns: color - -### negate() -Produces the negative of the color. - -- returns: color - -### hex() -Returns the color's RGB(A) hex representation (such as `#ffaa32` or `#020304fe`). -The alpha component (last two digits in `#020304fe`) is omitted if it is equal -to `ff` (255 / 100%). - -- returns: string - -### rgba() -Converts this color to sRGB and returns its components (R, G, B, A) as an array -of [integers]($type/integer). - -- returns: array - -### cmyk() -Converts this color to Digital CMYK and returns its components (C, M, Y, K) as -an array of [ratios]($type/ratio). Note that this function will throw an error -when applied to an [rgb]($func/rgb) color, since its conversion to CMYK is not -available. - -- returns: array - -### luma() -If this color was created with [luma]($func/luma), returns the [integer]($type/integer) -value used on construction. Otherwise (for [rgb]($func/rgb) and [cmyk]($func/cmyk) colors), -throws an error. - -- returns: integer - -# Datetime -Represents a date, a time, or a combination of both. Can be created by either -specifying a custom datetime using the [`datetime`]($func/datetime) function or -getting the current date with [`datetime.today`]($func/datetime.today). - -## Example -```example -#let date = datetime( - year: 2020, - month: 10, - day: 4, -) - -#date.display() \ -#date.display( - "y:[year repr:last_two]" -) - -#let time = datetime( - hour: 18, - minute: 2, - second: 23, -) - -#time.display() \ -#time.display( - "h:[hour repr:12][period]" -) -``` - -## Format -You can specify a customized formatting using the -[`display`]($type/datetime.display) method. The format of a datetime is -specified by providing _components_ with a specified number of _modifiers_. A -component represents a certain part of the datetime that you want to display, -and with the help of modifiers you can define how you want to display that -component. In order to display a component, you wrap the name of the component -in square brackets (e.g. `[[year]]` will display the year). In order to add -modifiers, you add a space after the component name followed by the name of the -modifier, a colon and the value of the modifier (e.g. `[[month repr:short]]` -will display the short representation of the month). - -The possible combination of components and their respective modifiers is as -follows: - -* `year`: Displays the year of the datetime. - * `padding`: Can be either `zero`, `space` or `none`. Specifies how the year - is padded. - * `repr` Can be either `full` in which case the full year is displayed or - `last_two` in which case only the last two digits are displayed. - * `sign`: Can be either `automatic` or `mandatory`. Specifies when the sign - should be displayed. -* `month`: Displays the month of the datetime. - * `padding`: Can be either `zero`, `space` or `none`. Specifies how the month - is padded. - * `repr`: Can be either `numerical`, `long` or `short`. Specifies if the month - should be displayed as a number or a word. Unfortunately, when choosing the - word representation, it can currently only display the English version. In - the future, it is planned to support localization. -* `day`: Displays the day of the datetime. - * `padding`: Can be either `zero`, `space` or `none`. Specifies how the day - is padded. -* `week_number`: Displays the week number of the datetime. - * `padding`: Can be either `zero`, `space` or `none`. Specifies how the week - number is padded. - * `repr`: Can be either `ISO`, `sunday` or `monday`. In the case of `ISO`, - week numbers are between 1 and 53, while the other ones are between 0 - and 53. -* `weekday`: Displays the weekday of the date. - * `repr` Can be either `long`, `short`, `sunday` or `monday`. In the case of - `long` and `short`, the corresponding English name will be displayed (same - as for the month, other languages are currently not supported). In the case - of `sunday` and `monday`, the numerical value will be displayed (assuming - Sunday and Monday as the first day of the week, respectively). - * `one_indexed`: Can be either `true` or `false`. Defines whether the - numerical representation of the week starts with 0 or 1. -* `hour`: Displays the hour of the date. - * `padding`: Can be either `zero`, `space` or `none`. Specifies how the hour - is padded. - * `repr`: Can be either `24` or `12`. Changes whether the hour is displayed in - the 24-hour or 12-hour format. -* `period`: The AM/PM part of the hour - * `case`: Can be `lower` to display it in lower case and `upper` to display it - in upper case. -* `minute`: Displays the minute of the date. - * `padding`: Can be either `zero`, `space` or `none`. Specifies how the minute - is padded. -* `second`: Displays the second of the date. - * `padding`: Can be either `zero`, `space` or `none`. Specifies how the second - is padded. - -Keep in mind that not always all components can be used. For example, if -you create a new datetime with `{datetime(year: 2023, month: 10, day: 13)}`, it -will be stored as a plain date internally, meaning that you cannot use -components such as `hour` or `minute`, which would only work on datetimes -that have a specified time. - -## Methods -### display() -Displays the datetime in a certain way. Depending on whether you have defined -just a date, a time or both, the default format will be different. -If you specified a date, it will be `[[year]-[month]-[day]]`. If you specified a -time, it will be `[[hour]:[minute]:[second]]`. In the case of a datetime, it -will be `[[year]-[month]-[day] [hour]:[minute]:[second]]`. - -- pattern: string (positional) - The format used to display the datetime. -- returns: string - -### year() -Returns the year of the datetime, if it exists. Otherwise, it returns `{none}`. - -- returns: integer or none - -### month() -Returns the month of the datetime, if it exists. Otherwise, it returns `{none}`. - -- returns: integer or none - -### weekday() -Returns the weekday of the datetime as a number starting with 1 from Monday, if -it exists. Otherwise, it returns `{none}`. - -- returns: integer or none - -### day() -Returns the day of the datetime, if it exists. Otherwise, it returns `{none}`. - -- returns: integer or none - -### hour() -Returns the hour of the datetime, if it exists. Otherwise, it returns `{none}`. - -- returns: integer or none - -### minute() -Returns the minute of the datetime, if it exists. Otherwise, it returns -`{none}`. - -- returns: integer or none - -### second() -Returns the second of the datetime, if it exists. Otherwise, it returns -`{none}`. - -- returns: integer or none - -### ordinal() -Returns the ordinal (day of the year) of the datetime, if it exists. -Otherwise, it returns `{none}`. - -- returns: integer or none - -# Duration -Represents a span of time. Can be created by either specifying a custom -duration using the [`duration`]($func/datetime) function or by subtracting two -[datetimes]($type/datetime). - -## Example -```example -#let duration = duration( - days: 4, - hours: 2, - minutes: 10, -) - -#duration.hours() -``` - -## Methods -### seconds() -Returns the duration in seconds as a floating-point value. - -This function returns the total duration represented in seconds as a -floating-point number. It does not provide the second component of the duration, -but rather gives the overall duration in terms of seconds. - -- returns: float - -### minutes() -Returns the duration in minutes as a floating-point value. - -This function returns the total duration represented in minutes as a -floating-point number. It does not provide the minute component of the duration, -but rather gives the overall duration in terms of minutes. - -- returns: float - -### hours() -Returns the duration in hours as a floating-point value. - -This function returns the total duration represented in hours as a -floating-point number. It does not provide the hour component of the duration, -but rather gives the overall duration in terms of hours. -- returns: float - -### days() -Returns the duration in days as a floating-point value. - -This function returns the total duration represented in days as a -floating-point number. It does not provide the day component of the duration, -but rather gives the overall duration in terms of days. -- returns: float - -### weeks() -Returns the duration in weeks as a floating-point value. - -This function returns the total duration represented in weeks as a -floating-point number. It does not provide the week component of the duration, -but rather gives the overall duration in terms of weeks. -- returns: float - -# Symbol -A Unicode symbol. - -Typst defines common symbols so that they can easily be written with standard -keyboards. The symbols are defined in modules, from which they can be accessed -using [field access notation]($scripting/#fields): - -- General symbols are defined in the [`sym` module]($category/symbols/sym) -- Emoji are defined in the [`emoji` module]($category/symbols/emoji) - -Moreover, you can define custom symbols with the [symbol]($func/symbol) -function. - -```example -#sym.arrow.r \ -#sym.gt.eq.not \ -$gt.eq.not$ \ -#emoji.face.halo -``` - -Many symbols have different variants, which can be selected by appending the -modifiers with dot notation. The order of the modifiers is not relevant. Visit -the documentation pages of the symbol modules and click on a symbol to see its -available variants. - -```example -$arrow.l$ \ -$arrow.r$ \ -$arrow.t.quad$ -``` - -# Bytes -A sequence of bytes. - -This is conceptually similar to an array of [integers]($type/integer) between -`{0}` and `{255}`, but represented much more efficiently. - -You can convert -- a [string]($type/string) or an [array]($type/array) of integers to bytes with - the [`bytes`]($func/bytes) function -- bytes to a string with the [`str`]($func/str) function -- bytes to an array of integers with the [`array`]($func/array) function - -When [reading]($func/read) data from a file, you can decide whether to load it -as a string or as raw bytes. - -```example -#bytes((123, 160, 22, 0)) \ -#bytes("Hello 😃") - -#let data = read( - "rhino.png", - encoding: none, -) - -// Magic bytes. -#array(data.slice(0, 4)) \ -#str(data.slice(1, 4)) -``` - -## Methods -### len() -The length in bytes. - -- returns: integer - -### at() -Returns the byte at the specified index. Returns the default value if the index -is out of bounds or fails with an error if no default value was specified. - -- index: integer (positional, required) - The index at which to retrieve the byte. -- default: any (named) - A default value to return if the index is out of bounds. -- returns: integer or any - -### slice() -Extract a subslice of the bytes. -Fails with an error if the start or index is out of bounds. - -- start: integer (positional, required) - The start index (inclusive). -- end: integer (positional) - The end index (exclusive). If omitted, the whole slice until the end is - extracted. -- count: integer (named) - The number of bytes to extract. This is equivalent to passing - `start + count` as the `end` position. Mutually exclusive with `end`. -- returns: bytes - -# String -A sequence of Unicode codepoints. - -You can iterate over the grapheme clusters of the string using a -[for loop]($scripting/#loops). Grapheme clusters are basically characters but -keep together things that belong together, e.g. multiple codepoints that -together form a flag emoji. Strings can be added with the `+` operator, -[joined together]($scripting/#blocks) and multiplied with integers. - -Typst provides utility methods for string manipulation. Many of these methods -(e.g., `split`, `trim` and `replace`) operate on _patterns:_ A pattern can be -either a string or a [regular expression]($func/regex). This makes the methods -quite versatile. - -All lengths and indices are expressed in terms of UTF-8 characters. Indices are -zero-based and negative indices wrap around to the end of the string. - -You can convert a value to a string with the [`str`]($func/str) function. - -### Example -```example -#"hello world!" \ -#"\"hello\n world\"!" \ -#"1 2 3".split() \ -#"1,2;3".split(regex("[,;]")) \ -#(regex("\d+") in "ten euros") \ -#(regex("\d+") in "10 euros") -``` - -### Escape sequences -Just like in markup, you can escape a few symbols in strings: -- `[\\]` for a backslash -- `[\"]` for a quote -- `[\n]` for a newline -- `[\r]` for a carriage return -- `[\t]` for a tab -- `[\u{1f600}]` for a hexadecimal Unicode escape sequence - -## Methods -### len() -The length of the string in UTF-8 encoded bytes. - -- returns: integer - -### first() -Extract the first grapheme cluster of the string. -Fails with an error if the string is empty. - -- returns: any - -### last() -Extract the last grapheme cluster of the string. -Fails with an error if the string is empty. - -- returns: any - -### at() -Extract the first grapheme cluster after the specified index. Returns the -default value if the index is out of bounds or fails with an error if no default -value was specified. - -- index: integer (positional, required) - The byte index. -- default: any (named) - A default value to return if the index is out of bounds. -- returns: string or any - -### slice() -Extract a substring of the string. -Fails with an error if the start or end index is out of bounds. - -- start: integer (positional, required) - The start byte index (inclusive). -- end: integer (positional) - The end byte index (exclusive). If omitted, the whole slice until the end of the - string is extracted. -- count: integer (named) - The number of bytes to extract. This is equivalent to passing `start + count` - as the `end` position. Mutually exclusive with `end`. -- returns: string - -### clusters() -Returns the grapheme clusters of the string as an array of substrings. - -- returns: array - -### codepoints() -Returns the Unicode codepoints of the string as an array of substrings. - -- returns: array - -### contains() -Whether the string contains the specified pattern. - -This method also has dedicated syntax: You can write `{"bc" in "abcd"}` instead -of `{"abcd".contains("bc")}`. - -- pattern: string or regex (positional, required) - The pattern to search for. -- returns: boolean - -### starts-with() -Whether the string starts with the specified pattern. - -- pattern: string or regex (positional, required) - The pattern the string might start with. -- returns: boolean - -### ends-with() -Whether the string ends with the specified pattern. - -- pattern: string or regex (positional, required) - The pattern the string might end with. -- returns: boolean - -### find() -Searches for the specified pattern in the string and returns the first match -as a string or `{none}` if there is no match. - -- pattern: string or regex (positional, required) - The pattern to search for. -- returns: string or none - -### position() -Searches for the specified pattern in the string and returns the index of the -first match as an integer or `{none}` if there is no match. - -- pattern: string or regex (positional, required) - The pattern to search for. -- returns: integer or none - -### match() -Searches for the specified pattern in the string and returns a dictionary -with details about the first match or `{none}` if there is no match. - -The returned dictionary has the following keys: -* `start`: The start offset of the match -* `end`: The end offset of the match -* `text`: The text that matched. -* `captures`: An array containing a string for each matched capturing group. The - first item of the array contains the first matched capturing, not the whole - match! This is empty unless the `pattern` was a regex with capturing groups. - -- pattern: string or regex (positional, required) - The pattern to search for. -- returns: dictionary or none - -### matches() -Searches for the specified pattern in the string and returns an array of -dictionaries with details about all matches. For details about the returned -dictionaries, see above. - -- pattern: string or regex (positional, required) - The pattern to search for. -- returns: array - -### replace() -Replaces all or a specified number of matches of a pattern with a replacement -string and returns the resulting string. - -- pattern: string or regex (positional, required) - The pattern to search for. -- replacement: string or function (positional, required) - The string to replace the matches with or a function that gets a dictionary for each match and can return individual replacement strings. -- count: integer (named) - If given, only the first `count` matches of the pattern are placed. -- returns: string - -### rev() -Reverses the grapheme clusters and returns the resulting string. - -- returns: string - -### trim() -Removes matches of a pattern from one or both sides of the string, once or -repeatedly and returns the resulting string. - -- pattern: string or regex (positional, required) - The pattern to search for. -- at: alignment (named) - Can be `start` or `end` to only trim the start or end of the string. - If omitted, both sides are trimmed. -- repeat: boolean (named) - Whether to repeatedly removes matches of the pattern or just once. - Defaults to `{true}`. -- returns: string - -### split() -Splits a string at matches of a specified pattern and returns an array of -the resulting parts. - -- pattern: string or regex (positional) - The pattern to split at. Defaults to whitespace. -- returns: array - -# Content -A piece of document content. - -This type is at the heart of Typst. All markup you write and most -[functions]($type/function) you call produce content values. You can create a -content value by enclosing markup in square brackets. This is also how you pass -content to functions. - -```example -Type of *Hello!* is -#type([*Hello!*]) -``` - -Content can be added with the `+` operator, -[joined together]($scripting/#blocks) and multiplied with -integers. Wherever content is expected, you can also pass a -[string]($type/string) or `{none}`. - -## Representation -Content consists of elements with fields. When constructing an element with -its _element function,_ you provide these fields as arguments and when you have -a content value, you can access its fields with -[field access syntax]($scripting/#field-access). - -Some fields are required: These must be provided when constructing an element -and as a consequence, they are always available through field access on content -of that type. Required fields are marked as such in the documentation. - -Most fields are optional: Like required fields, they can be passed to the -element function to configure them for a single element. However, these can also -be configured with [set rules]($styling/#set-rules) to apply them to all -elements within a scope. Optional fields are only available with field access -syntax when they are were explicitly passed to the element function, not when -they result from a set rule. - -Each element has a default appearance. However, you can also completely -customize its appearance with a [show rule]($styling/#show-rules). The show rule -is passed the element. It can access the element's field and produce arbitrary -content from it. - -In the web app, you can hover over a content variable to see exactly which -elements the content is composed of and what fields they have. Alternatively, -you can inspect the output of the [`repr`]($func/repr) function. - -## Methods -### func() -The content's element function. This function can be used to create the element -contained in this content. It can be used in set and show rules for the element. -Can be compared with global functions to check whether you have a specific -kind of element. - -- returns: function - -### has() -Whether the content has the specified field. - -- field: string (positional, required) - The field to look for. -- returns: boolean - -### at() -Access the specified field on the content. Returns the default value if the -field does not exist or fails with an error if no default value was specified. - -- field: string (positional, required) - The field to access. -- default: any (named) - A default value to return if the field does not exist. -- returns: any - -### fields() -Return the fields of this content. - -```example -#rect( - width: 10cm, - height: 10cm, -).fields() -``` - -- returns: dictionary - -### location() -The location of the content. This is only available on content returned by -[query]($func/query), for other content it will fail with an error. The -resulting location can be used with [counters]($func/counter), -[state]($func/state) and [queries]($func/query). - -- returns: location - -# Array -A sequence of values. - -You can construct an array by enclosing a comma-separated sequence of values -in parentheses. The values do not have to be of the same type. - -You can access and update array items with the `.at()` method. Indices are -zero-based and negative indices wrap around to the end of the array. You can -iterate over an array using a [for loop]($scripting/#loops). -Arrays can be added together with the `+` operator, -[joined together]($scripting/#blocks) and multiplied with -integers. - -**Note:** An array of length one needs a trailing comma, as in `{(1,)}`. This is -to disambiguate from a simple parenthesized expressions like `{(1 + 2) * 3}`. -An empty array is written as `{()}`. - -## Example -```example -#let values = (1, 7, 4, -3, 2) - -#values.at(0) \ -#(values.at(0) = 3) -#values.at(-1) \ -#values.find(calc.even) \ -#values.filter(calc.odd) \ -#values.map(calc.abs) \ -#values.rev() \ -#(1, (2, 3)).flatten() \ -#(("A", "B", "C") - .join(", ", last: " and ")) -``` - -## Methods -### len() -The number of values in the array. - -- returns: integer - -### first() -Returns the first item in the array. -May be used on the left-hand side of an assignment. -Fails with an error if the array is empty. - -- returns: any - -### last() -Returns the last item in the array. -May be used on the left-hand side of an assignment. -Fails with an error if the array is empty. - -- returns: any - -### at() -Returns the item at the specified index in the array. May be used on the -left-hand side of an assignment. Returns the default value if the index is out -of bounds or fails with an error if no default value was specified. - -- index: integer (positional, required) - The index at which to retrieve the item. -- default: any (named) - A default value to return if the index is out of bounds. -- returns: any - -### push() -Add a value to the end of the array. - -- value: any (positional, required) - The value to insert at the end of the array. - -### pop() -Remove the last item from the array and return it. -Fails with an error if the array is empty. - -- returns: any - The removed last value. - -### insert() -Insert a value into the array at the specified index. -Fails with an error if the index is out of bounds. - -- index: integer (positional, required) - The index at which to insert the item. -- value: any (positional, required) - The value to insert into the array. - -### remove() -Remove the value at the specified index from the array and return it. - -- index: integer (positional, required) - The index at which to remove the item. -- returns: any - -### slice() -Extract a subslice of the array. -Fails with an error if the start or index is out of bounds. - -- start: integer (positional, required) - The start index (inclusive). -- end: integer (positional) - The end index (exclusive). If omitted, the whole slice until the end of the - array is extracted. -- count: integer (named) - The number of items to extract. This is equivalent to passing - `start + count` as the `end` position. Mutually exclusive with `end`. -- returns: array - -### contains() -Whether the array contains the specified value. - -This method also has dedicated syntax: You can write `{2 in (1, 2, 3)}` instead -of `{(1, 2, 3).contains(2)}`. - -- value: any (positional, required) - The value to search for. -- returns: boolean - -### find() -Searches for an item for which the given function returns `{true}` and -returns the first match or `{none}` if there is no match. - -- searcher: function (positional, required) - The function to apply to each item. Must return a boolean. -- returns: any or none - -### position() -Searches for an item for which the given function returns `{true}` and -returns the index of the first match or `{none}` if there is no match. - -- searcher: function (positional, required) - The function to apply to each item. Must return a boolean. -- returns: integer or none - -### filter() -Produces a new array with only the items from the original one for which the -given function returns true. - -- test: function (positional, required) - The function to apply to each item. Must return a boolean. -- returns: array - -### map() -Produces a new array in which all items from the original one were -transformed with the given function. - -- mapper: function (positional, required) - The function to apply to each item. -- returns: array - -### enumerate() -Returns a new array with the values alongside their indices. - -The returned array consists of `(index, value)` pairs in the form of length-2 -arrays. These can be [destructured]($scripting/#bindings) with a let binding or -for loop. - -- start: integer (named) - The index returned for the first pair of the returned list. - Defaults to `{0}`. -- returns: array - -### zip() -Zips the array with other arrays. If the arrays are of unequal length, it will -only zip up until the last element of the shortest array and the remaining -elements will be ignored. The return value is an array where each element is -yet another array, the size of each of those is the number of zipped arrays. - -This method is variadic, meaning that you can zip multiple arrays together at -once: `(1, 2, 3).zip((3, 4, 5), (6, 7, 8))` returning: -`((1, 3, 6), (2, 4, 7), (3, 5, 8))`. - -- others: array (variadic) - The other arrays which should be zipped with the current one. -- returns: array - -### fold() -Folds all items into a single value using an accumulator function. - -- init: any (positional, required) - The initial value to start with. -- folder: function (positional, required) - The folding function. Must have two parameters: One for the accumulated value - and one for an item. -- returns: any - -### sum() -Sums all items (works for any types that can be added). - -- default: any (named) - What to return if the array is empty. Must be set if the array can be empty. -- returns: any - -### product() -Calculates the product all items (works for any types that can be multiplied) - -- default: any (named) - What to return if the array is empty. Must be set if the array can be empty. -- returns: any - -### any() -Whether the given function returns `{true}` for any item in the array. - -- test: function (positional, required) - The function to apply to each item. Must return a boolean. -- returns: boolean - -### all() -Whether the given function returns `{true}` for all items in the array. - -- test: function (positional, required) - The function to apply to each item. Must return a boolean. -- returns: boolean - -### flatten() -Combine all nested arrays into a single flat one. - -- returns: array - -### rev() -Return a new array with the same items, but in reverse order. - -- returns: array - -### join() -Combine all items in the array into one. - -- separator: any (positional) - A value to insert between each item of the array. -- last: any (named) - An alternative separator between the last two items -- returns: any - -### intersperse() -Returns a new array with a separator placed between adjacent items. - -- separator: any (positional) - The value to insert between each item of the array. - -### sorted() -Return a new array with the same items, but sorted. - -- key: function (named) - If given, applies this function to the elements in the array to determine the keys to sort by. -- returns: array - -### dedup() -Returns a new array with all duplicate items removed. - -Only the first element of each duplicate is kept. - -```example -#{ - (1, 1, 2, 3, 1).dedup() == (1, 2, 3) -} -``` - -- key: function (named) - If given, applies this function to the elements in the array to determine the keys to deduplicate by. -- returns: array - -# Dictionary -A map from string keys to values. - -You can construct a dictionary by enclosing comma-separated `key: value` pairs -in parentheses. The values do not have to be of the same type. Since empty -parentheses already yield an empty array, you have to use the special `(:)` -syntax to create an empty dictionary. - -A dictionary is conceptually similar to an array, but it is indexed by strings -instead of integers. You can access and create dictionary entries with the -`.at()` method. If you know the key statically, you can alternatively use -[field access notation]($scripting/#fields) (`.key`) to access -the value. Dictionaries can be added with the `+` operator and -[joined together]($scripting/#blocks). -To check whether a key is present in the dictionary, use the `in` keyword. - -You can iterate over the pairs in a dictionary using a -[for loop]($scripting/#loops). This will iterate in the order the pairs were -inserted / declared. - -## Example -```example -#let dict = ( - name: "Typst", - born: 2019, -) - -#dict.name \ -#(dict.launch = 20) -#dict.len() \ -#dict.keys() \ -#dict.values() \ -#dict.at("born") \ -#dict.insert("city", "Berlin ") -#("name" in dict) -``` - -## Methods -### len() -The number of pairs in the dictionary. - -- returns: integer - -### at() -Returns the value associated with the specified key in the dictionary. May be -used on the left-hand side of an assignment if the key is already present in the -dictionary. Returns the default value if the key is not part of the dictionary -or fails with an error if no default value was specified. - -- key: string (positional, required) - The key at which to retrieve the item. -- default: any (named) - A default value to return if the key is not part of the dictionary. -- returns: any - -### insert() -Insert a new pair into the dictionary and return the value. -If the dictionary already contains this key, the value is updated. - -- key: string (positional, required) - The key of the pair that should be inserted. -- value: any (positional, required) - The value of the pair that should be inserted. - -### keys() -Returns the keys of the dictionary as an array in insertion order. - -- returns: array - -### values() -Returns the values of the dictionary as an array in insertion order. - -- returns: array - -### pairs() -Returns the keys and values of the dictionary as an array of pairs. Each pair is -represented as an array of length two. - -- returns: array - -### remove() -Remove a pair from the dictionary by key and return the value. - -- key: string (positional, required) - The key of the pair that should be removed. -- returns: any - -# Function -A mapping from argument values to a return value. - -You can call a function by writing a comma-separated list of function -_arguments_ enclosed in parentheses directly after the function name. -Additionally, you can pass any number of trailing content blocks arguments to a -function _after_ the normal argument list. If the normal argument list would -become empty, it can be omitted. Typst supports positional and named arguments. -The former are identified by position and type, while the later are written as -`name: value`. - -Within math mode, function calls have special behaviour. See the -[math documentation]($category/math) for more details. - -### Example -```example -// Call a function. -#list([A], [B]) - -// Named arguments and trailing -// content blocks. -#enum(start: 2)[A][B] - -// Version without parentheses. -#list[A][B] -``` - -Functions are a fundamental building block of Typst. Typst provides functions -for a variety of typesetting tasks. Moreover, the markup you write is backed by -functions and all styling happens through functions. This reference lists all -available functions and how you can use them. Please also refer to the -documentation about [set]($styling/#set-rules) and -[show]($styling/#show-rules) rules to learn about additional ways -you can work with functions in Typst. - -### Element functions { #element-functions } -Some functions are associated with _elements_ like [headings]($func/heading) or -[tables]($func/table). When called, these create an element of their respective -kind. In contrast to normal functions, they can further be used in -[set rules]($styling/#set-rules), [show rules]($styling/#show-rules), and -[selectors]($type/selector). - -### Function scopes { #function-scopes } -Functions can hold related definitions in their own scope, similar to a -[module]($scripting/#modules). Examples of this are -[`assert.eq`]($func/assert.eq) or [`list.item`]($func/list.item). However, this -feature is currently only available for built-in functions. - -### Defining functions { #definitions } -You can define your own function with a -[let binding]($scripting/#bindings) that has a parameter list after -the binding's name. The parameter list can contain positional parameters, -named parameters with default values and -[argument sinks]($type/arguments). -The right-hand side of the binding can be a block or any other expression. It -defines the function's return value and can depend on the parameters. - -```example -#let alert(body, fill: red) = { - set text(white) - set align(center) - rect( - fill: fill, - inset: 8pt, - radius: 4pt, - [*Warning:\ #body*], - ) -} - -#alert[ - Danger is imminent! -] - -#alert(fill: blue)[ - KEEP OFF TRACKS -] -``` - -### Unnamed functions { #unnamed } -You can also created an unnamed function without creating a binding by -specifying a parameter list followed by `=>` and the function body. If your -function has just one parameter, the parentheses around the parameter list are -optional. Unnamed functions are mainly useful for show rules, but also for -settable properties that take functions like the page function's -[`footer`]($func/page.footer) property. - -```example -#show "once?": it => [#it #it] -once? -``` - -### Notable fact -In Typst, all functions are _pure._ This means that for the same -arguments, they always return the same result. They cannot "remember" things to -produce another value when they are called a second time. - -The only exception are built-in methods like -[`array.push(value)`]($type/array.push). These can modify the values they are -called on. - -## Methods -### with() -Returns a new function that has the given arguments pre-applied. - -- arguments: any (variadic) - The named and positional arguments to apply. -- returns: function - -### where() -Returns a selector that filters for elements belonging to this function -whose fields have the values of the given arguments. - -- fields: any (named, variadic) - The field values to filter by. -- returns: selector - -# Arguments -Captured arguments to a function. - -## Argument Sinks -Like built-in functions, custom functions can also take a variable number of -arguments. You can specify an _argument sink_ which collects all excess -arguments as `..sink`. The resulting `sink` value is of the `arguments` type. It -exposes methods to access the positional and named arguments. - -```example -#let format(title, ..authors) = { - let by = authors - .pos() - .join(", ", last: " and ") - - [*#title* \ _Written by #by;_] -} - -#format("ArtosFlow", "Jane", "Joe") -``` - -## Spreading -Inversely to an argument sink, you can _spread_ arguments, arrays and -dictionaries into a function call with the `..spread` operator: - -```example -#let array = (2, 3, 5) -#calc.min(..array) - -#let dict = (fill: blue) -#text(..dict)[Hello] -``` - -## Methods -### pos() -Returns the captured positional arguments as an array. - -- returns: array - -### named() -Returns the captured named arguments as a dictionary. - -- returns: dictionary - -# Selector -A filter for selecting elements within the document. - -You can construct a selector in the following ways: -- you can use an element [function]($type/function) -- you can filter for an element function with - [specific fields]($type/function.where) -- you can use a [string]($type/string) or [regular expression]($func/regex) -- you can use a [`{