This is eager-state.info, produced by makeinfo version 6.8 from
eager-state.texi.

INFO-DIR-SECTION Emacs
START-INFO-DIR-ENTRY
* Eager-State: (eager-state). Eagerly persist data onto disk.
END-INFO-DIR-ENTRY


File: eager-state.info,  Node: Top,  Next: Fix for end-users,  Up: (dir)

eager-state
***********

Problem.  I’m missing some data every time I start Emacs: I can’t find
Org entries by ‘org-id’, ‘recentf’ has forgotten a file I know I
accessed five minutes ago, and so on.

   Annoying, right?

   Here’s why it happens: many packages rely solely on ‘kill-emacs-hook’
to sync their state variables to disk.

   That’s always been sloppy design IMHO, but particularly since ~2015
norms that apps should eagerly cache their state.  (What’s a "save"
button?)

   Most of the time my Emacs goes down, it’s a drained battery, SIGKILL,
kernel panic, system freeze, Emacs freeze, segfault, forced log-out,
X11/Wayland crash, yanked power cord, accidental or intentional power
button event, or a perpetual process pause followed later by system
shutdown.

   Those things don’t happen often, but those things are nevertheless
the reason Emacs goes down, when it goes down.  Because... as long as
there is no problem to fix, it’s not as if I will shut it down just to
do a nice and orderly shutdown.

   Is that sinking in?  Either *there is no problem,* in which case I
don’t ‘C-x C-c’ (because I don’t need to), or *there is a problem,* in
which case I don’t ‘C-x C-c’ (because I can’t).

   I’m not sure who is the mythical user for whom Emacs usually goes
down through a clean shutdown.  As a package dev, you definitely can’t
assume that about all your users.

* Menu:

* Fix for end-users::
* For package devs::
* Comparison to persist-state.el: Comparison to persist-stateel.
* Tip Fixing slow functions::
* Tip recentf::

— The Detailed Node Listing —

For package devs

* Why not write our own timer?::



File: eager-state.info,  Node: Fix for end-users,  Next: For package devs,  Prev: Top,  Up: Top

1 Fix for end-users
*******************

Loading this package enables a global minor mode, ‘eager-state-mode’.

   (Yes, the mode enables itself on load – reasons for that later.)

   Out of the box, it has no effect.  You’ll want to configure at least
‘eager-state-kill-emacs-hook-subset’.  Here’s a possible setup:

     (use-package eager-state
       :config
       (setq eager-state-kill-emacs-hook-subset
             '(bookmark-exit-hook-internal
               savehist-autosave
               transient-maybe-save-history
               org-clock-save
               org-id-locations-save
               save-place-kill-emacs-hook
               recentf-save-list
               recentf-cleanup))
       (setq eager-state-kill-emacs-query-functions-subset
             '(eshell-save-some-history
               eshell-save-some-last-dir)))

   With the above setup, Emacs will try to run the listed functions
every now and then.

   How often it runs can be tuned through two variables:

     (setq eager-state-periodic-delay 60)
     (setq eager-state-idle-delay 15)

   That’s it in a nutshell.

   As a bonus, once you trust this mode, you may feel daring enough to
speed up your Emacs restarts through the following setting.  It inhibits
running any of the listed functions at shutdown time (i.e.  it mangles
your ‘kill-emacs-hook’):

     (setq eager-state-faster-shutdown t)


File: eager-state.info,  Node: For package devs,  Next: Comparison to persist-stateel,  Prev: Fix for end-users,  Up: Top

2 For package devs
******************

Here’s why the mode enables itself on load.

   In addition to the two end-user hooks

   • ‘eager-state-kill-emacs-hook-subset’
   • ‘eager-state-kill-emacs-query-functions-subset’

   there is also a general hook

   • ‘eager-state-sync-hook’

   which is run along with the rest.  So, any package may ‘require’ this
library, in order to gain access to that hook.  However, packages
mustn’t take it upon themselves to turn the mode on.  Had the user
turned it off, it must stay off.

   That’s why a mere ‘(require 'eager-state)’ is sufficient to turn the
mode on, barring user configuration such as ‘(setq eager-state-inhibit
t)’, in which case that’s their prerogative.

   Anyway, that’s it!  Add your persistence functions to
‘eager-state-sync-hook’.  Maybe also to ‘kill-emacs-hook’, but that’s a
matter of taste.

* Menu:

* Why not write our own timer?::


File: eager-state.info,  Node: Why not write our own timer?,  Up: For package devs

2.1 Why not write our own timer?
================================

Say a package dev Alice realizes that she should put her
state-persistence function on a timer, in addition to ‘kill-emacs-hook’.
Yay, progress into the new millennium!  So her package includes the
lines (simplified):

     (add-hook 'kill-emacs-hook #'alice-persist-function)
     (run-with-idle-timer 100 t #'alice-persist-function) ;; such innovation

   But now Bob, a long-time user of ‘eager-state-mode’, will find his
Emacs running ‘alice-persist-function’ _twice_ when he’s idle.  Because
his ‘eager-state-kill-emacs-hook-subset’ already contained a reference
to ‘alist-persist-function’.

   That’s normally not a problem, most persistence functions are
well-behaved if executed many times.  But we cannot know that for sure,
and it highlights the mess that may form once dozens of packages all
have their own idle timers.

   It’ll be hard for me or anyone to stay up-to-date about which
packages have an idle timer and which don’t, and crowd-source a good
definition for ‘eager-state-kill-emacs-hook-subset’.

   Furthermore, I’ve found it useful on occasion to spawn a throwaway
Emacs that should not sync anything to disk.  Then I’ve been glad to be
able to just turn off ‘eager-state-mode’ to prevent all of those
functions from running.  This is probably a feature worth keeping.

   Thus, I propose that we collectively put our state-persistence
functions in a single place with a clear semantic intent.  That’s
‘eager-state-sync-hook’.

   So instead of the aforementioned snippet

     (add-hook 'kill-emacs-hook #'alice-persist-function)
     (run-with-idle-timer 100 t #'alice-persist-function)

   Alice can type:

     (require 'eager-state)
     (add-hook 'eager-state-sync-hook #'alice-persist-function)
     (add-hook 'kill-emacs-hook #'alice-persist-function) ; optional

   If this proves popular, maybe we can take the idea to Emacs core.


File: eager-state.info,  Node: Comparison to persist-stateel,  Next: Tip Fixing slow functions,  Prev: For package devs,  Up: Top

3 Comparison to persist-state.el
********************************

This is similar to persist-state
(https://codeberg.org/bram85/emacs-persist-state.git), but
implementation differs:

  1. Eager-state gives you the ability to call functions if and only if
     they are also in ‘kill-emacs-hook’ at runtime.

     That leaves it to whatever package originally put the function
     there, to determine whether it is appropriate to call.

        • Persist-state instead uses wrappers that do their own checks,
          e.g.  check if ‘recentf-mode’ is bound and true before calling
          ‘recentf-save-list’.  That increases the amount of labor the
          user has to do to add more.

  1. Eager-state can be used as a library by other packages, as they can
     simply add to ‘eager-state-sync-hook’ without having to
     unhygienically enable a mode on the user’s behalf.


File: eager-state.info,  Node: Tip Fixing slow functions,  Next: Tip recentf,  Prev: Comparison to persist-stateel,  Up: Top

4 Tip: Fixing slow functions
****************************

It can happen after setting up ‘eager-state-mode’ that you notice Emacs
occasionally stuttering, or even freezing for a few seconds.

   If so, one of the functions in ‘eager-state-kill-emacs-hook-subset’
is performing very poorly.

   Track down the culprit by looking at the hidden log buffer:

     (pop-to-buffer " *eager-state*")


File: eager-state.info,  Node: Tip recentf,  Prev: Tip Fixing slow functions,  Up: Top

5 Tip: recentf
**************

If your issue is just with Recentf, you could use the following snippet
instead of this package.

     ;; Remember the file immediately on file visit.
     (advice-add #'recentf-track-opened-file :override
                 (defun my-recentf-track-file ()
                   (when (and buffer-file-name (recentf-keep-p buffer-file-name))
                     (let ((inhibit-message t)
                           (save-silently t))
                       (recentf-add-file buffer-file-name)
                       (recentf-save-list)))))

     ;; Fix performance issue (may break recentf for tramp, idk).
     (setq recentf-filename-handlers nil)

     ;; Remove it from shutdown hook.  Recentf has an insane predicate
     ;; `recentf-enabled-p' which... inspects said hook... which means we must not
     ;; remove it during runtime.
     (advice-add 'kill-emacs :before
                 (lambda (&rest _)
                   (remove-hook 'kill-emacs-hook #'recentf-save-list)))



Tag Table:
Node: Top209
Node: Fix for end-users1968
Node: For package devs3478
Node: Why not write our own timer?4557
Node: Comparison to persist-stateel6624
Node: Tip Fixing slow functions7661
Node: Tip recentf8192

End Tag Table


Local Variables:
coding: utf-8
End:
