Export target docs (#5812)

Co-authored-by: Martin Haug <3874949+reknih@users.noreply.github.com>
This commit is contained in:
Laurenz 2025-02-05 14:24:10 +01:00 committed by GitHub
parent 25f6a7ab16
commit 029ae4a5ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
49 changed files with 709 additions and 448 deletions

2
Cargo.lock generated
View File

@ -2822,6 +2822,8 @@ dependencies = [
"typst-assets",
"typst-dev-assets",
"typst-render",
"typst-utils",
"unicode-math-class",
"unscanny",
"yaml-front-matter",
]

View File

@ -85,16 +85,9 @@ use crate::engine::Engine;
use crate::routines::EvalMode;
use crate::{Feature, Features};
/// Foundational types and functions.
///
/// Here, you'll find documentation for basic data types like [integers]($int)
/// and [strings]($str) as well as details about core computational functions.
#[category]
pub static FOUNDATIONS: Category;
/// Hook up all `foundations` definitions.
pub(super) fn define(global: &mut Scope, inputs: Dict, features: &Features) {
global.start_category(FOUNDATIONS);
global.start_category(crate::Category::Foundations);
global.define_type::<bool>();
global.define_type::<i64>();
global.define_type::<f64>();
@ -125,6 +118,7 @@ pub(super) fn define(global: &mut Scope, inputs: Dict, features: &Features) {
}
global.define("calc", calc::module());
global.define("sys", sys::module(inputs));
global.reset_category();
}
/// Fails with an error.

View File

@ -1,6 +1,3 @@
#[doc(inline)]
pub use typst_macros::category;
use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
@ -8,14 +5,13 @@ use ecow::{eco_format, EcoString};
use indexmap::map::Entry;
use indexmap::IndexMap;
use typst_syntax::Span;
use typst_utils::Static;
use crate::diag::{bail, DeprecationSink, HintedStrResult, HintedString, StrResult};
use crate::foundations::{
Element, Func, IntoValue, NativeElement, NativeFunc, NativeFuncData, NativeType,
Type, Value,
};
use crate::Library;
use crate::{Category, Library};
/// A stack of scopes.
#[derive(Debug, Default, Clone)]
@ -361,46 +357,6 @@ pub enum Capturer {
Context,
}
/// A group of related definitions.
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Category(Static<CategoryData>);
impl Category {
/// Create a new category from raw data.
pub const fn from_data(data: &'static CategoryData) -> Self {
Self(Static(data))
}
/// The category's name.
pub fn name(&self) -> &'static str {
self.0.name
}
/// The type's title case name, for use in documentation (e.g. `String`).
pub fn title(&self) -> &'static str {
self.0.title
}
/// Documentation for the category.
pub fn docs(&self) -> &'static str {
self.0.docs
}
}
impl Debug for Category {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Category({})", self.name())
}
}
/// Defines a category.
#[derive(Debug)]
pub struct CategoryData {
pub name: &'static str,
pub title: &'static str,
pub docs: &'static str,
}
/// The error message when trying to mutate a variable from the standard
/// library.
#[cold]

View File

@ -3,7 +3,7 @@ use comemo::Tracked;
use crate::diag::HintedStrResult;
use crate::foundations::{elem, func, Cast, Context};
/// The compilation target.
/// The export target.
#[derive(Debug, Default, Copy, Clone, PartialEq, Hash, Cast)]
pub enum Target {
/// The target that is used for paged, fully laid-out content.
@ -28,7 +28,49 @@ pub struct TargetElem {
pub target: Target,
}
/// Returns the current compilation target.
/// Returns the current export target.
///
/// This function returns either
/// - `{"paged"}` (for PDF, PNG, and SVG export), or
/// - `{"html"}` (for HTML export).
///
/// The design of this function is not yet finalized and for this reason it is
/// guarded behind the `html` feature. Visit the [HTML documentation
/// page]($html) for more details.
///
/// # When to use it
/// This function allows you to format your document properly across both HTML
/// and paged export targets. It should primarily be used in templates and show
/// rules, rather than directly in content. This way, the document's contents
/// can be fully agnostic to the export target and content can be shared between
/// PDF and HTML export.
///
/// # Varying targets
/// This function is [contextual]($context) as the target can vary within a
/// single compilation: When exporting to HTML, the target will be `{"paged"}`
/// while within an [`html.frame`].
///
/// # Example
/// ```example
/// #let kbd(it) = context {
/// if target() == "html" {
/// html.elem("kbd", it)
/// } else {
/// set text(fill: rgb("#1f2328"))
/// let r = 3pt
/// box(
/// fill: rgb("#f6f8fa"),
/// stroke: rgb("#d1d9e0b3"),
/// outset: (y: r),
/// inset: (x: r),
/// radius: r,
/// raw(it)
/// )
/// }
/// }
///
/// Press #kbd("F1") for help.
/// ```
#[func(contextual)]
pub fn target(context: Tracked<Context>) -> HintedStrResult<Target> {
Ok(TargetElem::target_in(context.styles()?))

View File

@ -6,53 +6,77 @@ pub use self::dom::*;
use ecow::EcoString;
use crate::foundations::{category, elem, Category, Content, Module, Scope};
/// HTML output.
#[category]
pub static HTML: Category;
use crate::foundations::{elem, Content, Module, Scope};
/// Create a module with all HTML definitions.
pub fn module() -> Module {
let mut html = Scope::deduplicating();
html.start_category(HTML);
html.start_category(crate::Category::Html);
html.define_elem::<HtmlElem>();
html.define_elem::<FrameElem>();
Module::new("html", html)
}
/// A HTML element that can contain Typst content.
/// An HTML element that can contain Typst content.
///
/// Typst's HTML export automatically generates the appropriate tags for most
/// elements. However, sometimes, it is desirable to retain more control. For
/// example, when using Typst to generate your blog, you could use this function
/// to wrap each article in an `<article>` tag.
///
/// Typst is aware of what is valid HTML. A tag and its attributes must form
/// syntactically valid HTML. Some tags, like `meta` do not accept content.
/// Hence, you must not provide a body for them. We may add more checks in the
/// future, so be sure that you are generating valid HTML when using this
/// function.
///
/// Normally, Typst will generate `html`, `head`, and `body` tags for you. If
/// you instead create them with this function, Typst will omit its own tags.
///
/// ```typ
/// #html.elem("div", attrs: (style: "background: aqua"))[
/// A div with _Typst content_ inside!
/// ]
/// ```
#[elem(name = "elem")]
pub struct HtmlElem {
/// The element's tag.
#[required]
pub tag: HtmlTag,
/// The element's attributes.
/// The element's HTML attributes.
#[borrowed]
pub attrs: HtmlAttrs,
/// The contents of the HTML element.
///
/// The body can be arbitrary Typst content.
#[positional]
#[borrowed]
pub body: Option<Content>,
}
impl HtmlElem {
/// Add an atribute to the element.
/// Add an attribute to the element.
pub fn with_attr(mut self, attr: HtmlAttr, value: impl Into<EcoString>) -> Self {
self.attrs.get_or_insert_with(Default::default).push(attr, value);
self
}
}
/// An element that forces its contents to be laid out.
/// An element that lays out its content as an inline SVG.
///
/// Integrates content that requires layout (e.g. a plot) into HTML output
/// by turning it into an inline SVG.
/// Sometimes, converting Typst content to HTML is not desirable. This can be
/// the case for plots and other content that relies on positioning and styling
/// to convey its message.
///
/// This function allows you to use the Typst layout engine that would also be
/// used for PDF, SVG, and PNG export to render a part of your document exactly
/// how it would appear when exported in one of these formats. It embeds the
/// content as an inline SVG.
#[elem]
pub struct FrameElem {
/// The contents that shall be laid out.
/// The content that shall be laid out.
#[positional]
#[required]
pub body: Content,

View File

@ -25,24 +25,11 @@ pub use self::query_::*;
pub use self::state::*;
pub use self::tag::*;
use crate::foundations::{category, Category, Scope};
/// Interactions between document parts.
///
/// This category is home to Typst's introspection capabilities: With the
/// `counter` function, you can access and manipulate page, section, figure, and
/// equation counters or create custom ones. Meanwhile, the `query` function
/// lets you search for elements in the document to construct things like a list
/// of figures or headers which show the current chapter title.
///
/// Most of the functions are _contextual._ It is recommended to read the chapter
/// on [context] before continuing here.
#[category]
pub static INTROSPECTION: Category;
use crate::foundations::Scope;
/// Hook up all `introspection` definitions.
pub fn define(global: &mut Scope) {
global.start_category(INTROSPECTION);
global.start_category(crate::Category::Introspection);
global.define_type::<Location>();
global.define_type::<Counter>();
global.define_type::<State>();
@ -50,4 +37,5 @@ pub fn define(global: &mut Scope) {
global.define_func::<here>();
global.define_func::<query>();
global.define_func::<locate>();
global.reset_category();
}

View File

@ -64,17 +64,11 @@ pub use self::spacing::*;
pub use self::stack::*;
pub use self::transform::*;
use crate::foundations::{category, Category, Scope};
/// Arranging elements on the page in different ways.
///
/// By combining layout functions, you can create complex and automatic layouts.
#[category]
pub static LAYOUT: Category;
use crate::foundations::Scope;
/// Hook up all `layout` definitions.
pub fn define(global: &mut Scope) {
global.start_category(LAYOUT);
global.start_category(crate::Category::Layout);
global.define_type::<Length>();
global.define_type::<Angle>();
global.define_type::<Ratio>();
@ -103,4 +97,5 @@ pub fn define(global: &mut Scope) {
global.define_elem::<HideElem>();
global.define_func::<measure>();
global.define_func::<layout>();
global.reset_category();
}

View File

@ -29,6 +29,7 @@ pub mod visualize;
use std::ops::{Deref, Range};
use serde::{Deserialize, Serialize};
use typst_syntax::{FileId, Source, Span};
use typst_utils::{LazyHash, SmallBitSet};
@ -236,31 +237,72 @@ pub enum Feature {
Html,
}
/// A group of related standard library definitions.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Category {
Foundations,
Introspection,
Layout,
DataLoading,
Math,
Model,
Symbols,
Text,
Visualize,
Pdf,
Html,
Svg,
Png,
}
impl Category {
/// The kebab-case name of the category.
pub fn name(&self) -> &'static str {
match self {
Self::Foundations => "foundations",
Self::Introspection => "introspection",
Self::Layout => "layout",
Self::DataLoading => "data-loading",
Self::Math => "math",
Self::Model => "model",
Self::Symbols => "symbols",
Self::Text => "text",
Self::Visualize => "visualize",
Self::Pdf => "pdf",
Self::Html => "html",
Self::Svg => "svg",
Self::Png => "png",
}
}
}
/// Construct the module with global definitions.
fn global(math: Module, inputs: Dict, features: &Features) -> Module {
let mut global = Scope::deduplicating();
self::foundations::define(&mut global, inputs, features);
self::model::define(&mut global);
self::text::define(&mut global);
global.reset_category();
global.define("math", math);
self::layout::define(&mut global);
self::visualize::define(&mut global);
self::introspection::define(&mut global);
self::loading::define(&mut global);
self::symbols::define(&mut global);
self::pdf::define(&mut global);
global.reset_category();
global.define("math", math);
global.define("pdf", self::pdf::module());
if features.is_enabled(Feature::Html) {
global.define("html", self::html::module());
}
prelude(&mut global);
Module::new("global", global)
}
/// Defines scoped values that are globally available, too.
fn prelude(global: &mut Scope) {
global.reset_category();
global.define("black", Color::BLACK);
global.define("gray", Color::GRAY);
global.define("silver", Color::SILVER);

View File

@ -34,9 +34,6 @@ pub fn cbor(
#[scope]
impl cbor {
/// Reads structured data from CBOR bytes.
///
/// This function is deprecated. The [`cbor`] function now accepts bytes
/// directly.
#[func(title = "Decode CBOR")]
#[deprecated = "`cbor.decode` is deprecated, directly pass bytes to `cbor` instead"]
pub fn decode(

View File

@ -96,9 +96,6 @@ pub fn csv(
#[scope]
impl csv {
/// Reads structured data from a CSV string/bytes.
///
/// This function is deprecated. The [`csv`] function now accepts bytes
/// directly.
#[func(title = "Decode CSV")]
#[deprecated = "`csv.decode` is deprecated, directly pass bytes to `csv` instead"]
pub fn decode(

View File

@ -65,9 +65,6 @@ pub fn json(
#[scope]
impl json {
/// Reads structured data from a JSON string/bytes.
///
/// This function is deprecated. The [`json`] function now accepts bytes
/// directly.
#[func(title = "Decode JSON")]
#[deprecated = "`json.decode` is deprecated, directly pass bytes to `json` instead"]
pub fn decode(

View File

@ -29,19 +29,12 @@ pub use self::yaml_::*;
use crate::diag::{At, SourceResult};
use crate::foundations::OneOrMultiple;
use crate::foundations::{cast, category, Bytes, Category, Scope, Str};
use crate::foundations::{cast, Bytes, Scope, Str};
use crate::World;
/// Data loading from external files.
///
/// These functions help you with loading and embedding data, for example from
/// the results of an experiment.
#[category]
pub static DATA_LOADING: Category;
/// Hook up all `data-loading` definitions.
pub(super) fn define(global: &mut Scope) {
global.start_category(DATA_LOADING);
global.start_category(crate::Category::DataLoading);
global.define_func::<read>();
global.define_func::<csv>();
global.define_func::<json>();
@ -49,6 +42,7 @@ pub(super) fn define(global: &mut Scope) {
global.define_func::<yaml>();
global.define_func::<cbor>();
global.define_func::<xml>();
global.reset_category();
}
/// Something we can retrieve byte data from.

View File

@ -44,9 +44,6 @@ pub fn toml(
#[scope]
impl toml {
/// Reads structured data from a TOML string/bytes.
///
/// This function is deprecated. The [`toml`] function now accepts bytes
/// directly.
#[func(title = "Decode TOML")]
#[deprecated = "`toml.decode` is deprecated, directly pass bytes to `toml` instead"]
pub fn decode(

View File

@ -77,9 +77,6 @@ pub fn xml(
#[scope]
impl xml {
/// Reads structured data from an XML string/bytes.
///
/// This function is deprecated. The [`xml`] function now accepts bytes
/// directly.
#[func(title = "Decode XML")]
#[deprecated = "`xml.decode` is deprecated, directly pass bytes to `xml` instead"]
pub fn decode(

View File

@ -55,9 +55,6 @@ pub fn yaml(
#[scope]
impl yaml {
/// Reads structured data from a YAML string/bytes.
///
/// This function is deprecated. The [`yaml`] function now accepts bytes
/// directly.
#[func(title = "Decode YAML")]
#[deprecated = "`yaml.decode` is deprecated, directly pass bytes to `yaml` instead"]
pub fn decode(

View File

@ -27,119 +27,10 @@ pub use self::underover::*;
use typst_utils::singleton;
use unicode_math_class::MathClass;
use crate::foundations::{
category, elem, Category, Content, Module, NativeElement, Scope,
};
use crate::foundations::{elem, Content, Module, NativeElement, Scope};
use crate::layout::{Em, HElem};
use crate::text::TextElem;
/// 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 $]`).
///
/// # Variables
/// 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 [hash syntax]($scripting/#expressions).
///
/// ```example
/// $ A = pi r^2 $
/// $ "area" = pi dot "radius"^2 $
/// $ cal(A) :=
/// { x in RR | x "is natural" } $
/// #let x = 5
/// $ #x < 17 $
/// ```
///
/// # Symbols
/// 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]($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.
///
/// ```example
/// $ x < y => x gt.eq.not y $
/// ```
///
/// # Line Breaks
/// 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 \
/// &= (n(n+1)) / 2 $
/// ```
///
/// # Function calls
/// Math mode supports special function calls without the hash 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 hash syntax to pass code expressions
/// (except for strings, which are available in the math syntax).
/// - They support positional and named arguments, as well as argument
/// spreading.
/// - They don't support trailing content blocks.
/// - They provide additional syntax for 2-dimensional argument lists. The
/// semicolon (`;`) merges preceding arguments separated by commas into an
/// array argument.
///
/// ```example
/// $ frac(a^2, 2) $
/// $ vec(1, 2, delim: "[") $
/// $ mat(1, 2; 3, 4) $
/// $ mat(..#range(1, 5).chunks(2)) $
/// $ lim_x =
/// op("lim", limits: #true)_x $
/// ```
///
/// To write a verbatim comma or semicolon in a math call, escape it with a
/// backslash. The colon on the other hand is only recognized in a special way
/// if directly preceded by an identifier, so to display it verbatim in those
/// cases, you can just insert a space before it.
///
/// Functions calls preceded by a hash are normal code function calls and not
/// affected by these rules.
///
/// # Alignment
/// When equations include multiple _alignment points_ (`&`), this creates
/// blocks of alternatingly right- and left-aligned columns. In the example
/// below, the expression `(3x + y) / 7` is right-aligned and `= 9` is
/// left-aligned. The word "given" is also left-aligned because `&&` creates two
/// alignment points in a row, alternating the alignment twice. `& &` and `&&`
/// behave exactly the same way. Meanwhile, "multiply by 7" is right-aligned
/// because just one `&` precedes it. Each alignment point simply alternates
/// between right-aligned/left-aligned.
///
/// ```example
/// $ (3x + y) / 7 &= 9 && "given" \
/// 3x + y &= 63 & "multiply by 7" \
/// 3x &= 63 - y && "subtract y" \
/// x &= 21 - y/3 & "divide by 3" $
/// ```
///
/// # Math fonts
/// You can set the math font by with a [show-set rule]($styling/#show-rules) as
/// demonstrated below. Note that only special OpenType math fonts are suitable
/// for typesetting maths.
///
/// ```example
/// #show math.equation: set text(font: "Fira Math")
/// $ sum_(i in NN) 1 + i $
/// ```
///
/// # Math module
/// All math functions are part of the `math` [module]($scripting/#modules),
/// which is available by default in equations. Outside of equations, they can
/// be accessed with the `math.` prefix.
#[category]
pub static MATH: Category;
// Spacings.
pub const THIN: Em = Em::new(1.0 / 6.0);
pub const MEDIUM: Em = Em::new(2.0 / 9.0);
@ -150,7 +41,7 @@ pub const WIDE: Em = Em::new(2.0);
/// Create a module with all math definitions.
pub fn module() -> Module {
let mut math = Scope::deduplicating();
math.start_category(MATH);
math.start_category(crate::Category::Math);
math.define_elem::<EquationElem>();
math.define_elem::<TextElem>();
math.define_elem::<LrElem>();

View File

@ -40,19 +40,11 @@ pub use self::strong::*;
pub use self::table::*;
pub use self::terms::*;
use crate::foundations::{category, Category, Scope};
/// Document structuring.
///
/// Here, you can find functions to structure your document and interact with
/// that structure. This includes section headings, figures, bibliography
/// management, cross-referencing and more.
#[category]
pub static MODEL: Category;
use crate::foundations::Scope;
/// Hook up all `model` definitions.
pub fn define(global: &mut Scope) {
global.start_category(MODEL);
global.start_category(crate::Category::Model);
global.define_elem::<DocumentElem>();
global.define_elem::<RefElem>();
global.define_elem::<LinkElem>();
@ -72,4 +64,5 @@ pub fn define(global: &mut Scope) {
global.define_elem::<EmphElem>();
global.define_elem::<StrongElem>();
global.define_func::<numbering>();
global.reset_category();
}

View File

@ -4,21 +4,12 @@ mod embed;
pub use self::embed::*;
use crate::foundations::{category, Category, Module, Scope};
/// PDF-specific functionality.
#[category]
pub static PDF: Category;
/// Hook up the `pdf` module.
pub(super) fn define(global: &mut Scope) {
global.start_category(PDF);
global.define("pdf", module());
}
use crate::foundations::{Module, Scope};
/// Hook up all `pdf` definitions.
pub fn module() -> Module {
let mut scope = Scope::deduplicating();
scope.define_elem::<EmbedElem>();
Module::new("pdf", scope)
let mut pdf = Scope::deduplicating();
pdf.start_category(crate::Category::Pdf);
pdf.define_elem::<EmbedElem>();
Module::new("pdf", pdf)
}

View File

@ -1,19 +1,12 @@
//! Modifiable symbols.
use crate::foundations::{category, Category, Module, Scope, Symbol, Value};
/// These two modules give names to symbols and emoji to make them easy to
/// insert with a normal keyboard. Alternatively, you can also always directly
/// enter Unicode symbols into your text and formulas. In addition to the
/// symbols listed below, math mode defines `dif` and `Dif`. These are not
/// normal symbol values because they also affect spacing and font style.
#[category]
pub static SYMBOLS: Category;
use crate::foundations::{Module, Scope, Symbol, Value};
/// Hook up all `symbol` definitions.
pub(super) fn define(global: &mut Scope) {
global.start_category(SYMBOLS);
global.start_category(crate::Category::Symbols);
extend_scope_from_codex_module(global, codex::ROOT);
global.reset_category();
}
/// Hook up all math `symbol` definitions, i.e., elements of the `sym` module.

View File

@ -45,9 +45,9 @@ use typst_utils::singleton;
use crate::diag::{bail, warning, HintedStrResult, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
cast, category, dict, elem, Args, Array, Cast, Category, Construct, Content, Dict,
Fold, IntoValue, NativeElement, Never, NoneValue, Packed, PlainText, Regex, Repr,
Resolve, Scope, Set, Smart, StyleChain,
cast, dict, elem, Args, Array, Cast, Construct, Content, Dict, Fold, IntoValue,
NativeElement, Never, NoneValue, Packed, PlainText, Regex, Repr, Resolve, Scope, Set,
Smart, StyleChain,
};
use crate::layout::{Abs, Axis, Dir, Em, Length, Ratio, Rel};
use crate::math::{EquationElem, MathSize};
@ -55,15 +55,9 @@ use crate::model::ParElem;
use crate::visualize::{Color, Paint, RelativeTo, Stroke};
use crate::World;
/// Text styling.
///
/// The [text function]($text) is of particular interest.
#[category]
pub static TEXT: Category;
/// Hook up all `text` definitions.
pub(super) fn define(global: &mut Scope) {
global.start_category(TEXT);
global.start_category(crate::Category::Text);
global.define_elem::<TextElem>();
global.define_elem::<LinebreakElem>();
global.define_elem::<SmartQuoteElem>();
@ -78,6 +72,7 @@ pub(super) fn define(global: &mut Scope) {
global.define_func::<lower>();
global.define_func::<upper>();
global.define_func::<lorem>();
global.reset_category();
}
/// Customizes the look and layout of text in a variety of ways.

View File

@ -50,6 +50,17 @@ pub struct ImageElem {
/// supported [formats]($image.format).
///
/// For more details about paths, see the [Paths section]($syntax/#paths).
///
/// ```example
/// #let original = read("diagram.svg")
/// #let changed = original.replace(
/// "#2B80FF", // blue
/// green.to-hex(),
/// )
///
/// #image(bytes(original))
/// #image(bytes(changed))
/// ```
#[required]
#[parse(
let source = args.expect::<Spanned<DataSource>>("source")?;
@ -156,20 +167,6 @@ pub struct ImageElem {
#[allow(clippy::too_many_arguments)]
impl ImageElem {
/// Decode a raster or vector graphic from bytes or a string.
///
/// This function is deprecated. The [`image`] function now accepts bytes
/// directly.
///
/// ```example
/// #let original = read("diagram.svg")
/// #let changed = original.replace(
/// "#2B80FF", // blue
/// green.to-hex(),
/// )
///
/// #image.decode(original)
/// #image.decode(changed)
/// ```
#[func(title = "Decode Image")]
#[deprecated = "`image.decode` is deprecated, directly pass bytes to `image` instead"]
pub fn decode(

View File

@ -24,19 +24,11 @@ pub use self::shape::*;
pub use self::stroke::*;
pub use self::tiling::*;
use crate::foundations::{category, Category, Element, Scope, Type};
/// Drawing and data visualization.
///
/// If you want to create more advanced drawings or plots, also have a look at
/// the [CetZ](https://github.com/johannes-wolf/cetz) package as well as more
/// specialized [packages]($universe) for your use case.
#[category]
pub static VISUALIZE: Category;
use crate::foundations::{Element, Scope, Type};
/// Hook up all visualize definitions.
pub(super) fn define(global: &mut Scope) {
global.start_category(VISUALIZE);
global.start_category(crate::Category::Visualize);
global.define_type::<Color>();
global.define_type::<Gradient>();
global.define_type::<Tiling>();
@ -55,4 +47,5 @@ pub(super) fn define(global: &mut Scope) {
global
.define("pattern", Type::of::<Tiling>())
.deprecated("the name `pattern` is deprecated, use `tiling` instead");
global.reset_category();
}

View File

@ -21,9 +21,6 @@ use crate::visualize::{FillRule, Paint, Stroke};
/// ((50%, 0pt), (40pt, 0pt)),
/// )
/// ```
///
/// # Deprecation
/// This function is deprecated. The [`curve`] function should be used instead.
#[elem(Show)]
pub struct PathElem {
/// How to fill the path.

View File

@ -1,59 +0,0 @@
use heck::{ToKebabCase, ToTitleCase};
use proc_macro2::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{Attribute, Ident, Result, Token, Type, Visibility};
use crate::util::{documentation, foundations};
/// Expand the `#[category]` macro.
pub fn category(_: TokenStream, item: syn::Item) -> Result<TokenStream> {
let syn::Item::Verbatim(stream) = item else {
bail!(item, "expected bare static");
};
let BareStatic { attrs, vis, ident, ty, .. } = syn::parse2(stream)?;
let name = ident.to_string().to_kebab_case();
let title = name.to_title_case();
let docs = documentation(&attrs);
Ok(quote! {
#(#attrs)*
#[allow(rustdoc::broken_intra_doc_links)]
#vis static #ident: #ty = {
static DATA: #foundations::CategoryData = #foundations::CategoryData {
name: #name,
title: #title,
docs: #docs,
};
#foundations::Category::from_data(&DATA)
};
})
}
/// Parse a bare `pub static CATEGORY: Category;` item.
#[allow(dead_code)]
pub struct BareStatic {
pub attrs: Vec<Attribute>,
pub vis: Visibility,
pub static_token: Token![static],
pub ident: Ident,
pub colon_token: Token![:],
pub ty: Type,
pub semi_token: Token![;],
}
impl Parse for BareStatic {
fn parse(input: ParseStream) -> Result<Self> {
Ok(Self {
attrs: input.call(Attribute::parse_outer)?,
vis: input.parse()?,
static_token: input.parse()?,
ident: input.parse()?,
colon_token: input.parse()?,
ty: input.parse()?,
semi_token: input.parse()?,
})
}
}

View File

@ -5,7 +5,6 @@ extern crate proc_macro;
#[macro_use]
mod util;
mod cast;
mod category;
mod elem;
mod func;
mod scope;
@ -266,15 +265,6 @@ pub fn scope(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
.into()
}
/// Defines a category of definitions.
#[proc_macro_attribute]
pub fn category(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
let item = syn::parse_macro_input!(item as syn::Item);
category::category(stream.into(), item)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
/// Implements `Reflect`, `FromValue`, and `IntoValue` for a type.
///
/// - `Reflect` makes Typst's runtime aware of the type's characteristics.

View File

@ -17,6 +17,8 @@ cli = ["clap", "typst-render", "serde_json"]
[dependencies]
typst = { workspace = true }
typst-render = { workspace = true, optional = true }
typst-utils = { workspace = true }
typst-assets = { workspace = true, features = ["fonts"] }
typst-dev-assets = { workspace = true }
clap = { workspace = true, optional = true }
@ -28,7 +30,7 @@ serde_json = { workspace = true, optional = true }
serde_yaml = { workspace = true }
syntect = { workspace = true, features = ["html"] }
typed-arena = { workspace = true }
typst-render = { workspace = true, optional = true }
unicode-math-class = { workspace = true }
unscanny = { workspace = true }
yaml-front-matter = { workspace = true }

View File

@ -657,7 +657,8 @@ applicable, contains possible workarounds.
- **Well-established plotting ecosystem.** LaTeX users often create elaborate
charts along with their documents in PGF/TikZ. The Typst ecosystem does not
yet offer the same breadth of available options, but the ecosystem around the
[`cetz`](https://github.com/cetz-package/cetz) package is catching up quickly.
[`cetz` package](https://typst.app/universe/package/cetz) is catching up
quickly.
- **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
@ -670,4 +671,6 @@ applicable, contains possible workarounds.
format, but you can easily convert both into SVG files with [online
tools](https://cloudconvert.com/pdf-to-svg) or
[Inkscape](https://inkscape.org/). The web app will automatically convert PDF
files to SVG files upon uploading them.
files to SVG files upon uploading them. You can also use the
community-provided [`muchpdf` package](https://typst.app/universe/package/muchpdf)
to embed PDFs. It internally converts PDFs to SVGs on-the-fly.

View File

@ -0,0 +1,61 @@
<div class="info-box">
Typst's HTML export is currently under active development. The feature is still
very incomplete and only available for experimentation behind a feature flag. Do
not use this feature for production use cases. In the CLI, you can experiment
with HTML export by passing `--features html` or setting the `TYPST_FEATURES`
environment variables to `html`. In the web app, HTML export is not available at
this time. Visit the [tracking issue](https://github.com/typst/typst/issues/5512)
to follow progress on HTML export and learn more about planned features.
</div>
HTML files describe a document structurally. The aim of Typst's HTML export is
to capture the structure of an input document and produce semantically rich HTML
that retains this structure. The resulting HTML should be accessible,
human-readable, and editable by hand and downstream tools.
PDF, PNG, and SVG export, in contrast, all produce _visual_ representations of a
fully-laid out document. This divergence in the formats' intents means that
Typst cannot simply produce perfect HTML for your existing Typst documents. It
cannot always know what the best semantic HTML representation of your content
is.
Instead, it gives _you_ full control: You can check the current export format
through the [`target`] function and when it is set to HTML, generate [raw HTML
elements]($html.elem). The primary intended use of these elements is in
templates and show rules. This way, the document's contents can be fully
agnostic to the export target and content can be shared between PDF and HTML
export.
Currently, Typst will always output a single HTML file. Support for outputting
directories with multiple HTML documents and assets, as well as support for
outputting fragments that can be integrated into other HTML documents is
planned.
Typst currently does not output CSS style sheets, instead focussing on emitting
semantic markup. You can of course write your own CSS styles and still benefit
from sharing your _content_ between PDF and HTML. For the future, we plan to
give you the option of automatically emitting CSS, taking more of your existing
set rules into account.
# Exporting as HTML
## Command Line
Pass `--format html` to the `compile` or `watch` subcommand or provide an output
file name that ends with `.html`. Note that you must also pass `--features html`
or set `TYPST_FEATURES=html` to enable this experimental export target.
When using `typst watch`, Typst will spin up a live-reloading HTTP server. You
can configure it as follows:
- Pass `--port` to change the port. (Defaults to the first free port in the
range 3000-3005.)
- Pass `--no-reload` to disable injection of a live reload script. (The HTML
that is written to disk isn't affected either way.)
- Pass `--no-serve` to disable the server altogether.
## Web App
Not currently available.
# HTML-specific functionality
Typst exposes HTML-specific functionality in the global `html` module. See below
for the definitions it contains.

View File

@ -0,0 +1,71 @@
PDF files focus on accurately describing documents visually, but also have
facilities for annotating their structure. This hybrid approach makes
them a good fit for document exchange: They render exactly the same on every
device, but also support extraction of a document's content and structure (at
least to an extent). Unlike PNG files, PDFs are not bound to a specific
resolution. Hence, you can view them at any size without incurring a loss of
quality.
# PDF standards
The International Standards Organization (ISO) has published the base PDF
standard and various standards that extend it to make PDFs more suitable for
specific use-cases. By default, Typst exports PDF 1.7 files. Adobe Acrobat 8 and
later as well as all other commonly used PDF viewers are compatible with this
PDF version.
## PDF/A
Typst optionally supports emitting PDF/A-conformant files. PDF/A files are
geared towards maximum compatibility with current and future PDF tooling. They
do not rely on difficult-to-implement or proprietary features and contain
exhaustive metadata. This makes them suitable for long-term archival.
The PDF/A Standard has multiple versions (_parts_ in ISO terminology) and most
parts have multiple profiles that indicate the file's conformance level.
Currently, Typst supports these PDF/A output profiles:
- PDF/A-2b: The basic conformance level of ISO 19005-2. This version of PDF/A is
based on PDF 1.7 and results in self-contained, archivable PDF files.
- PDF/A-3b: The basic conformance level of ISO 19005-3. This version of PDF/A is
based on PDF 1.7 and results in archivable PDF files that can contain
arbitrary other related files as [attachments]($pdf.embed). The only
difference between it and PDF/A-2b is the capability to embed
non-PDF/A-conformant files within.
When choosing between exporting PDF/A and regular PDF, keep in mind that PDF/A
files contain additional metadata, and that some readers will prevent the user
from modifying a PDF/A file. Some features of Typst may be disabled depending on
the PDF standard you choose.
# Exporting as PDF
## Command Line
PDF is Typst's default export format. Running the `compile` or `watch`
subcommand without specifying a format will create a PDF. When exporting to PDF,
you have the following configuration options:
- Which PDF standards Typst should enforce conformance with by specifying
`--pdf-standard` followed by one or multiple comma-separated standards. Valid
standards are `1.7`, `a-2b`, and `a-3b`. By default, Typst outputs
PDF-1.7-compliant files.
- Which pages to export by specifying `--pages` followed by a comma-separated
list of numbers or dash-separated number ranges. Ranges can be half-open.
Example: `2,3,7-9,11-`.
## Web App
Click the quick download button at the top right to export a PDF with default
settings. For further configuration, click "File" > "Export as" > "PDF" or click
the downwards-facing arrow next to the quick download button and select "Export
as PDF". When exporting to PDF, you have the following configuration options:
- Which PDF standards Typst should enforce conformance with. By default, Typst
outputs PDF-1.7-compliant files. Valid additional standards are `A-2b` and
`A-3b`.
- Which pages to export. Valid options are "All pages", "Current page", and
"Custom ranges". Custom ranges are a comma-separated list of numbers or
dash-separated number ranges. Ranges can be half-open. Example: `2,3,7-9,11-`.
# PDF-specific functionality
Typst exposes PDF-specific functionality in the global `pdf` module. See below
for the definitions it contains.

View File

@ -0,0 +1,61 @@
Instead of creating a PDF, Typst can also directly render pages to PNG raster
graphics. PNGs are losslessly compressed images that can contain one page at a
time. When exporting a multi-page document, Typst will emit multiple PNGs. PNGs
are a good choice when you want to use Typst's output in an image editing
software or when you can use none of Typst's other export formats.
In contrast to Typst's other export formats, PNGs are bound to a specific
resolution. When exporting to PNG, you can configure the resolution as pixels
per inch (PPI). If the medium you view the PNG on has a finer resolution than
the PNG you exported, you will notice a loss of quality. Typst calculates the
resolution of your PNGs based on each page's physical dimensions and the PPI. If
you need guidance for choosing a PPI value, consider the following:
- A DPI value of 300 or 600 is typical for desktop printing.
- Professional prints of detailed graphics can go up to 1200 PPI.
- If your document is only viewed at a distance, e.g. a poster, you may choose a
smaller value than 300.
- If your document is viewed on screens, a typical PPI value for a smartphone is
400-500.
Because PNGs only contain a pixel raster, the text within cannot be extracted
automatically (without OCR), for example by copy/paste or a screen reader. If
you need the text to be accessible, export a PDF or HTML file instead.
PNGs can have transparent backgrounds. By default, Typst will output a PNG with
an opaque white background. You can make the background transparent using
`[#set page(fill: none)]`. Learn more on the
[`page` function's reference page]($page.fill).
# Exporting as PNG
## Command Line
Pass `--format png` to the `compile` or `watch` subcommand or provide an output
file name that ends with `.png`.
If your document has more than one page, Typst will create multiple image files.
The output file name must then be a template string containing at least one of
- `[{p}]`, which will be replaced by the page number
- `[{0p}]`, which will be replaced by the zero-padded page number (so that all
numbers have the same length)
- `[{t}]`, which will be replaced by the total number of pages
When exporting to PNG, you have the following configuration options:
- Which resolution to render at by specifying `--ppi` followed by a number of
pixels per inch. The default is `144`.
- Which pages to export by specifying `--pages` followed by a comma-separated
list of numbers or dash-separated number ranges. Ranges can be half-open.
Example: `2,3,7-9,11-`.
## Web App
Click "File" > "Export as" > "PNG" or click the downwards-facing arrow next to
the quick download button and select "Export as PNG". When exporting to PNG, you
have the following configuration options:
- The resolution at which the pages should be rendered, as a number of pixels
per inch. The default is `144`.
- Which pages to export. Valid options are "All pages", "Current page", and
"Custom ranges". Custom ranges are a comma-separated list of numbers or
dash-separated number ranges. Ranges can be half-open. Example: `2,3,7-9,11-`.

View File

@ -0,0 +1,48 @@
Instead of creating a PDF, Typst can also directly render pages to scalable
vector graphics (SVGs), which are the preferred format for embedding vector
graphics in web pages. Like PDF files, SVGs display your document exactly how
you have laid it out in Typst. Likewise, they share the benefit of not being
bound to a specific resolution. Hence, you can print or view SVG files on any
device without incurring a loss of quality. (Note that font printing quality may
be better with a PDF.) In contrast to a PDF, an SVG cannot contain multiple
pages. When exporting a multi-page document, Typst will emit multiple SVGs.
SVGs can represent text in two ways: By embedding the text itself and rendering
it with the fonts available on the viewer's computer or by embedding the shapes
of each glyph in the font used to create the document. To ensure that the SVG
file looks the same across all devices it is viewed on, Typst chooses the latter
method. This means that the text in the SVG cannot be extracted automatically,
for example by copy/paste or a screen reader. If you need the text to be
accessible, export a PDF or HTML file instead.
SVGs can have transparent backgrounds. By default, Typst will output an SVG with
an opaque white background. You can make the background transparent using
`[#set page(fill: none)]`. Learn more on the
[`page` function's reference page]($page.fill).
# Exporting as SVG
## Command Line
Pass `--format svg` to the `compile` or `watch` subcommand or provide an output
file name that ends with `.svg`.
If your document has more than one page, Typst will create multiple image files.
The output file name must then be a template string containing at least one of
- `[{p}]`, which will be replaced by the page number
- `[{0p}]`, which will be replaced by the zero-padded page number (so that all
numbers have the same length)
- `[{t}]`, which will be replaced by the total number of pages
When exporting to SVG, you have the following configuration options:
- Which pages to export by specifying `--pages` followed by a comma-separated
list of numbers or dash-separated number ranges. Ranges can be half-open.
Example: `2,3,7-9,11-`.
## Web App
Click "File" > "Export as" > "SVG" or click the downwards-facing arrow next to
the quick download button and select "Export as SVG". When exporting to SVG, you
have the following configuration options:
- Which pages to export. Valid options are "All pages", "Current page", and
"Custom ranges". Custom ranges are a comma-separated list of numbers or
dash-separated number ranges. Ranges can be half-open. Example: `2,3,7-9,11-`.

View File

@ -0,0 +1,4 @@
Data loading from external files.
These functions help you with loading and embedding data, for example from the
results of an experiment.

View File

@ -0,0 +1,4 @@
Foundational types and functions.
Here, you'll find documentation for basic data types like [integers]($int) and
[strings]($str) as well as details about core computational functions.

View File

@ -0,0 +1,10 @@
Interactions between document parts.
This category is home to Typst's introspection capabilities: With the `counter`
function, you can access and manipulate page, section, figure, and equation
counters or create custom ones. Meanwhile, the `query` function lets you search
for elements in the document to construct things like a list of figures or
headers which show the current chapter title.
Most of the functions are _contextual._ It is recommended to read the chapter on
[context] before continuing here.

View File

@ -0,0 +1,3 @@
Arranging elements on the page in different ways.
By combining layout functions, you can create complex and automatic layouts.

View File

@ -0,0 +1,101 @@
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 $]`).
# Variables
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 [hash syntax]($scripting/#expressions).
```example
$ A = pi r^2 $
$ "area" = pi dot "radius"^2 $
$ cal(A) :=
{ x in RR | x "is natural" } $
#let x = 5
$ #x < 17 $
```
# Symbols
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]($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.
```example
$ x < y => x gt.eq.not y $
```
# Line Breaks
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 \
&= (n(n+1)) / 2 $
```
# Function calls
Math mode supports special function calls without the hash 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 hash syntax to pass code expressions (except for
strings, which are available in the math syntax).
- They support positional and named arguments, as well as argument spreading.
- They don't support trailing content blocks.
- They provide additional syntax for 2-dimensional argument lists. The semicolon
(`;`) merges preceding arguments separated by commas into an array argument.
```example
$ frac(a^2, 2) $
$ vec(1, 2, delim: "[") $
$ mat(1, 2; 3, 4) $
$ mat(..#range(1, 5).chunks(2)) $
$ lim_x =
op("lim", limits: #true)_x $
```
To write a verbatim comma or semicolon in a math call, escape it with a
backslash. The colon on the other hand is only recognized in a special way if
directly preceded by an identifier, so to display it verbatim in those cases,
you can just insert a space before it.
Functions calls preceded by a hash are normal code function calls and not
affected by these rules.
# Alignment
When equations include multiple _alignment points_ (`&`), this creates blocks of
alternatingly right- and left-aligned columns. In the example below, the
expression `(3x + y) / 7` is right-aligned and `= 9` is left-aligned. The word
"given" is also left-aligned because `&&` creates two alignment points in a row,
alternating the alignment twice. `& &` and `&&` behave exactly the same way.
Meanwhile, "multiply by 7" is right-aligned because just one `&` precedes it.
Each alignment point simply alternates between right-aligned/left-aligned.
```example
$ (3x + y) / 7 &= 9 && "given" \
3x + y &= 63 & "multiply by 7" \
3x &= 63 - y && "subtract y" \
x &= 21 - y/3 & "divide by 3" $
```
# Math fonts
You can set the math font by with a [show-set rule]($styling/#show-rules) as
demonstrated below. Note that only special OpenType math fonts are suitable for
typesetting maths.
```example
#show math.equation: set text(font: "Fira Math")
$ sum_(i in NN) 1 + i $
```
# Math module
All math functions are part of the `math` [module]($scripting/#modules), which
is available by default in equations. Outside of equations, they can be accessed
with the `math.` prefix.

View File

@ -0,0 +1,5 @@
Document structuring.
Here, you can find functions to structure your document and interact with that
structure. This includes section headings, figures, bibliography management,
cross-referencing and more.

View File

@ -0,0 +1,5 @@
These two modules give names to symbols and emoji to make them easy to insert
with a normal keyboard. Alternatively, you can also always directly enter
Unicode symbols into your text and formulas. In addition to the symbols listed
below, math mode defines `dif` and `Dif`. These are not normal symbol values
because they also affect spacing and font style.

View File

@ -0,0 +1,3 @@
Text styling.
The [text function]($text) is of particular interest.

View File

@ -0,0 +1,5 @@
Drawing and data visualization.
If you want to create more advanced drawings or plots, also have a look at the
[CetZ](https://github.com/johannes-wolf/cetz) package as well as more
specialized [packages]($universe) for your use case.

View File

@ -1,6 +0,0 @@
Typst [packages]($scripting/#packages) encapsulate reusable building blocks
and make them reusable across projects. Below is a list of Typst packages
created by the community. Due to the early and experimental nature of Typst's
package management, they all live in a `preview` namespace. Click on a package's
name to view its documentation and use the copy button on the right to get a
full import statement for it.

View File

@ -301,7 +301,10 @@ impl<'a> Handler<'a> {
return;
}
let default = self.peeked.as_ref().map(|text| text.to_kebab_case());
let body = self.peeked.as_ref();
let default = body.map(|text| text.to_kebab_case());
let has_id = id_slot.is_some();
let id: &'a str = match (&id_slot, default) {
(Some(id), default) => {
if Some(*id) == default.as_deref() {
@ -316,10 +319,10 @@ impl<'a> Handler<'a> {
*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.into()
} else {
id.to_title_case().into()
let name = match &body {
_ if id.starts_with('v') && id.contains('.') => id.into(),
Some(body) if !has_id => body.as_ref().into(),
_ => id.to_title_case().into(),
};
let mut children = &mut self.outline;

View File

@ -12,27 +12,20 @@ pub use self::model::*;
use std::collections::HashSet;
use ecow::{eco_format, EcoString};
use heck::ToTitleCase;
use serde::Deserialize;
use serde_yaml as yaml;
use std::sync::LazyLock;
use typst::diag::{bail, StrResult};
use typst::foundations::Binding;
use typst::foundations::{
AutoValue, Bytes, CastInfo, Category, Func, Module, NoneValue, ParamInfo, Repr,
Scope, Smart, Type, Value, FOUNDATIONS,
AutoValue, Binding, Bytes, CastInfo, Func, Module, NoneValue, ParamInfo, Repr, Scope,
Smart, Type, Value,
};
use typst::html::HTML;
use typst::introspection::INTROSPECTION;
use typst::layout::{Abs, Margin, PageElem, PagedDocument, LAYOUT};
use typst::loading::DATA_LOADING;
use typst::math::MATH;
use typst::model::MODEL;
use typst::pdf::PDF;
use typst::symbols::SYMBOLS;
use typst::text::{Font, FontBook, TEXT};
use typst::layout::{Abs, Margin, PageElem, PagedDocument};
use typst::text::{Font, FontBook};
use typst::utils::LazyHash;
use typst::visualize::VISUALIZE;
use typst::{Feature, Library, LibraryBuilder};
use typst::{Category, Feature, Library, LibraryBuilder};
use unicode_math_class::MathClass;
macro_rules! load {
($path:literal) => {
@ -64,9 +57,10 @@ static LIBRARY: LazyLock<LazyHash<Library>> = LazyLock::new(|| {
let scope = lib.global.scope_mut();
// Add those types, so that they show up in the docs.
scope.start_category(FOUNDATIONS);
scope.start_category(Category::Foundations);
scope.define_type::<NoneValue>();
scope.define_type::<AutoValue>();
scope.reset_category();
// Adjust the default look.
lib.styles
@ -155,21 +149,24 @@ fn reference_pages(resolver: &dyn Resolver) -> PageModel {
let mut page = md_page(resolver, resolver.base(), load!("reference/welcome.md"));
let base = format!("{}reference/", resolver.base());
page.children = vec![
md_page(resolver, &base, load!("reference/syntax.md")).with_part("Language"),
md_page(resolver, &base, load!("reference/styling.md")),
md_page(resolver, &base, load!("reference/scripting.md")),
md_page(resolver, &base, load!("reference/context.md")),
category_page(resolver, FOUNDATIONS).with_part("Library"),
category_page(resolver, MODEL),
category_page(resolver, TEXT),
category_page(resolver, MATH),
category_page(resolver, SYMBOLS),
category_page(resolver, LAYOUT),
category_page(resolver, VISUALIZE),
category_page(resolver, INTROSPECTION),
category_page(resolver, DATA_LOADING),
category_page(resolver, PDF),
category_page(resolver, HTML),
md_page(resolver, &base, load!("reference/language/syntax.md"))
.with_part("Language"),
md_page(resolver, &base, load!("reference/language/styling.md")),
md_page(resolver, &base, load!("reference/language/scripting.md")),
md_page(resolver, &base, load!("reference/language/context.md")),
category_page(resolver, Category::Foundations).with_part("Library"),
category_page(resolver, Category::Model),
category_page(resolver, Category::Text),
category_page(resolver, Category::Math),
category_page(resolver, Category::Symbols),
category_page(resolver, Category::Layout),
category_page(resolver, Category::Visualize),
category_page(resolver, Category::Introspection),
category_page(resolver, Category::DataLoading),
category_page(resolver, Category::Pdf).with_part("Export"),
category_page(resolver, Category::Html),
category_page(resolver, Category::Png),
category_page(resolver, Category::Svg),
];
page
}
@ -219,14 +216,16 @@ fn category_page(resolver: &dyn Resolver, category: Category) -> PageModel {
let mut markup = vec![];
let mut math = vec![];
let (module, path): (&Module, &[&str]) = if category == MATH {
(&LIBRARY.math, &["math"])
} else {
(&LIBRARY.global, &[])
let docs = category_docs(category);
let (module, path): (&Module, &[&str]) = match category {
Category::Math => (&LIBRARY.math, &["math"]),
Category::Pdf => (get_module(&LIBRARY.global, "pdf").unwrap(), &["pdf"]),
Category::Html => (get_module(&LIBRARY.global, "html").unwrap(), &["html"]),
_ => (&LIBRARY.global, &[]),
};
// Add groups.
for group in GROUPS.iter().filter(|g| g.category == category.name()).cloned() {
for group in GROUPS.iter().filter(|g| g.category == category).cloned() {
if matches!(group.name.as_str(), "sym" | "emoji") {
let subpage = symbols_page(resolver, &route, &group);
let BodyModel::Symbols(model) = &subpage.body else { continue };
@ -243,7 +242,7 @@ fn category_page(resolver: &dyn Resolver, category: Category) -> PageModel {
items.push(CategoryItem {
name: group.name.clone(),
route: subpage.route.clone(),
oneliner: oneliner(category.docs()).into(),
oneliner: oneliner(docs).into(),
code: true,
});
children.push(subpage);
@ -256,15 +255,15 @@ fn category_page(resolver: &dyn Resolver, category: Category) -> PageModel {
}
// Add symbol pages. These are ordered manually.
if category == SYMBOLS {
if category == Category::Symbols {
shorthands = Some(ShorthandsModel { markup, math });
}
let mut skip = HashSet::new();
if category == MATH {
if category == Category::Math {
skip = GROUPS
.iter()
.filter(|g| g.category == category.name())
.filter(|g| g.category == category)
.flat_map(|g| &g.filter)
.map(|s| s.as_str())
.collect();
@ -273,6 +272,11 @@ fn category_page(resolver: &dyn Resolver, category: Category) -> PageModel {
skip.insert("text");
}
// Tiling would be duplicate otherwise.
if category == Category::Visualize {
skip.insert("pattern");
}
// Add values and types.
let scope = module.scope();
for (name, binding) in scope.iter() {
@ -287,8 +291,8 @@ fn category_page(resolver: &dyn Resolver, category: Category) -> PageModel {
match binding.read() {
Value::Func(func) => {
let name = func.name().unwrap();
let subpage = func_page(resolver, &route, func, path);
let subpage =
func_page(resolver, &route, func, path, binding.deprecation());
items.push(CategoryItem {
name: name.into(),
route: subpage.route.clone(),
@ -311,31 +315,39 @@ fn category_page(resolver: &dyn Resolver, category: Category) -> PageModel {
}
}
if category != SYMBOLS {
if category != Category::Symbols {
children.sort_by_cached_key(|child| child.title.clone());
items.sort_by_cached_key(|item| item.name.clone());
}
let name = category.title();
let details = Html::markdown(resolver, category.docs(), Some(1));
let title = EcoString::from(match category {
Category::Pdf | Category::Html | Category::Png | Category::Svg => {
category.name().to_uppercase()
}
_ => category.name().to_title_case(),
});
let details = Html::markdown(resolver, docs, Some(1));
let mut outline = vec![OutlineItem::from_name("Summary")];
outline.extend(details.outline());
outline.push(OutlineItem::from_name("Definitions"));
if !items.is_empty() {
outline.push(OutlineItem::from_name("Definitions"));
}
if shorthands.is_some() {
outline.push(OutlineItem::from_name("Shorthands"));
}
PageModel {
route,
title: name.into(),
title: title.clone(),
description: eco_format!(
"Documentation for functions related to {name} in Typst."
"Documentation for functions related to {title} in Typst."
),
part: None,
outline,
body: BodyModel::Category(CategoryModel {
name: category.name(),
title: category.title(),
title,
details,
items,
shorthands,
@ -344,14 +356,34 @@ fn category_page(resolver: &dyn Resolver, category: Category) -> PageModel {
}
}
/// Retrieve the docs for a category.
fn category_docs(category: Category) -> &'static str {
match category {
Category::Foundations => load!("reference/library/foundations.md"),
Category::Introspection => load!("reference/library/introspection.md"),
Category::Layout => load!("reference/library/layout.md"),
Category::DataLoading => load!("reference/library/data-loading.md"),
Category::Math => load!("reference/library/math.md"),
Category::Model => load!("reference/library/model.md"),
Category::Symbols => load!("reference/library/symbols.md"),
Category::Text => load!("reference/library/text.md"),
Category::Visualize => load!("reference/library/visualize.md"),
Category::Pdf => load!("reference/export/pdf.md"),
Category::Html => load!("reference/export/html.md"),
Category::Svg => load!("reference/export/svg.md"),
Category::Png => load!("reference/export/png.md"),
}
}
/// Create a page for a function.
fn func_page(
resolver: &dyn Resolver,
parent: &str,
func: &Func,
path: &[&str],
deprecation: Option<&'static str>,
) -> PageModel {
let model = func_model(resolver, func, path, false);
let model = func_model(resolver, func, path, false, deprecation);
let name = func.name().unwrap();
PageModel {
route: eco_format!("{parent}{}/", urlify(name)),
@ -370,6 +402,7 @@ fn func_model(
func: &Func,
path: &[&str],
nested: bool,
deprecation: Option<&'static str>,
) -> FuncModel {
let name = func.name().unwrap();
let scope = func.scope().unwrap();
@ -383,7 +416,11 @@ fn func_model(
}
let mut returns = vec![];
casts(resolver, &mut returns, &mut vec![], func.returns().unwrap());
let mut strings = vec![];
casts(resolver, &mut returns, &mut strings, func.returns().unwrap());
if !strings.is_empty() && !returns.contains(&"str") {
returns.push("str");
}
returns.sort_by_key(|ty| type_index(ty));
if returns == ["none"] {
returns.clear();
@ -401,6 +438,7 @@ fn func_model(
oneliner: oneliner(details),
element: func.element().is_some(),
contextual: func.contextual().unwrap_or(false),
deprecation,
details: Html::markdown(resolver, details, nesting),
example: example.map(|md| Html::markdown(resolver, md, None)),
self_,
@ -483,7 +521,7 @@ fn scope_models(resolver: &dyn Resolver, name: &str, scope: &Scope) -> Vec<FuncM
.iter()
.filter_map(|(_, binding)| {
let Value::Func(func) = binding.read() else { return None };
Some(func_model(resolver, func, &[name], true))
Some(func_model(resolver, func, &[name], true, binding.deprecation()))
})
.collect()
}
@ -559,9 +597,11 @@ fn group_page(
let mut outline_items = vec![];
for name in &group.filter {
let value = group.module().scope().get(name).unwrap().read();
let Ok(ref func) = value.clone().cast::<Func>() else { panic!("not a function") };
let func = func_model(resolver, func, &path, true);
let binding = group.module().scope().get(name).unwrap();
let Ok(ref func) = binding.read().clone().cast::<Func>() else {
panic!("not a function")
};
let func = func_model(resolver, func, &path, true, binding.deprecation());
let id_base = urlify(&eco_format!("functions-{}", func.name));
let children = func_outline(&func, &id_base);
outline_items.push(OutlineItem {
@ -628,7 +668,7 @@ fn type_model(resolver: &dyn Resolver, ty: &Type) -> TypeModel {
constructor: ty
.constructor()
.ok()
.map(|func| func_model(resolver, &func, &[], true)),
.map(|func| func_model(resolver, &func, &[], true, None)),
scope: scope_models(resolver, ty.short_name(), ty.scope()),
}
}
@ -682,10 +722,19 @@ fn symbols_model(resolver: &dyn Resolver, group: &GroupData) -> SymbolsModel {
list.iter().copied().find(|&(_, x)| x == c).map(|(s, _)| s)
};
let name = complete(variant);
let deprecation = match name.as_str() {
"integral.sect" => {
Some("`integral.sect` is deprecated, use `integral.inter` instead")
}
_ => binding.deprecation(),
};
list.push(SymbolModel {
name: complete(variant),
name,
markup_shorthand: shorthand(typst::syntax::ast::Shorthand::LIST),
math_shorthand: shorthand(typst::syntax::ast::MathShorthand::LIST),
math_class: typst_utils::default_math_class(c).map(math_class_name),
codepoint: c as _,
accent: typst::math::Accent::combine(c).is_some(),
alternates: symbol
@ -693,6 +742,7 @@ fn symbols_model(resolver: &dyn Resolver, group: &GroupData) -> SymbolsModel {
.filter(|(other, _)| other != &variant)
.map(|(other, _)| complete(other))
.collect(),
deprecation,
});
}
}
@ -769,12 +819,32 @@ const TYPE_ORDER: &[&str] = &[
"stroke",
];
fn math_class_name(class: MathClass) -> &'static str {
match class {
MathClass::Normal => "Normal",
MathClass::Alphabetic => "Alphabetic",
MathClass::Binary => "Binary",
MathClass::Closing => "Closing",
MathClass::Diacritic => "Diacritic",
MathClass::Fence => "Fence",
MathClass::GlyphPart => "Glyph Part",
MathClass::Large => "Large",
MathClass::Opening => "Opening",
MathClass::Punctuation => "Punctuation",
MathClass::Relation => "Relation",
MathClass::Space => "Space",
MathClass::Unary => "Unary",
MathClass::Vary => "Vary",
MathClass::Special => "Special",
}
}
/// Data about a collection of functions.
#[derive(Debug, Clone, Deserialize)]
struct GroupData {
name: EcoString,
title: EcoString,
category: EcoString,
category: Category,
#[serde(default)]
path: Vec<EcoString>,
#[serde(default)]

View File

@ -44,6 +44,8 @@ fn resolve_known(head: &str, base: &str) -> Option<String> {
"$styling" => format!("{base}reference/styling"),
"$scripting" => format!("{base}reference/scripting"),
"$context" => format!("{base}reference/context"),
"$html" => format!("{base}reference/html"),
"$pdf" => format!("{base}reference/pdf"),
"$guides" => format!("{base}guides"),
"$changelog" => format!("{base}changelog"),
"$universe" => "https://typst.app/universe".into(),
@ -73,11 +75,14 @@ fn resolve_definition(head: &str, base: &str) -> StrResult<String> {
// Handle grouped functions.
if let Some(group) = GROUPS.iter().find(|group| {
group.category == category.name() && group.filter.iter().any(|func| func == name)
group.category == category && group.filter.iter().any(|func| func == name)
}) {
let mut route = format!(
"{}reference/{}/{}/#functions-{}",
base, group.category, group.name, name
base,
group.category.name(),
group.name,
name
);
if let Some(param) = parts.next() {
route.push('-');

View File

@ -64,7 +64,7 @@ pub enum BodyModel {
#[derive(Debug, Serialize)]
pub struct CategoryModel {
pub name: &'static str,
pub title: &'static str,
pub title: EcoString,
pub details: Html,
pub items: Vec<CategoryItem>,
pub shorthands: Option<ShorthandsModel>,
@ -89,6 +89,7 @@ pub struct FuncModel {
pub oneliner: &'static str,
pub element: bool,
pub contextual: bool,
pub deprecation: Option<&'static str>,
pub details: Html,
/// This example is only for nested function models. Others can have
/// their example directly in their details.
@ -163,6 +164,8 @@ pub struct SymbolModel {
pub alternates: Vec<EcoString>,
pub markup_shorthand: Option<&'static str>,
pub math_shorthand: Option<&'static str>,
pub math_class: Option<&'static str>,
pub deprecation: Option<&'static str>,
}
/// Shorthands listed on a category page.