diff --git a/src/AppDelegate.swift b/src/AppDelegate.swift index 5aff247..b392cd0 100644 --- a/src/AppDelegate.swift +++ b/src/AppDelegate.swift @@ -32,6 +32,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuItemValidation { /// eval overlay. By writing straight to disk, autosave can't disturb /// what the user sees. private var autosaveTimer: Timer? + /// Hash of the viewport text the last time autosave actually wrote to + /// disk. The 100ms timer compares against this and skips the write if + /// nothing has changed — without this gate, autosave rewrites the + /// entire file every tick (~500 KB/s sustained on a 50 KB doc, which + /// macOS flags as a disk-writes throttle event). + private var lastAutosavedHash: Int? private var viewport: IcedViewportView? { window?.contentView as? IcedViewportView @@ -719,7 +725,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuItemValidation { guard let w = window, let vp = w.contentView as? IcedViewportView else { return } let text = vp.getText() guard !AppState.isEffectivelyBlank(text) else { return } + let hash = text.hashValue + if hash == lastAutosavedHash { return } appState.writeAutosavedCopy(text: text) + lastAutosavedHash = hash } private func observeDocumentTitle() { diff --git a/viewport/src/lib.rs b/viewport/src/lib.rs index 242054e..a20cf85 100644 --- a/viewport/src/lib.rs +++ b/viewport/src/lib.rs @@ -48,6 +48,29 @@ pub struct ViewportHandle { pub needs_redraw: bool, } +/// Install a panic hook that flushes a full backtrace to stderr before +/// the process aborts. Called once on first viewport_create. Without this +/// the host (Swift / winit) often eats the panic message and the user sees +/// only a silent SIGABRT with no `.ips` file. +fn install_panic_hook() { + use std::sync::Once; + static ONCE: Once = Once::new(); + ONCE.call_once(|| { + let prior = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |info| { + use std::io::Write; + let mut err = std::io::stderr().lock(); + let _ = writeln!(err, "===== ACORD RUST PANIC ====="); + let _ = writeln!(err, "{}", info); + let bt = std::backtrace::Backtrace::force_capture(); + let _ = writeln!(err, "{}", bt); + let _ = writeln!(err, "============================"); + let _ = err.flush(); + prior(info); + })); + }); +} + #[unsafe(no_mangle)] pub extern "C" fn viewport_create( nsview: *mut c_void, @@ -55,6 +78,7 @@ pub extern "C" fn viewport_create( height: f32, scale: f32, ) -> *mut ViewportHandle { + install_panic_hook(); if nsview.is_null() { return std::ptr::null_mut(); }