From 0f7415f356424cf7bd8d68a9612ef6ceb6086a17 Mon Sep 17 00:00:00 2001 From: Nash Aschenbath Date: Sat, 31 Oct 2026 16:39:38 +0800 Subject: [PATCH 1/4] fix(odt): restart a continued sublist when its parent item advances Word saves nested lists as ODT with text:continue-numbering on every level, and the level-2 counter carried over from the previous parent's sublist, so item 2's first child read 2.3 instead of 2.1. A new item now clears the counters of the levels below it, as ODF and LibreOffice do. Co-Authored-By: Claude Opus 5.5 --- src/formats/odf/mod.rs | 28 ++++++++++++++++++++++++++++ src/formats/odf/text.rs | 6 ++++++ 2 files changed, 34 insertions(+) diff --git a/src/formats/odf/mod.rs b/src/formats/odf/mod.rs index e4a64553..f06800af 100644 --- a/src/formats/odf/mod.rs +++ b/src/formats/odf/mod.rs @@ -236,6 +236,34 @@ mod tests { ); } + #[test] + fn continued_sublist_restarts_when_its_parent_advances() { + // Word saves nested lists as ODT with continue-numbering on every + // level; item 2's sublist must read 2.1, not 2.3. + let content = r#" + + + + + + + Subject + Duty A + Duty B + + Price + Payment + + + "#; + let md = crate::document_to_markdown(&parse(&odt_with_content(content)).unwrap()); + assert!(md.contains("1.2. Duty B"), "{md}"); + assert!(md.contains("2.1. Payment"), "{md}"); + } + #[test] fn repeated_rows_parse_content_once() { // A note inside a repeated row must register once, not per copy. diff --git a/src/formats/odf/text.rs b/src/formats/odf/text.rs index da64da40..8236344f 100644 --- a/src/formats/odf/text.rs +++ b/src/formats/odf/text.rs @@ -195,6 +195,12 @@ fn parse_list( } } let number = current.start.saturating_add(current.items.len() as u64); + if !header { + // A new item at this level restarts the levels below it, so a + // `continue-numbering` sublist under it starts afresh. + let style = style_name.unwrap_or(""); + ctx.list_counters.borrow_mut().retain(|(s, d), _| s != style || *d <= depth); + } let mut chain = ancestors.to_vec(); chain.push(number); let mut item_blocks = Vec::new(); From d8f41c65ea650bffa19c863e1b5bef165a111762 Mon Sep 17 00:00:00 2001 From: Nash Aschenbath Date: Sat, 31 Oct 2026 17:48:09 +0800 Subject: [PATCH 2/4] fix(odt): clear deeper counters of every list style when a parent advances Co-Authored-By: Claude Opus 5.5 --- src/formats/odf/text.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/formats/odf/text.rs b/src/formats/odf/text.rs index 8236344f..bf888826 100644 --- a/src/formats/odf/text.rs +++ b/src/formats/odf/text.rs @@ -198,8 +198,8 @@ fn parse_list( if !header { // A new item at this level restarts the levels below it, so a // `continue-numbering` sublist under it starts afresh. - let style = style_name.unwrap_or(""); - ctx.list_counters.borrow_mut().retain(|(s, d), _| s != style || *d <= depth); + // Every style: a sublist may name its own list style. + ctx.list_counters.borrow_mut().retain(|(_, d), _| *d <= depth); } let mut chain = ancestors.to_vec(); chain.push(number); From f5f8ab42c571c83729b1eef0c8ab4dec347df1ef Mon Sep 17 00:00:00 2001 From: Nash Aschenbath Date: Sat, 31 Oct 2026 18:04:03 +0800 Subject: [PATCH 3/4] test(odt): cover a sublist with its own list style Co-Authored-By: Claude Opus 5.5 --- src/formats/odf/mod.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/formats/odf/mod.rs b/src/formats/odf/mod.rs index f06800af..90c97961 100644 --- a/src/formats/odf/mod.rs +++ b/src/formats/odf/mod.rs @@ -262,6 +262,22 @@ mod tests { let md = crate::document_to_markdown(&parse(&odt_with_content(content)).unwrap()); assert!(md.contains("1.2. Duty B"), "{md}"); assert!(md.contains("2.1. Payment"), "{md}"); + + // The same when each sublist names its own list style. + let styled = content + .replace( + "", + r#" + + "#, + ) + .replace( + r#""#, + r#""#, + ); + let md = crate::document_to_markdown(&parse(&odt_with_content(&styled)).unwrap()); + assert!(md.contains("Payment"), "{md}"); + assert!(!md.contains("3. Payment"), "{md}"); } #[test] From b96404a3c474bdc586ddcc7dce8408317d51f4fb Mon Sep 17 00:00:00 2001 From: Nash Aschenbath Date: Sat, 31 Oct 2026 21:28:18 +0800 Subject: [PATCH 4/4] test(odt): assert the restarted number of a styled sublist Co-Authored-By: Claude Opus 5.5 --- src/formats/odf/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/formats/odf/mod.rs b/src/formats/odf/mod.rs index 90c97961..85d95910 100644 --- a/src/formats/odf/mod.rs +++ b/src/formats/odf/mod.rs @@ -276,7 +276,7 @@ mod tests { r#""#, ); let md = crate::document_to_markdown(&parse(&odt_with_content(&styled)).unwrap()); - assert!(md.contains("Payment"), "{md}"); + assert!(md.contains("1. Payment"), "{md}"); assert!(!md.contains("3. Payment"), "{md}"); }