Save: File > Save checks the files written beside the path against the file read from

File > Save to the document's own file planned the save with acknowledgeLoss
set, so that only its own path was asked about, and then wrote that plan. With
the acknowledgment in the plan, the files written beside the path (an EPS
saved with Use Artboards writes one per artboard) were not checked against the
file the document was read from. A document opened from one of those artboard
files, with things left out when it was read, was written over by File > Save
without the question. Save As and Save a Copy already plan again without the
acknowledgment before writing; File > Save now does the same.

Follow-up to #6751.
This commit is contained in:
dotCooCoo
2026-11-14 14:37:55 -08:00
parent 9ddbf25bef
commit 196231d233
2 changed files with 34 additions and 1 deletions
+3 -1
View File
@@ -263,7 +263,9 @@ pub(super) fn save_current_in(app: &mut VectorcraftApp, p: &Value, folder: Optio
return Ok(r);
}
let retry = super::read_only_save::Retry::new(&first, folder);
write_plan(app, first, ("file.save", p.clone()), Some(retry))
// Only `path` was asked about: the files written beside it are checked against the file the
// document was read from when the save writes them.
write_plan(app, plan(app, SaveMode::Save, p)?, ("file.save", p.clone()), Some(retry))
}
/// File → Save, Save As…, Save a Copy…, Save as Template… and the options dialogs' OK. `p` is the
+31
View File
@@ -355,6 +355,37 @@ fn a_save_does_not_replace_the_file_read_from_through_a_file_written_beside_the_
let _ = std::fs::remove_dir_all(d);
}
#[test]
fn file_save_does_not_replace_the_file_read_from_through_a_file_written_beside_the_path() {
let d = dir("lossy-beside-save");
let path = d.join("Art.eps").to_string_lossy().to_string();
let mut app = app();
checks_files_there(&mut app);
app.run("file.new", json!({"width": 200, "height": 100, "artboards": 2})).unwrap();
rect(&mut app);
let options = json!({"useArtboards": true, "previewFormat": "none", "thumbnails": false});
let _ = app.run("file.saveAs", json!({"path": path, "options": options}));
crate::background::wait_all(&mut app);
let siblings = artboard_files(&d);
assert_eq!(siblings.len(), 2, "{siblings:?}");
let sibling = siblings[0].clone();
let saved = std::fs::read(&sibling).unwrap();
std::fs::remove_file(&path).unwrap();
let st = app.session.doc_mut().unwrap();
st.imported_from = Some(sibling.clone());
st.import_losses = vec!["hidden type".into()];
rect(&mut app);
// File > Save to the document's own file reuses its options, so it writes the artboard files
// again; the one the document was read from is refused, as on Save As.
let _ = app.run("file.save", json!({}));
crate::background::wait_all(&mut app);
assert!(app.ui.dialog.as_ref().is_none_or(|d| d.kind != dialogs::confirm::REPLACE_FILES));
assert_eq!(std::fs::read(&sibling).unwrap(), saved);
assert!(!std::path::Path::new(&path).exists(), "no file of the set is written when one is refused");
assert!(app.session.active().unwrap().is_dirty());
let _ = std::fs::remove_dir_all(d);
}
/// The app checks for files that are there, as the desktop app does.
fn checks_files_there(app: &mut VectorcraftApp) {
app.services.exists = Some(std::sync::Arc::new(|p: &str| std::path::Path::new(p).exists()));