Repository: helix-editor/helix
File: helix-term/src/handlers/auto_save.rs (Rust)

Code before:
};

#[derive(Debug)]
pub(super) struct AutoSaveHandler {
    save_pending: bool,
}

impl AutoSaveHandler {
    pub fn new() -> AutoSaveHandler {
        AutoSaveHandler {
            save_pending: false,
        }
    }
}

impl helix_event::AsyncHook for AutoSaveHandler {
    type Event = AutoSaveEvent;

    fn handle_event(
        &mut self,
        event: Self::Event,
        existing_debounce: Option<tokio::time::Instant>,
    ) -> Option<Instant> {
        match event {
            Self::Event::DocumentChanged { debounce } => {
                self.save_pending = true;
                Some(Instant::now() + Duration::from_millis(debounce))
            }
            Self::Event::LeftInsertMode => {
                if existing_debounce.is_some() {
                    // If the change happened more recently than the debounce, let the
                    // debounce run down before saving.
                    existing_debounce
                } else {
                    // Otherwise if there is a save pending, save immediately.
                    if self.save_pending {
                        self.finish_debounce();
                    }
                    None
                }
            }
        }
    }

    fn finish_debounce(&mut self) {
        job::dispatch_blocking(move |editor, _| {
            if editor.mode() != Mode::Insert {
                request_auto_save(editor)
            }
        })
    }

Review comment (bug):
as far as I can see we never reset `save_pending` back to false. I guess that would need to happen when leaving insertmodel? This also sets this to true in normal mode would also cause excess/incorrect saves. 

I actually had something else in mind:

* make `save_pending` an `Arc<AtomicBool>`, only inside finish_deboucne/dispatch_blocking if the mode is insert mode store true in there (needs to be an Arc/AtomicBool so we can move it inside the callback)
* Another clone of the arc is moved into the mode switch callback. When going from insert to normal mode that callback will check the value of the shared bool. If it's true it will instantly save (not send an even, save right in the mode switch handler) and then store false in the callback

I think doing it as an event that triggers a new save could work too. But I think the Arc/AtomicBool that only gets set to true if the calblack actually failed (so only we we are actually in normal mode when trying to save) is definitly necessary

Code after:
use tokio::time::Instant;

use crate::{
    commands, compositor,
    events::OnModeSwitch,
    job::{self, Jobs},
};

#[derive(Debug)]
pub(super) struct AutoSaveHandler {
    save_pending: Arc<AtomicBool>,
}

impl AutoSaveHandler {
    pub fn new() -> AutoSaveHandler {
        AutoSaveHandler {
            save_pending: Default::default(),
        }
    }
}

impl helix_event::AsyncHook for AutoSaveHandler {
    type Event = AutoSaveEvent;

    fn handle_event(
        &mut self,
        event: Self::Event,
        existing_debounce: Option<tokio::time::Instant>,
    ) -> Option<Instant> {
        match event {
            Self::Event::DocumentChanged { save_after } => {
                Some(Instant::now() + Duration::from_millis(save_after))
            }
            Self::Event::LeftInsertMode => {
                if existing_debounce.is_some() {
                    // If the change happened more recently than the debounce, let the
                    // debounce run down before saving.
                    existing_debounce
                } else {
                    // Otherwise if there is a save pending, save immediately.
                    if self.save_pending.load(atomic::Ordering::Relaxed) {
                        self.finish_debounce();
                    }
                    None
                }
            }
        }
    }

    fn finish_debounce(&mut self) {
        let save_pending = self.save_pending.clone();