Text: footnotes insert into a story whose references lack footnotes

This commit is contained in:
aiongg
2026-10-11 12:54:42 +00:00
parent 6cf1933a13
commit fe8a724da4
4 changed files with 22 additions and 12 deletions
+9 -6
View File
@@ -166,15 +166,18 @@ impl Story {
self.text.match_indices(FOOTNOTE_REF).nth(k).map(|(i, _)| i)
}
/// Insert a footnote reference at `pos` with the given footnote text. Returns the footnote id.
pub fn insert_note(&mut self, pos: usize, text: &str, para: ParaFormat) -> u64 {
/// Insert a footnote reference at `pos` with the given footnote text. Returns the footnote
/// id, or `None` if the story has no footnote for the new reference.
pub fn insert_note(&mut self, pos: usize, text: &str, para: ParaFormat) -> Option<u64> {
let pos = crate::story::floor_char_boundary(&self.text, pos.min(self.len()));
let fmt = self.char_format_at(pos).clone();
self.insert_with(pos, &FOOTNOTE_REF.to_string(), fmt);
// A file may hold fewer footnotes than references; give every reference one.
self.fix_notes();
let k = self.notes_before(pos);
let note = Arc::make_mut(&mut self.notes[k]);
let note = Arc::make_mut(self.notes.get_mut(k)?);
note.text = Story::with_text(StoryId(0), text, para);
note.id
Some(note.id)
}
pub(crate) fn next_note_id(&self) -> u64 {
@@ -283,8 +286,8 @@ mod tests {
#[test]
fn references_own_notes_in_order() {
let mut s = st("alpha beta gamma");
let b = s.insert_note(10, "second", ParaFormat::default());
let a = s.insert_note(5, "first", ParaFormat::default());
let b = s.insert_note(10, "second", ParaFormat::default()).unwrap();
let a = s.insert_note(5, "first", ParaFormat::default()).unwrap();
s.check().unwrap();
assert_eq!(s.notes.len(), 2);
assert_eq!(s.notes[0].id, a);
+5 -2
View File
@@ -142,18 +142,21 @@ mod tests {
s.execute("file.new", &json!({})).unwrap();
let r = s.execute("frame.create", &json!({"rect": [72, 72, 400, 300], "content": "text", "text": "Draft copy."})).unwrap();
let sid = designcraft_doc::StoryId(r["story"].as_u64().unwrap());
// A damaged file: a note anchor and an endnote reference without payloads.
// A damaged file: a note anchor, an endnote and a footnote reference without payloads.
let st = std::sync::Arc::make_mut(&mut s.doc_mut().unwrap().doc).story_mut(sid).unwrap();
st.insert(0, &format!("{}{}", designcraft_doc::NOTE_MARK, designcraft_doc::ENDNOTE_REF));
st.insert(0, &format!("{}{}{}", designcraft_doc::NOTE_MARK, designcraft_doc::ENDNOTE_REF, designcraft_doc::FOOTNOTE_REF));
st.editorial.clear();
st.endnotes.clear();
st.notes.clear();
let end = st.len();
s.execute("text.select", &json!({"story": r["story"], "anchor": end, "focus": end})).unwrap();
s.execute("note.new", &json!({"text": "check this"})).unwrap();
s.execute("endnote.insert", &json!({"text": "An endnote."})).unwrap();
s.execute("footnote.insert", &json!({"text": "A footnote."})).unwrap();
let st = &s.doc().unwrap().doc.stories[&sid];
st.check().unwrap();
assert_eq!(st.editorial[1].text, "check this");
assert_eq!(st.endnotes[1].text.text, "An endnote.");
assert_eq!(st.notes[1].text.text, "A footnote.");
}
}
+1 -1
View File
@@ -66,7 +66,7 @@ fn insert(s: &mut Session, p: &Value) -> Result<Value> {
let r = t.range();
let r = r.start.min(st.len())..r.end.min(st.len());
st.delete(r.clone());
let id = st.insert_note(r.start, &text, para);
let id = st.insert_note(r.start, &text, para).ok_or_else(|| bad("footnote.insert", "the story's footnotes don't match their references"))?;
sel.text = Some(TextSel { story: t.story, anchor: text.len(), focus: text.len(), frame: t.frame, cell: Some(CellAddr::footnote(id)) });
Ok(json!({"story": t.story.0, "id": id}))
})
+7 -3
View File
@@ -305,9 +305,13 @@ impl Ctx {
self.blocks(&note, &mut ns, false);
// Word puts the reference mark in the note text: we generate the number.
let len = st.len();
let id = st.insert_note(len, "", ParaFormat::default());
if let Some(n) = st.note_mut(id) {
n.text = ns;
match st.insert_note(len, "", ParaFormat::default()) {
Some(id) => {
if let Some(n) = st.note_mut(id) {
n.text = ns;
}
}
None => self.warnings.push("A footnote in the Word file could not be imported.".into()),
}
}
continue;