diff --git a/crates/engine/src/cmd/fileio/load.rs b/crates/engine/src/cmd/fileio/load.rs index 302240baa..d7fe553be 100644 --- a/crates/engine/src/cmd/fileio/load.rs +++ b/crates/engine/src/cmd/fileio/load.rs @@ -463,8 +463,12 @@ fn open_loaded(s: &mut Session, loaded: Loaded, links: Resolved, path: Option Value { json!({ "uid": st.uid, "title": st.title(), + // The File Info title (`file.info`), where `title` comes from the file name once the document has one. + "fileInfoTitle": d.title, "path": st.path, "dirty": st.is_dirty(), "revision": st.revision, diff --git a/crates/engine/src/tests_fileinfo.rs b/crates/engine/src/tests_fileinfo.rs index 09d1efd17..bbe8eca66 100644 --- a/crates/engine/src/tests_fileinfo.rs +++ b/crates/engine/src/tests_fileinfo.rs @@ -112,6 +112,29 @@ fn saving_to_a_file_stamps_the_modified_date() { assert!(s.execute("file.info", &json!({})).unwrap()["modified"].is_string()); } +#[test] +fn inspect_and_open_report_the_file_info_title_beside_the_file_name() { + let dir = std::env::temp_dir().join(format!("vc-fileinfotitle-{}", std::process::id())); + std::fs::create_dir_all(&dir).unwrap(); + let path = dir.join("poster.vectorcraft"); + let mut s = session(); + // Untitled, the two are the same. + let r = s.execute("document.inspect", &json!({"depth": 0})).unwrap(); + assert_eq!((r["title"].as_str(), r["fileInfoTitle"].as_str()), (Some("Poster"), Some("Poster"))); + s.execute("file.info", &json!({"title": "Poster: final"})).unwrap(); + s.execute("document.save", &json!({"path": path.to_str().unwrap()})).unwrap(); + let r = s.execute("document.inspect", &json!({"depth": 0})).unwrap(); + assert_eq!((r["title"].as_str(), r["fileInfoTitle"].as_str()), (Some("poster.vectorcraft"), Some("Poster: final"))); + // Opened from the file, in another session. + let mut s = Session::new(); + let opened = s.execute("document.open", &json!({"path": path.to_str().unwrap()})).unwrap(); + assert_eq!((opened["title"].as_str(), opened["fileInfoTitle"].as_str()), (Some("poster.vectorcraft"), Some("Poster: final"))); + let r = s.execute("document.inspect", &json!({"depth": 0})).unwrap(); + assert_eq!((r["title"].as_str(), r["fileInfoTitle"].as_str()), (Some("poster.vectorcraft"), Some("Poster: final"))); + assert_eq!(r["fileInfoTitle"], s.execute("file.info", &json!({})).unwrap()["title"]); + let _ = std::fs::remove_dir_all(dir); +} + #[test] fn pdf_info_carries_author_subject_and_keywords() { let mut s = session(); diff --git a/docs/mcp.md b/docs/mcp.md index d5fb60d25..ae4eb5778 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -3184,6 +3184,9 @@ packet (`dc:creator`, `dc:description`, `dc:subject` or `pdf:Keywords`, `xmpRigh `Subject`, `Keywords`, `CreationDate`, `ModDate`) give, the XMP packet's value where both give one. A value that `file.info` would refuse is left out, and the title is set as for any other open (the file name, or a new untitled name for a template). +`document.inspect` (`inspect_document`) and the `document.open` (`open_file`) reply report the File Info title as +`fileInfoTitle`. Their `title` is the document's tab name, which comes from the file name once the document is saved +to or opened from a file (with ` [Converted]` or ` [Recovered]` where that applies). `file.new {created}` and a save to a file (`document.save` or `file.saveAs` with `{path, modified}`) use the given date (Unix seconds, or `null` for none) instead of now. Their journal entries always record the date they used, so replaying the journal makes the same document whenever it runs (an action recorded in