Skip to content

Commit

Permalink
slint-viewer: handle path being renamed and replaced
Browse files Browse the repository at this point in the history
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: slint-ui#3641
  • Loading branch information
0x6e committed Apr 19, 2024
1 parent 97940ee commit 67a720c
Showing 1 changed file with 37 additions and 24 deletions.
61 changes: 37 additions & 24 deletions tools/viewer/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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<Mutex<notify::RecommendedWatcher>>) {
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() {
Expand Down

0 comments on commit 67a720c

Please sign in to comment.