diff options
Diffstat (limited to 'crates/dinggerd')
| -rw-r--r-- | crates/dinggerd/src/main.rs | 3 | ||||
| -rw-r--r-- | crates/dinggerd/src/watcher.rs | 83 |
2 files changed, 56 insertions, 30 deletions
diff --git a/crates/dinggerd/src/main.rs b/crates/dinggerd/src/main.rs index 9606d5b..2783899 100644 --- a/crates/dinggerd/src/main.rs +++ b/crates/dinggerd/src/main.rs @@ -253,8 +253,7 @@ fn status() -> Result<(), String> { } panes.sort_by_key(|pane| { let status = watcher::Status::from_label(&pane.written_state); - let since = pane.written_since.parse::<u64>().unwrap_or(u64::MAX); - (std::cmp::Reverse(status.priority()), since, pane.display_target.clone()) + (watcher::list_order(status, pane.written_since.parse().ok()), pane.display_target.clone()) }); let place = |pane: &tmux::Pane| pane.display_target.replacen(':', " ยท ", 1); let width = panes.iter().map(|pane| place(pane).chars().count()).max().unwrap_or(5).max(5); diff --git a/crates/dinggerd/src/watcher.rs b/crates/dinggerd/src/watcher.rs index 346bfa8..8951623 100644 --- a/crates/dinggerd/src/watcher.rs +++ b/crates/dinggerd/src/watcher.rs @@ -20,7 +20,7 @@ //! up exactly where it left off: //! //! - `@agent_state` display status (what every surface renders) -//! - `@dingger_since` unix ms the pane started needing the user (FIFO) +//! - `@dingger_since` unix ms the pane entered its current status //! - `@dingger_hook` last state an agent extension reported //! - `@dingger_ack` one-shot "the user jumped here" event from `dingger focus` //! - `@agent_session` the agent's session reference, for restore @@ -100,8 +100,9 @@ pub fn notification_kind_for_transition( pub struct PaneTrack { pub state: AgentState, pub seen: bool, - /// Unix millisecond the pane started needing the user (done or blocked); - /// orders the FIFO queue within a status. + /// Unix millisecond the pane entered its current status: the queue + /// serves done and blocked oldest first; working and idle are listed + /// most recent first. pub since: Option<u64>, } @@ -133,12 +134,7 @@ impl PaneTrack { "blocked" => (AgentState::Blocked, true), _ => return PaneTrack::NEW, }; - let track = PaneTrack { state, seen, since }; - let needs_user = matches!(track.status(), Status::Done | Status::Blocked); - PaneTrack { - since: if needs_user { since } else { None }, - ..track - } + PaneTrack { state, seen, since } } } @@ -165,20 +161,28 @@ pub fn advance( } else if matches!(prev.state, AgentState::Working | AgentState::Blocked) { next.seen = attended; } - next.since = None; // a new episode } if attended || acked { next.seen = true; } - let needs_user = matches!(next.status(), Status::Done | Status::Blocked); - next.since = match (needs_user, next.since) { - (true, Some(since)) => Some(since), - (true, None) => Some(now), - (false, _) => None, - }; + if next.status() != prev.status() || next.since.is_none() { + next.since = Some(now); + } next } +/// The order agents are listed in: herdr's status priority; within a +/// status, done and blocked oldest first (the queue: nothing waits +/// forever), working and idle most recent first; then by place. +pub fn list_order(status: Status, since: Option<u64>) -> (std::cmp::Reverse<u8>, i128) { + let since = since.map(i128::from); + let key = match status { + Status::Done | Status::Blocked => since.unwrap_or(i128::MAX), + _ => since.map(|since| -since).unwrap_or(i128::MAX), + }; + (std::cmp::Reverse(status.priority()), key) +} + pub struct Watcher { tracks: HashMap<String, PaneTrack>, snapshots: crate::snapshot::Recorder, @@ -363,7 +367,9 @@ impl Watcher { Status::Working => working += 1, Status::Idle | Status::Unknown => {} } - if let Some(since) = track.since { + // The queue: what needs the user, oldest first. + if matches!(track.status(), Status::Blocked | Status::Done) { + let since = track.since.unwrap_or(u64::MAX); queue.push((std::cmp::Reverse(track.status().priority()), since, pane.pane_id.as_str())); } } @@ -498,7 +504,7 @@ mod tests { fn first_observation_of_idle_is_idle() { let track = step(PaneTrack::NEW, Idle, false); assert_eq!(track.status(), Status::Idle); - assert_eq!(track.since, None); + assert_eq!(track.since, Some(100)); } // herdr: state_changed_idle_in_background_marks_unseen @@ -515,9 +521,9 @@ mod tests { #[test] fn completion_while_looking_is_idle() { let working = step(PaneTrack::NEW, Working, true); - let idle = step(working, Idle, true); + let idle = advance(working, Idle, true, false, 150); assert_eq!(idle.status(), Status::Idle); - assert_eq!(idle.since, None); + assert_eq!(idle.since, Some(150), "idle since the turn ended"); } // herdr: switch_workspace_marks_panes_seen (mark_active_tab_seen) @@ -527,9 +533,9 @@ mod tests { assert_eq!(done.status(), Status::Done); let still_done = step(done, Idle, false); assert_eq!(still_done.status(), Status::Done); - let seen = step(still_done, Idle, true); + let seen = advance(still_done, Idle, true, false, 170); assert_eq!(seen.status(), Status::Idle); - assert_eq!(seen.since, None); + assert_eq!(seen.since, Some(170), "idle since the user looked"); } // herdr: agent_focus_marks_already_focused_done_agent_seen @@ -543,9 +549,9 @@ mod tests { #[test] fn working_again_clears_done_and_the_next_completion_is_done_again() { let done = step(step(PaneTrack::NEW, Working, false), Idle, false); - let working = step(done, Working, false); + let working = advance(done, Working, false, false, 250); assert_eq!(working.status(), Status::Working); - assert_eq!(working.since, None); + assert_eq!(working.since, Some(250)); let done_again = advance(working, Idle, false, false, 300); assert_eq!(done_again.status(), Status::Done); assert_eq!(done_again.since, Some(300)); @@ -562,10 +568,31 @@ mod tests { } #[test] - fn fifo_stamp_survives_unchanged_ticks() { + fn the_stamp_survives_unchanged_ticks() { let done = advance(step(PaneTrack::NEW, Working, false), Idle, false, false, 50); - let later = advance(done, Idle, false, false, 999); - assert_eq!(later.since, Some(50)); + assert_eq!(advance(done, Idle, false, false, 999).since, Some(50)); + let idle = advance(done, Idle, true, false, 60); + assert_eq!(advance(idle, Idle, true, false, 999).since, Some(60)); + } + + #[test] + fn waiting_oldest_first_the_rest_most_recent_first() { + let mut rows = [ + ("idle old", Status::Idle, Some(10)), + ("done new", Status::Done, Some(90)), + ("idle unknown", Status::Idle, None), + ("working old", Status::Working, Some(20)), + ("idle new", Status::Idle, Some(80)), + ("done old", Status::Done, Some(30)), + ("blocked", Status::Blocked, Some(99)), + ("working new", Status::Working, Some(70)), + ]; + rows.sort_by_key(|(_, status, since)| list_order(*status, *since)); + let names: Vec<_> = rows.iter().map(|(name, ..)| *name).collect(); + assert_eq!( + names, + ["blocked", "done old", "done new", "working new", "working old", "idle new", "idle old", "idle unknown"] + ); } #[test] @@ -583,7 +610,7 @@ mod tests { assert_eq!(step(done, Idle, false).status(), Status::Done); assert_eq!(PaneTrack::restore("idle", "").status(), Status::Idle); assert_eq!(PaneTrack::restore("", "").status(), Status::Unknown); - assert_eq!(PaneTrack::restore("idle", "42").since, None); + assert_eq!(PaneTrack::restore("idle", "42").since, Some(42), "recency survives a restart"); } #[test] |
