mirror of
https://github.com/storytold/wordcraft.git
synced 2026-11-09 11:50:20 -06:00
M-ODT gap 19: lists without an xml:id kept as read, given one
Older files' lists have no xml🆔 kept as read all the same, given one on save so a list going on with it can name it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
This commit is contained in:
@@ -357,8 +357,8 @@ impl<'a> Body<'a> {
|
||||
/// [`Body::feed_at`] for a top-level list (§5.3.1): kept whole, as read, when each of its
|
||||
/// paragraphs is (a paragraph's own markup can't say how its list numbers and draws its
|
||||
/// labels: a label followed by a space, a picture bullet, an unnumbered header). Not one
|
||||
/// continuing another by its `xml:id`, which WordCraft may write anew, nor one without
|
||||
/// its own, which a list continuing it can't name. `None` when it took the event.
|
||||
/// continuing another by its `xml:id`, which WordCraft may write anew (one without its own
|
||||
/// is given one, for a list continuing it to name). `None` when it took the event.
|
||||
fn whole_list(&mut self, ev: Ev, at: (usize, usize)) -> Option<Ev> {
|
||||
match &ev {
|
||||
Ev::Start(q, a)
|
||||
@@ -367,7 +367,6 @@ impl<'a> Body<'a> {
|
||||
&& self.lists.is_empty()
|
||||
&& self.para.is_none()
|
||||
&& get(a, "text:continue-list").is_none()
|
||||
&& get(a, "xml:id").is_some_and(|i| !i.is_empty())
|
||||
&& !self.raw.is_empty()
|
||||
&& self.in_text
|
||||
&& self.skip == 0
|
||||
|
||||
@@ -725,3 +725,19 @@ fn a_section_ending_in_a_list_closes_before_the_list_goes_on() {
|
||||
}
|
||||
check(&e, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_list_without_an_id_is_kept_and_given_one() {
|
||||
// Older files' lists have no `xml:id`: kept as read, one is given, and the list going on with
|
||||
// it (by `text:continue-numbering`, read as the same list) names it when written from the model.
|
||||
let content = LISTS.content.replace(r#"<text:list xml:id="list1" text:style-name="L1">"#, r#"<text:list text:style-name="L1">"#).replace(
|
||||
r#"<text:list xml:id="list2" text:continue-list="list1" text:style-name="L1">"#,
|
||||
r#"<text:list text:continue-numbering="true" text:style-name="L1">"#,
|
||||
);
|
||||
let leaked: &'static str = Box::leak(content.into_boxed_str());
|
||||
let d = import(&package(&Sample { name: "older lists", content: leaked, styles: "", meta: "" })).unwrap();
|
||||
let c = content_of(&export(&d).unwrap());
|
||||
assert!(c.contains(r#"<text:list xml:id="wclstk1" text:style-name="L1">"#) && c.contains(r#"text:label-followed-by="space""#), "{c}");
|
||||
let going_on = c.split("Between the lists.").nth(1).unwrap_or("");
|
||||
assert!(going_on.contains(r#"text:continue-list="wclstk1""#) || going_on.contains(r#"text:continue-numbering="true""#), "{c}");
|
||||
}
|
||||
|
||||
@@ -226,6 +226,8 @@ pub(super) struct Writer {
|
||||
/// The file's tracked changes markup written as read marks, written as read among the
|
||||
/// regions (each once).
|
||||
kept_changes: std::collections::BTreeSet<String>,
|
||||
/// Lists kept as read given an `xml:id` (see `kept_run`).
|
||||
kept_lists: usize,
|
||||
/// The file's own package paths, kept as read: names WordCraft's own pictures don't take.
|
||||
pub(super) reserved: std::sync::Arc<std::collections::BTreeSet<String>>,
|
||||
pub(super) kept_used: super::kept::Needed,
|
||||
@@ -775,6 +777,15 @@ impl Writer {
|
||||
if !self.kept_markup(&ix.0, &mut markup) {
|
||||
return None;
|
||||
}
|
||||
// A list without its own `xml:id` (older files' lists) is given one, for a list going on
|
||||
// with it to name.
|
||||
if p.list.is_some()
|
||||
&& list_xml_id(&ix.0).is_none()
|
||||
&& let Some(at) = markup.find("<text:list").map(|i| i + "<text:list".len())
|
||||
{
|
||||
self.kept_lists += 1;
|
||||
markup.insert_str(at, &format!(" xml:id=\"{}lstk{}\"", self.prefix, self.kept_lists));
|
||||
}
|
||||
let last = match blocks.get(i + ix.1.max(1) - 1) {
|
||||
Some(FBlock::Para(q)) => q.wrap.as_deref().map(|w| (Vec::new(), w.1)),
|
||||
_ => None,
|
||||
@@ -785,7 +796,7 @@ impl Writer {
|
||||
self.master = None;
|
||||
// A list kept so: a list continuing it, written from the model, names it.
|
||||
if let Some(l) = p.list
|
||||
&& let Some(id) = list_xml_id(&ix.0)
|
||||
&& let Some(id) = markup.find("<text:list").and_then(|i| markup.get(i..)).and_then(list_xml_id)
|
||||
{
|
||||
if l.list != 0 {
|
||||
self.list_xml.entry(l.list).or_insert_with(|| id.clone());
|
||||
|
||||
Reference in New Issue
Block a user