use std::time::{Duration, Instant};
use super::App;
pub(super) const DIFF_ADVANCE_DELAY: Duration = Duration::from_millis(350);
impl App {
pub(crate) fn arm_diff_advance(&mut self) {
self.diff_advance_pending_since = Some(Instant::now());
}
pub(crate) fn cancel_diff_advance(&mut self) {
self.diff_advance_pending_since = None;
}
pub(crate) fn apply_diff_advance(&mut self) {
self.diff_advance_pending_since = None;
if let Some(d) = self.editor.diff.as_mut() {
d.advance_to_next_pending();
self.editor.pending_focus_scroll = true;
self.needs_draw = true;
}
self.check_diff_resolution();
}
pub(super) fn tick_diff_advance(&mut self) {
let Some(since) = self.diff_advance_pending_since else {
return;
};
if since.elapsed() < DIFF_ADVANCE_DELAY {
return;
}
self.apply_diff_advance();
}
pub(super) fn diff_advance_deadline(&self) -> Option<Instant> {
self.diff_advance_pending_since
.map(|t| t + DIFF_ADVANCE_DELAY)
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use super::*;
use crate::app::test_utils::make_app;
use crate::diff::{Decision, DiffState};
fn app_in_diff() -> App {
let mut app = make_app();
let old = "a\nb\nc\nd\ne\n";
let new = "A\nb\nC\nd\nE\n"; let diff = DiffState::new(old, new).expect("non-empty diff");
assert_eq!(diff.hunks.len(), 3);
app.editor.enter_diff_mode(diff);
app
}
#[test]
fn decision_defers_advance_until_window_elapses() {
let mut app = app_in_diff();
let first = app.editor.diff.as_ref().unwrap().focused_id;
app.editor
.diff
.as_mut()
.unwrap()
.decide_focused(Decision::Accepted);
app.arm_diff_advance();
assert_eq!(app.editor.diff.as_ref().unwrap().focused_id, first);
assert!(app.diff_advance_pending_since.is_some());
app.tick_diff_advance();
assert_eq!(app.editor.diff.as_ref().unwrap().focused_id, first);
app.diff_advance_pending_since =
Some(Instant::now() - DIFF_ADVANCE_DELAY - Duration::from_millis(5));
app.tick_diff_advance();
assert_ne!(app.editor.diff.as_ref().unwrap().focused_id, first);
assert!(app.diff_advance_pending_since.is_none());
assert!(app.editor.pending_focus_scroll);
}
#[test]
fn cancel_drops_pending_advance() {
let mut app = app_in_diff();
let first = app.editor.diff.as_ref().unwrap().focused_id;
app.editor
.diff
.as_mut()
.unwrap()
.decide_focused(Decision::Accepted);
app.arm_diff_advance();
app.diff_advance_pending_since =
Some(Instant::now() - DIFF_ADVANCE_DELAY - Duration::from_millis(5));
app.cancel_diff_advance();
assert!(app.diff_advance_pending_since.is_none());
app.tick_diff_advance();
assert_eq!(app.editor.diff.as_ref().unwrap().focused_id, first);
}
#[test]
fn resolving_last_hunk_pops_confirm_modal_after_delay() {
let mut app = app_in_diff();
for _ in 0..3 {
app.editor
.diff
.as_mut()
.unwrap()
.decide_focused(Decision::Accepted);
app.diff_advance_pending_since =
Some(Instant::now() - DIFF_ADVANCE_DELAY - Duration::from_millis(5));
app.tick_diff_advance();
}
assert!(app
.modal_stack
.contains::<crate::app::modal::DiffResolveConfirmModal>());
}
}