Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-term/src/handlers/auto_reload.rs')
| -rw-r--r-- | helix-term/src/handlers/auto_reload.rs | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/helix-term/src/handlers/auto_reload.rs b/helix-term/src/handlers/auto_reload.rs new file mode 100644 index 00000000..34080bce --- /dev/null +++ b/helix-term/src/handlers/auto_reload.rs @@ -0,0 +1,117 @@ +use std::io; +use std::sync::atomic::{self, AtomicBool}; +use std::sync::Arc; +use std::time::SystemTime; + +use helix_core::file_watcher::{EventType, FileSystemDidChange}; +use helix_event::register_hook; +use helix_view::editor::Config; +use helix_view::events::ConfigDidChange; + +use crate::job; + +struct AutoReload { + enable: AtomicBool, + prompt_if_modified: AtomicBool, +} + +impl AutoReload { + pub fn refresh_config(&self, config: &Config) { + self.enable + .store(config.file_watcher.enable, atomic::Ordering::Relaxed); + self.prompt_if_modified + .store(config.file_watcher.enable, atomic::Ordering::Relaxed); + } + + fn on_file_did_change(&self, event: &mut FileSystemDidChange) { + if !self.enable.load(atomic::Ordering::Relaxed) { + return; + } + let fs_events = event.fs_events.clone(); + if !fs_events + .iter() + .any(|event| event.ty == EventType::Modified) + { + return; + } + job::dispatch_blocking(move |editor, _| { + let config = editor.config(); + let mut vcs_reload = false; + for fs_event in &*fs_events { + if fs_event.ty != EventType::Modified { + continue; + } + vcs_reload |= editor.diff_providers.needs_reload(fs_event); + let Some(doc_id) = editor.document_id_by_path(fs_event.path.as_std_path()) else { + return; + }; + let doc = doc_mut!(editor, &doc_id); + let mtime = match doc.path().unwrap().metadata() { + Ok(meta) => meta.modified().unwrap_or(SystemTime::now()), + Err(err) if err.kind() == io::ErrorKind::NotFound => continue, + Err(_) => SystemTime::now(), + }; + if mtime == doc.last_saved_time { + continue; + } + if doc.is_modified() { + let msg = format!( + "{} auto-reload failed due to unsaved changes, use :reload to refresh", + doc.relative_path().unwrap().display() + ); + editor.set_warning(msg); + } else { + let scrolloff = config.scrolloff; + let view = view_mut!(editor); + match doc.reload(view, &editor.diff_providers) { + Ok(_) => { + view.ensure_cursor_in_view(doc, scrolloff); + let msg = format!( + "{} auto-reload external changes", + doc.relative_path().unwrap().display() + ); + editor.set_status(msg); + } + Err(err) => { + let doc = doc!(editor, &doc_id); + let msg = format!( + "{} auto-reload failed: {err}", + doc.relative_path().unwrap().display() + ); + editor.set_error(msg); + } + } + } + } + if vcs_reload { + for doc in editor.documents.values_mut() { + let Some(path) = doc.path() else { + continue; + }; + match editor.diff_providers.get_diff_base(path) { + Some(diff_base) => doc.set_diff_base(diff_base), + None => doc.diff_handle = None, + } + } + } + }); + } +} + +pub(super) fn register_hooks(config: &Config) { + let handler = Arc::new(AutoReload { + enable: config.auto_reload.enable.into(), + prompt_if_modified: config.auto_reload.prompt_if_modified.into(), + }); + let handler_ = handler.clone(); + register_hook!(move |event: &mut ConfigDidChange<'_>| { + // when a document is initially opened, request colors for it + handler_.refresh_config(event.new); + Ok(()) + }); + register_hook!(move |event: &mut FileSystemDidChange| { + // when a document is initially opened, request colors for it + handler.on_file_did_change(event); + Ok(()) + }); +} |