Merge pull request #5303 from storytold/fix/sec-thread-reflow-unique-frames

Threaded text: a frame listed twice flows the story once
This commit is contained in:
BFlat
2026-11-03 16:28:57 +07:00
committed by GitHub
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();