From fc0fb5f8a8a4bac464d92f32c3a229fdd1e535dd Mon Sep 17 00:00:00 2001 From: Miguel Perez Date: Thu, 15 Feb 2024 10:34:07 -0800 Subject: [PATCH 01/13] auto save after delay --- helix-term/src/application.rs | 14 ++++++++++++++ helix-term/src/ui/editor.rs | 9 +++++++++ helix-view/src/editor.rs | 27 +++++++++++++++++++++++++++ helix-view/src/input.rs | 1 + 4 files changed, 51 insertions(+) diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index f71eed209eed..b250921df804 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -535,6 +535,16 @@ impl Application { } } + pub async fn handle_save_delay_timeout(&mut self) { + let mut cx = crate::compositor::Context { + editor: &mut self.editor, + jobs: &mut self.jobs, + scroll: None, + }; + + self.compositor.handle_event(&Event::SaveDelayTimeout, &mut cx); + } + pub fn handle_document_write(&mut self, doc_save_event: DocumentSavedEventResult) { let doc_save_event = match doc_save_event { Ok(event) => event, @@ -614,6 +624,10 @@ impl Application { return true; } } + EditorEvent::SaveDelayTimer => { + self.editor.clear_save_delay_timer(); + self.handle_save_delay_timeout().await; + } } false diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 97f90f625549..240494192a14 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -1346,6 +1346,7 @@ impl Component for EditorView { } Event::Key(mut key) => { cx.editor.reset_idle_timer(); + cx.editor.reset_save_delay_timer(); canonicalize_key(&mut key); // clear status @@ -1446,6 +1447,14 @@ impl Component for EditorView { Event::Mouse(event) => self.handle_mouse_event(event, &mut cx), Event::IdleTimeout => self.handle_idle_timeout(&mut cx), + Event::SaveDelayTimeout => { + if context.editor.config().auto_save { + if let Err(e) = commands::typed::write_all_impl(context, false, false) { + context.editor.set_error(format!("{}", e)); + } + } + EventResult::Consumed(None) + } Event::FocusGained => { self.terminal_focused = true; EventResult::Consumed(None) diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index ef4918539a54..7b2d8f36a817 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -277,6 +277,13 @@ pub struct Config { deserialize_with = "deserialize_duration_millis" )] pub idle_timeout: Duration, + /// Time in milliseconds since last keypress before auto save timers trigger. + /// Used for various UI timeouts. Defaults to 1000ms. + #[serde( + serialize_with = "serialize_duration_millis", + deserialize_with = "deserialize_duration_millis" + )] + pub save_delay_timeout: Duration, /// Time in milliseconds after typing a word character before auto completions /// are shown, set to 5 for instant. Defaults to 250ms. #[serde( @@ -883,6 +890,7 @@ impl Default for Config { auto_format: true, auto_save: false, idle_timeout: Duration::from_millis(250), + save_delay_timeout: Duration::from_millis(1000), completion_timeout: Duration::from_millis(250), preview_completion_insert: true, completion_trigger_len: 2, @@ -988,6 +996,7 @@ pub struct Editor { pub auto_pairs: Option, pub idle_timer: Pin>, + pub save_delay_timer: Pin>, redraw_timer: Pin>, last_motion: Option, pub last_completion: Option, @@ -1023,6 +1032,7 @@ pub enum EditorEvent { LanguageServerMessage((LanguageServerId, Call)), DebuggerEvent(dap::Payload), IdleTimer, + SaveDelayTimer, Redraw, } @@ -1118,6 +1128,7 @@ impl Editor { status_msg: None, autoinfo: None, idle_timer: Box::pin(sleep(conf.idle_timeout)), + save_delay_timer: Box::pin(sleep(conf.save_delay_timeout)), redraw_timer: Box::pin(sleep(Duration::MAX)), last_motion: None, last_completion: None, @@ -1187,6 +1198,19 @@ impl Editor { .reset(Instant::now() + config.idle_timeout); } + pub fn clear_save_delay_timer(&mut self) { + self.save_delay_timer + .as_mut() + .reset(Instant::now() + Duration::from_secs(86400 * 365 * 30)); + } + + pub fn reset_save_delay_timer(&mut self) { + let config = self.config(); + self.save_delay_timer + .as_mut() + .reset(Instant::now() + config.save_delay_timeout); + } + pub fn clear_status(&mut self) { self.status_msg = None; } @@ -2004,6 +2028,9 @@ impl Editor { _ = &mut self.idle_timer => { return EditorEvent::IdleTimer } + _ = &mut self.save_delay_timer => { + return EditorEvent::SaveDelayTimer + } } } } diff --git a/helix-view/src/input.rs b/helix-view/src/input.rs index 5f5067eac9f8..0d516a6f690f 100644 --- a/helix-view/src/input.rs +++ b/helix-view/src/input.rs @@ -15,6 +15,7 @@ pub enum Event { Paste(String), Resize(u16, u16), IdleTimeout, + SaveDelayTimeout } #[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] From 3ca4f1b50fb338b72703cff665c23bd7623d4b11 Mon Sep 17 00:00:00 2001 From: Miguel Perez Date: Thu, 15 Feb 2024 17:21:14 -0800 Subject: [PATCH 02/13] configable --- helix-term/src/ui/editor.rs | 6 +++--- helix-view/src/editor.rs | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 240494192a14..492bdbc1cddf 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -21,7 +21,7 @@ use helix_core::{ }; use helix_view::{ document::{Mode, SavePoint, SCRATCH_BUFFER_NAME}, - editor::{CompleteAction, CursorShapeConfig}, + editor::{CompleteAction, CursorShapeConfig, SaveStyle}, graphics::{Color, CursorKind, Modifier, Rect, Style}, input::{KeyEvent, MouseButton, MouseEvent, MouseEventKind}, keyboard::{KeyCode, KeyModifiers}, @@ -1448,7 +1448,7 @@ impl Component for EditorView { Event::Mouse(event) => self.handle_mouse_event(event, &mut cx), Event::IdleTimeout => self.handle_idle_timeout(&mut cx), Event::SaveDelayTimeout => { - if context.editor.config().auto_save { + if context.editor.config().auto_save && context.editor.config().save_style == SaveStyle::Delay { if let Err(e) = commands::typed::write_all_impl(context, false, false) { context.editor.set_error(format!("{}", e)); } @@ -1460,7 +1460,7 @@ impl Component for EditorView { EventResult::Consumed(None) } Event::FocusLost => { - if context.editor.config().auto_save { + if context.editor.config().auto_save && context.editor.config().save_style == SaveStyle::Focus { if let Err(e) = commands::typed::write_all_impl(context, false, false) { context.editor.set_error(format!("{}", e)); } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 7b2d8f36a817..a764a547ff27 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -268,6 +268,8 @@ pub struct Config { pub auto_format: bool, /// Automatic save on focus lost. Defaults to false. pub auto_save: bool, + /// When saves are performed. Defaults to on focus lost. + pub save_style: SaveStyle, /// Set a global text_width pub text_width: usize, /// Time in milliseconds since last keypress before idle timers trigger. @@ -778,6 +780,13 @@ impl WhitespaceRender { } } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum SaveStyle { + Focus, + Delay +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(default)] pub struct WhitespaceCharacters { @@ -889,6 +898,7 @@ impl Default for Config { auto_completion: true, auto_format: true, auto_save: false, + save_style: SaveStyle::Focus, idle_timeout: Duration::from_millis(250), save_delay_timeout: Duration::from_millis(1000), completion_timeout: Duration::from_millis(250), From 38d19211a6b4800429e17f79f5b3432aa02a0b71 Mon Sep 17 00:00:00 2001 From: Miguel Perez Date: Fri, 16 Feb 2024 09:32:34 -0800 Subject: [PATCH 03/13] clearer names --- helix-term/src/ui/editor.rs | 4 ++-- helix-view/src/editor.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 492bdbc1cddf..386bbe066186 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -1448,7 +1448,7 @@ impl Component for EditorView { Event::Mouse(event) => self.handle_mouse_event(event, &mut cx), Event::IdleTimeout => self.handle_idle_timeout(&mut cx), Event::SaveDelayTimeout => { - if context.editor.config().auto_save && context.editor.config().save_style == SaveStyle::Delay { + if context.editor.config().auto_save && context.editor.config().save_style == SaveStyle::AfterDelay { if let Err(e) = commands::typed::write_all_impl(context, false, false) { context.editor.set_error(format!("{}", e)); } @@ -1460,7 +1460,7 @@ impl Component for EditorView { EventResult::Consumed(None) } Event::FocusLost => { - if context.editor.config().auto_save && context.editor.config().save_style == SaveStyle::Focus { + if context.editor.config().auto_save && context.editor.config().save_style == SaveStyle::FocusLost { if let Err(e) = commands::typed::write_all_impl(context, false, false) { context.editor.set_error(format!("{}", e)); } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index a764a547ff27..0292e0f5cac7 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -783,8 +783,8 @@ impl WhitespaceRender { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] pub enum SaveStyle { - Focus, - Delay + FocusLost, + AfterDelay } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -898,7 +898,7 @@ impl Default for Config { auto_completion: true, auto_format: true, auto_save: false, - save_style: SaveStyle::Focus, + save_style: SaveStyle::FocusLost, idle_timeout: Duration::from_millis(250), save_delay_timeout: Duration::from_millis(1000), completion_timeout: Duration::from_millis(250), From fa501215ac016436ea6fdc548660970a571a2a69 Mon Sep 17 00:00:00 2001 From: Miguel Perez Date: Tue, 27 Feb 2024 19:21:30 -0800 Subject: [PATCH 04/13] init --- helix-term/src/application.rs | 14 ----- helix-term/src/handlers.rs | 7 +++ helix-term/src/handlers/auto_save.rs | 81 ++++++++++++++++++++++++++++ helix-term/src/ui/editor.rs | 9 ++-- helix-view/src/editor.rs | 21 +------- helix-view/src/handlers.rs | 1 + helix-view/src/handlers/lsp.rs | 11 ++++ helix-view/src/input.rs | 2 +- 8 files changed, 108 insertions(+), 38 deletions(-) create mode 100644 helix-term/src/handlers/auto_save.rs diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index b250921df804..f71eed209eed 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -535,16 +535,6 @@ impl Application { } } - pub async fn handle_save_delay_timeout(&mut self) { - let mut cx = crate::compositor::Context { - editor: &mut self.editor, - jobs: &mut self.jobs, - scroll: None, - }; - - self.compositor.handle_event(&Event::SaveDelayTimeout, &mut cx); - } - pub fn handle_document_write(&mut self, doc_save_event: DocumentSavedEventResult) { let doc_save_event = match doc_save_event { Ok(event) => event, @@ -624,10 +614,6 @@ impl Application { return true; } } - EditorEvent::SaveDelayTimer => { - self.editor.clear_save_delay_timer(); - self.handle_save_delay_timeout().await; - } } false diff --git a/helix-term/src/handlers.rs b/helix-term/src/handlers.rs index d45809d36a94..e958c4a9e0a8 100644 --- a/helix-term/src/handlers.rs +++ b/helix-term/src/handlers.rs @@ -7,23 +7,30 @@ use crate::config::Config; use crate::events; use crate::handlers::completion::CompletionHandler; use crate::handlers::signature_help::SignatureHelpHandler; +use crate::handlers::auto_save::AutoSaveHandler; pub use completion::trigger_auto_completion; pub use helix_view::handlers::Handlers; pub mod completion; mod signature_help; +mod auto_save; pub fn setup(config: Arc>) -> Handlers { events::register(); let completions = CompletionHandler::new(config).spawn(); let signature_hints = SignatureHelpHandler::new().spawn(); + let auto_save = AutoSaveHandler::new().spawn(); + let handlers = Handlers { completions, signature_hints, + auto_save, }; + completion::register_hooks(&handlers); signature_help::register_hooks(&handlers); + auto_save::register_hooks(&handlers); handlers } diff --git a/helix-term/src/handlers/auto_save.rs b/helix-term/src/handlers/auto_save.rs new file mode 100644 index 000000000000..1ff1fa884bf1 --- /dev/null +++ b/helix-term/src/handlers/auto_save.rs @@ -0,0 +1,81 @@ +use arc_swap::access::Access; + +use helix_event::{cancelation, register_hook, send_blocking, CancelRx, CancelTx}; +use helix_view::{ + editor::SaveStyle, + events::DocumentDidChange, + handlers::{ + lsp::{AutoSaveEvent, AutoSaveInvoked}, + Handlers, + }, Editor, +}; +use tokio::time::Instant; + +use crate::job; + +#[derive(Debug)] +enum State { + Open, + Closed, + Pending { request: CancelTx }, +} + +#[derive(Debug)] +pub(super) struct AutoSaveHandler { + trigger: Option, + state: State, +} + +impl AutoSaveHandler { + pub fn new() -> AutoSaveHandler { + AutoSaveHandler { + trigger: None, + state: State::Closed, + } + } +} + +impl helix_event::AsyncHook for AutoSaveHandler { + type Event = AutoSaveEvent; + + fn handle_event( + &mut self, + event: Self::Event, + timeout: Option, + ) -> Option { + match event { + AutoSaveEvent::Invoked => todo!(), + AutoSaveEvent::Cancel => { + self.state = State::Closed; + None + }, + } + } + + fn finish_debounce(&mut self) { + let invocation = self.trigger.take().unwrap(); + let (tx, rx) = cancelation(); + self.state = State::Pending { request: tx }; + job::dispatch_blocking(move |editor, _| request_auto_save(editor, invocation, rx)) + } +} + +fn request_auto_save( + editor: &mut Editor, + invoked: AutoSaveInvoked, + cancel: CancelRx, +) { + +} + +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 && config.save_style == SaveStyle::AfterDelay { + send_blocking(&tx, AutoSaveEvent::Cancel); + send_blocking(&tx, AutoSaveEvent::Invoked); + } + Ok(()) + }); +} diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 386bbe066186..c65fb0247349 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -1346,7 +1346,6 @@ impl Component for EditorView { } Event::Key(mut key) => { cx.editor.reset_idle_timer(); - cx.editor.reset_save_delay_timer(); canonicalize_key(&mut key); // clear status @@ -1448,7 +1447,9 @@ impl Component for EditorView { Event::Mouse(event) => self.handle_mouse_event(event, &mut cx), Event::IdleTimeout => self.handle_idle_timeout(&mut cx), Event::SaveDelayTimeout => { - if context.editor.config().auto_save && context.editor.config().save_style == SaveStyle::AfterDelay { + if context.editor.config().auto_save + && context.editor.config().save_style == SaveStyle::AfterDelay + { if let Err(e) = commands::typed::write_all_impl(context, false, false) { context.editor.set_error(format!("{}", e)); } @@ -1460,7 +1461,9 @@ impl Component for EditorView { EventResult::Consumed(None) } Event::FocusLost => { - if context.editor.config().auto_save && context.editor.config().save_style == SaveStyle::FocusLost { + if context.editor.config().auto_save + && context.editor.config().save_style == SaveStyle::FocusLost + { if let Err(e) = commands::typed::write_all_impl(context, false, false) { context.editor.set_error(format!("{}", e)); } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 0292e0f5cac7..172818a1959d 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -784,7 +784,7 @@ impl WhitespaceRender { #[serde(rename_all = "kebab-case")] pub enum SaveStyle { FocusLost, - AfterDelay + AfterDelay, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -1006,7 +1006,6 @@ pub struct Editor { pub auto_pairs: Option, pub idle_timer: Pin>, - pub save_delay_timer: Pin>, redraw_timer: Pin>, last_motion: Option, pub last_completion: Option, @@ -1042,7 +1041,6 @@ pub enum EditorEvent { LanguageServerMessage((LanguageServerId, Call)), DebuggerEvent(dap::Payload), IdleTimer, - SaveDelayTimer, Redraw, } @@ -1138,7 +1136,6 @@ impl Editor { status_msg: None, autoinfo: None, idle_timer: Box::pin(sleep(conf.idle_timeout)), - save_delay_timer: Box::pin(sleep(conf.save_delay_timeout)), redraw_timer: Box::pin(sleep(Duration::MAX)), last_motion: None, last_completion: None, @@ -1208,19 +1205,6 @@ impl Editor { .reset(Instant::now() + config.idle_timeout); } - pub fn clear_save_delay_timer(&mut self) { - self.save_delay_timer - .as_mut() - .reset(Instant::now() + Duration::from_secs(86400 * 365 * 30)); - } - - pub fn reset_save_delay_timer(&mut self) { - let config = self.config(); - self.save_delay_timer - .as_mut() - .reset(Instant::now() + config.save_delay_timeout); - } - pub fn clear_status(&mut self) { self.status_msg = None; } @@ -2038,9 +2022,6 @@ impl Editor { _ = &mut self.idle_timer => { return EditorEvent::IdleTimer } - _ = &mut self.save_delay_timer => { - return EditorEvent::SaveDelayTimer - } } } } diff --git a/helix-view/src/handlers.rs b/helix-view/src/handlers.rs index 724e7b1921ca..fdd030b442e3 100644 --- a/helix-view/src/handlers.rs +++ b/helix-view/src/handlers.rs @@ -11,6 +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, } impl Handlers { diff --git a/helix-view/src/handlers/lsp.rs b/helix-view/src/handlers/lsp.rs index beb106b2bf83..ad9d08ce39c6 100644 --- a/helix-view/src/handlers/lsp.rs +++ b/helix-view/src/handlers/lsp.rs @@ -44,6 +44,17 @@ pub enum SignatureHelpEvent { RequestComplete { open: bool }, } +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum AutoSaveInvoked { + AfterDelay, + FocusLost, +} + +pub enum AutoSaveEvent { + Invoked, + Cancel, +} + #[derive(Debug)] pub struct ApplyEditError { pub kind: ApplyEditErrorKind, diff --git a/helix-view/src/input.rs b/helix-view/src/input.rs index 0d516a6f690f..82837516c908 100644 --- a/helix-view/src/input.rs +++ b/helix-view/src/input.rs @@ -15,7 +15,7 @@ pub enum Event { Paste(String), Resize(u16, u16), IdleTimeout, - SaveDelayTimeout + SaveDelayTimeout, } #[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] From 345b72ab410d129c34b1a5a1eef1094ffb226161 Mon Sep 17 00:00:00 2001 From: Miguel Perez Date: Wed, 28 Feb 2024 00:53:51 -0800 Subject: [PATCH 05/13] working with some odd behaviour --- helix-term/src/handlers/auto_save.rs | 65 +++++++++++++++++----------- helix-term/src/ui/editor.rs | 10 ----- helix-view/src/handlers/lsp.rs | 5 +-- helix-view/src/input.rs | 1 - 4 files changed, 42 insertions(+), 39 deletions(-) diff --git a/helix-term/src/handlers/auto_save.rs b/helix-term/src/handlers/auto_save.rs index 1ff1fa884bf1..28412b23d4cd 100644 --- a/helix-term/src/handlers/auto_save.rs +++ b/helix-term/src/handlers/auto_save.rs @@ -1,23 +1,27 @@ +use std::time::Duration; + +use anyhow::Ok; use arc_swap::access::Access; -use helix_event::{cancelation, register_hook, send_blocking, CancelRx, CancelTx}; +use helix_event::{register_hook, send_blocking}; use helix_view::{ editor::SaveStyle, events::DocumentDidChange, handlers::{ lsp::{AutoSaveEvent, AutoSaveInvoked}, Handlers, - }, Editor, + }, + Editor, }; use tokio::time::Instant; -use crate::job; +use crate::{ + commands, compositor, job::{self, Jobs} +}; #[derive(Debug)] enum State { - Open, Closed, - Pending { request: CancelTx }, } #[derive(Debug)] @@ -40,32 +44,44 @@ impl helix_event::AsyncHook for AutoSaveHandler { fn handle_event( &mut self, - event: Self::Event, - timeout: Option, + _: Self::Event, + _: Option, ) -> Option { - match event { - AutoSaveEvent::Invoked => todo!(), - AutoSaveEvent::Cancel => { - self.state = State::Closed; - None - }, - } + // match event { + // AutoSaveEvent::Trigger => { + // if matches!(self.state, State::Closed) { + // self.trigger = Some(AutoSaveInvoked::Automatic); + // return Some(Instant::now() + Duration::from_millis(1000)); + // } + // } + // AutoSaveEvent::Cancel => { + // self.state = State::Closed; + // return None; + // } + // } + + // if self.trigger.is_none() { + // self.trigger = Some(AutoSaveInvoked::Automatic) + // } + + Some(Instant::now() + Duration::from_millis(1000)) } fn finish_debounce(&mut self) { - let invocation = self.trigger.take().unwrap(); - let (tx, rx) = cancelation(); - self.state = State::Pending { request: tx }; - job::dispatch_blocking(move |editor, _| request_auto_save(editor, invocation, rx)) + job::dispatch_blocking(move |editor, _| request_auto_save(editor)) } } -fn request_auto_save( - editor: &mut Editor, - invoked: AutoSaveInvoked, - cancel: CancelRx, -) { +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) { @@ -73,8 +89,7 @@ pub(super) fn register_hooks(handlers: &Handlers) { register_hook!(move |event: &mut DocumentDidChange<'_>| { let config = event.doc.config.load(); if config.auto_save && config.save_style == SaveStyle::AfterDelay { - send_blocking(&tx, AutoSaveEvent::Cancel); - send_blocking(&tx, AutoSaveEvent::Invoked); + send_blocking(&tx, AutoSaveEvent::Trigger); } Ok(()) }); diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index c65fb0247349..d35528298dfa 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -1446,16 +1446,6 @@ impl Component for EditorView { Event::Mouse(event) => self.handle_mouse_event(event, &mut cx), Event::IdleTimeout => self.handle_idle_timeout(&mut cx), - Event::SaveDelayTimeout => { - if context.editor.config().auto_save - && context.editor.config().save_style == SaveStyle::AfterDelay - { - if let Err(e) = commands::typed::write_all_impl(context, false, false) { - context.editor.set_error(format!("{}", e)); - } - } - EventResult::Consumed(None) - } Event::FocusGained => { self.terminal_focused = true; EventResult::Consumed(None) diff --git a/helix-view/src/handlers/lsp.rs b/helix-view/src/handlers/lsp.rs index ad9d08ce39c6..15c526ee8e89 100644 --- a/helix-view/src/handlers/lsp.rs +++ b/helix-view/src/handlers/lsp.rs @@ -46,12 +46,11 @@ pub enum SignatureHelpEvent { #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum AutoSaveInvoked { - AfterDelay, - FocusLost, + Automatic, } pub enum AutoSaveEvent { - Invoked, + Trigger, Cancel, } diff --git a/helix-view/src/input.rs b/helix-view/src/input.rs index 82837516c908..5f5067eac9f8 100644 --- a/helix-view/src/input.rs +++ b/helix-view/src/input.rs @@ -15,7 +15,6 @@ pub enum Event { Paste(String), Resize(u16, u16), IdleTimeout, - SaveDelayTimeout, } #[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] From 54ba8d2f3cf25b7b4e3501592d49f8bfd8ada1d2 Mon Sep 17 00:00:00 2001 From: Miguel Perez Date: Thu, 29 Feb 2024 17:39:02 -0800 Subject: [PATCH 06/13] working with greater consistency --- helix-term/src/handlers/auto_save.rs | 39 ++++++++++++++++------------ 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/helix-term/src/handlers/auto_save.rs b/helix-term/src/handlers/auto_save.rs index 28412b23d4cd..acfb898c2879 100644 --- a/helix-term/src/handlers/auto_save.rs +++ b/helix-term/src/handlers/auto_save.rs @@ -16,7 +16,8 @@ use helix_view::{ use tokio::time::Instant; use crate::{ - commands, compositor, job::{self, Jobs} + commands, compositor, + job::{self, Jobs}, }; #[derive(Debug)] @@ -44,25 +45,25 @@ impl helix_event::AsyncHook for AutoSaveHandler { fn handle_event( &mut self, - _: Self::Event, + event: Self::Event, _: Option, ) -> Option { - // match event { - // AutoSaveEvent::Trigger => { - // if matches!(self.state, State::Closed) { - // self.trigger = Some(AutoSaveInvoked::Automatic); - // return Some(Instant::now() + Duration::from_millis(1000)); - // } - // } - // AutoSaveEvent::Cancel => { - // self.state = State::Closed; - // return None; - // } - // } + match event { + AutoSaveEvent::Trigger => { + if matches!(self.state, State::Closed) { + self.trigger = Some(AutoSaveInvoked::Automatic); + return Some(Instant::now() + Duration::from_millis(1000)); + } + } + AutoSaveEvent::Cancel => { + self.state = State::Closed; + return None; + } + } - // if self.trigger.is_none() { - // self.trigger = Some(AutoSaveInvoked::Automatic) - // } + if self.trigger.is_none() { + self.trigger = Some(AutoSaveInvoked::Automatic) + } Some(Instant::now() + Duration::from_millis(1000)) } @@ -82,6 +83,10 @@ fn request_auto_save(editor: &mut Editor) { if let Err(e) = commands::typed::write_all_impl(context, false, false) { context.editor.set_error(format!("{}", e)); } + + if let Err(e) = context.block_try_flush_writes() { + context.editor.set_error(format!("{}", e)); + } } pub(super) fn register_hooks(handlers: &Handlers) { From 6a78f70c32f621fc1b0a759786bb1c012a44766f Mon Sep 17 00:00:00 2001 From: Hendrik Wolff Date: Thu, 6 Jun 2024 21:58:12 +0200 Subject: [PATCH 07/13] Apply reviewer suggestions - Remove unneccessary field - Remove blocking save --- helix-term/src/handlers/auto_save.rs | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/helix-term/src/handlers/auto_save.rs b/helix-term/src/handlers/auto_save.rs index acfb898c2879..d3faecdcc238 100644 --- a/helix-term/src/handlers/auto_save.rs +++ b/helix-term/src/handlers/auto_save.rs @@ -7,10 +7,7 @@ use helix_event::{register_hook, send_blocking}; use helix_view::{ editor::SaveStyle, events::DocumentDidChange, - handlers::{ - lsp::{AutoSaveEvent, AutoSaveInvoked}, - Handlers, - }, + handlers::{lsp::AutoSaveEvent, Handlers}, Editor, }; use tokio::time::Instant; @@ -20,6 +17,8 @@ use crate::{ job::{self, Jobs}, }; +const DEFAULT_DELAY: u64 = 3000; + #[derive(Debug)] enum State { Closed, @@ -27,14 +26,12 @@ enum State { #[derive(Debug)] pub(super) struct AutoSaveHandler { - trigger: Option, state: State, } impl AutoSaveHandler { pub fn new() -> AutoSaveHandler { AutoSaveHandler { - trigger: None, state: State::Closed, } } @@ -51,8 +48,7 @@ impl helix_event::AsyncHook for AutoSaveHandler { match event { AutoSaveEvent::Trigger => { if matches!(self.state, State::Closed) { - self.trigger = Some(AutoSaveInvoked::Automatic); - return Some(Instant::now() + Duration::from_millis(1000)); + return Some(Instant::now() + Duration::from_millis(DEFAULT_DELAY)); } } AutoSaveEvent::Cancel => { @@ -61,11 +57,7 @@ impl helix_event::AsyncHook for AutoSaveHandler { } } - if self.trigger.is_none() { - self.trigger = Some(AutoSaveInvoked::Automatic) - } - - Some(Instant::now() + Duration::from_millis(1000)) + Some(Instant::now() + Duration::from_millis(DEFAULT_DELAY)) } fn finish_debounce(&mut self) { @@ -83,10 +75,6 @@ fn request_auto_save(editor: &mut Editor) { if let Err(e) = commands::typed::write_all_impl(context, false, false) { context.editor.set_error(format!("{}", e)); } - - if let Err(e) = context.block_try_flush_writes() { - context.editor.set_error(format!("{}", e)); - } } pub(super) fn register_hooks(handlers: &Handlers) { From aad895f3e8ed42cc525131921fcfe3e9b756d91f Mon Sep 17 00:00:00 2001 From: Hendrik Wolff Date: Fri, 7 Jun 2024 12:49:34 +0200 Subject: [PATCH 08/13] Improve auto-save configuration Auto save can be configured to trigger on focus loss: ```toml auto-save.focus-lost = true|false ``` and after a time delay (in milli seconds) since last keypress: ```toml auto-save.after-delay.enable = true|false auto-save.after-delay.timeout = [0, u64::MAX] # default: 3000 ``` --- helix-term/src/handlers.rs | 4 +- helix-term/src/handlers/auto_save.rs | 14 ++- helix-term/src/ui/editor.rs | 6 +- helix-view/src/editor.rs | 130 +++++++++++++++++++++++---- helix-view/src/handlers/lsp.rs | 3 +- 5 files changed, 123 insertions(+), 34 deletions(-) diff --git a/helix-term/src/handlers.rs b/helix-term/src/handlers.rs index e958c4a9e0a8..fc927313785a 100644 --- a/helix-term/src/handlers.rs +++ b/helix-term/src/handlers.rs @@ -5,16 +5,16 @@ use helix_event::AsyncHook; use crate::config::Config; use crate::events; +use crate::handlers::auto_save::AutoSaveHandler; use crate::handlers::completion::CompletionHandler; use crate::handlers::signature_help::SignatureHelpHandler; -use crate::handlers::auto_save::AutoSaveHandler; pub use completion::trigger_auto_completion; pub use helix_view::handlers::Handlers; +mod auto_save; pub mod completion; mod signature_help; -mod auto_save; pub fn setup(config: Arc>) -> Handlers { events::register(); diff --git a/helix-term/src/handlers/auto_save.rs b/helix-term/src/handlers/auto_save.rs index d3faecdcc238..05f9cf1fa190 100644 --- a/helix-term/src/handlers/auto_save.rs +++ b/helix-term/src/handlers/auto_save.rs @@ -5,7 +5,7 @@ use arc_swap::access::Access; use helix_event::{register_hook, send_blocking}; use helix_view::{ - editor::SaveStyle, + editor::DEFAULT_AUTO_SAVE_DELAY, events::DocumentDidChange, handlers::{lsp::AutoSaveEvent, Handlers}, Editor, @@ -17,8 +17,6 @@ use crate::{ job::{self, Jobs}, }; -const DEFAULT_DELAY: u64 = 3000; - #[derive(Debug)] enum State { Closed, @@ -46,9 +44,9 @@ impl helix_event::AsyncHook for AutoSaveHandler { _: Option, ) -> Option { match event { - AutoSaveEvent::Trigger => { + AutoSaveEvent::Trigger(delay) => { if matches!(self.state, State::Closed) { - return Some(Instant::now() + Duration::from_millis(DEFAULT_DELAY)); + return Some(Instant::now() + Duration::from_millis(delay)); } } AutoSaveEvent::Cancel => { @@ -57,7 +55,7 @@ impl helix_event::AsyncHook for AutoSaveHandler { } } - Some(Instant::now() + Duration::from_millis(DEFAULT_DELAY)) + Some(Instant::now() + Duration::from_millis(DEFAULT_AUTO_SAVE_DELAY)) } fn finish_debounce(&mut self) { @@ -81,8 +79,8 @@ 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 && config.save_style == SaveStyle::AfterDelay { - send_blocking(&tx, AutoSaveEvent::Trigger); + if let Some(delay) = config.auto_save.after_delay { + send_blocking(&tx, AutoSaveEvent::Trigger(delay)); } Ok(()) }); diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index d35528298dfa..d584afbb005a 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -21,7 +21,7 @@ use helix_core::{ }; use helix_view::{ document::{Mode, SavePoint, SCRATCH_BUFFER_NAME}, - editor::{CompleteAction, CursorShapeConfig, SaveStyle}, + editor::{CompleteAction, CursorShapeConfig}, graphics::{Color, CursorKind, Modifier, Rect, Style}, input::{KeyEvent, MouseButton, MouseEvent, MouseEventKind}, keyboard::{KeyCode, KeyModifiers}, @@ -1451,9 +1451,7 @@ impl Component for EditorView { EventResult::Consumed(None) } Event::FocusLost => { - if context.editor.config().auto_save - && context.editor.config().save_style == SaveStyle::FocusLost - { + if context.editor.config().auto_save.focus_lost { if let Err(e) = commands::typed::write_all_impl(context, false, false) { context.editor.set_error(format!("{}", e)); } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 172818a1959d..f6be5c216865 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -55,6 +55,8 @@ use arc_swap::{ ArcSwap, }; +pub const DEFAULT_AUTO_SAVE_DELAY: u64 = 3000; + fn deserialize_duration_millis<'de, D>(deserializer: D) -> Result where D: serde::Deserializer<'de>, @@ -266,10 +268,10 @@ pub struct Config { pub auto_completion: bool, /// Automatic formatting on save. Defaults to true. pub auto_format: bool, - /// Automatic save on focus lost. Defaults to false. - pub auto_save: bool, - /// When saves are performed. Defaults to on focus lost. - pub save_style: SaveStyle, + /// Automatic save on focus lost and/or after delay. + /// Time delay in milliseconds since last keypress after which auto save timer triggers. + /// Time delay defaults to false with 3000ms delay. Focus lost defaults to false. + pub auto_save: AutoSave, /// Set a global text_width pub text_width: usize, /// Time in milliseconds since last keypress before idle timers trigger. @@ -279,13 +281,6 @@ pub struct Config { deserialize_with = "deserialize_duration_millis" )] pub idle_timeout: Duration, - /// Time in milliseconds since last keypress before auto save timers trigger. - /// Used for various UI timeouts. Defaults to 1000ms. - #[serde( - serialize_with = "serialize_duration_millis", - deserialize_with = "deserialize_duration_millis" - )] - pub save_delay_timeout: Duration, /// Time in milliseconds after typing a word character before auto completions /// are shown, set to 5 for instant. Defaults to 250ms. #[serde( @@ -780,11 +775,110 @@ impl WhitespaceRender { } } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] -pub enum SaveStyle { - FocusLost, - AfterDelay, +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct AutoSave { + /// Auto save after `n` milli seconds. Defaults to None. + pub after_delay: Option, + /// Auto save on focus lost. Defaults to false. + pub focus_lost: bool, +} + +// Intermediate type to have a flattened config +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] +#[serde(untagged, deny_unknown_fields, rename_all = "kebab-case")] +enum AutoSaveToml { + EnableFocusLost(bool), + #[serde(rename_all = "kebab-case")] + AutoSave { + #[serde(default)] + after_delay: AutoSaveAfterDelay, + #[serde(default)] + focus_lost: bool, + }, +} + +// Intermediate type to have a flattened config +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +struct AutoSaveAfterDelay { + #[serde(default)] + pub enable: bool, + #[serde(default = "default_auto_save_delay")] + pub timeout: u64, +} + +fn default_auto_save_delay() -> u64 { + DEFAULT_AUTO_SAVE_DELAY +} + +impl From for AutoSaveToml { + fn from(value: AutoSave) -> Self { + let after_delay = match value.after_delay { + Some(timeout) => AutoSaveAfterDelay { + enable: true, + timeout, + }, + None => Default::default(), + }; + Self::AutoSave { + after_delay, + focus_lost: value.focus_lost, + } + } +} + +impl From for AutoSave { + fn from(value: AutoSaveToml) -> Self { + match value { + AutoSaveToml::EnableFocusLost(focus_lost) => Self { + focus_lost, + ..Default::default() + }, + AutoSaveToml::AutoSave { + after_delay, + focus_lost, + } => { + let after_delay: Option = after_delay.enable.then_some(after_delay.timeout); + Self { + after_delay, + focus_lost, + } + } + } + } +} + +impl Default for AutoSaveToml { + fn default() -> Self { + Self::EnableFocusLost(false) + } +} + +impl Default for AutoSaveAfterDelay { + fn default() -> Self { + Self { + enable: false, + timeout: DEFAULT_AUTO_SAVE_DELAY, + } + } +} + +impl<'de> Deserialize<'de> for AutoSave { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(AutoSaveToml::deserialize(deserializer)?.into()) + } +} +impl Serialize for AutoSave { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let a: AutoSaveToml = self.clone().into(); + a.serialize(serializer) + } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -897,10 +991,8 @@ impl Default for Config { auto_pairs: AutoPairConfig::default(), auto_completion: true, auto_format: true, - auto_save: false, - save_style: SaveStyle::FocusLost, + auto_save: AutoSave::default(), idle_timeout: Duration::from_millis(250), - save_delay_timeout: Duration::from_millis(1000), completion_timeout: Duration::from_millis(250), preview_completion_insert: true, completion_trigger_len: 2, diff --git a/helix-view/src/handlers/lsp.rs b/helix-view/src/handlers/lsp.rs index 15c526ee8e89..b0092e076ad1 100644 --- a/helix-view/src/handlers/lsp.rs +++ b/helix-view/src/handlers/lsp.rs @@ -50,7 +50,8 @@ pub enum AutoSaveInvoked { } pub enum AutoSaveEvent { - Trigger, + // contains delay in milli seconds + Trigger(u64), Cancel, } From b130b4c72c72e1fe9f5c1ea63ca9cbd9274d8dfa Mon Sep 17 00:00:00 2001 From: Hendrik Wolff Date: Sat, 8 Jun 2024 22:05:15 +0200 Subject: [PATCH 09/13] Remove boilerplate and unnecessary types --- helix-term/src/handlers/auto_save.rs | 5 +- helix-view/src/editor.rs | 106 +++++++-------------------- 2 files changed, 30 insertions(+), 81 deletions(-) diff --git a/helix-term/src/handlers/auto_save.rs b/helix-term/src/handlers/auto_save.rs index 05f9cf1fa190..b27a669b7be9 100644 --- a/helix-term/src/handlers/auto_save.rs +++ b/helix-term/src/handlers/auto_save.rs @@ -79,8 +79,9 @@ 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 let Some(delay) = config.auto_save.after_delay { - send_blocking(&tx, AutoSaveEvent::Trigger(delay)); + if config.auto_save.after_delay.enable { + let timeout = config.auto_save.after_delay.timeout; + send_blocking(&tx, AutoSaveEvent::Trigger(timeout)); } Ok(()) }); diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index f6be5c216865..c16dec74a8f9 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -271,6 +271,7 @@ pub struct Config { /// Automatic save on focus lost and/or after delay. /// Time delay in milliseconds since last keypress after which auto save timer triggers. /// Time delay defaults to false with 3000ms delay. Focus lost defaults to false. + #[serde(deserialize_with = "deserialize_auto_save")] pub auto_save: AutoSave, /// Set a global text_width pub text_width: usize, @@ -775,85 +776,26 @@ impl WhitespaceRender { } } -#[derive(Debug, Default, Clone, PartialEq, Eq)] +#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case")] pub struct AutoSave { /// Auto save after `n` milli seconds. Defaults to None. - pub after_delay: Option, + #[serde(default)] + pub after_delay: AutoSaveAfterDelay, /// Auto save on focus lost. Defaults to false. + #[serde(default)] pub focus_lost: bool, } -// Intermediate type to have a flattened config -#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] -#[serde(untagged, deny_unknown_fields, rename_all = "kebab-case")] -enum AutoSaveToml { - EnableFocusLost(bool), - #[serde(rename_all = "kebab-case")] - AutoSave { - #[serde(default)] - after_delay: AutoSaveAfterDelay, - #[serde(default)] - focus_lost: bool, - }, -} - -// Intermediate type to have a flattened config #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] #[serde(deny_unknown_fields)] -struct AutoSaveAfterDelay { +pub struct AutoSaveAfterDelay { #[serde(default)] pub enable: bool, #[serde(default = "default_auto_save_delay")] pub timeout: u64, } -fn default_auto_save_delay() -> u64 { - DEFAULT_AUTO_SAVE_DELAY -} - -impl From for AutoSaveToml { - fn from(value: AutoSave) -> Self { - let after_delay = match value.after_delay { - Some(timeout) => AutoSaveAfterDelay { - enable: true, - timeout, - }, - None => Default::default(), - }; - Self::AutoSave { - after_delay, - focus_lost: value.focus_lost, - } - } -} - -impl From for AutoSave { - fn from(value: AutoSaveToml) -> Self { - match value { - AutoSaveToml::EnableFocusLost(focus_lost) => Self { - focus_lost, - ..Default::default() - }, - AutoSaveToml::AutoSave { - after_delay, - focus_lost, - } => { - let after_delay: Option = after_delay.enable.then_some(after_delay.timeout); - Self { - after_delay, - focus_lost, - } - } - } - } -} - -impl Default for AutoSaveToml { - fn default() -> Self { - Self::EnableFocusLost(false) - } -} - impl Default for AutoSaveAfterDelay { fn default() -> Self { Self { @@ -863,21 +805,27 @@ impl Default for AutoSaveAfterDelay { } } -impl<'de> Deserialize<'de> for AutoSave { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Ok(AutoSaveToml::deserialize(deserializer)?.into()) - } +fn default_auto_save_delay() -> u64 { + DEFAULT_AUTO_SAVE_DELAY } -impl Serialize for AutoSave { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let a: AutoSaveToml = self.clone().into(); - a.serialize(serializer) + +fn deserialize_auto_save<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + #[derive(Deserialize, Serialize)] + #[serde(untagged, deny_unknown_fields, rename_all = "kebab-case")] + enum AutoSaveToml { + EnableFocusLost(bool), + AutoSave(AutoSave), + } + + match AutoSaveToml::deserialize(deserializer)? { + AutoSaveToml::EnableFocusLost(focus_lost) => Ok(AutoSave { + focus_lost, + ..Default::default() + }), + AutoSaveToml::AutoSave(auto_save) => Ok(auto_save), } } From cf7389f5486550d4add5f7bca3002161442a0fab Mon Sep 17 00:00:00 2001 From: Hendrik Wolff Date: Sat, 8 Jun 2024 22:18:39 +0200 Subject: [PATCH 10/13] Remove more useless types --- helix-term/src/handlers/auto_save.rs | 41 +++++----------------------- helix-view/src/editor.rs | 2 +- helix-view/src/handlers.rs | 2 +- helix-view/src/handlers/lsp.rs | 11 -------- 4 files changed, 9 insertions(+), 47 deletions(-) diff --git a/helix-term/src/handlers/auto_save.rs b/helix-term/src/handlers/auto_save.rs index b27a669b7be9..d3f7f6fc124a 100644 --- a/helix-term/src/handlers/auto_save.rs +++ b/helix-term/src/handlers/auto_save.rs @@ -4,12 +4,7 @@ use anyhow::Ok; use arc_swap::access::Access; use helix_event::{register_hook, send_blocking}; -use helix_view::{ - editor::DEFAULT_AUTO_SAVE_DELAY, - events::DocumentDidChange, - handlers::{lsp::AutoSaveEvent, Handlers}, - Editor, -}; +use helix_view::{events::DocumentDidChange, handlers::Handlers, Editor}; use tokio::time::Instant; use crate::{ @@ -18,44 +13,23 @@ use crate::{ }; #[derive(Debug)] -enum State { - Closed, -} - -#[derive(Debug)] -pub(super) struct AutoSaveHandler { - state: State, -} +pub(super) struct AutoSaveHandler; impl AutoSaveHandler { pub fn new() -> AutoSaveHandler { - AutoSaveHandler { - state: State::Closed, - } + AutoSaveHandler } } impl helix_event::AsyncHook for AutoSaveHandler { - type Event = AutoSaveEvent; + type Event = u64; fn handle_event( &mut self, - event: Self::Event, + timeout: Self::Event, _: Option, ) -> Option { - match event { - AutoSaveEvent::Trigger(delay) => { - if matches!(self.state, State::Closed) { - return Some(Instant::now() + Duration::from_millis(delay)); - } - } - AutoSaveEvent::Cancel => { - self.state = State::Closed; - return None; - } - } - - Some(Instant::now() + Duration::from_millis(DEFAULT_AUTO_SAVE_DELAY)) + Some(Instant::now() + Duration::from_millis(timeout)) } fn finish_debounce(&mut self) { @@ -80,8 +54,7 @@ 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 { - let timeout = config.auto_save.after_delay.timeout; - send_blocking(&tx, AutoSaveEvent::Trigger(timeout)); + send_blocking(&tx, config.auto_save.after_delay.timeout); } Ok(()) }); diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index c16dec74a8f9..e2ce49aa25a2 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -55,7 +55,7 @@ use arc_swap::{ ArcSwap, }; -pub const DEFAULT_AUTO_SAVE_DELAY: u64 = 3000; +const DEFAULT_AUTO_SAVE_DELAY: u64 = 3000; fn deserialize_duration_millis<'de, D>(deserializer: D) -> Result where diff --git a/helix-view/src/handlers.rs b/helix-view/src/handlers.rs index fdd030b442e3..352abb881306 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 { diff --git a/helix-view/src/handlers/lsp.rs b/helix-view/src/handlers/lsp.rs index b0092e076ad1..beb106b2bf83 100644 --- a/helix-view/src/handlers/lsp.rs +++ b/helix-view/src/handlers/lsp.rs @@ -44,17 +44,6 @@ pub enum SignatureHelpEvent { RequestComplete { open: bool }, } -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum AutoSaveInvoked { - Automatic, -} - -pub enum AutoSaveEvent { - // contains delay in milli seconds - Trigger(u64), - Cancel, -} - #[derive(Debug)] pub struct ApplyEditError { pub kind: ApplyEditErrorKind, From 0a8d8d5ac80605bcde1b2b3cf74310cf52ad31af Mon Sep 17 00:00:00 2001 From: Hendrik Wolff Date: Sat, 8 Jun 2024 22:49:46 +0200 Subject: [PATCH 11/13] Update docs for auto-save.after-delay --- book/src/editor.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/book/src/editor.md b/book/src/editor.md index 88541a7bc0e1..62d5dd8dc252 100644 --- a/book/src/editor.md +++ b/book/src/editor.md @@ -32,7 +32,9 @@ | `gutters` | Gutters to display: Available are `diagnostics` and `diff` and `line-numbers` and `spacer`, note that `diagnostics` also includes other features like breakpoints, 1-width padding will be inserted if gutters is non-empty | `["diagnostics", "spacer", "line-numbers", "spacer", "diff"]` | | `auto-completion` | Enable automatic pop up of auto-completion | `true` | | `auto-format` | Enable automatic formatting on save | `true` | -| `auto-save` | Enable automatic saving on the focus moving away from Helix. Requires [focus event support](https://github.com/helix-editor/helix/wiki/Terminal-Support) from your terminal | `false` | +| `auto-save.focus-lost` | Enable automatic saving on the focus moving away from Helix. Requires [focus event support](https://github.com/helix-editor/helix/wiki/Terminal-Support) from your terminal | `false` | +| `auto-save.after-delay.enable` | Enable automatic saving after `auto-save.after-delay.timeout` milliseconds have passed since last keypress. | `false` | +| `auto-save.after-delay.timeout` | Time in milliseconds since last keypress before auto save timer triggers. | `3000` | | `idle-timeout` | Time in milliseconds since last keypress before idle timers trigger. | `250` | | `completion-timeout` | Time in milliseconds after typing a word character before completions are shown, set to 5 for instant. | `250` | | `preview-completion-insert` | Whether to apply completion item instantly when selected | `true` | From 5a221f73a4e4d97925c1f3afaa9e3083e22183ab Mon Sep 17 00:00:00 2001 From: Hendrik Wolff Date: Sat, 8 Jun 2024 23:00:15 +0200 Subject: [PATCH 12/13] Fix wording of (doc) comments relating to auto-save --- book/src/editor.md | 4 ++-- helix-view/src/editor.rs | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/book/src/editor.md b/book/src/editor.md index 62d5dd8dc252..c170629d72f5 100644 --- a/book/src/editor.md +++ b/book/src/editor.md @@ -33,8 +33,8 @@ | `auto-completion` | Enable automatic pop up of auto-completion | `true` | | `auto-format` | Enable automatic formatting on save | `true` | | `auto-save.focus-lost` | Enable automatic saving on the focus moving away from Helix. Requires [focus event support](https://github.com/helix-editor/helix/wiki/Terminal-Support) from your terminal | `false` | -| `auto-save.after-delay.enable` | Enable automatic saving after `auto-save.after-delay.timeout` milliseconds have passed since last keypress. | `false` | -| `auto-save.after-delay.timeout` | Time in milliseconds since last keypress before auto save timer triggers. | `3000` | +| `auto-save.after-delay.enable` | Enable automatic saving after `auto-save.after-delay.timeout` milliseconds have passed since last edit. | `false` | +| `auto-save.after-delay.timeout` | Time in milliseconds since last edit before auto save timer triggers. | `3000` | | `idle-timeout` | Time in milliseconds since last keypress before idle timers trigger. | `250` | | `completion-timeout` | Time in milliseconds after typing a word character before completions are shown, set to 5 for instant. | `250` | | `preview-completion-insert` | Whether to apply completion item instantly when selected | `true` | diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index e2ce49aa25a2..635f72619dde 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -55,7 +55,7 @@ use arc_swap::{ ArcSwap, }; -const DEFAULT_AUTO_SAVE_DELAY: u64 = 3000; +pub const DEFAULT_AUTO_SAVE_DELAY: u64 = 3000; fn deserialize_duration_millis<'de, D>(deserializer: D) -> Result where @@ -269,7 +269,7 @@ pub struct Config { /// Automatic formatting on save. Defaults to true. pub auto_format: bool, /// Automatic save on focus lost and/or after delay. - /// Time delay in milliseconds since last keypress after which auto save timer triggers. + /// Time delay in milliseconds since last edit after which auto save timer triggers. /// Time delay defaults to false with 3000ms delay. Focus lost defaults to false. #[serde(deserialize_with = "deserialize_auto_save")] pub auto_save: AutoSave, @@ -779,7 +779,7 @@ impl WhitespaceRender { #[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] pub struct AutoSave { - /// Auto save after `n` milli seconds. Defaults to None. + /// Auto save after a delay in milliseconds. Defaults to disabled. #[serde(default)] pub after_delay: AutoSaveAfterDelay, /// Auto save on focus lost. Defaults to false. @@ -791,8 +791,10 @@ pub struct AutoSave { #[serde(deny_unknown_fields)] pub struct AutoSaveAfterDelay { #[serde(default)] + /// Enable auto save after delay. Defaults to false. pub enable: bool, #[serde(default = "default_auto_save_delay")] + /// Time delay in milliseconds. Defaults to [DEFAULT_AUTO_SAVE_DELAY]. pub timeout: u64, } From c28c2a1f931fcd26c7c3ccc04729f958bff53c04 Mon Sep 17 00:00:00 2001 From: Hendrik Wolff Date: Mon, 10 Jun 2024 18:16:36 +0200 Subject: [PATCH 13/13] book: Move auto-save descriptions to separate section --- book/src/editor.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/book/src/editor.md b/book/src/editor.md index c170629d72f5..ba03e90e581e 100644 --- a/book/src/editor.md +++ b/book/src/editor.md @@ -32,9 +32,6 @@ | `gutters` | Gutters to display: Available are `diagnostics` and `diff` and `line-numbers` and `spacer`, note that `diagnostics` also includes other features like breakpoints, 1-width padding will be inserted if gutters is non-empty | `["diagnostics", "spacer", "line-numbers", "spacer", "diff"]` | | `auto-completion` | Enable automatic pop up of auto-completion | `true` | | `auto-format` | Enable automatic formatting on save | `true` | -| `auto-save.focus-lost` | Enable automatic saving on the focus moving away from Helix. Requires [focus event support](https://github.com/helix-editor/helix/wiki/Terminal-Support) from your terminal | `false` | -| `auto-save.after-delay.enable` | Enable automatic saving after `auto-save.after-delay.timeout` milliseconds have passed since last edit. | `false` | -| `auto-save.after-delay.timeout` | Time in milliseconds since last edit before auto save timer triggers. | `3000` | | `idle-timeout` | Time in milliseconds since last keypress before idle timers trigger. | `250` | | `completion-timeout` | Time in milliseconds after typing a word character before completions are shown, set to 5 for instant. | `250` | | `preview-completion-insert` | Whether to apply completion item instantly when selected | `true` | @@ -224,6 +221,16 @@ name = "rust" '<' = '>' ``` +### `[editor.auto-save]` Section + +Control auto save behavior. + +| Key | Description | Default | +|--|--|---------| +| `focus-lost` | Enable automatic saving on the focus moving away from Helix. Requires [focus event support](https://github.com/helix-editor/helix/wiki/Terminal-Support) from your terminal | `false` | +| `after-delay.enable` | Enable automatic saving after `auto-save.after-delay.timeout` milliseconds have passed since last edit. | `false` | +| `after-delay.timeout` | Time in milliseconds since last edit before auto save timer triggers. | `3000` | + ### `[editor.search]` Section Search specific options.