diff --git a/helix-term/src/handlers/auto_save.rs b/helix-term/src/handlers/auto_save.rs index d3f7f6fc124ad..77d9262e2b672 100644 --- a/helix-term/src/handlers/auto_save.rs +++ b/helix-term/src/handlers/auto_save.rs @@ -1,39 +1,75 @@ -use std::time::Duration; +use std::{ + sync::{ + atomic::{self, AtomicBool}, + Arc, + }, + time::Duration, +}; use anyhow::Ok; use arc_swap::access::Access; use helix_event::{register_hook, send_blocking}; -use helix_view::{events::DocumentDidChange, handlers::Handlers, Editor}; +use helix_view::{document::Mode, events::DocumentDidChange, handlers::Handlers, Editor}; use tokio::time::Instant; use crate::{ commands, compositor, + events::OnModeSwitch, job::{self, Jobs}, }; #[derive(Debug)] -pub(super) struct AutoSaveHandler; +pub(super) struct AutoSaveHandler { + save_pending: Arc, +} impl AutoSaveHandler { pub fn new() -> AutoSaveHandler { - AutoSaveHandler + AutoSaveHandler { + save_pending: Default::default(), + } } } impl helix_event::AsyncHook for AutoSaveHandler { - type Event = u64; + type Event = Option; fn handle_event( &mut self, - timeout: Self::Event, - _: Option, + event: Self::Event, + existing_debounce: Option, ) -> Option { - Some(Instant::now() + Duration::from_millis(timeout)) + match event { + Some(delay) => Some(Instant::now() + Duration::from_millis(delay)), + None => { + 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) { - job::dispatch_blocking(move |editor, _| request_auto_save(editor)) + let save_pending = self.save_pending.clone(); + job::dispatch_blocking(move |editor, _| { + if editor.mode() == Mode::Insert { + // Avoid saving while in insert mode since this mixes up + // the modification indicator and prevents future saves. + save_pending.store(true, atomic::Ordering::Relaxed); + } else { + request_auto_save(editor); + save_pending.store(false, atomic::Ordering::Relaxed); + } + }) } } @@ -54,7 +90,15 @@ pub(super) fn register_hooks(handlers: &Handlers) { register_hook!(move |event: &mut DocumentDidChange<'_>| { let config = event.doc.config.load(); if config.auto_save.after_delay.enable { - send_blocking(&tx, config.auto_save.after_delay.timeout); + send_blocking(&tx, Some(config.auto_save.after_delay.timeout)); + } + Ok(()) + }); + + let tx = handlers.auto_save.clone(); + register_hook!(move |event: &mut OnModeSwitch<'_, '_>| { + if event.old_mode == Mode::Insert { + send_blocking(&tx, None) } Ok(()) }); diff --git a/helix-view/src/handlers.rs b/helix-view/src/handlers.rs index 352abb8813068..ef9c97b658037 100644 --- a/helix-view/src/handlers.rs +++ b/helix-view/src/handlers.rs @@ -11,7 +11,7 @@ pub struct Handlers { // only public because most of the actual implementation is in helix-term right now :/ pub completions: Sender, pub signature_hints: Sender, - pub auto_save: Sender, + pub auto_save: Sender>, } impl Handlers {