Skip to content

Commit

Permalink
Delay auto-save until exiting insert mode (#11047)
Browse files Browse the repository at this point in the history
Saving while in insert mode causes issues with the modification
indicator and this is very easy to reproduce with the current state of
the auto-save hook. We can tweak the hook slightly to await the mode
switch out of insert mode to perform the save.

The debounce is preserved: if you save and then immediately exit insert
mode the debounce will be respected. If the debounce lapses while you
are in insert mode, the save occurs as you switch out of insert mode
immediately.
  • Loading branch information
the-mikedavis authored Jun 29, 2024
1 parent b4811f7 commit dca952c
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 11 deletions.
76 changes: 66 additions & 10 deletions helix-term/src/handlers/auto_save.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,82 @@
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::{AutoSaveEvent, 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<AtomicBool>,
}

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

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

fn handle_event(
&mut self,
timeout: Self::Event,
_: Option<tokio::time::Instant>,
event: Self::Event,
existing_debounce: Option<tokio::time::Instant>,
) -> Option<Instant> {
Some(Instant::now() + Duration::from_millis(timeout))
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) {
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);
}
})
}
}

Expand All @@ -54,7 +97,20 @@ 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,
AutoSaveEvent::DocumentChanged {
save_after: 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, AutoSaveEvent::LeftInsertMode)
}
Ok(())
});
Expand Down
8 changes: 7 additions & 1 deletion helix-view/src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ use crate::{DocumentId, Editor, ViewId};
pub mod dap;
pub mod lsp;

#[derive(Debug)]
pub enum AutoSaveEvent {
DocumentChanged { save_after: u64 },
LeftInsertMode,
}

pub struct Handlers {
// only public because most of the actual implementation is in helix-term right now :/
pub completions: Sender<lsp::CompletionEvent>,
pub signature_hints: Sender<lsp::SignatureHelpEvent>,
pub auto_save: Sender<u64>,
pub auto_save: Sender<AutoSaveEvent>,
}

impl Handlers {
Expand Down

0 comments on commit dca952c

Please sign in to comment.