Fix enum parsing and allow zero in numberings

Fixes #353.
This commit is contained in:
Laurenz 2023-03-27 11:58:17 +02:00
parent 10d8fd9661
commit a5c88c19b3
10 changed files with 51 additions and 42 deletions

View File

@ -100,8 +100,8 @@ pub struct EnumElem {
/// [Ahead], /// [Ahead],
/// ) /// )
/// ``` /// ```
#[default(NonZeroUsize::ONE)] #[default(1)]
pub start: NonZeroUsize, pub start: usize,
/// Whether to display the full numbering, including the numbers of /// Whether to display the full numbering, including the numbers of
/// all parent enumerations. /// all parent enumerations.
@ -225,7 +225,7 @@ impl Layout for EnumElem {
pub struct EnumItem { pub struct EnumItem {
/// The item's number. /// The item's number.
#[positional] #[positional]
pub number: Option<NonZeroUsize>, pub number: Option<usize>,
/// The item's body. /// The item's body.
#[required] #[required]
@ -245,11 +245,11 @@ cast_from_value! {
v: Content => v.to::<Self>().cloned().unwrap_or_else(|| Self::new(v.clone())), v: Content => v.to::<Self>().cloned().unwrap_or_else(|| Self::new(v.clone())),
} }
struct Parent(NonZeroUsize); struct Parent(usize);
cast_from_value! { cast_from_value! {
Parent, Parent,
v: NonZeroUsize => Self(v), v: usize => Self(v),
} }
cast_to_value! { cast_to_value! {
@ -257,7 +257,7 @@ cast_to_value! {
} }
impl Fold for Parent { impl Fold for Parent {
type Output = Vec<NonZeroUsize>; type Output = Vec<usize>;
fn fold(self, mut outer: Self::Output) -> Self::Output { fn fold(self, mut outer: Self::Output) -> Self::Output {
outer.push(self.0); outer.push(self.0);

View File

@ -372,8 +372,8 @@ impl Counter {
) -> SourceResult<EcoVec<(CounterState, NonZeroUsize)>> { ) -> SourceResult<EcoVec<(CounterState, NonZeroUsize)>> {
let mut vt = Vt { world, tracer, provider, introspector }; let mut vt = Vt { world, tracer, provider, introspector };
let mut state = CounterState(match &self.0 { let mut state = CounterState(match &self.0 {
CounterKey::Selector(_) => smallvec![], CounterKey::Selector(_) => smallvec![0],
_ => smallvec![NonZeroUsize::ONE], _ => smallvec![1],
}); });
let mut page = NonZeroUsize::ONE; let mut page = NonZeroUsize::ONE;
let mut stops = eco_vec![(state.clone(), page)]; let mut stops = eco_vec![(state.clone(), page)];
@ -506,7 +506,7 @@ pub trait Count {
/// Counts through elements with different levels. /// Counts through elements with different levels.
#[derive(Debug, Clone, PartialEq, Hash)] #[derive(Debug, Clone, PartialEq, Hash)]
pub struct CounterState(pub SmallVec<[NonZeroUsize; 3]>); pub struct CounterState(pub SmallVec<[usize; 3]>);
impl CounterState { impl CounterState {
/// Advance the counter and return the numbers for the given heading. /// Advance the counter and return the numbers for the given heading.
@ -534,13 +534,13 @@ impl CounterState {
} }
while self.0.len() < level { while self.0.len() < level {
self.0.push(NonZeroUsize::ONE); self.0.push(1);
} }
} }
/// Get the first number of the state. /// Get the first number of the state.
pub fn first(&self) -> NonZeroUsize { pub fn first(&self) -> usize {
self.0.first().copied().unwrap_or(NonZeroUsize::ONE) self.0.first().copied().unwrap_or(1)
} }
/// Display the counter state with a numbering. /// Display the counter state with a numbering.
@ -551,7 +551,7 @@ impl CounterState {
cast_from_value! { cast_from_value! {
CounterState, CounterState,
num: NonZeroUsize => Self(smallvec![num]), num: usize => Self(smallvec![num]),
array: Array => Self(array array: Array => Self(array
.into_iter() .into_iter()
.map(Value::cast) .map(Value::cast)

View File

@ -62,7 +62,7 @@ pub fn numbering(
/// If `numbering` is a pattern and more numbers than counting symbols are /// If `numbering` is a pattern and more numbers than counting symbols are
/// given, the last counting symbol with its prefix is repeated. /// given, the last counting symbol with its prefix is repeated.
#[variadic] #[variadic]
numbers: Vec<NonZeroUsize>, numbers: Vec<usize>,
) -> Value { ) -> Value {
numbering.apply_vm(vm, &numbers)? numbering.apply_vm(vm, &numbers)?
} }
@ -78,25 +78,23 @@ pub enum Numbering {
impl Numbering { impl Numbering {
/// Apply the pattern to the given numbers. /// Apply the pattern to the given numbers.
pub fn apply_vm(&self, vm: &mut Vm, numbers: &[NonZeroUsize]) -> SourceResult<Value> { pub fn apply_vm(&self, vm: &mut Vm, numbers: &[usize]) -> SourceResult<Value> {
Ok(match self { Ok(match self {
Self::Pattern(pattern) => Value::Str(pattern.apply(numbers).into()), Self::Pattern(pattern) => Value::Str(pattern.apply(numbers).into()),
Self::Func(func) => { Self::Func(func) => {
let args = Args::new( let args =
func.span(), Args::new(func.span(), numbers.iter().map(|&n| Value::Int(n as i64)));
numbers.iter().map(|n| Value::Int(n.get() as i64)),
);
func.call_vm(vm, args)? func.call_vm(vm, args)?
} }
}) })
} }
/// Apply the pattern to the given numbers. /// Apply the pattern to the given numbers.
pub fn apply_vt(&self, vt: &mut Vt, numbers: &[NonZeroUsize]) -> SourceResult<Value> { pub fn apply_vt(&self, vt: &mut Vt, numbers: &[usize]) -> SourceResult<Value> {
Ok(match self { Ok(match self {
Self::Pattern(pattern) => Value::Str(pattern.apply(numbers).into()), Self::Pattern(pattern) => Value::Str(pattern.apply(numbers).into()),
Self::Func(func) => { 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 { impl NumberingPattern {
/// Apply the pattern to the given number. /// 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 fmt = EcoString::new();
let mut numbers = numbers.into_iter(); let mut numbers = numbers.into_iter();
@ -179,7 +177,7 @@ impl NumberingPattern {
} }
/// Apply only the k-th segment of the pattern to a number. /// 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(); let mut fmt = EcoString::new();
if let Some((prefix, _, _)) = self.pieces.first() { if let Some((prefix, _, _)) = self.pieces.first() {
fmt.push_str(prefix); fmt.push_str(prefix);
@ -282,13 +280,16 @@ impl NumberingKind {
} }
/// Apply the numbering to the given number. /// Apply the numbering to the given number.
pub fn apply(self, n: NonZeroUsize, case: Case) -> EcoString { pub fn apply(self, mut n: usize, case: Case) -> EcoString {
let mut n = n.get();
match self { match self {
Self::Arabic => { Self::Arabic => {
eco_format!("{n}") eco_format!("{n}")
} }
Self::Letter => { Self::Letter => {
if n == 0 {
return '-'.into();
}
n -= 1; n -= 1;
let mut letters = vec![]; let mut letters = vec![];
@ -308,6 +309,10 @@ impl NumberingKind {
String::from_utf8(letters).unwrap().into() String::from_utf8(letters).unwrap().into()
} }
Self::Roman => { Self::Roman => {
if n == 0 {
return 'N'.into();
}
// Adapted from Yann Villessuzanne's roman.rs under the // Adapted from Yann Villessuzanne's roman.rs under the
// Unlicense, at https://github.com/linfir/roman.rs/ // Unlicense, at https://github.com/linfir/roman.rs/
let mut fmt = EcoString::new(); let mut fmt = EcoString::new();
@ -347,6 +352,10 @@ impl NumberingKind {
fmt fmt
} }
Self::Symbol => { Self::Symbol => {
if n == 0 {
return '-'.into();
}
const SYMBOLS: &[char] = &['*', '†', '‡', '§', '¶', '‖']; const SYMBOLS: &[char] = &['*', '†', '‡', '§', '¶', '‖'];
let symbol = SYMBOLS[(n - 1) % SYMBOLS.len()]; let symbol = SYMBOLS[(n - 1) % SYMBOLS.len()];
let amount = ((n - 1) / SYMBOLS.len()) + 1; let amount = ((n - 1) / SYMBOLS.len()) + 1;

View File

@ -74,7 +74,7 @@ pub struct LangItems {
/// An item in a bullet list: `- ...`. /// An item in a bullet list: `- ...`.
pub list_item: fn(body: Content) -> Content, pub list_item: fn(body: Content) -> Content,
/// An item in an enumeration (numbered list): `+ ...` or `1. ...`. /// An item in an enumeration (numbered list): `+ ...` or `1. ...`.
pub enum_item: fn(number: Option<NonZeroUsize>, body: Content) -> Content, pub enum_item: fn(number: Option<usize>, body: Content) -> Content,
/// An item in a term list: `/ Term: Details`. /// An item in a term list: `/ Term: Details`.
pub term_item: fn(term: Content, description: Content) -> Content, pub term_item: fn(term: Content, description: Content) -> Content,
/// A mathematical equation: `$x$`, `$ x^2 $`. /// A mathematical equation: `$x$`, `$ x^2 $`.

View File

@ -681,7 +681,7 @@ node! {
impl EnumItem { impl EnumItem {
/// The explicit numbering, if any: `23.`. /// The explicit numbering, if any: `23.`.
pub fn number(&self) -> Option<NonZeroUsize> { pub fn number(&self) -> Option<usize> {
self.0.children().find_map(|node| match node.kind() { self.0.children().find_map(|node| match node.kind() {
SyntaxKind::EnumMarker => node.text().trim_end_matches('.').parse().ok(), SyntaxKind::EnumMarker => node.text().trim_end_matches('.').parse().ok(),
_ => Option::None, _ => Option::None,

View File

@ -170,7 +170,6 @@ impl Lexer<'_> {
'`' => self.raw(), '`' => self.raw(),
'h' if self.s.eat_if("ttp://") => self.link(), 'h' if self.s.eat_if("ttp://") => self.link(),
'h' if self.s.eat_if("ttps://") => 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(), '<' if self.s.at(is_id_continue) => self.label(),
'@' => self.ref_marker(), '@' => self.ref_marker(),
@ -200,6 +199,7 @@ impl Lexer<'_> {
'-' if self.space_or_end() => SyntaxKind::ListMarker, '-' if self.space_or_end() => SyntaxKind::ListMarker,
'+' if self.space_or_end() => SyntaxKind::EnumMarker, '+' if self.space_or_end() => SyntaxKind::EnumMarker,
'/' if self.space_or_end() => SyntaxKind::TermMarker, '/' if self.space_or_end() => SyntaxKind::TermMarker,
'0'..='9' => self.numbering(start),
_ => self.text(), _ => self.text(),
} }
@ -284,12 +284,8 @@ impl Lexer<'_> {
self.s.eat_while(char::is_ascii_digit); self.s.eat_while(char::is_ascii_digit);
let read = self.s.from(start); let read = self.s.from(start);
if self.s.eat_if('.') { if self.s.eat_if('.') && self.space_or_end() {
if let Ok(number) = read.parse::<usize>() { if read.parse::<usize>().is_ok() {
if number == 0 {
return self.error("must be positive");
}
return SyntaxKind::EnumMarker; return SyntaxKind::EnumMarker;
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@ -4,6 +4,7 @@
#enum[Embrace][Extend][Extinguish] #enum[Embrace][Extend][Extinguish]
--- ---
0. Before first!
1. First. 1. First.
2. Indented 2. Indented
@ -21,8 +22,15 @@
+ Numbered List + Numbered List
/ Term: List / Term: List
---
// In the line.
1.2 \
This is 0. \
See 0.3. \
--- ---
// Edge cases. // Edge cases.
+ +
Empty Empty \
+Nope +Nope \
a + 0.

View File

@ -1,7 +1,7 @@
// Test integrated numbering patterns. // Test integrated numbering patterns.
--- ---
#for i in range(1, 9) { #for i in range(0, 9) {
numbering("*", i) numbering("*", i)
[ and ] [ and ]
numbering("I.a", i, i) numbering("I.a", i, i)
@ -10,9 +10,5 @@
} }
--- ---
// Error: 17-18 number must be positive // Error: 17-19 number must be at least zero
#numbering("1", 0)
---
// Error: 17-19 number must be positive
#numbering("1", -1) #numbering("1", -1)