summaryrefslogtreecommitdiff
path: root/library/src/basics
diff options
context:
space:
mode:
Diffstat (limited to 'library/src/basics')
-rw-r--r--library/src/basics/enum.rs206
-rw-r--r--library/src/basics/heading.rs200
-rw-r--r--library/src/basics/list.rs160
-rw-r--r--library/src/basics/mod.rs14
-rw-r--r--library/src/basics/table.rs236
-rw-r--r--library/src/basics/terms.rs178
6 files changed, 0 insertions, 994 deletions
diff --git a/library/src/basics/enum.rs b/library/src/basics/enum.rs
deleted file mode 100644
index 2bff7133..00000000
--- a/library/src/basics/enum.rs
+++ /dev/null
@@ -1,206 +0,0 @@
-use std::str::FromStr;
-
-use crate::compute::{Numbering, NumberingPattern};
-use crate::layout::{BlockNode, GridNode, ParNode, Spacing, TrackSizing};
-use crate::prelude::*;
-
-/// # Numbered List
-/// A numbered list.
-///
-/// Displays a sequence of items vertically and numbers them consecutively.
-///
-/// ## Example
-/// ```example
-/// Automatically numbered:
-/// + Preparations
-/// + Analysis
-/// + Conclusions
-///
-/// Manually numbered:
-/// 2. What is the first step?
-/// 5. I am confused.
-/// + Moving on ...
-///
-/// Function call.
-/// #enum[First][Second]
-/// ```
-///
-/// ## Syntax
-/// This functions also has dedicated syntax:
-///
-/// - Starting a line with a plus sign creates an automatically numbered
-/// enumeration item.
-/// - Start a line with a number followed by a dot creates an explicitly
-/// numbered enumeration item.
-///
-/// Enumeration items can contain multiple paragraphs and other block-level
-/// content. All content that is indented more than an item's plus sign or dot
-/// becomes part of that item.
-///
-/// ## Parameters
-/// - items: `Content` (positional, variadic)
-/// The enumeration's children.
-///
-/// When using the enum syntax, adjacent items are automatically collected
-/// into enumerations, even through constructs like for loops.
-///
-/// ```example
-/// #for phase in (
-/// "Launch",
-/// "Orbit",
-/// "Descent",
-/// ) [+ #phase]
-/// ```
-///
-/// - start: `NonZeroUsize` (named)
-/// Which number to start the enumeration with.
-///
-/// ```example
-/// #enum(
-/// start: 3,
-/// [Skipping],
-/// [Ahead],
-/// )
-/// ```
-///
-/// - tight: `bool` (named)
-/// If this is `{false}`, the items are spaced apart with
-/// [enum spacing]($func/enum.spacing). If it is `{true}`, they use normal
-/// [leading]($func/par.leading) instead. This makes the enumeration more
-/// compact, which can look better if the items are short.
-///
-/// ```example
-/// + If an enum has a lot of text, and
-/// maybe other inline content, it
-/// should not be tight anymore.
-///
-/// + To make an enum wide, simply
-/// insert a blank line between the
-/// items.
-/// ```
-///
-/// ## Category
-/// basics
-#[func]
-#[capable(Layout)]
-#[derive(Debug, Hash)]
-pub struct EnumNode {
- /// If true, the items are separated by leading instead of list spacing.
- pub tight: bool,
- /// The individual numbered items.
- pub items: StyleVec<(Option<NonZeroUsize>, Content)>,
-}
-
-#[node]
-impl EnumNode {
- /// How to number the enumeration. Accepts a
- /// [numbering pattern or function]($func/numbering).
- ///
- /// ```example
- /// #set enum(numbering: "(a)")
- ///
- /// + Different
- /// + Numbering
- /// + Style
- /// ```
- #[property(referenced)]
- pub const NUMBERING: Numbering =
- Numbering::Pattern(NumberingPattern::from_str("1.").unwrap());
-
- /// The indentation of each item's label.
- #[property(resolve)]
- pub const INDENT: Length = Length::zero();
-
- /// The space between the numbering and the body of each item.
- #[property(resolve)]
- pub const BODY_INDENT: Length = Em::new(0.5).into();
-
- /// The spacing between the items of a wide (non-tight) enumeration.
- ///
- /// If set to `{auto}` uses the spacing [below blocks]($func/block.below).
- pub const SPACING: Smart<Spacing> = Smart::Auto;
-
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let mut number: NonZeroUsize =
- args.named("start")?.unwrap_or(NonZeroUsize::new(1).unwrap());
-
- Ok(Self {
- tight: args.named("tight")?.unwrap_or(true),
- items: args
- .all()?
- .into_iter()
- .map(|body| {
- let item = (Some(number), body);
- number = number.saturating_add(1);
- item
- })
- .collect(),
- }
- .pack())
- }
-
- fn field(&self, name: &str) -> Option<Value> {
- match name {
- "tight" => Some(Value::Bool(self.tight)),
- "items" => Some(Value::Array(
- self.items
- .items()
- .map(|(number, body)| {
- Value::Dict(dict! {
- "number" => match *number {
- Some(n) => Value::Int(n.get() as i64),
- None => Value::None,
- },
- "body" => Value::Content(body.clone()),
- })
- })
- .collect(),
- )),
- _ => None,
- }
- }
-}
-
-impl Layout for EnumNode {
- fn layout(
- &self,
- vt: &mut Vt,
- styles: StyleChain,
- regions: Regions,
- ) -> SourceResult<Fragment> {
- let numbering = styles.get(Self::NUMBERING);
- let indent = styles.get(Self::INDENT);
- let body_indent = styles.get(Self::BODY_INDENT);
- let gutter = if self.tight {
- styles.get(ParNode::LEADING).into()
- } else {
- styles
- .get(Self::SPACING)
- .unwrap_or_else(|| styles.get(BlockNode::BELOW).amount)
- };
-
- let mut cells = vec![];
- let mut number = NonZeroUsize::new(1).unwrap();
- for ((n, item), map) in self.items.iter() {
- number = n.unwrap_or(number);
- let resolved = numbering.apply(vt.world(), &[number])?.display();
- cells.push(Content::empty());
- cells.push(resolved.styled_with_map(map.clone()));
- cells.push(Content::empty());
- cells.push(item.clone().styled_with_map(map.clone()));
- number = number.saturating_add(1);
- }
-
- GridNode {
- tracks: Axes::with_x(vec![
- TrackSizing::Relative(indent.into()),
- TrackSizing::Auto,
- TrackSizing::Relative(body_indent.into()),
- TrackSizing::Auto,
- ]),
- gutter: Axes::with_y(vec![gutter.into()]),
- cells,
- }
- .layout(vt, styles, regions)
- }
-}
diff --git a/library/src/basics/heading.rs b/library/src/basics/heading.rs
deleted file mode 100644
index 7ae76920..00000000
--- a/library/src/basics/heading.rs
+++ /dev/null
@@ -1,200 +0,0 @@
-use typst::font::FontWeight;
-
-use crate::compute::Numbering;
-use crate::layout::{BlockNode, VNode};
-use crate::prelude::*;
-use crate::text::{SpaceNode, TextNode, TextSize};
-
-/// # Heading
-/// A section heading.
-///
-/// With headings, you can structure your document into sections. Each heading
-/// has a _level,_ which starts at one and is unbounded upwards. This level
-/// indicates the logical role of the following content (section, subsection,
-/// etc.) A top-level heading indicates a top-level section of the document
-/// (not the document's title).
-///
-/// Typst can automatically number your headings for you. To enable numbering,
-/// specify how you want your headings to be numbered with a
-/// [numbering pattern or function]($func/numbering).
-///
-/// Independently from the numbering, Typst can also automatically generate an
-/// [outline]($func/outline) of all headings for you. To exclude one or more
-/// headings from this outline, you can set the `outlined` parameter to
-/// `{false}`.
-///
-/// ## Example
-/// ```example
-/// #set heading(numbering: "1.a)")
-///
-/// = Introduction
-/// In recent years, ...
-///
-/// == Preliminaries
-/// To start, ...
-/// ```
-///
-/// ## Syntax
-/// Headings have dedicated syntax: They can be created by starting a line with
-/// one or multiple equals signs, followed by a space. The number of equals
-/// signs determines the heading's logical nesting depth.
-///
-/// ## Parameters
-/// - title: `Content` (positional, required)
-/// The heading's title.
-///
-/// - level: `NonZeroUsize` (named)
-/// The logical nesting depth of the heading, starting from one.
-///
-/// ## Category
-/// basics
-#[func]
-#[capable(Prepare, Show, Finalize)]
-#[derive(Debug, Hash)]
-pub struct HeadingNode {
- /// The logical nesting depth of the section, starting from one. In the
- /// default style, this controls the text size of the heading.
- pub level: NonZeroUsize,
- /// The heading's contents.
- pub title: Content,
-}
-
-#[node]
-impl HeadingNode {
- /// How to number the heading. Accepts a
- /// [numbering pattern or function]($func/numbering).
- ///
- /// ```example
- /// #set heading(numbering: "1.a.")
- ///
- /// = A section
- /// == A subsection
- /// === A sub-subsection
- /// ```
- #[property(referenced)]
- pub const NUMBERING: Option<Numbering> = None;
-
- /// Whether the heading should appear in the outline.
- ///
- /// ```example
- /// #outline()
- ///
- /// #heading[Normal]
- /// This is a normal heading.
- ///
- /// #heading(outlined: false)[Hidden]
- /// This heading does not appear
- /// in the outline.
- /// ```
- pub const OUTLINED: bool = true;
-
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self {
- title: args.expect("title")?,
- level: args.named("level")?.unwrap_or(NonZeroUsize::new(1).unwrap()),
- }
- .pack())
- }
-
- fn field(&self, name: &str) -> Option<Value> {
- match name {
- "level" => Some(Value::Int(self.level.get() as i64)),
- "title" => Some(Value::Content(self.title.clone())),
- _ => None,
- }
- }
-}
-
-impl Prepare for HeadingNode {
- fn prepare(
- &self,
- vt: &mut Vt,
- mut this: Content,
- styles: StyleChain,
- ) -> SourceResult<Content> {
- let my_id = vt.identify(&this);
-
- let mut counter = HeadingCounter::new();
- for (node_id, node) in vt.locate(Selector::node::<HeadingNode>()) {
- if node_id == my_id {
- break;
- }
-
- let numbers = node.field("numbers").unwrap();
- if numbers != Value::None {
- let heading = node.to::<Self>().unwrap();
- counter.advance(heading);
- }
- }
-
- let mut numbers = Value::None;
- if let Some(numbering) = styles.get(Self::NUMBERING) {
- numbers = numbering.apply(vt.world(), counter.advance(self))?;
- }
-
- this.push_field("outlined", Value::Bool(styles.get(Self::OUTLINED)));
- this.push_field("numbers", numbers);
-
- let meta = Meta::Node(my_id, this.clone());
- Ok(this.styled(Meta::DATA, vec![meta]))
- }
-}
-
-impl Show for HeadingNode {
- fn show(&self, _: &mut Vt, this: &Content, _: StyleChain) -> SourceResult<Content> {
- let mut realized = self.title.clone();
- let numbers = this.field("numbers").unwrap();
- if numbers != Value::None {
- realized = numbers.display() + SpaceNode.pack() + realized;
- }
- Ok(BlockNode(realized).pack())
- }
-}
-
-impl Finalize for HeadingNode {
- fn finalize(&self, realized: Content) -> Content {
- let scale = match self.level.get() {
- 1 => 1.4,
- 2 => 1.2,
- _ => 1.0,
- };
-
- let size = Em::new(scale);
- let above = Em::new(if self.level.get() == 1 { 1.8 } else { 1.44 }) / scale;
- let below = Em::new(0.66) / scale;
-
- let mut map = StyleMap::new();
- map.set(TextNode::SIZE, TextSize(size.into()));
- map.set(TextNode::WEIGHT, FontWeight::BOLD);
- map.set(BlockNode::ABOVE, VNode::block_around(above.into()));
- map.set(BlockNode::BELOW, VNode::block_around(below.into()));
- map.set(BlockNode::STICKY, true);
- realized.styled_with_map(map)
- }
-}
-
-/// Counters through headings with different levels.
-pub struct HeadingCounter(Vec<NonZeroUsize>);
-
-impl HeadingCounter {
- /// Create a new heading counter.
- pub fn new() -> Self {
- Self(vec![])
- }
-
- /// Advance the counter and return the numbers for the given heading.
- pub fn advance(&mut self, heading: &HeadingNode) -> &[NonZeroUsize] {
- let level = heading.level.get();
-
- if self.0.len() >= level {
- self.0[level - 1] = self.0[level - 1].saturating_add(1);
- self.0.truncate(level);
- }
-
- while self.0.len() < level {
- self.0.push(NonZeroUsize::new(1).unwrap());
- }
-
- &self.0
- }
-}
diff --git a/library/src/basics/list.rs b/library/src/basics/list.rs
deleted file mode 100644
index 4b9fdafb..00000000
--- a/library/src/basics/list.rs
+++ /dev/null
@@ -1,160 +0,0 @@
-use crate::layout::{BlockNode, GridNode, ParNode, Spacing, TrackSizing};
-use crate::prelude::*;
-use crate::text::TextNode;
-
-/// # Bullet List
-/// A bullet list.
-///
-/// Displays a sequence of items vertically, with each item introduced by a
-/// marker.
-///
-/// ## Example
-/// ```example
-/// - *Content*
-/// - Basics
-/// - Text
-/// - Math
-/// - Layout
-/// - Visualize
-/// - Meta
-///
-/// - *Compute*
-/// #list(
-/// [Foundations],
-/// [Calculate],
-/// [Create],
-/// [Data Loading],
-/// [Utility],
-/// )
-/// ```
-///
-/// ## Syntax
-/// This functions also has dedicated syntax: Start a line with a hyphen,
-/// followed by a space to create a list item. A list item can contain multiple
-/// paragraphs and other block-level content. All content that is indented
-/// more than an item's hyphen becomes part of that item.
-///
-/// ## Parameters
-/// - items: `Content` (positional, variadic)
-/// The list's children.
-///
-/// When using the list syntax, adjacent items are automatically collected
-/// into lists, even through constructs like for loops.
-///
-/// ```example
-/// #for letter in "ABC" [
-/// - Letter #letter
-/// ]
-/// ```
-///
-/// - tight: `bool` (named)
-/// If this is `{false}`, the items are spaced apart with [list
-/// spacing]($func/list.spacing). If it is `{true}`, they use normal
-/// [leading]($func/par.leading) instead. This makes the list more compact,
-/// which can look better if the items are short.
-///
-/// ```example
-/// - If a list has a lot of text, and
-/// maybe other inline content, it
-/// should not be tight anymore.
-///
-/// - To make a list wide, simply insert
-/// a blank line between the items.
-/// ```
-///
-/// ## Category
-/// basics
-#[func]
-#[capable(Layout)]
-#[derive(Debug, Hash)]
-pub struct ListNode {
- /// If true, the items are separated by leading instead of list spacing.
- pub tight: bool,
- /// The individual bulleted or numbered items.
- pub items: StyleVec<Content>,
-}
-
-#[node]
-impl ListNode {
- /// The marker which introduces each element.
- ///
- /// ```example
- /// #set list(marker: [--])
- ///
- /// - A more classic list
- /// - With en-dashes
- /// ```
- #[property(referenced)]
- pub const MARKER: Content = TextNode::packed('•');
-
- /// The indent of each item's marker.
- #[property(resolve)]
- pub const INDENT: Length = Length::zero();
-
- /// The spacing between the marker and the body of each item.
- #[property(resolve)]
- pub const BODY_INDENT: Length = Em::new(0.5).into();
-
- /// The spacing between the items of a wide (non-tight) list.
- ///
- /// If set to `{auto}` uses the spacing [below blocks]($func/block.below).
- pub const SPACING: Smart<Spacing> = Smart::Auto;
-
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self {
- tight: args.named("tight")?.unwrap_or(true),
- items: args.all()?.into_iter().collect(),
- }
- .pack())
- }
-
- fn field(&self, name: &str) -> Option<Value> {
- match name {
- "tight" => Some(Value::Bool(self.tight)),
- "items" => Some(Value::Array(
- self.items.items().cloned().map(Value::Content).collect(),
- )),
- _ => None,
- }
- }
-}
-
-impl Layout for ListNode {
- fn layout(
- &self,
- vt: &mut Vt,
- styles: StyleChain,
- regions: Regions,
- ) -> SourceResult<Fragment> {
- let marker = styles.get(Self::MARKER);
- let indent = styles.get(Self::INDENT);
- let body_indent = styles.get(Self::BODY_INDENT);
- let gutter = if self.tight {
- styles.get(ParNode::LEADING).into()
- } else {
- styles
- .get(Self::SPACING)
- .unwrap_or_else(|| styles.get(BlockNode::BELOW).amount)
- };
-
- let mut cells = vec![];
- for (item, map) in self.items.iter() {
- cells.push(Content::empty());
- cells.push(marker.clone());
- cells.push(Content::empty());
- cells.push(item.clone().styled_with_map(map.clone()));
- }
-
- GridNode {
- tracks: Axes::with_x(vec![
- TrackSizing::Relative(indent.into()),
- TrackSizing::Auto,
- TrackSizing::Relative(body_indent.into()),
- TrackSizing::Auto,
- ]),
- gutter: Axes::with_y(vec![gutter.into()]),
- cells,
- }
- .layout(vt, styles, regions)
- }
-}
diff --git a/library/src/basics/mod.rs b/library/src/basics/mod.rs
deleted file mode 100644
index 7520c42d..00000000
--- a/library/src/basics/mod.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-//! Common document elements.
-
-#[path = "enum.rs"]
-mod enum_;
-mod heading;
-mod list;
-mod table;
-mod terms;
-
-pub use self::enum_::*;
-pub use self::heading::*;
-pub use self::list::*;
-pub use self::table::*;
-pub use self::terms::*;
diff --git a/library/src/basics/table.rs b/library/src/basics/table.rs
deleted file mode 100644
index 56df2cea..00000000
--- a/library/src/basics/table.rs
+++ /dev/null
@@ -1,236 +0,0 @@
-use crate::layout::{AlignNode, GridNode, TrackSizing, TrackSizings};
-use crate::prelude::*;
-
-/// # Table
-/// A table of items.
-///
-/// Tables are used to arrange content in cells. Cells can contain arbitrary
-/// content, including multiple paragraphs and are specified in row-major order.
-/// Because tables are just grids with configurable cell properties, refer to
-/// the [grid documentation]($func/grid) for more information on how to size the
-/// table tracks.
-///
-/// ## Example
-/// ```example
-/// #table(
-/// columns: (1fr, auto, auto),
-/// inset: 10pt,
-/// align: horizon,
-/// [], [*Area*], [*Parameters*],
-/// image("cylinder.svg"),
-/// $ pi h (D^2 - d^2) / 4 $,
-/// [
-/// $h$: height \
-/// $D$: outer radius \
-/// $d$: inner radius
-/// ],
-/// image("tetrahedron.svg"),
-/// $ sqrt(2) / 12 a^3 $,
-/// [$a$: edge length]
-/// )
-/// ```
-///
-/// ## Parameters
-/// - cells: `Content` (positional, variadic)
-/// The contents of the table cells.
-///
-/// - rows: `TrackSizings` (named)
-/// Defines the row sizes.
-/// See the [grid documentation]($func/grid) for more information on track
-/// sizing.
-///
-/// - columns: `TrackSizings` (named)
-/// Defines the column sizes.
-/// See the [grid documentation]($func/grid) for more information on track
-/// sizing.
-///
-/// - gutter: `TrackSizings` (named)
-/// Defines the gaps between rows & columns.
-/// See the [grid documentation]($func/grid) for more information on gutters.
-///
-/// - column-gutter: `TrackSizings` (named)
-/// Defines the gaps between columns. Takes precedence over `gutter`.
-/// See the [grid documentation]($func/grid) for more information on gutters.
-///
-/// - row-gutter: `TrackSizings` (named)
-/// Defines the gaps between rows. Takes precedence over `gutter`.
-/// See the [grid documentation]($func/grid) for more information on gutters.
-///
-/// ## Category
-/// basics
-#[func]
-#[capable(Layout)]
-#[derive(Debug, Hash)]
-pub struct TableNode {
- /// Defines sizing for content rows and columns.
- pub tracks: Axes<Vec<TrackSizing>>,
- /// Defines sizing of gutter rows and columns between content.
- pub gutter: Axes<Vec<TrackSizing>>,
- /// The content to be arranged in the table.
- pub cells: Vec<Content>,
-}
-
-#[node]
-impl TableNode {
- /// How to fill the cells.
- ///
- /// This can be a color or a function that returns a color. The function is
- /// passed the cell's column and row index, starting at zero. This can be
- /// used to implement striped tables.
- ///
- /// ```example
- /// #table(
- /// fill: (col, _) => if calc.odd(col) { luma(240) } else { white },
- /// align: (col, row) =>
- /// if row == 0 { center }
- /// else if col == 0 { left }
- /// else { right },
- /// columns: 4,
- /// [], [*Q1*], [*Q2*], [*Q3*],
- /// [Revenue:], [1000 €], [2000 €], [3000 €],
- /// [Expenses:], [500 €], [1000 €], [1500 €],
- /// [Profit:], [500 €], [1000 €], [1500 €],
- /// )
- /// ```
- #[property(referenced)]
- pub const FILL: Celled<Option<Paint>> = Celled::Value(None);
-
- /// How to align the cell's content.
- ///
- /// This can either be a single alignment or a function that returns an
- /// alignment. The function is passed the cell's column and row index,
- /// starting at zero. If set to `{auto}`, the outer alignment is used.
- #[property(referenced)]
- pub const ALIGN: Celled<Smart<Axes<Option<GenAlign>>>> = Celled::Value(Smart::Auto);
-
- /// How to stroke the cells.
- ///
- /// This can be a color, a stroke width, both, or `{none}` to disable
- /// the stroke.
- #[property(resolve, fold)]
- pub const STROKE: Option<PartialStroke> = Some(PartialStroke::default());
-
- /// How much to pad the cells's content.
- ///
- /// The default value is `{5pt}`.
- pub const INSET: Rel<Length> = Abs::pt(5.0).into();
-
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let TrackSizings(columns) = args.named("columns")?.unwrap_or_default();
- let TrackSizings(rows) = args.named("rows")?.unwrap_or_default();
- let TrackSizings(base_gutter) = args.named("gutter")?.unwrap_or_default();
- let column_gutter = args.named("column-gutter")?.map(|TrackSizings(v)| v);
- let row_gutter = args.named("row-gutter")?.map(|TrackSizings(v)| v);
- Ok(Self {
- tracks: Axes::new(columns, rows),
- gutter: Axes::new(
- column_gutter.unwrap_or_else(|| base_gutter.clone()),
- row_gutter.unwrap_or(base_gutter),
- ),
- cells: args.all()?,
- }
- .pack())
- }
-
- fn field(&self, name: &str) -> Option<Value> {
- match name {
- "columns" => Some(TrackSizing::encode_slice(&self.tracks.x)),
- "rows" => Some(TrackSizing::encode_slice(&self.tracks.y)),
- "column-gutter" => Some(TrackSizing::encode_slice(&self.gutter.x)),
- "row-gutter" => Some(TrackSizing::encode_slice(&self.gutter.y)),
- "cells" => Some(Value::Array(
- self.cells.iter().cloned().map(Value::Content).collect(),
- )),
- _ => None,
- }
- }
-}
-
-impl Layout for TableNode {
- fn layout(
- &self,
- vt: &mut Vt,
- styles: StyleChain,
- regions: Regions,
- ) -> SourceResult<Fragment> {
- let fill = styles.get(Self::FILL);
- let stroke = styles.get(Self::STROKE).map(PartialStroke::unwrap_or_default);
- let inset = styles.get(Self::INSET);
- let align = styles.get(Self::ALIGN);
-
- let cols = self.tracks.x.len().max(1);
- let cells = self
- .cells
- .iter()
- .cloned()
- .enumerate()
- .map(|(i, child)| {
- let mut child = child.padded(Sides::splat(inset));
-
- let x = i % cols;
- let y = i / cols;
- if let Smart::Custom(alignment) = align.resolve(vt, x, y)? {
- child = child.styled(AlignNode::ALIGNS, alignment)
- }
-
- if let Some(stroke) = stroke {
- child = child.stroked(stroke);
- }
-
- if let Some(fill) = fill.resolve(vt, x, y)? {
- child = child.filled(fill);
- }
-
- Ok(child)
- })
- .collect::<SourceResult<_>>()?;
-
- GridNode {
- tracks: self.tracks.clone(),
- gutter: self.gutter.clone(),
- cells,
- }
- .layout(vt, styles, regions)
- }
-}
-
-/// A value that can be configured per cell.
-#[derive(Debug, Clone, PartialEq, Hash)]
-pub enum Celled<T> {
- /// A bare value, the same for all cells.
- Value(T),
- /// A closure mapping from cell coordinates to a value.
- Func(Func),
-}
-
-impl<T: Cast + Clone> Celled<T> {
- /// Resolve the value based on the cell position.
- pub fn resolve(&self, vt: &Vt, x: usize, y: usize) -> SourceResult<T> {
- Ok(match self {
- Self::Value(value) => value.clone(),
- Self::Func(func) => {
- let args =
- Args::new(func.span(), [Value::Int(x as i64), Value::Int(y as i64)]);
- func.call_detached(vt.world(), args)?.cast().at(func.span())?
- }
- })
- }
-}
-
-impl<T: Cast> Cast for Celled<T> {
- fn is(value: &Value) -> bool {
- matches!(value, Value::Func(_)) || T::is(value)
- }
-
- fn cast(value: Value) -> StrResult<Self> {
- match value {
- Value::Func(v) => Ok(Self::Func(v)),
- v if T::is(&v) => Ok(Self::Value(T::cast(v)?)),
- v => <Self as Cast>::error(v),
- }
- }
-
- fn describe() -> CastInfo {
- T::describe() + CastInfo::Type("function")
- }
-}
diff --git a/library/src/basics/terms.rs b/library/src/basics/terms.rs
deleted file mode 100644
index 2e52c87d..00000000
--- a/library/src/basics/terms.rs
+++ /dev/null
@@ -1,178 +0,0 @@
-use crate::layout::{BlockNode, GridNode, HNode, ParNode, Spacing, TrackSizing};
-use crate::prelude::*;
-use crate::text::{SpaceNode, TextNode};
-
-/// # Term List
-/// A list of terms and their descriptions.
-///
-/// Displays a sequence of terms and their descriptions vertically. When the
-/// descriptions span over multiple lines, they use hanging indent to
-/// communicate the visual hierarchy.
-///
-/// ## Syntax
-/// This function also has dedicated syntax: Starting a line with a slash,
-/// followed by a term, a colon and a description creates a term list item.
-///
-/// ## Example
-/// ```example
-/// / Ligature: A merged glyph.
-/// / Kerning: A spacing adjustment
-/// between two adjacent letters.
-/// ```
-///
-/// ## Parameters
-/// - items: `Content` (positional, variadic)
-/// The term list's children.
-///
-/// When using the term list syntax, adjacent items are automatically
-/// collected into term lists, even through constructs like for loops.
-///
-/// ```example
-/// #for year, product in (
-/// "1978": "TeX",
-/// "1984": "LaTeX",
-/// "2019": "Typst",
-/// ) [/ #product: Born in #year.]
-/// ```
-///
-/// - tight: `bool` (named)
-/// If this is `{false}`, the items are spaced apart with [term list
-/// spacing]($func/terms.spacing). If it is `{true}`, they use normal
-/// [leading]($func/par.leading) instead. This makes the term list more
-/// compact, which can look better if the items are short.
-///
-/// ```example
-/// / Fact: If a term list has a lot
-/// of text, and maybe other inline
-/// content, it should not be tight
-/// anymore.
-///
-/// / Tip: To make it wide, simply
-/// insert a blank line between the
-/// items.
-/// ```
-///
-/// ## Category
-/// basics
-#[func]
-#[capable(Layout)]
-#[derive(Debug, Hash)]
-pub struct TermsNode {
- /// If true, the items are separated by leading instead of list spacing.
- pub tight: bool,
- /// The individual bulleted or numbered items.
- pub items: StyleVec<TermItem>,
-}
-
-#[node]
-impl TermsNode {
- /// The indentation of each item's term.
- #[property(resolve)]
- pub const INDENT: Length = Length::zero();
-
- /// The hanging indent of the description.
- ///
- /// ```example
- /// #set terms(hanging-indent: 0pt)
- /// / Term: This term list does not
- /// make use of hanging indents.
- /// ```
- #[property(resolve)]
- pub const HANGING_INDENT: Length = Em::new(1.0).into();
-
- /// The spacing between the items of a wide (non-tight) term list.
- ///
- /// If set to `{auto}` uses the spacing [below blocks]($func/block.below).
- pub const SPACING: Smart<Spacing> = Smart::Auto;
-
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self {
- tight: args.named("tight")?.unwrap_or(true),
- items: args.all()?.into_iter().collect(),
- }
- .pack())
- }
-
- fn field(&self, name: &str) -> Option<Value> {
- match name {
- "tight" => Some(Value::Bool(self.tight)),
- "items" => {
- Some(Value::Array(self.items.items().map(|item| item.encode()).collect()))
- }
- _ => None,
- }
- }
-}
-
-impl Layout for TermsNode {
- fn layout(
- &self,
- vt: &mut Vt,
- styles: StyleChain,
- regions: Regions,
- ) -> SourceResult<Fragment> {
- let indent = styles.get(Self::INDENT);
- let body_indent = styles.get(Self::HANGING_INDENT);
- let gutter = if self.tight {
- styles.get(ParNode::LEADING).into()
- } else {
- styles
- .get(Self::SPACING)
- .unwrap_or_else(|| styles.get(BlockNode::BELOW).amount)
- };
-
- let mut cells = vec![];
- for (item, map) in self.items.iter() {
- let body = Content::sequence(vec![
- HNode { amount: (-body_indent).into(), weak: false }.pack(),
- (item.term.clone() + TextNode::packed(':')).strong(),
- SpaceNode.pack(),
- item.description.clone(),
- ]);
-
- cells.push(Content::empty());
- cells.push(body.styled_with_map(map.clone()));
- }
-
- GridNode {
- tracks: Axes::with_x(vec![
- TrackSizing::Relative((indent + body_indent).into()),
- TrackSizing::Auto,
- ]),
- gutter: Axes::with_y(vec![gutter.into()]),
- cells,
- }
- .layout(vt, styles, regions)
- }
-}
-
-/// A term list item.
-#[derive(Debug, Clone, Hash)]
-pub struct TermItem {
- /// The term described by the list item.
- pub term: Content,
- /// The description of the term.
- pub description: Content,
-}
-
-impl TermItem {
- /// Encode the item into a value.
- fn encode(&self) -> Value {
- Value::Array(array![
- Value::Content(self.term.clone()),
- Value::Content(self.description.clone()),
- ])
- }
-}
-
-castable! {
- TermItem,
- array: Array => {
- let mut iter = array.into_iter();
- let (term, description) = match (iter.next(), iter.next(), iter.next()) {
- (Some(a), Some(b), None) => (a.cast()?, b.cast()?),
- _ => Err("array must contain exactly two entries")?,
- };
- Self { term, description }
- },
-}