pax_global_header00006660000000000000000000000064146354245110014517gustar00rootroot0000000000000052 comment=d5b9e202f0c527222bdd3baebd2853c99b36b966 persist-el-0.6.1+dfsg/000077500000000000000000000000001463542451100145315ustar00rootroot00000000000000persist-el-0.6.1+dfsg/.elpaignore000066400000000000000000000000301463542451100166500ustar00rootroot00000000000000Makefile Cask .gitignorepersist-el-0.6.1+dfsg/persist-pkg.el000066400000000000000000000006211463542451100173220ustar00rootroot00000000000000;; Generated package description from persist.el -*- no-byte-compile: t -*- (define-package "persist" "0.6.1" "Persist Variables between Emacs Sessions" '((emacs "26.1")) :commit "5ea8f32ef50ce2b444d6918e17eedce9f74629af" :url "https://elpa.gnu.org/packages/persist.html" :authors '(("Phillip Lord" . "phillip.lord@russet.org.uk")) :maintainer '("Joseph Turner" . "persist-el@breatheoutbreathe.in")) persist-el-0.6.1+dfsg/persist.el000066400000000000000000000215731463542451100165540ustar00rootroot00000000000000;;; persist.el --- Persist Variables between Emacs Sessions -*- lexical-binding: t -*- ;; Copyright (C) 2019, 2024 Free Software Foundation, Inc. ;; Author: Phillip Lord ;; Maintainer: Joseph Turner ;; Package-Type: multi ;; Package-Requires: ((emacs "26.1")) ;; Version: 0.6.1 ;; The contents of this file are subject to the GPL License, Version 3.0. ;; This file is not part of Emacs ;; 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: ;; This package provides variables which persist across sessions. ;; The main entry point is `persist-defvar' which behaves like ;; `defvar' but which persists the variables between session. Variables ;; are automatically saved when Emacs exits. ;; Other useful functions are `persist-save' which saves the variable ;; immediately, `persist-load' which loads the saved value, ;; `persist-reset' which resets to the default value. ;; Values are stored in a directory in `user-emacs-directory', using ;; one file per value. This makes it easy to delete or remove unused ;; variables. ;;; Code: (defvar persist--directory-location (locate-user-emacs-file "persist") "The location of persist directory.") (defvar persist--symbols nil "List of symbols to persist.") (defvar persist-load-hook nil "Special hook run on loading a variable. Hook functions are called with two values: the symbol and the value it will be set to. If any function returns nil, the variable is not set to the value.") (defun persist--file-location (symbol) "Return the file name at which SYMBOL does or will persist." (expand-file-name (symbol-name symbol) (or (get symbol 'persist-location) persist--directory-location))) (defun persist--defvar-1 (symbol location) "Set symbol up for persistence." (when location (persist-location symbol location)) (persist-symbol symbol (symbol-value symbol)) (persist-load symbol)) (defmacro persist-defvar (symbol initvalue docstring &optional location) "Define SYMBOL as a persistent variable and return SYMBOL. This form is nearly equivalent to `defvar', except that the variable persists between Emacs sessions. It does not support the optional parameters. Both INITVALUE and DOCSTRING need to be given." ;; We cannot distinguish between calls with initvalue of nil and a ;; single parameter call. Unfortunately, these two calls have ;; different semantics -- the single arity shuts up the byte ;; compiler, but does not define the symbol. So, don't support a ;; single arity persist-defvar. ;; Don't support 2-arity calls either because we are lazy and ;; because if you want to persist it, you want to doc it. (declare (debug (symbolp form stringp &optional form)) (doc-string 3) (indent defun)) ;; Define inside progn so the byte compiler sees defvar `(progn (defvar ,symbol ,initvalue ,docstring) ;; Access initvalue through its symbol because the defvar form ;; has to stay at first level within a progn (persist--defvar-1 ',symbol ,location) ',symbol)) (defun persist-location (symbol directory) "Set the directory for persisting the value of symbol. This does not force the loading of value from this directory, so to persist a variable, you will normally need to call `persist-load' to load a previously saved location." (put symbol 'persist-location (expand-file-name directory))) (defun persist-symbol (symbol &optional initvalue) "Make SYMBOL a persistent variable. If non-nil, INITVALUE is the value to which SYMBOL will be set if `persist-reset' is called. Otherwise, the INITVALUE will be the current `symbol-value' of SYMBOL. INITVALUE is set for the session and will itself not persist across sessions. This does force the loading of value from this directory, so to persist a variable, you will normally need to call `persist-load' to load a previously saved location." (let ((initvalue (or initvalue (symbol-value symbol)))) (add-to-list 'persist--symbols symbol) (put symbol 'persist t) (put symbol 'persist-default (persist-copy initvalue)))) (defun persist--persistant-p (symbol) "Return non-nil if SYMBOL is a persistent variable." (get symbol 'persist)) (defun persist-save (symbol) "Save SYMBOL now. Normally, it should not be necessary to call this explicitly, as variables persist automatically when Emacs exits." (unless (persist--persistant-p symbol) (error (format "Symbol %s is not persistent" symbol))) (let ((symbol-file-loc (persist--file-location symbol))) (if (persist-equal (symbol-value symbol) (persist-default symbol)) (when (file-exists-p symbol-file-loc) (delete-file symbol-file-loc)) (let ((dir-loc (file-name-directory symbol-file-loc))) (unless (file-exists-p dir-loc) (mkdir dir-loc)) (with-temp-buffer (let (print-level print-length print-quoted (print-escape-control-characters t) (print-escape-nonascii t) (print-circle t)) (print (symbol-value symbol) (current-buffer))) (write-region (point-min) (point-max) symbol-file-loc nil 'quiet)))))) (defun persist-default (symbol) "Return the default value for SYMBOL." (get symbol 'persist-default)) (defun persist-reset (symbol) "Set the value of SYMBOL to a copy of the default." (set symbol (persist-copy (persist-default symbol)))) (defun persist-load (symbol) "Load the saved value of SYMBOL." (when (file-exists-p (persist--file-location symbol)) (with-temp-buffer (insert-file-contents (persist--file-location symbol)) (let ((val (read (current-buffer)))) (when (run-hook-with-args-until-failure 'persist-load-hook symbol val) (set symbol val)))))) (defun persist-unpersist (symbol) "Stop the value in SYMBOL from persisting. This does not remove any saved value of SYMBOL." (put symbol 'persist nil) (setq persist--symbols (remove symbol persist--symbols))) (defun persist--save-all () "Save all persistent symbols." (mapc 'persist-save persist--symbols)) ;; Save on kill-emacs-hook anyway (add-hook 'kill-emacs-hook 'persist--save-all) (defun persist-equal (a b) "Return non-nil when the values of A and B are equal. A and B are compared using `equal' unless they are both hash tables. In that case, the following are compared: - hash table count - hash table predicate - values, using `persist-equal'" (if (and (hash-table-p a) (hash-table-p b)) (and (= (hash-table-count a) (hash-table-count b)) (eq (hash-table-test a) (hash-table-test b)) (catch 'done (maphash (lambda (key a-value) (unless (persist-equal a-value (gethash key b (not a-value))) (throw 'done nil))) a) t)) (equal a b))) (defun persist-copy-tree (tree &optional vectors-and-records) "Make a copy of TREE. If TREE is a cons cell, this recursively copies both its car and its cdr. Contrast to `copy-sequence', which copies only along the cdrs. With the second argument VECTORS-AND-RECORDS non-nil, this traverses and copies vectors and records as well as conses." (declare (side-effect-free error-free)) (if (consp tree) (let (result) (while (consp tree) (let ((newcar (car tree))) (if (or (consp (car tree)) (and vectors-and-records (or (vectorp (car tree)) (recordp (car tree))))) (setq newcar (persist-copy-tree (car tree) vectors-and-records))) (push newcar result)) (setq tree (cdr tree))) (nconc (nreverse result) (if (and vectors-and-records (or (vectorp tree) (recordp tree))) (persist-copy-tree tree vectors-and-records) tree))) (if (and vectors-and-records (or (vectorp tree) (recordp tree))) (let ((i (length (setq tree (copy-sequence tree))))) (while (>= (setq i (1- i)) 0) (aset tree i (persist-copy-tree (aref tree i) vectors-and-records))) tree) tree))) (defun persist-copy (obj) "Return copy of OBJ." (if (hash-table-p obj) (copy-hash-table obj) (persist-copy-tree obj t))) (provide 'persist) ;;; persist.el ends here persist-el-0.6.1+dfsg/test/000077500000000000000000000000001463542451100155105ustar00rootroot00000000000000persist-el-0.6.1+dfsg/test/persist-tests.el000066400000000000000000000117631463542451100206730ustar00rootroot00000000000000(require 'persist) (require 'seq) (defmacro with-local-temp-persist (&rest body) (declare (debug body)) `(unwind-protect (let ((persist--directory-location "./persist/") (persist--symbols nil)) ,@body) (delete-directory "./persist" t))) (ert-deftest test-persist-symbol () (should (let ((persist--symbols nil) (sym (cl-gensym))) (persist-symbol sym 10) (seq-contains persist--symbols sym)))) (ert-deftest test-persist-save-only-persistant () ;; do not save not persist variables (should-error (with-local-temp-persist (persist-save (cl-gensym))) :type 'error :exclude-subtypes t)) (defmacro persist-test-persist-save (init default change printed-changed) "Test persisting symbols. - symbol is not persisted when value is set to INIT and default value is set to DEFAULT. - symbol is persisted when value is changed according to CHANGE. - persisted file contents match PRINTED-CHANGED. - symbol is not persisted after value is set back to DEFAULT." `(with-local-temp-persist (let ((sym (cl-gensym))) (should-not (file-exists-p (persist--file-location sym))) (set sym ,init) (persist-symbol sym ,default) (persist-save sym) (should t) (should-not (file-exists-p (persist--file-location sym))) ,change (persist-save sym) (should (file-exists-p (persist--file-location sym))) (should (string-match-p ,printed-changed (with-temp-buffer (insert-file-contents (persist--file-location sym)) (buffer-string)))) (set sym ,default) (persist-save sym) (should-not (file-exists-p (persist--file-location sym)))))) (ert-deftest test-persist-save-number () "Test saving number." (persist-test-persist-save 1 1 (set sym 2) "2")) (ert-deftest test-persist-save-string () "Test saving string." (persist-test-persist-save "foo" "foo" (set sym "bar") "bar")) (ert-deftest test-persist-save-hash () "Test saving hash table." (let* ((hash (make-hash-table)) (default (copy-hash-table hash))) (persist-test-persist-save hash default (puthash 'foo "bar" (symbol-value sym)) "#s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data (foo \"bar\"))"))) (ert-deftest test-persist-save-record () "Test saving record." (let* ((rec (record 'foo 'a 'b)) (default (copy-sequence rec))) (persist-test-persist-save rec default (setf (aref (symbol-value sym) 2) 'quux) "#s(foo a quux)"))) (ert-deftest test-persist-load () (with-local-temp-persist (let ((sym (cl-gensym))) (set sym 10) ;; set this different to force save (persist-symbol sym 1) (persist-save sym) (should (equal 10 (symbol-value sym))) (set sym 30) (should (equal 30 (symbol-value sym))) (persist-load sym) (should (equal 10 (symbol-value sym)))))) (ert-deftest test-persist-remove () (with-local-temp-persist (let ((sym (cl-gensym))) (should-not (persist--persistant-p sym)) (persist-symbol sym 10) (should (persist--persistant-p sym)) (persist-unpersist sym) (should-not (persist--persistant-p sym))))) (ert-deftest test-persist-defvar () (with-local-temp-persist (defvar test-no-persist-variable 10 "docstring") (persist-defvar test-persist-variable 20 "docstring") (should-not (persist--persistant-p 'test-no-persist-variable)) (should (persist--persistant-p 'test-persist-variable)) (should (= 20 (persist-default 'test-persist-variable))))) (ert-deftest test-persist-location () (unwind-protect (let ((sym (cl-gensym))) (delete-directory "./persist-defined-location" t) (set sym 10) (persist-symbol sym 10) (persist-location sym "./persist-defined-location") (should (equal (expand-file-name (symbol-name sym) "./persist-defined-location/") (persist--file-location sym))) (persist-save sym) (should-not (file-exists-p (persist--file-location sym))) (set sym 20) (persist-save sym) (should (file-exists-p (persist--file-location sym))) (should (string-match-p "20" (with-temp-buffer (insert-file-contents (persist--file-location sym)) (buffer-string)))) (should-error (persist-save 'fred))) (delete-directory "./persist-defined-location" t))) (ert-deftest test-persist-reset () "Symbol should be reset to a copy of the default." (with-local-temp-persist (persist-defvar persist--test-reset-variable (make-hash-table) "docstring") (should-not (eq persist--test-reset-variable (persist-default 'persist--test-reset-variable))) (persist-reset 'persist--test-reset-variable) (should-not (eq persist--test-reset-variable (persist-default 'persist--test-reset-variable)))))