From 67a720cbc6febf1c65f64b3b816cbe56d3134257 Mon Sep 17 00:00:00 2001 From: Nathan Collins Date: Fri, 19 Apr 2024 12:36:15 +0100 Subject: [PATCH] slint-viewer: handle path being renamed and replaced Some editors, such as vim, rename (move) a file to a backup location, then write the new contents to a new location when the user saves their changes. notify stops watching the renamed file, and does not automatically start watching the new file created. Additionally, slint-viewer attempts to reload before the editor has written the new file, which causes an error. The file is then never reloaded because the watcher was lost. This patch solves the problem by attempting to watch the file again, if the previous watch failed due to a Generic or PathNotFound error. Generic is required because this is error type we get on macOS for "No such file or directory.". We delay the retry by a small timeout to give the editor a chance to write the new file. Note that this still results in an error being printed about the missing file. Tested manually by editing both root .slint file, and .slint files imported from sub-directories. Closes: #3641 --- tools/viewer/main.rs | 61 +++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/tools/viewer/main.rs b/tools/viewer/main.rs index 5f8a234a1f7..df70af0177c 100644 --- a/tools/viewer/main.rs +++ b/tools/viewer/main.rs @@ -10,6 +10,7 @@ use slint_interpreter::{ }; use std::collections::HashMap; use std::io::{BufReader, BufWriter}; +use std::path::PathBuf; use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; @@ -182,39 +183,51 @@ fn init_compiler( compiler.set_style(style.clone()); } if let Some(watcher) = fswatcher { - notify::Watcher::watch( - &mut *watcher.lock().unwrap(), - &args.path, - notify::RecursiveMode::NonRecursive, - ) - .unwrap_or_else(|err| { - eprintln!("Warning: error while watching {}: {:?}", args.path.display(), err) - }); + watch_with_retry(&args.path, &watcher); if let Some(data_path) = &args.load_data { - notify::Watcher::watch( - &mut *watcher.lock().unwrap(), - data_path, - notify::RecursiveMode::NonRecursive, - ) - .unwrap_or_else(|err| { - eprintln!("Warning: error while watching {}: {:?}", data_path.display(), err) - }); + watch_with_retry(&data_path, &watcher); } compiler.set_file_loader(move |path| { - notify::Watcher::watch( - &mut *watcher.lock().unwrap(), - path, - notify::RecursiveMode::NonRecursive, - ) - .unwrap_or_else(|err| { - eprintln!("Warning: error while watching {}: {:?}", path.display(), err) - }); + watch_with_retry(&path.into(), &watcher); Box::pin(async { None }) }) } compiler } +fn watch_with_retry(path: &PathBuf, watcher: &Arc>) { + notify::Watcher::watch( + &mut *watcher.lock().unwrap(), + &path, + notify::RecursiveMode::NonRecursive, + ) + .unwrap_or_else(|err| match err.kind { + notify::ErrorKind::PathNotFound | notify::ErrorKind::Generic(_) => { + let path = path.clone(); + let watcher = watcher.clone(); + static RETRY_DURATION: u64 = 100; + i_slint_core::timers::Timer::single_shot( + std::time::Duration::from_millis(RETRY_DURATION), + move || { + notify::Watcher::watch( + &mut *watcher.lock().unwrap(), + &path, + notify::RecursiveMode::NonRecursive, + ) + .unwrap_or_else(|err| { + eprintln!( + "Warning: error while watching missing path {}: {:?}", + path.display(), + err + ) + }); + }, + ); + } + _ => eprintln!("Warning: error while watching {}: {:?}", path.display(), err), + }); +} + fn init_dialog(instance: &ComponentInstance) { for cb in instance.definition().callbacks() { let exit_code = match cb.as_str() {