Merge #456: Wrap saved story revision counters consistently

Conflict: crates/doc/src/edit.rs: next's thread_frames (append_story); every rev += 1 is rev.wrapping_add(1)
Conflict: crates/engine/src/cmd/fonts.rs: next's replace (the local-formatting pass moved out of this hunk)
Conflict: crates/engine/src/cmd/text.rs: next's format_chars (apply_char_attrs, typing format); rev += 1 is rev.wrapping_add(1)
This commit is contained in:
aiongg
2026-10-11 12:20:08 +00:00
15 changed files with 73 additions and 28 deletions
+1 -1
View File
@@ -190,7 +190,7 @@ fn options(s: &mut Session, p: &Value) -> Result<Value> {
let st = d.story_mut(sid).ok_or(designcraft_doc::DocError::NoStory(sid))?;
let o = st.objects.get_mut(k).ok_or_else(|| bad("anchored.options", "no such anchored object"))?;
Arc::make_mut(o).position = position.clone();
st.rev += 1;
st.rev = st.rev.wrapping_add(1);
Ok(json!({"position": position}))
})
}
+1 -1
View File
@@ -47,7 +47,7 @@ pub fn specs() -> Vec<CommandSpec> {
if let Some(a) = author {
n.author = a;
}
st.rev += 1;
st.rev = st.rev.wrapping_add(1);
ok()
})
}),
+21 -1
View File
@@ -12,7 +12,7 @@ fn copy_into(d: &mut Document, parent: StoryId, child: StoryId) -> Result<()> {
let src = d.story(parent).ok_or_else(|| bad("story.link", format!("no story {}", parent.0)))?.clone();
let st = d.story_mut(child).ok_or_else(|| bad("story.link", format!("no story {}", child.0)))?;
let (frames, rev) = (std::mem::take(&mut st.frames), st.rev);
*st = Story { id: child, frames, rev: rev + 1, link: Some((parent, src.rev)), ..src };
*st = Story { id: child, frames, rev: rev.wrapping_add(1), link: Some((parent, src.rev)), ..src };
Ok(())
}
@@ -94,6 +94,26 @@ mod tests {
use crate::Session;
#[test]
fn linked_story_update_wraps_the_child_revision() {
let mut s = Session::new();
s.execute("file.new", &json!({})).unwrap();
let p = s.execute("frame.create", &json!({"rect": [0, 0, 100, 100], "content": "text", "text": "Original"})).unwrap();
let c = s.execute("story.placeAndLink", &json!({"story": p["story"], "rect": [100, 0, 200, 100]})).unwrap();
let child = designcraft_doc::StoryId(c["story"].as_u64().unwrap());
std::sync::Arc::make_mut(&mut s.doc_mut().unwrap().doc).story_mut(child).unwrap().rev = u64::MAX;
s.execute("story.setText", &json!({"story": p["story"], "text": "Revised"})).unwrap();
s.execute("story.updateLink", &json!({"story": c["story"]})).unwrap();
let st = s.doc().unwrap().doc.story(child).unwrap();
assert_eq!(st.text, "Revised");
assert_eq!(st.rev, 0);
assert_eq!(s.execute("story.links", &json!({})).unwrap()[0]["outOfDate"], false);
s.execute("edit.undo", &json!({})).unwrap();
assert_eq!(s.doc().unwrap().doc.story(child).unwrap().text, "Original");
s.execute("edit.redo", &json!({})).unwrap();
assert_eq!(s.doc().unwrap().doc.story(child).unwrap().text, "Revised");
}
#[test]
fn linked_story_goes_out_of_date_and_updates() {
let mut s = Session::new();
+1 -1
View File
@@ -623,7 +623,7 @@ fn rename_style(d: &mut designcraft_doc::Document, para: bool, from: &str, to: &
rename_char_refs(&mut f.para, from, to);
}
}
st.rev += 1;
st.rev = st.rev.wrapping_add(1);
});
}
}
+2 -2
View File
@@ -1363,7 +1363,7 @@ fn reapply_cell_styles(d: &mut Document, names: &[String]) {
}
}
}
st.rev += 1;
st.rev = st.rev.wrapping_add(1);
}
}
@@ -1488,7 +1488,7 @@ fn table_style_edit(s: &mut Session, p: &Value) -> Result<Value> {
ts.apply_to(std::sync::Arc::make_mut(t), &cells);
}
}
st.rev += 1;
st.rev = st.rev.wrapping_add(1);
}
ok()
})
+2 -2
View File
@@ -801,7 +801,7 @@ pub(crate) fn set_story_direction(d: &mut designcraft_doc::Document, stories: &[
&& st.vertical != vertical
{
st.vertical = vertical;
st.rev += 1;
st.rev = st.rev.wrapping_add(1);
}
}
}
@@ -1329,7 +1329,7 @@ pub(crate) fn apply_char_attrs(d: &mut designcraft_doc::Document, targets: &[Tar
&& let Some(run) = st.chars.first_mut()
{
run.format.over.merge(a);
st.rev += 1;
st.rev = st.rev.wrapping_add(1);
}
continue;
}
+1 -1
View File
@@ -438,7 +438,7 @@ fn import_xml(s: &mut Session, p: &Value) -> Result<Value> {
}
}
fresh.frames = frames;
fresh.rev = st.rev + 1;
fresh.rev = st.rev.wrapping_add(1);
*st = fresh;
}
Ok(json!({"placed": placed, "unmatched": unmatched}))
+1 -1
View File
@@ -162,7 +162,7 @@ fn set_format(s: &mut Session, p: &Value) -> Result<Value> {
let st = d.story_mut(sid).ok_or(designcraft_doc::DocError::NoStory(sid))?;
let x = st.xrefs.get_mut(k).ok_or_else(|| bad("xref.setFormat", "no such cross-reference"))?;
std::sync::Arc::make_mut(x).format = format;
st.rev += 1;
st.rev = st.rev.wrapping_add(1);
Ok(Value::Null)
})
}