M7: keep saved document titles in sync with filenames

This commit is contained in:
YarilaFox
2026-10-20 17:56:30 +05:00
parent 594dc8c6cf
commit 23463f719b
4 changed files with 54 additions and 5 deletions
+9 -2
View File
@@ -542,7 +542,8 @@ fn load_with(
return Ok((figcraft_format::Loaded { issues: warnings, ..doc.into() }, true));
}
let mut loaded = figcraft_format::load_bytes_with_versions(bytes).map_err(|e| EngineError::Other(e.to_string()))?;
if loaded.doc.name.is_empty() {
// Older files saved an untouched default title even after the user chose a filename.
if loaded.doc.name.is_empty() || loaded.doc.name == "Untitled" {
loaded.doc.name = file_stem(name_hint);
}
Ok((loaded, false))
@@ -853,8 +854,14 @@ fn save_to(s: &mut Session, path: String) -> Result<Value> {
}
let i = crate::branches::file_tab(s);
let ds = s.docs.get_mut(i).ok_or(EngineError::NoDocument)?;
let bytes = file_bytes(ds)?;
let mut snapshot = FileSnapshot::of(ds);
let follows_path = ds.path.as_deref().map_or(ds.doc.name == "Untitled", |old| ds.doc.name == file_stem(old));
if follows_path && ds.path.as_deref() != Some(path.as_str()) {
Arc::make_mut(&mut snapshot.doc).name = file_stem(&path);
}
let bytes = snapshot.encode()?;
write(&path, &bytes)?;
ds.doc = snapshot.doc;
ds.path = Some(path.clone());
ds.mark_saved();
ds.revision += 1;
+3 -1
View File
@@ -1062,7 +1062,9 @@ impl Session {
};
let Some(ds) = self.docs.iter_mut().find(|d| d.uid == uid) else { return };
let changed = !Arc::ptr_eq(&ds.doc, &bdoc);
if changed {
// Saving may adopt the chosen filename as the document title. The encoded snapshot is
// already final; normalising it again would detach it from the saved baseline.
if changed && !matches!(spec.id, "file.save" | "file.saveAs") {
normalize(ds.doc_mut(), &bdoc);
}
let edit = changed && ds.interaction.is_none();
+2 -2
View File
@@ -265,7 +265,7 @@ fn revert_offline_file_works_while_another_file_is_live_and_after_leaving() {
s.execute("file.new", &json!({})).unwrap();
let offline_path = saved_file(&mut s);
s.execute("file.revert", &json!({})).unwrap();
assert_eq!(s.doc().unwrap().name, "Untitled");
assert_eq!(s.doc().unwrap().name, offline_path.file_stem().unwrap().to_string_lossy());
assert!(!s.ds().unwrap().is_dirty());
assert_eq!(s.ds().unwrap().versions.len(), 1);
assert_eq!(s.live_of(uid).doc_uid, Some(uid));
@@ -273,7 +273,7 @@ fn revert_offline_file_works_while_another_file_is_live_and_after_leaving() {
s.execute("doc.switch", &json!({"index": 0})).unwrap();
s.execute("collab.leave", &json!({})).unwrap();
s.execute("file.revert", &json!({})).unwrap();
assert_eq!(s.doc().unwrap().name, "Untitled");
assert_eq!(s.doc().unwrap().name, path.file_stem().unwrap().to_string_lossy());
assert!(!s.ds().unwrap().is_dirty());
assert_eq!(s.ds().unwrap().versions.len(), 1);
assert_eq!(s.live().doc_uid, None);
+40
View File
@@ -89,6 +89,46 @@ fn assert_save_state_unchanged(s: &Session, before: &DocState, recent: &[String]
assert_eq!(s.prefs.recent_files, recent);
}
#[test]
fn first_native_save_uses_chosen_filename_for_an_untitled_document() {
let mut files = SaveFiles::new();
let path = files.path("Redis.figcraft");
let copy = files.path("Redis copy.figcraft");
let manual_copy = files.path("Other.figcraft");
let mut s = Session::new();
s.execute("file.saveAs", &json!({"path": path.to_string_lossy()})).unwrap();
assert_eq!(s.ds().unwrap().doc.name, "Redis");
assert!(!s.ds().unwrap().is_dirty());
assert_eq!(figcraft_format::load_bytes(&std::fs::read(&path).unwrap()).unwrap().0.name, "Redis");
let mut reopened = Session::empty();
reopened.execute("file.open", &json!({"path": path.to_string_lossy()})).unwrap();
assert_eq!(reopened.ds().unwrap().doc.name, "Redis");
s.execute("file.saveAs", &json!({"path": copy.to_string_lossy()})).unwrap();
assert_eq!(s.ds().unwrap().doc.name, "Redis copy");
s.execute("file.rename", &json!({"name": "Chosen title"})).unwrap();
s.execute("file.saveAs", &json!({"path": manual_copy.to_string_lossy()})).unwrap();
assert_eq!(s.ds().unwrap().doc.name, "Chosen title");
}
#[test]
fn opening_an_older_file_uses_its_filename_only_for_the_default_title() {
let mut files = SaveFiles::new();
let old_path = files.path("Redis.figcraft");
let named_path = files.path("Other.figcraft");
let mut original = Session::new();
std::fs::write(&old_path, crate::cmd::file::file_bytes(original.ds().unwrap()).unwrap()).unwrap();
original.execute("file.rename", &json!({"name": "Chosen title"})).unwrap();
std::fs::write(&named_path, crate::cmd::file::file_bytes(original.ds().unwrap()).unwrap()).unwrap();
let mut reopened = Session::empty();
reopened.execute("file.open", &json!({"path": old_path.to_string_lossy()})).unwrap();
assert_eq!(reopened.ds().unwrap().doc.name, "Redis");
reopened.execute("file.open", &json!({"path": named_path.to_string_lossy()})).unwrap();
assert_eq!(reopened.ds().unwrap().doc.name, "Chosen title");
}
fn read_fig(path: &std::path::Path, result: &Value) -> figcraft_doc::Document {
let bytes = std::fs::read(path).unwrap();
assert_eq!(result["format"], "fig");