;;; varhist.el --- Persist variables' values across sessions -*- lexical-binding: t; -*- ;; Copyright (C) 2023 Free Software Foundation, Inc. ;; Author: Adam Porter ;; Keywords: convenience, convenience ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; ;;; Code: (require 'cl-lib) (require 'multisession) (define-multisession-variable varhist-values (make-hash-table) "Table storing variables and values." :synchronized t) (defvar varhist-timer nil) (defgroup varhist nil "Persist the history of certain variables across sessions." :group 'convenience) (defcustom varhist-variables nil "Vars to save." :type '(repeat symbol)) (defcustom varhist-timeout 0.2 "Timeout in seconds for saving all variables." :type 'natnum) (defcustom varhist-repeat "5 minutes" "Save values during a session this often. A value accepted by `run-at-time', which see." :type '(choice (string :tag "A relative time string") (natnum :tag "Number of seconds") (const :tag "Don't save during a session" nil))) (defcustom varhist-quietly t "Load and save without displaying messages." :type 'boolean) (define-minor-mode varhist-mode "Persist the history of certain variables across sessions. Loads values for variables in `varhist-variables' when activated; saves values when deactivated." :global t (if varhist-mode (progn (add-hook 'kill-emacs-hook #'varhist--killing-emacs) (when varhist-repeat (setf varhist-timer (run-at-time varhist-repeat (cl-typecase varhist-repeat (number varhist-repeat) (string (timer-duration varhist-repeat))) #'varhist--save))) (varhist--load)) (remove-hook 'kill-emacs-hook #'varhist--killing-emacs) (when (timerp varhist-timer) (cancel-timer varhist-timer) (setf varhist-timer nil)) (varhist--save))) (defun varhist--killing-emacs () "Call `varhist--save' safely. Ignores errors and uses a long timeout. Safe for calling from `kill-emacs-hook'." (ignore-errors ;; Use a long timeout to ensure values are saved when exiting. (varhist--save nil 10))) (defun varhist--save (&optional variables timeout) ;; First, reload the values table in case another session has ;; changed some of them (even though we will overwrite them, the ;; other session might include variables we don't, so we should ;; re-read it first). (let ((values (multisession-value varhist-values)) (variables (or variables varhist-variables)) (timeout (or timeout varhist-timeout))) (with-timeout (timeout (display-warning 'varhist "Saving timed out")) (dolist (variable variables) (setf (gethash variable values) (symbol-value variable)) (sit-for 0 'nodisp)) (setf (multisession-value varhist-values) values)) (unless varhist-quietly (message "varhist: Saved.")))) (defun varhist--load (&optional variables) (let ((values (multisession-value varhist-values)) (variables (or variables varhist-variables))) (dolist (variable variables) ;; Only set variables present in the table. (pcase (gethash variable values 'varhist-not-found) ('varhist-not-found) (value (setf (symbol-value variable) value)))) (unless varhist-quietly (message "varhist: Loaded.")))) (provide 'varhist) ;;; varhist.el ends here