mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-25 02:07:09 -06:00
Linked files another app changes while their document is open now follow Preferences > File Handling > Update Links, as they do when it opens: Automatically reads them again (an Update Links undo step), Ask When Modified offers to (a question worded for files changed on disk while working), Manually leaves them modified in the Links panel. The desktop app stamps the active document's linked files (size + modification time) on a worker thread every two seconds and when its window comes back to the front; no look starts while a dialog is open. Stamps are kept per document; a change is acted on once; a file still being written is tried again. links.updateChanged reads changed files again regardless of the preference (CLI, MCP). New strings translated in all ten catalogs; ROADMAP updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
89 lines
3.4 KiB
Rust
89 lines
3.4 KiB
Rust
//! Linked files changing while their document is open (`link_watch`): `links.updateChanged`,
|
|
//! and the background look following Preferences → File Handling → Update Links.
|
|
|
|
use serde_json::{Value, json};
|
|
|
|
use super::*;
|
|
use crate::tests_links::{BLUE, Folder, RED, centre_colour, place, png, session};
|
|
|
|
/// Write `bytes` to `path` so its modification time moves even on coarse file systems.
|
|
fn save(path: &str, bytes: &[u8]) {
|
|
std::thread::sleep(std::time::Duration::from_millis(30));
|
|
std::fs::write(path, bytes).unwrap();
|
|
}
|
|
|
|
fn colour(s: &Session) -> [u8; 3] {
|
|
centre_colour(&s.doc().unwrap().doc)
|
|
}
|
|
|
|
/// One background look, waited for.
|
|
fn look(s: &mut Session) -> Value {
|
|
s.start_link_scan();
|
|
let t0 = std::time::Instant::now();
|
|
loop {
|
|
if let Some(r) = s.poll_link_scan() {
|
|
return r.unwrap();
|
|
}
|
|
assert!(t0.elapsed().as_secs() < 10, "the look never finished");
|
|
std::thread::yield_now();
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn update_changed_reads_changed_files_again_once() {
|
|
let dir = Folder::new("watch-update");
|
|
let path = dir.file("art.png");
|
|
std::fs::write(&path, png(40, 40, RED)).unwrap();
|
|
let mut s = session();
|
|
let id = place(&mut s, &path);
|
|
// First look: remembered, nothing to read.
|
|
assert_eq!(s.execute("links.updateChanged", &json!({})).unwrap()["updated"], json!([]));
|
|
save(&path, &png(40, 40, BLUE));
|
|
assert_eq!(s.execute("links.updateChanged", &json!({})).unwrap()["updated"], json!([id.0]));
|
|
assert_eq!(colour(&s), BLUE);
|
|
// Undoing it is respected: the link stays modified until the file changes again.
|
|
s.execute("edit.undo", &json!({})).unwrap();
|
|
assert_eq!(colour(&s), RED);
|
|
assert_eq!(s.execute("links.updateChanged", &json!({})).unwrap()["updated"], json!([]));
|
|
assert_eq!(s.execute("links.check", &json!({})).unwrap()["modified"], 1);
|
|
}
|
|
|
|
#[test]
|
|
fn a_background_look_follows_update_links() {
|
|
for (mode, key) in [("automatically", "updated"), ("askWhenModified", "ask"), ("manually", "modified")] {
|
|
let dir = Folder::new(&format!("watch-{mode}"));
|
|
let path = dir.file("art.png");
|
|
std::fs::write(&path, png(40, 40, RED)).unwrap();
|
|
let mut s = session();
|
|
s.prefs.update_links = mode.into();
|
|
let id = place(&mut s, &path);
|
|
assert_eq!(look(&mut s), json!({"updated": [], "ask": [], "modified": []}), "{mode}");
|
|
save(&path, &png(40, 40, BLUE));
|
|
let r = look(&mut s);
|
|
assert_eq!(r[key], json!([id.0]), "{mode}: {r}");
|
|
assert_eq!(colour(&s), if mode == "automatically" { BLUE } else { RED }, "{mode}");
|
|
// Acted on once: the next look finds nothing new.
|
|
assert_eq!(look(&mut s), json!({"updated": [], "ask": [], "modified": []}), "{mode}");
|
|
assert!(s.link_scan.is_none());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn a_file_still_being_written_is_tried_again() {
|
|
let dir = Folder::new("watch-partial");
|
|
let path = dir.file("art.png");
|
|
std::fs::write(&path, png(40, 40, RED)).unwrap();
|
|
let mut s = session();
|
|
s.prefs.update_links = "automatically".into();
|
|
let id = place(&mut s, &path);
|
|
look(&mut s);
|
|
// Another app has only written the start of the file so far.
|
|
let full = png(40, 40, BLUE);
|
|
save(&path, &full[..16]);
|
|
assert_eq!(look(&mut s)["updated"], json!([]));
|
|
assert_eq!(colour(&s), RED, "the last good pixels stay");
|
|
save(&path, &full);
|
|
assert_eq!(look(&mut s)["updated"], json!([id.0]));
|
|
assert_eq!(colour(&s), BLUE);
|
|
}
|