diff --git a/src/tui/app.rs b/src/tui/app.rs index 42fdda3..daae73c 100644 --- a/src/tui/app.rs +++ b/src/tui/app.rs @@ -1,6 +1,9 @@ use { crate::*, - anyhow::Result, + anyhow::{ + Result, + anyhow, + }, crokey::*, std::{ io::Write, @@ -172,13 +175,17 @@ fn run_mission( recv(ticker.tick_receiver) -> _ => { // just redraw } - recv(mission_watcher.receiver) -> _ => { + recv(mission_watcher.receiver) -> trigger_path => { + let Ok(trigger_path) = trigger_path else { + return Err(anyhow!("watcher channel was closed")); + }; + debug!("watch event received"); if task_executor.is_in_grace_period() { debug!("ignoring notify event in grace period"); continue; } - mission_state.receive_watch_event(); + mission_state.receive_watch_event(trigger_path); if mission_state.auto_refresh.is_enabled() { if !mission_state.is_computing() || on_change_strategy == OnChangeStrategy::KillThenRestart { actions.push(Action::ReRun); diff --git a/src/tui/mission_state.rs b/src/tui/mission_state.rs index a3ded66..e2425a5 100644 --- a/src/tui/mission_state.rs +++ b/src/tui/mission_state.rs @@ -4,6 +4,7 @@ use { crokey::KeyCombination, std::{ io::Write, + path::PathBuf, process::ExitStatus, time::Instant, }, @@ -79,6 +80,8 @@ pub struct MissionState<'a, 'm> { pub search: SearchState, /// The dialog that may be displayed over the rest of the UI pub dialog: Dialog, + /// The last seen path that triggered a re-run. + pub last_trigger_path: Option, } impl<'a, 'm> MissionState<'a, 'm> { @@ -134,6 +137,7 @@ impl<'a, 'm> MissionState<'a, 'm> { dialog: Dialog::None, app_state, mission, + last_trigger_path: None, }) } pub fn open_jobs_menu(&mut self) { @@ -545,8 +549,12 @@ impl<'a, 'm> MissionState<'a, 'm> { pub fn computation_stops(&mut self) { self.computing = false; } - pub fn receive_watch_event(&mut self) { + pub fn receive_watch_event( + &mut self, + trigger_path: PathBuf, + ) { self.changes_since_last_job_start += 1; + self.last_trigger_path = Some(trigger_path); } fn scroll_to_top(&mut self) { self.scroll = 0; @@ -826,12 +834,33 @@ impl<'a, 'm> MissionState<'a, 'm> { let skin = self.mission.job.skin; let width = self.width as usize; if self.computing { + let text = if let Some(last_trigger_path) = &self.last_trigger_path { + let path_display = last_trigger_path.display().to_string(); + + let prefix_len = "watch trigger: ".len(); + let padding_len = 2; + + let should_be_shortened = + last_trigger_path.as_os_str().len() + prefix_len + padding_len > width; + if should_be_shortened { + let start_index = path_display.ceil_char_boundary( + (path_display.len() + "...".len() + prefix_len + padding_len) + .saturating_sub(width), + ); + "watch trigger: ...".to_string() + &path_display[start_index..] + } else { + format!("watch trigger: {path_display}") + } + } else { + "computing...".to_string() + }; + write!( w, "\u{1b}[38;5;{}m\u{1b}[48;5;{}m{:^w$}\u{1b}[0m", skin.computing_fg(), skin.computing_bg(), - "computing...", + text, w = width )?; } else { diff --git a/src/watcher.rs b/src/watcher.rs index 74fc6e1..6f8d800 100644 --- a/src/watcher.rs +++ b/src/watcher.rs @@ -22,7 +22,7 @@ use { /// A file watcher, providing a channel to receive notifications pub struct Watcher { - pub receiver: Receiver<()>, + pub receiver: Receiver, _notify_watcher: RecommendedWatcher, } @@ -32,11 +32,11 @@ impl Watcher { mut ignorer: IgnorerSet, ) -> Result { info!("watcher on {paths_to_watch:#?}"); - let (sender, receiver) = bounded(0); + let (sender, receiver) = bounded::(0); let mut notify_watcher = notify::recommended_watcher(move |res: notify::Result| { match res { - Ok(we) => { + Ok(mut we) => { match we.kind { EventKind::Modify(ModifyKind::Metadata(_)) => { //debug!("ignoring metadata change"); @@ -69,7 +69,7 @@ impl Watcher { warn!("exclusion check failed: {e}"); } } - if let Err(e) = sender.send(()) { + if let Err(e) = sender.send(we.paths.swap_remove(0)) { debug!("error when notifying on notify event: {e}"); } }