Threaded text: a frame listed twice flows the story once

Reflow copied each listed frame's text into its thread's story, so a
document that listed one frame several times in a thread, or in several
threads, repeated that text in the story and wrote the grown story back
for the next thread to copy again, growing the text with every
repetition while the document opened. Reflow now keeps each frame at its
first place in the thread list only.
This commit is contained in:
Dylan M. Taylor
2026-11-03 15:56:53 +07:00
committed by bflatastic
parent d9e3df23b1
commit a4aa1960cf
2 changed files with 24 additions and 1 deletions
+6 -1
View File
@@ -93,7 +93,12 @@ thread_local! {
/// makes "changed" a pointer comparison). Deleted frames' text flows into the remaining ones; a
/// frame that is now point type leaves its thread with the text it holds.
pub(crate) fn reflow(before: &Document, doc: &mut Document) {
let threads = std::mem::take(&mut doc.text_threads);
// A frame is in one thread, once: a file that lists it again (in its thread or another) would
// copy its text into the story each time, and write the grown story back for the next thread
// to copy again. Only its first place counts.
let mut seen = std::collections::HashSet::new();
let threads: Vec<Vec<NodeId>> =
std::mem::take(&mut doc.text_threads).into_iter().map(|t| t.into_iter().filter(|id| seen.insert(*id)).collect()).collect();
let mut kept = vec![];
for thread in threads {
let changed = thread.iter().any(|id| match (before.node(*id), doc.node(*id)) {
+18
View File
@@ -40,6 +40,24 @@ fn thread_flows_reflows_on_edit_and_survives_delete() {
assert_eq!(s.doc().unwrap().doc.text_threads.len(), 1);
}
/// A file that lists a frame more than once, in its own thread or in another, flows the story
/// through each frame once: repeated places would copy its text again and again.
#[test]
fn frames_listed_twice_flow_the_story_once() {
let (mut s, a, b) = setup();
s.execute("text.thread.create", &json!({})).unwrap();
let mut d = (*s.doc().unwrap().doc).clone();
let (fa, fb) = (NodeId(a), NodeId(b));
d.text_threads = vec![vec![fa, fb, fa, fb, fa], vec![fb, fa], vec![fa, fb]];
reflow(&Document::new(1.0, 1.0), &mut d);
assert_eq!(d.text_threads, vec![vec![fa, fb]]);
let plain = |id: NodeId| match &d.node(id).unwrap().kind {
NodeKind::Text(t) => t.plain_text(),
_ => panic!("not text"),
};
assert_eq!(format!("{}{}", plain(fa), plain(fb)), TEXT.repeat(4));
}
#[test]
fn edits_copy_only_the_stories_of_threads_whose_frames_changed() {
let (mut s, a, b) = setup();