From ba294e2670814243122c9e4f560e0f21a4bec13d Mon Sep 17 00:00:00 2001 From: Laurenz Date: Wed, 21 Dec 2022 12:50:33 +0100 Subject: [PATCH] Split up list type into three separate types and document them --- library/src/basics/desc.rs | 183 +++++++++++++++++ library/src/basics/enum.rs | 251 ++++++++++++++++++++++ library/src/basics/heading.rs | 4 +- library/src/basics/list.rs | 320 ++++++++--------------------- library/src/basics/mod.rs | 5 + library/src/compute/data.rs | 8 +- library/src/compute/foundations.rs | 3 +- library/src/layout/mod.rs | 55 ++++- library/src/lib.rs | 8 +- library/src/text/deco.rs | 3 + library/src/text/mod.rs | 20 +- library/src/text/raw.rs | 4 +- src/model/eval.rs | 4 +- src/model/library.rs | 2 +- src/syntax/ast.rs | 4 +- tests/ref/compiler/construct.png | Bin 6043 -> 6118 bytes tests/typ/basics/desc.typ | 8 +- tests/typ/basics/enum.typ | 20 +- tests/typ/basics/list.typ | 2 +- tests/typ/compiler/construct.typ | 8 +- tests/typ/compiler/set.typ | 2 +- 21 files changed, 623 insertions(+), 291 deletions(-) create mode 100644 library/src/basics/desc.rs create mode 100644 library/src/basics/enum.rs diff --git a/library/src/basics/desc.rs b/library/src/basics/desc.rs new file mode 100644 index 000000000..2c764c595 --- /dev/null +++ b/library/src/basics/desc.rs @@ -0,0 +1,183 @@ +use crate::layout::{BlockNode, GridNode, HNode, ParNode, Spacing, TrackSizing}; +use crate::prelude::*; +use crate::text::{SpaceNode, TextNode}; + +/// # Description 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 description list +/// item. +/// +/// ## Example +/// ``` +/// / Ligature: A merged glyph. +/// / Kerning: A spacing adjustment +/// between two adjacent letters. +/// ``` +/// +/// ## Parameters +/// - items: Content (positional, variadic) +/// The descrition list's children. +/// +/// When using the description list syntax, adjacents items are automatically +/// collected into description 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 [description list +/// spacing](@desc/spacing). If it is `{true}`, they use normal +/// [leading](@par/leading) instead. This makes the description list more +/// compact, which can look better if the items are short. +/// +/// ### Example +/// ``` +/// / Fact: If a description 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 DescNode { + /// If true, the items are separated by leading instead of list spacing. + pub tight: bool, + /// The individual bulleted or numbered items. + pub items: StyleVec, +} + +#[node] +impl DescNode { + /// The indentation of each item's term. + #[property(resolve)] + pub const INDENT: Length = Length::zero(); + + /// The hanging indent of the description. + /// + /// # Example + /// ``` + /// #set desc(hanging-indent: 0pt) + /// / Term: This description 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) description list. + /// + /// If set to `{auto}` uses the spacing [below blocks](@block/below). + pub const SPACING: Smart = Smart::Auto; + + fn construct(_: &Vm, args: &mut Args) -> SourceResult { + Ok(Self { + tight: args.named("tight")?.unwrap_or(true), + items: args.all()?.into_iter().collect(), + } + .pack()) + } + + fn field(&self, name: &str) -> Option { + match name { + "tight" => Some(Value::Bool(self.tight)), + "items" => { + Some(Value::Array(self.items.items().map(|item| item.encode()).collect())) + } + _ => None, + } + } +} + +impl Layout for DescNode { + fn layout( + &self, + vt: &mut Vt, + styles: StyleChain, + regions: Regions, + ) -> SourceResult { + 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 description list item. +#[derive(Debug, Clone, Hash)] +pub struct DescItem { + /// The term described by the list item. + pub term: Content, + /// The description of the term. + pub description: Content, +} + +impl DescItem { + /// Encode the item into a value. + fn encode(&self) -> Value { + Value::Array(array![ + Value::Content(self.term.clone()), + Value::Content(self.description.clone()), + ]) + } +} + +castable! { + DescItem, + 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 } + }, +} diff --git a/library/src/basics/enum.rs b/library/src/basics/enum.rs new file mode 100644 index 000000000..f0ca217ac --- /dev/null +++ b/library/src/basics/enum.rs @@ -0,0 +1,251 @@ +use std::str::FromStr; + +use crate::compute::NumberingPattern; +use crate::layout::{BlockNode, GridNode, ParNode, Spacing, TrackSizing}; +use crate::prelude::*; +use crate::text::TextNode; + +/// # Enumeration +/// An ordered list. +/// +/// Displays a sequence of items vertically and numbers them consecutively. +/// +/// ## 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. +/// +/// ## 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] +/// ``` +/// +/// ## Parameters +/// - items: Content (positional, variadic) +/// The enumeration's children. +/// +/// When using the enum syntax, adjacents 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](@enum/spacing). If it is `{true}`, they use normal +/// [leading](@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, Content)>, +} + +#[node] +impl EnumNode { + /// How to number the enumeration. Accepts a + /// [numbering pattern](@numbering). + /// + /// # Example + /// ``` + /// #set enum(numbering: "(a)") + /// + /// + Different + /// + Numbering + /// + Style + /// ``` + #[property(referenced)] + pub const NUMBERING: EnumNumbering = + EnumNumbering::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](@block/below). + pub const SPACING: Smart = Smart::Auto; + + fn construct(_: &Vm, args: &mut Args) -> SourceResult { + 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 { + 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 { + 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.resolve(vt, number)?; + 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) + } +} + +/// How to number an enumeration. +#[derive(Debug, Clone, Hash)] +pub enum EnumNumbering { + /// A pattern with prefix, numbering, lower / upper case and suffix. + Pattern(NumberingPattern), + /// A closure mapping from an item's number to content. + Func(Func, Span), +} + +impl EnumNumbering { + /// Resolve the marker based on the number. + pub fn resolve(&self, vt: &Vt, number: NonZeroUsize) -> SourceResult { + Ok(match self { + Self::Pattern(pattern) => TextNode::packed(pattern.apply(&[number])), + Self::Func(func, span) => { + let args = Args::new(*span, [Value::Int(number.get() as i64)]); + func.call_detached(vt.world(), args)?.display() + } + }) + } +} + +impl Cast> for EnumNumbering { + fn is(value: &Spanned) -> bool { + matches!(&value.v, Value::Content(_) | Value::Func(_)) + } + + fn cast(value: Spanned) -> StrResult { + match value.v { + Value::Str(v) => Ok(Self::Pattern(v.parse()?)), + Value::Func(v) => Ok(Self::Func(v, value.span)), + v => Self::error(v), + } + } + + fn describe() -> CastInfo { + CastInfo::Union(vec![CastInfo::Type("string"), CastInfo::Type("function")]) + } +} diff --git a/library/src/basics/heading.rs b/library/src/basics/heading.rs index 12a6ac665..925d23a20 100644 --- a/library/src/basics/heading.rs +++ b/library/src/basics/heading.rs @@ -11,8 +11,8 @@ use crate::text::{SpaceNode, TextNode, TextSize}; /// 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.) Top-level heading indicates a top-level section of the document (not -/// the document's title). +/// 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 diff --git a/library/src/basics/list.rs b/library/src/basics/list.rs index ca60576c7..eaba65943 100644 --- a/library/src/basics/list.rs +++ b/library/src/basics/list.rs @@ -1,28 +1,67 @@ -use crate::compute::NumberingPattern; -use crate::layout::{BlockNode, GridNode, HNode, ParNode, Spacing, TrackSizing}; +use crate::layout::{BlockNode, GridNode, ParNode, Spacing, TrackSizing}; use crate::prelude::*; -use crate::text::{SpaceNode, TextNode}; +use crate::text::TextNode; /// # List -/// An unordered (bulleted) or ordered (numbered) list. +/// An unordered list. +/// +/// Displays a sequence of items vertically, with each item introduced by a +/// marker. +/// +/// ## 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. +/// +/// ## Example +/// ``` +/// - *Content* +/// - Basics +/// - Text +/// - Math +/// - Layout +/// - Visualize +/// - Meta +/// +/// - *Compute* +/// #list( +/// [Foundations], +/// [Calculate], +/// [Create], +/// [Data Loading], +/// [Utility], +/// ) +/// ``` /// /// ## Parameters /// - items: Content (positional, variadic) -/// The contents of the list items. +/// The list's children. /// -/// - start: NonZeroUsize (named) -/// Which number to start the enumeration with. -/// -/// - tight: bool (named) -/// Makes the list more compact, if enabled. This looks better if the items -/// fit into a single line each. +/// When using the list syntax, adjacents items are automatically collected +/// into lists, even through constructs like for loops. /// /// ### Example /// ``` -/// #show columns.with(2) -/// #list(tight: true)[Tight][List] -/// #colbreak() -/// #list(tight: false)[Wide][List] +/// #for letter in "ABC" [ +/// - Letter #letter +/// ] +/// ``` +/// +/// - tight: bool (named) +/// If this is `{false}`, the items are spaced apart with +/// [list spacing](@list/spacing). If it is `{true}`, they use normal +/// [leading](@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 @@ -30,88 +69,67 @@ use crate::text::{SpaceNode, TextNode}; #[func] #[capable(Layout)] #[derive(Debug, Hash)] -pub struct ListNode { +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, + pub items: StyleVec, } -/// An ordered list. -pub type EnumNode = ListNode; - -/// A description list. -pub type DescNode = ListNode; - #[node] -impl ListNode { - /// How the list is labelled. +impl ListNode { + /// The marker which introduces each element. + /// + /// # Example + /// ``` + /// #set list(marker: [--]) + /// + /// - A more classic list + /// - With en-dashes + /// ``` #[property(referenced)] - pub const LABEL: ListLabel = ListLabel::Default; - /// The indentation of each item's label. + pub const MARKER: Content = TextNode::packed('•'); + + /// The indent of each item's marker. #[property(resolve)] pub const INDENT: Length = Length::zero(); - /// The space between the label and the body of each item. + + /// The spacing between the marker and the body of each item. #[property(resolve)] - pub const BODY_INDENT: Length = Em::new(match L { - LIST | ENUM => 0.5, - DESC | _ => 1.0, - }) - .into(); + 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](@block/below). pub const SPACING: Smart = Smart::Auto; fn construct(_: &Vm, args: &mut Args) -> SourceResult { - let items = match L { - LIST => args - .all()? - .into_iter() - .map(|body| ListItem::List(Box::new(body))) - .collect(), - ENUM => { - let mut number: NonZeroUsize = - args.named("start")?.unwrap_or(NonZeroUsize::new(1).unwrap()); - args.all()? - .into_iter() - .map(|body| { - let item = ListItem::Enum(Some(number), Box::new(body)); - number = number.saturating_add(1); - item - }) - .collect() - } - DESC | _ => args - .all()? - .into_iter() - .map(|item| ListItem::Desc(Box::new(item))) - .collect(), - }; - - Ok(Self { tight: args.named("tight")?.unwrap_or(true), items }.pack()) + Ok(Self { + tight: args.named("tight")?.unwrap_or(true), + items: args.all()?.into_iter().collect(), + } + .pack()) } fn field(&self, name: &str) -> Option { match name { "tight" => Some(Value::Bool(self.tight)), - "items" => { - Some(Value::Array(self.items.items().map(|item| item.encode()).collect())) - } + "items" => Some(Value::Array( + self.items.items().cloned().map(Value::Content).collect(), + )), _ => None, } } } -impl Layout for ListNode { +impl Layout for ListNode { fn layout( &self, vt: &mut Vt, styles: StyleChain, regions: Regions, ) -> SourceResult { - let mut cells = vec![]; - let mut number = NonZeroUsize::new(1).unwrap(); - - let label = styles.get(Self::LABEL); + 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 { @@ -122,35 +140,12 @@ impl Layout for ListNode { .unwrap_or_else(|| styles.get(BlockNode::BELOW).amount) }; + let mut cells = vec![]; for (item, map) in self.items.iter() { - if let &ListItem::Enum(Some(n), _) = item { - number = n; - } - cells.push(Content::empty()); - - let label = if L == LIST || L == ENUM { - label.resolve(vt, L, number)?.styled_with_map(map.clone()) - } else { - Content::empty() - }; - - cells.push(label); + cells.push(marker.clone()); cells.push(Content::empty()); - - let body = match &item { - ListItem::List(body) => body.as_ref().clone(), - ListItem::Enum(_, body) => body.as_ref().clone(), - ListItem::Desc(item) => Content::sequence(vec![ - HNode { amount: (-body_indent).into(), weak: false }.pack(), - (item.term.clone() + TextNode::packed(':')).strong(), - SpaceNode.pack(), - item.body.clone(), - ]), - }; - - cells.push(body.styled_with_map(map.clone())); - number = number.saturating_add(1); + cells.push(item.clone().styled_with_map(map.clone())); } GridNode { @@ -166,142 +161,3 @@ impl Layout for ListNode { .layout(vt, styles, regions) } } - -/// An item in a list. -#[capable] -#[derive(Debug, Clone, Hash)] -pub enum ListItem { - /// An item of an unordered list. - List(Box), - /// An item of an ordered list. - Enum(Option, Box), - /// An item of a description list. - Desc(Box), -} - -impl ListItem { - /// What kind of item this is. - pub fn kind(&self) -> ListKind { - match self { - Self::List(_) => LIST, - Self::Enum { .. } => ENUM, - Self::Desc { .. } => DESC, - } - } - - /// Encode the item into a value. - fn encode(&self) -> Value { - match self { - Self::List(body) => Value::Content(body.as_ref().clone()), - Self::Enum(number, body) => Value::Dict(dict! { - "number" => match *number { - Some(n) => Value::Int(n.get() as i64), - None => Value::None, - }, - "body" => Value::Content(body.as_ref().clone()), - }), - Self::Desc(item) => Value::Dict(dict! { - "term" => Value::Content(item.term.clone()), - "body" => Value::Content(item.body.clone()), - }), - } - } -} - -#[node] -impl ListItem {} - -/// A description list item. -#[derive(Debug, Clone, Hash)] -pub struct DescItem { - /// The term described by the list item. - pub term: Content, - /// The description of the term. - pub body: Content, -} - -castable! { - DescItem, - mut dict: Dict => { - let term: Content = dict.take("term")?.cast()?; - let body: Content = dict.take("body")?.cast()?; - dict.finish(&["term", "body"])?; - Self { term, body } - }, -} - -/// How to label a list. -pub type ListKind = usize; - -/// An unordered list. -pub const LIST: ListKind = 0; - -/// An ordered list. -pub const ENUM: ListKind = 1; - -/// A description list. -pub const DESC: ListKind = 2; - -/// How to label a list or enumeration. -#[derive(Debug, Clone, Hash)] -pub enum ListLabel { - /// The default labelling. - Default, - /// A pattern with prefix, numbering, lower / upper case and suffix. - Pattern(NumberingPattern), - /// Bare content. - Content(Content), - /// A closure mapping from an item number to a value. - Func(Func, Span), -} - -impl ListLabel { - /// Resolve the label based on the level. - pub fn resolve( - &self, - vt: &Vt, - kind: ListKind, - number: NonZeroUsize, - ) -> SourceResult { - Ok(match self { - Self::Default => match kind { - LIST => TextNode::packed('•'), - ENUM => TextNode::packed(format_eco!("{}.", number)), - DESC | _ => panic!("description lists don't have a label"), - }, - Self::Pattern(pattern) => TextNode::packed(pattern.apply(&[number])), - Self::Content(content) => content.clone(), - Self::Func(func, span) => { - let args = Args::new(*span, [Value::Int(number.get() as i64)]); - func.call_detached(vt.world(), args)?.display() - } - }) - } -} - -impl Cast> for ListLabel { - fn is(value: &Spanned) -> bool { - matches!( - &value.v, - Value::None | Value::Str(_) | Value::Content(_) | Value::Func(_) - ) - } - - fn cast(value: Spanned) -> StrResult { - match value.v { - Value::None => Ok(Self::Content(Content::empty())), - Value::Str(v) => Ok(Self::Pattern(v.parse()?)), - Value::Content(v) => Ok(Self::Content(v)), - Value::Func(v) => Ok(Self::Func(v, value.span)), - v => Self::error(v), - } - } - - fn describe() -> CastInfo { - CastInfo::Union(vec![ - CastInfo::Type("string"), - CastInfo::Type("content"), - CastInfo::Type("function"), - ]) - } -} diff --git a/library/src/basics/mod.rs b/library/src/basics/mod.rs index 5916df6bc..431cb004f 100644 --- a/library/src/basics/mod.rs +++ b/library/src/basics/mod.rs @@ -1,9 +1,14 @@ //! Common document elements. +mod desc; +#[path = "enum.rs"] +mod enum_; mod heading; mod list; mod table; +pub use self::desc::*; +pub use self::enum_::*; pub use self::heading::*; pub use self::list::*; pub use self::table::*; diff --git a/library/src/compute/data.rs b/library/src/compute/data.rs index 3751292c4..43d067530 100644 --- a/library/src/compute/data.rs +++ b/library/src/compute/data.rs @@ -129,8 +129,7 @@ fn format_csv_error(error: csv::Error) -> String { /// ) /// #h(6pt) /// #set text(22pt, baseline: -8pt) -/// {day.temperature} -/// °{day.unit} +/// {day.temperature} °{day.unit} /// ] /// /// #forecast(json("monday.json")) @@ -180,10 +179,7 @@ fn convert_json(value: serde_json::Value) -> Value { /// Format the user-facing JSON error message. fn format_json_error(error: serde_json::Error) -> String { assert!(error.is_syntax() || error.is_eof()); - format!( - "failed to parse json file: syntax error in line {}", - error.line() - ) + format!("failed to parse json file: syntax error in line {}", error.line()) } /// # XML diff --git a/library/src/compute/foundations.rs b/library/src/compute/foundations.rs index 91c97ecc1..06795887a 100644 --- a/library/src/compute/foundations.rs +++ b/library/src/compute/foundations.rs @@ -41,7 +41,8 @@ pub fn type_(args: &mut Args) -> SourceResult { /// ``` /// { none } vs #repr(none) \ /// { "hello" } vs #repr("hello") \ -/// { (1, 2) } vs #repr((1, 2)) +/// { (1, 2) } vs #repr((1, 2)) \ +/// { [*Hi*] } vs #repr([*Hi*]) /// ``` /// /// ## Parameters diff --git a/library/src/layout/mod.rs b/library/src/layout/mod.rs index afa1344fe..bd4b04304 100644 --- a/library/src/layout/mod.rs +++ b/library/src/layout/mod.rs @@ -40,7 +40,7 @@ use typst::model::{ StyleVecBuilder, StyledNode, }; -use crate::basics::{DescNode, EnumNode, ListItem, ListNode, DESC, ENUM, LIST}; +use crate::basics::{DescItem, DescNode, EnumNode, ListNode}; use crate::meta::DocumentNode; use crate::prelude::*; use crate::shared::BehavedBuilder; @@ -589,12 +589,9 @@ impl<'a> ListBuilder<'a> { } if let Some(item) = content.to::() { - if self - .items - .items() - .next() - .map_or(true, |first| item.kind() == first.kind()) - { + if self.items.items().next().map_or(true, |first| { + std::mem::discriminant(item) == std::mem::discriminant(first) + }) { self.items.push(item.clone(), styles); self.tight &= self.staged.drain(..).all(|(t, _)| !t.is::()); return true; @@ -607,10 +604,31 @@ impl<'a> ListBuilder<'a> { fn finish(self) -> (Content, StyleChain<'a>) { let (items, shared) = self.items.finish(); let item = items.items().next().unwrap(); - let output = match item.kind() { - LIST => ListNode:: { tight: self.tight, items }.pack(), - ENUM => ListNode:: { tight: self.tight, items }.pack(), - DESC | _ => ListNode:: { tight: self.tight, items }.pack(), + let output = match item { + ListItem::List(_) => ListNode { + tight: self.tight, + items: items.map(|item| match item { + ListItem::List(item) => item.clone(), + _ => panic!("wrong list item"), + }), + } + .pack(), + ListItem::Enum(..) => EnumNode { + tight: self.tight, + items: items.map(|item| match item { + ListItem::Enum(number, body) => (*number, body.clone()), + _ => panic!("wrong list item"), + }), + } + .pack(), + ListItem::Desc(_) => DescNode { + tight: self.tight, + items: items.map(|item| match item { + ListItem::Desc(item) => item.clone(), + _ => panic!("wrong list item"), + }), + } + .pack(), }; (output, shared) } @@ -625,3 +643,18 @@ impl Default for ListBuilder<'_> { } } } + +/// An item in a list. +#[capable] +#[derive(Debug, Clone, Hash)] +pub enum ListItem { + /// An item of an unordered list. + List(Content), + /// An item of an ordered list. + Enum(Option, Content), + /// An item of a description list. + Desc(DescItem), +} + +#[node] +impl ListItem {} diff --git a/library/src/lib.rs b/library/src/lib.rs index 6b6cae17d..267277118 100644 --- a/library/src/lib.rs +++ b/library/src/lib.rs @@ -198,10 +198,10 @@ fn items() -> LangItems { link: |url| meta::LinkNode::from_url(url).pack(), ref_: |target| meta::RefNode(target).pack(), heading: |level, body| basics::HeadingNode { level, title: body }.pack(), - list_item: |body| basics::ListItem::List(Box::new(body)).pack(), - enum_item: |number, body| basics::ListItem::Enum(number, Box::new(body)).pack(), - desc_item: |term, body| { - basics::ListItem::Desc(Box::new(basics::DescItem { term, body })).pack() + list_item: |body| layout::ListItem::List(body).pack(), + enum_item: |number, body| layout::ListItem::Enum(number, body).pack(), + desc_item: |term, description| { + layout::ListItem::Desc(basics::DescItem { term, description }).pack() }, math: |children, block| math::MathNode { children, block }.pack(), math_atom: |atom| math::AtomNode(atom).pack(), diff --git a/library/src/text/deco.rs b/library/src/text/deco.rs index f74a45a21..6f454573f 100644 --- a/library/src/text/deco.rs +++ b/library/src/text/deco.rs @@ -224,6 +224,9 @@ impl StrikeNode { /// How to stroke the line. The text color and thickness are read from the /// font tables if `{auto}`. /// + /// _Note:_ Please don't use this for real redaction as you can still + /// copy paste the text. + /// /// # Example /// ``` /// This is #strike(stroke: 1.5pt + red)[very stricken through]. \ diff --git a/library/src/text/mod.rs b/library/src/text/mod.rs index 191b1fb83..4a25197db 100644 --- a/library/src/text/mod.rs +++ b/library/src/text/mod.rs @@ -43,8 +43,8 @@ use crate::prelude::*; /// ``` /// /// ## Parameters -/// - family: EcoString (positional, variadic, settable) A prioritized sequence -/// of font families. +/// - family: EcoString (positional, variadic, settable) +/// A prioritized sequence of font families. /// /// When processing text, Typst tries all specified font families in order /// until it finds a font that has the necessary glyphs. In the example below, @@ -63,8 +63,8 @@ use crate::prelude::*; /// /// ``` /// -/// - body: Content (positional, required) Content in which all text is styled -/// according to the other arguments. +/// - body: Content (positional, required) +/// Content in which all text is styled according to the other arguments. /// /// ## Category /// text @@ -141,6 +141,7 @@ impl TextNode { /// #text(weight: "light")[Light] \ /// #text(weight: "regular")[Regular] \ /// #text(weight: "medium")[Medium] \ + /// #text(weight: 500)[Medium] \ /// #text(weight: "bold")[Bold] /// ``` pub const WEIGHT: FontWeight = FontWeight::REGULAR; @@ -296,10 +297,10 @@ impl TextNode { /// - `{rtl}`: Layout text from right to left. /// /// When writing in right-to-left scripts like Arabic or Hebrew, you should - /// set the language or direction. While individual runs of text are - /// automatically layouted in the correct direction, setting the dominant - /// direction gives the bidirectional reordering algorithm the necessary - /// information to correctly place punctuation and inline objects. + /// set the [text language](@text/lang) or direction. While individual runs + /// of text are automatically layouted in the correct direction, setting the + /// dominant direction gives the bidirectional reordering algorithm the + /// necessary information to correctly place punctuation and inline objects. /// Furthermore, setting the direction affects the alignment values `start` /// and `end`, which are equivalent to `left` and `right` in `ltr` text and /// the other way around in `rtl` text. @@ -319,6 +320,9 @@ impl TextNode { /// Whether to hyphenate text to improve line breaking. When `{auto}`, text /// will be hyphenated if and only if justification is enabled. /// + /// Setting the [text language](@text/lang) ensures that the correct + /// hyphenation patterns are used. + /// /// # Example /// ``` /// #set par(justify: true) diff --git a/library/src/text/raw.rs b/library/src/text/raw.rs index 938224d04..3cf7e8e7e 100644 --- a/library/src/text/raw.rs +++ b/library/src/text/raw.rs @@ -41,8 +41,8 @@ use crate::prelude::*; /// /// ### Example /// ```` -/// // Parse numbers in raw blocks with the `mydsl` tag and -/// // sum them up. +/// // Parse numbers in raw blocks with the +/// // `mydsl` tag and sum them up. /// #show raw.where(lang: "mydsl"): it => { /// let sum = 0 /// for part in it.text.split("+") { diff --git a/src/model/eval.rs b/src/model/eval.rs index a9fa2e145..3223ef8f9 100644 --- a/src/model/eval.rs +++ b/src/model/eval.rs @@ -398,8 +398,8 @@ impl Eval for ast::DescItem { fn eval(&self, vm: &mut Vm) -> SourceResult { let term = self.term().eval(vm)?; - let body = self.body().eval(vm)?; - Ok((vm.items.desc_item)(term, body)) + let description = self.description().eval(vm)?; + Ok((vm.items.desc_item)(term, description)) } } diff --git a/src/model/library.rs b/src/model/library.rs index 8b767b0e3..41a5e8d41 100644 --- a/src/model/library.rs +++ b/src/model/library.rs @@ -64,7 +64,7 @@ pub struct LangItems { /// An item in an enumeration (ordered list): `+ ...` or `1. ...`. pub enum_item: fn(number: Option, body: Content) -> Content, /// An item in a description list: `/ Term: Details`. - pub desc_item: fn(term: Content, body: Content) -> Content, + pub desc_item: fn(term: Content, description: Content) -> Content, /// A mathematical formula: `$x$`, `$ x^2 $`. pub math: fn(children: Vec, block: bool) -> Content, /// An atom in a formula: `x`, `+`, `12`. diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index abbca5ec1..5847e8168 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -407,10 +407,10 @@ impl DescItem { } /// The description of the term. - pub fn body(&self) -> Markup { + pub fn description(&self) -> Markup { self.0 .cast_last_child() - .expect("description list item is missing body") + .expect("description list item is missing description") } } diff --git a/tests/ref/compiler/construct.png b/tests/ref/compiler/construct.png index 829b072b51dcdbf77c367116a134002d598b61c3..aac79268fd8b519b1298423b4d8a74c83eee8bfd 100644 GIT binary patch literal 6118 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 R0PgQeV7Ihx7AnD?{0-rb%HaS2 literal 6043 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 table( columns: 2, padding: 3pt, - ..it.items.map(item => (emph(item.term), item.body)).flatten(), + ..it.items.map(item => (emph(item(0)), item(1))).flatten(), ) / A: One letter diff --git a/tests/typ/basics/enum.typ b/tests/typ/basics/enum.typ index 0c62a2de9..2bec3e9e9 100644 --- a/tests/typ/basics/enum.typ +++ b/tests/typ/basics/enum.typ @@ -17,16 +17,16 @@ --- // Test label pattern. -#set enum(label: "~ A:") +#set enum(numbering: "~ A:") 1. First + Second -#set enum(label: "(*)") +#set enum(numbering: "(*)") + A + B + C -#set enum(label: "i)") +#set enum(numbering: "i)") + A + B @@ -37,17 +37,17 @@ / Desc: List --- -// Test label closure. +// Test numbering with closure. #enum( start: 4, spacing: 0.65em - 3pt, tight: false, - label: n => text(fill: (red, green, blue)(mod(n, 3)), numbering("A", n)), + numbering: n => text(fill: (red, green, blue)(mod(n, 3)), numbering("A", n)), [Red], [Green], [Blue], ) --- -#set enum(label: n => n > 1) +#set enum(numbering: n => n > 1) + A + B @@ -57,9 +57,9 @@ No enum --- -// Error: 18-20 invalid numbering pattern -#set enum(label: "") +// Error: 22-24 invalid numbering pattern +#set enum(numbering: "") --- -// Error: 18-24 invalid numbering pattern -#set enum(label: "(())") +// Error: 22-28 invalid numbering pattern +#set enum(numbering: "(())") diff --git a/tests/typ/basics/list.typ b/tests/typ/basics/list.typ index 9ed5993a2..4a9481315 100644 --- a/tests/typ/basics/list.typ +++ b/tests/typ/basics/list.typ @@ -48,6 +48,6 @@ _Shopping list_ - B with 2 tabs --- -#set list(label: [-]) +#set list(marker: [-]) - Bare hyphen - is not a list diff --git a/tests/typ/compiler/construct.typ b/tests/typ/compiler/construct.typ index df5368b7d..3ed8fed31 100644 --- a/tests/typ/compiler/construct.typ +++ b/tests/typ/compiler/construct.typ @@ -10,8 +10,8 @@ // Ensure that constructor styles win, but not over outer styles. // The outer paragraph should be right-aligned, // but the B should be center-aligned. -#set list(label: [>]) -#list(label: [--])[ +#set list(marker: [>]) +#list(marker: [--])[ #rect(width: 2cm, fill: conifer, inset: 4pt, list[A]) ] @@ -27,5 +27,5 @@ A #rect(fill: yellow, inset: 5pt, rect()) B --- // The constructor property should still work // when there are recursive show rules. -#show list: set text(blue) -#list(label: "(a)", [A], list[B]) +#show enum: set text(blue) +#enum(numbering: "(a)", [A], enum[B]) diff --git a/tests/typ/compiler/set.typ b/tests/typ/compiler/set.typ index fc5053b1d..7414ad5e1 100644 --- a/tests/typ/compiler/set.typ +++ b/tests/typ/compiler/set.typ @@ -39,7 +39,7 @@ Hello *{x}* --- // Test relative path resolving in layout phase. #let choice = ("monkey.svg", "rhino.png", "tiger.jpg") -#set enum(label: n => { +#set enum(numbering: n => { let path = "../../res/" + choice(n - 1) move(dy: -0.15em, image(path, width: 1em, height: 1em)) })