helix/helix-term/src/handlers/auto_save.rs

118 lines
3.2 KiB
Rust
Raw Normal View History

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::{
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 {
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();
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);
}
})
}
}
fn request_auto_save(editor: &mut Editor) {
let context = &mut compositor::Context {
editor,
scroll: Some(0),
jobs: &mut Jobs::new(),
};
if let Err(e) = commands::typed::write_all_impl(context, false, false) {
context.editor.set_error(format!("{}", e));
}
}
pub(super) fn register_hooks(handlers: &Handlers) {
let tx = handlers.auto_save.clone();
register_hook!(move |event: &mut DocumentDidChange<'_>| {
let config = event.doc.config.load();
if config.auto_save.after_delay.enable {
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(())
});
}