diff --git a/library/src/layout/enum.rs b/library/src/layout/enum.rs index 1be57d4ce..ec4fbc184 100644 --- a/library/src/layout/enum.rs +++ b/library/src/layout/enum.rs @@ -100,8 +100,8 @@ pub struct EnumElem { /// [Ahead], /// ) /// ``` - #[default(NonZeroUsize::ONE)] - pub start: NonZeroUsize, + #[default(1)] + pub start: usize, /// Whether to display the full numbering, including the numbers of /// all parent enumerations. @@ -225,7 +225,7 @@ impl Layout for EnumElem { pub struct EnumItem { /// The item's number. #[positional] - pub number: Option, + pub number: Option, /// The item's body. #[required] @@ -245,11 +245,11 @@ cast_from_value! { v: Content => v.to::().cloned().unwrap_or_else(|| Self::new(v.clone())), } -struct Parent(NonZeroUsize); +struct Parent(usize); cast_from_value! { Parent, - v: NonZeroUsize => Self(v), + v: usize => Self(v), } cast_to_value! { @@ -257,7 +257,7 @@ cast_to_value! { } impl Fold for Parent { - type Output = Vec; + type Output = Vec; fn fold(self, mut outer: Self::Output) -> Self::Output { outer.push(self.0); diff --git a/library/src/meta/counter.rs b/library/src/meta/counter.rs index 0ebf144cd..fb656c766 100644 --- a/library/src/meta/counter.rs +++ b/library/src/meta/counter.rs @@ -372,8 +372,8 @@ impl Counter { ) -> SourceResult> { let mut vt = Vt { world, tracer, provider, introspector }; let mut state = CounterState(match &self.0 { - CounterKey::Selector(_) => smallvec![], - _ => smallvec![NonZeroUsize::ONE], + CounterKey::Selector(_) => smallvec![0], + _ => smallvec![1], }); let mut page = NonZeroUsize::ONE; let mut stops = eco_vec![(state.clone(), page)]; @@ -506,7 +506,7 @@ pub trait Count { /// Counts through elements with different levels. #[derive(Debug, Clone, PartialEq, Hash)] -pub struct CounterState(pub SmallVec<[NonZeroUsize; 3]>); +pub struct CounterState(pub SmallVec<[usize; 3]>); impl CounterState { /// Advance the counter and return the numbers for the given heading. @@ -534,13 +534,13 @@ impl CounterState { } while self.0.len() < level { - self.0.push(NonZeroUsize::ONE); + self.0.push(1); } } /// Get the first number of the state. - pub fn first(&self) -> NonZeroUsize { - self.0.first().copied().unwrap_or(NonZeroUsize::ONE) + pub fn first(&self) -> usize { + self.0.first().copied().unwrap_or(1) } /// Display the counter state with a numbering. @@ -551,7 +551,7 @@ impl CounterState { cast_from_value! { CounterState, - num: NonZeroUsize => Self(smallvec![num]), + num: usize => Self(smallvec![num]), array: Array => Self(array .into_iter() .map(Value::cast) diff --git a/library/src/meta/numbering.rs b/library/src/meta/numbering.rs index 6facb8338..0827e1606 100644 --- a/library/src/meta/numbering.rs +++ b/library/src/meta/numbering.rs @@ -62,7 +62,7 @@ pub fn numbering( /// If `numbering` is a pattern and more numbers than counting symbols are /// given, the last counting symbol with its prefix is repeated. #[variadic] - numbers: Vec, + numbers: Vec, ) -> Value { numbering.apply_vm(vm, &numbers)? } @@ -78,25 +78,23 @@ pub enum Numbering { impl Numbering { /// Apply the pattern to the given numbers. - pub fn apply_vm(&self, vm: &mut Vm, numbers: &[NonZeroUsize]) -> SourceResult { + pub fn apply_vm(&self, vm: &mut Vm, numbers: &[usize]) -> SourceResult { Ok(match self { Self::Pattern(pattern) => Value::Str(pattern.apply(numbers).into()), Self::Func(func) => { - let args = Args::new( - func.span(), - numbers.iter().map(|n| Value::Int(n.get() as i64)), - ); + let args = + Args::new(func.span(), numbers.iter().map(|&n| Value::Int(n as i64))); func.call_vm(vm, args)? } }) } /// Apply the pattern to the given numbers. - pub fn apply_vt(&self, vt: &mut Vt, numbers: &[NonZeroUsize]) -> SourceResult { + pub fn apply_vt(&self, vt: &mut Vt, numbers: &[usize]) -> SourceResult { Ok(match self { Self::Pattern(pattern) => Value::Str(pattern.apply(numbers).into()), Self::Func(func) => { - func.call_vt(vt, numbers.iter().map(|n| Value::Int(n.get() as i64)))? + func.call_vt(vt, numbers.iter().map(|&n| Value::Int(n as i64)))? } }) } @@ -147,7 +145,7 @@ pub struct NumberingPattern { impl NumberingPattern { /// Apply the pattern to the given number. - pub fn apply(&self, numbers: &[NonZeroUsize]) -> EcoString { + pub fn apply(&self, numbers: &[usize]) -> EcoString { let mut fmt = EcoString::new(); let mut numbers = numbers.into_iter(); @@ -179,7 +177,7 @@ impl NumberingPattern { } /// Apply only the k-th segment of the pattern to a number. - pub fn apply_kth(&self, k: usize, number: NonZeroUsize) -> EcoString { + pub fn apply_kth(&self, k: usize, number: usize) -> EcoString { let mut fmt = EcoString::new(); if let Some((prefix, _, _)) = self.pieces.first() { fmt.push_str(prefix); @@ -282,13 +280,16 @@ impl NumberingKind { } /// Apply the numbering to the given number. - pub fn apply(self, n: NonZeroUsize, case: Case) -> EcoString { - let mut n = n.get(); + pub fn apply(self, mut n: usize, case: Case) -> EcoString { match self { Self::Arabic => { eco_format!("{n}") } Self::Letter => { + if n == 0 { + return '-'.into(); + } + n -= 1; let mut letters = vec![]; @@ -308,6 +309,10 @@ impl NumberingKind { String::from_utf8(letters).unwrap().into() } Self::Roman => { + if n == 0 { + return 'N'.into(); + } + // Adapted from Yann Villessuzanne's roman.rs under the // Unlicense, at https://github.com/linfir/roman.rs/ let mut fmt = EcoString::new(); @@ -347,6 +352,10 @@ impl NumberingKind { fmt } Self::Symbol => { + if n == 0 { + return '-'.into(); + } + const SYMBOLS: &[char] = &['*', '†', '‡', '§', '¶', '‖']; let symbol = SYMBOLS[(n - 1) % SYMBOLS.len()]; let amount = ((n - 1) / SYMBOLS.len()) + 1; diff --git a/src/eval/library.rs b/src/eval/library.rs index 85d5647b9..5b0ff8e6d 100644 --- a/src/eval/library.rs +++ b/src/eval/library.rs @@ -74,7 +74,7 @@ pub struct LangItems { /// An item in a bullet list: `- ...`. pub list_item: fn(body: Content) -> Content, /// An item in an enumeration (numbered list): `+ ...` or `1. ...`. - pub enum_item: fn(number: Option, body: Content) -> Content, + pub enum_item: fn(number: Option, body: Content) -> Content, /// An item in a term list: `/ Term: Details`. pub term_item: fn(term: Content, description: Content) -> Content, /// A mathematical equation: `$x$`, `$ x^2 $`. diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index bd8fa230b..b064da88b 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -681,7 +681,7 @@ node! { impl EnumItem { /// The explicit numbering, if any: `23.`. - pub fn number(&self) -> Option { + pub fn number(&self) -> Option { self.0.children().find_map(|node| match node.kind() { SyntaxKind::EnumMarker => node.text().trim_end_matches('.').parse().ok(), _ => Option::None, diff --git a/src/syntax/lexer.rs b/src/syntax/lexer.rs index 90a10f522..c46fa37bc 100644 --- a/src/syntax/lexer.rs +++ b/src/syntax/lexer.rs @@ -170,7 +170,6 @@ impl Lexer<'_> { '`' => self.raw(), 'h' if self.s.eat_if("ttp://") => self.link(), 'h' if self.s.eat_if("ttps://") => self.link(), - '0'..='9' => self.numbering(start), '<' if self.s.at(is_id_continue) => self.label(), '@' => self.ref_marker(), @@ -200,6 +199,7 @@ impl Lexer<'_> { '-' if self.space_or_end() => SyntaxKind::ListMarker, '+' if self.space_or_end() => SyntaxKind::EnumMarker, '/' if self.space_or_end() => SyntaxKind::TermMarker, + '0'..='9' => self.numbering(start), _ => self.text(), } @@ -284,12 +284,8 @@ impl Lexer<'_> { self.s.eat_while(char::is_ascii_digit); let read = self.s.from(start); - if self.s.eat_if('.') { - if let Ok(number) = read.parse::() { - if number == 0 { - return self.error("must be positive"); - } - + if self.s.eat_if('.') && self.space_or_end() { + if read.parse::().is_ok() { return SyntaxKind::EnumMarker; } } diff --git a/tests/ref/layout/enum.png b/tests/ref/layout/enum.png index d80a584c4..a52ad989e 100644 Binary files a/tests/ref/layout/enum.png and b/tests/ref/layout/enum.png differ diff --git a/tests/ref/meta/numbering.png b/tests/ref/meta/numbering.png index 93b9f3948..036889e3c 100644 Binary files a/tests/ref/meta/numbering.png and b/tests/ref/meta/numbering.png differ diff --git a/tests/typ/layout/enum.typ b/tests/typ/layout/enum.typ index 9512e1b55..341afed48 100644 --- a/tests/typ/layout/enum.typ +++ b/tests/typ/layout/enum.typ @@ -4,6 +4,7 @@ #enum[Embrace][Extend][Extinguish] --- +0. Before first! 1. First. 2. Indented @@ -21,8 +22,15 @@ + Numbered List / Term: List +--- +// In the line. +1.2 \ +This is 0. \ +See 0.3. \ + --- // Edge cases. + -Empty -+Nope +Empty \ ++Nope \ +a + 0. diff --git a/tests/typ/meta/numbering.typ b/tests/typ/meta/numbering.typ index 63e453623..ecc3bdbf9 100644 --- a/tests/typ/meta/numbering.typ +++ b/tests/typ/meta/numbering.typ @@ -1,7 +1,7 @@ // Test integrated numbering patterns. --- -#for i in range(1, 9) { +#for i in range(0, 9) { numbering("*", i) [ and ] numbering("I.a", i, i) @@ -10,9 +10,5 @@ } --- -// Error: 17-18 number must be positive -#numbering("1", 0) - ---- -// Error: 17-19 number must be positive +// Error: 17-19 number must be at least zero #numbering("1", -1)