diff --git a/crates/ui-egui/src/scripts_menu.rs b/crates/ui-egui/src/scripts_menu.rs index 58be7b7b8..e62a5d887 100644 --- a/crates/ui-egui/src/scripts_menu.rs +++ b/crates/ui-egui/src/scripts_menu.rs @@ -246,7 +246,8 @@ pub fn readable(p: &Value) -> bool { pub fn run(app: &mut VectorcraftApp, p: &Value) -> Result { let path = p.get("path").and_then(Value::as_str).ok_or("missing `path`")?; let source = app.session.installed_script(path).map_err(|e| { - refresh(app, None); + let ctx = app.ui_ctx.clone(); + refresh(app, ctx.as_ref()); e.to_string() })?; app.run("script.run", json!({"source": source, "name": vectorcraft_engine::cmd::fileio::file_name(path)})) diff --git a/crates/ui-egui/src/tests/scripts_menu.rs b/crates/ui-egui/src/tests/scripts_menu.rs index f18b60f63..3dc66acca 100644 --- a/crates/ui-egui/src/tests/scripts_menu.rs +++ b/crates/ui-egui/src/tests/scripts_menu.rs @@ -200,6 +200,28 @@ fn a_script_that_left_the_folder_is_not_run_and_the_folder_is_read_again() { let _ = std::fs::remove_dir_all(dir); } +#[test] +fn the_read_after_a_script_that_left_the_folder_asks_for_a_frame() { + let dir = folder("gone-frame"); + let mut app = app(&dir); + let gone = write(&dir, "gone.js", DRAW); + read(&app); + std::fs::remove_file(&gone).unwrap(); + let ctx = egui::Context::default(); + let (tx, frames) = std::sync::mpsc::channel(); + ctx.set_request_repaint_callback(move |_| { + let _ = tx.send(()); + }); + app.ui_ctx = Some(ctx); + assert!(menus::run_ui_command(&mut app, scripts_menu::COMMAND, &json!({ "path": gone })).unwrap().is_err()); + // With the app's copy of the context dropped, only the read's thread can still ask for a + // frame; the channel closes without one when that thread ends without asking. + app.ui_ctx = None; + assert_eq!(frames.recv(), Ok(()), "the finished read asks for a frame so an idle window takes it in"); + scripts_menu::wait(); + let _ = std::fs::remove_dir_all(dir); +} + #[test] fn the_menu_notes_a_listing_that_stopped_and_a_folder_it_cannot_read() { let dir = folder("notes");