diff --git a/elisp/chronometrist.el b/elisp/chronometrist.el index 4909d5a..8426968 100644 --- a/elisp/chronometrist.el +++ b/elisp/chronometrist.el @@ -587,7 +587,7 @@ last s-expression. REST-START and REST-END represent the start of the file and the end of the second-last s-expression.") -(defun chronometrist-file-hash (&optional start end hash) +(defun chronometrist-file-hash (file &optional start end hash) "Calculate hash of `chronometrist-file' between START and END. START can be a number or marker, @@ -603,7 +603,7 @@ Return (START END) if HASH is nil, else (START END HASH). Return a list in the form (A B HASH), where A and B are markers in `chronometrist-file' describing the region for which HASH was calculated." - (chronometrist-sexp-in-file chronometrist-file + (chronometrist-sexp-in-file file (let* ((start (cond ((number-or-marker-p start) start) ((eq :before-last start) (goto-char (point-max)) @@ -630,8 +630,8 @@ in `chronometrist-file' describing the region for which HASH was calculated." (funcall position))) (ignore-errors (read (current-buffer))))) -(defun chronometrist-file-change-type (state) - "Determine the type of change made to `chronometrist-file'. +(defun chronometrist-file-change-type (file state) + "Determine the type of change made to FILE. STATE must be a plist. (see `chronometrist--file-state') Return @@ -646,9 +646,9 @@ Return (last-expr-file (chronometrist-read-from last-start)) (last-expr-ht (chronometrist-events-last)) (last-same-p (equal last-expr-ht last-expr-file)) - (file-new-length (chronometrist-sexp-in-file chronometrist-file (point-max))) + (file-new-length (chronometrist-sexp-in-file file (point-max))) (rest-same-p (unless (< file-new-length rest-end) - (--> (chronometrist-file-hash rest-start rest-end t) + (--> (chronometrist-file-hash file rest-start rest-end t) (cl-third it) (equal rest-hash it))))) ;; (message "chronometrist - last-start\nlast-expr-file - %S\nlast-expr-ht - %S" @@ -1277,9 +1277,10 @@ Re-read `chronometrist-file', update `chronometrist-events', and refresh the `chronometrist' buffer." (run-hooks 'chronometrist-file-change-hook) ;; (message "chronometrist - file %s" fs-event) - (-let* (((descriptor action _ _) fs-event) + (-let* ((file (chronometrist-backend-file (chronometrist-active-backend))) + ((descriptor action _ _) fs-event) (change (when chronometrist--file-state - (chronometrist-file-change-type chronometrist--file-state))) + (chronometrist-file-change-type file chronometrist--file-state))) (reset-watch (or (eq action 'deleted) (eq action 'renamed)))) ;; (message "chronometrist - file change type is %s" change) @@ -1318,8 +1319,8 @@ refresh the `chronometrist' buffer." (puthash date it chronometrist-events)))) ((pred null) nil))))) (setq chronometrist--file-state - (list :last (chronometrist-file-hash :before-last nil) - :rest (chronometrist-file-hash nil :before-last t))) + (list :last (chronometrist-file-hash file :before-last nil) + :rest (chronometrist-file-hash file nil :before-last t))) ;; REVIEW - can we move most/all of this to the `chronometrist-file-change-hook'? (chronometrist-refresh))) diff --git a/elisp/chronometrist.org b/elisp/chronometrist.org index 292df3b..a3980e5 100644 --- a/elisp/chronometrist.org +++ b/elisp/chronometrist.org @@ -1063,12 +1063,36 @@ The reasons I like this format are - :header-args: :tangle chronometrist-tests.el :END: -#+BEGIN_SRC emacs-lisp :load test +1. [X] finding the test input file + * =buffer-file-name= returns nil when Emacs is run in batch mode; I've tried using =(concat (or (ignore-errors (file-name-directory (buffer-file-name))) default-directory) "test.sexp")=, but that resulted in ="~/.emacs.d/test.sexp"= being used instead, for some reason. + * maybe we can store the test file contents in a string instead, and create a temporary test file using =make-temp-file=? + +Boilerplate for updating state between file operations in tests. +#+BEGIN_SRC emacs-lisp :load test :tangle chronometrist-tests.el +(defmacro chronometrist-tests--change-type-and-update (state file) + `(prog1 (chronometrist-file-change-type ,state) + (setq ,state + (list :last (chronometrist-file-hash ,file :before-last nil) + :rest (chronometrist-file-hash ,file nil :before-last t))))) +#+END_SRC +**** backend :class: +#+BEGIN_SRC emacs-lisp +(defclass chronometrist-plist-backend (chronometrist-elisp-sexp-backend) ()) + +(add-to-list 'chronometrist-backends-alist + `(:plist "Store records as plists." + ,(make-instance 'chronometrist-plist-backend + :path chronometrist-file + :ext "plist"))) +#+END_SRC + +***** tests +#+BEGIN_SRC emacs-lisp :load test :tangle chronometrist-tests.el (defvar chronometrist-plist-test-backend (make-instance 'chronometrist-plist-backend :file (make-temp-file "chronometrist-plist-test-" nil ".sexp"))) -(with-current-buffer (find-file-noselect) +(with-current-buffer (find-file-noselect (chronometrist-backend-file chronometrist-plist-test-backend)) (mapcar (lambda (plist) ;; to use this, we'd have to move `chronometrist-plist-pp' before this @@ -1126,28 +1150,6 @@ The reasons I like this format are - :stop "2020-05-10T17:10:48+0530")))) #+END_SRC -1. [X] finding the test input file - * =buffer-file-name= returns nil when Emacs is run in batch mode; I've tried using =(concat (or (ignore-errors (file-name-directory (buffer-file-name))) default-directory) "test.sexp")=, but that resulted in ="~/.emacs.d/test.sexp"= being used instead, for some reason. - * maybe we can store the test file contents in a string instead, and create a temporary test file using =make-temp-file=? - -Boilerplate for updating state between file operations in tests. -#+BEGIN_SRC emacs-lisp :load test -(defmacro chronometrist-tests--change-type-and-update (state) - `(prog1 (chronometrist-file-change-type ,state) - (setq ,state - (list :last (chronometrist-file-hash :before-last nil) - :rest (chronometrist-file-hash nil :before-last t))))) -#+END_SRC -**** backend :class: -#+BEGIN_SRC emacs-lisp -(defclass chronometrist-plist-backend (chronometrist-backend) ()) - -(add-to-list 'chronometrist-backends-alist - `(:plist "Store records as plists." - ,(make-instance 'chronometrist-plist-backend - :path chronometrist-file - :ext "plist"))) -#+END_SRC **** pretty-print-function :custom:variable: #+BEGIN_SRC emacs-lisp (defcustom chronometrist-sexp-pretty-print-function #'chronometrist-plist-pp @@ -1320,7 +1322,7 @@ This is meant to be run in `chronometrist-file' when using the s-expression back ***** tests #+BEGIN_SRC emacs-lisp :tangle chronometrist-tests.el :load test (ert-deftest task-list () - (let ((task-list (chronometrist-list-tasks backend))) + (let ((task-list (chronometrist-list-tasks chronometrist-plist-test-backend))) (should (listp task-list)) (should (seq-every-p #'stringp task-list)))) #+END_SRC @@ -1350,7 +1352,7 @@ end of the second-last s-expression.") #+END_SRC **** file-hash :reader: #+BEGIN_SRC emacs-lisp -(defun chronometrist-file-hash (&optional start end hash) +(defun chronometrist-file-hash (file &optional start end hash) "Calculate hash of `chronometrist-file' between START and END. START can be a number or marker, @@ -1366,7 +1368,7 @@ Return (START END) if HASH is nil, else (START END HASH). Return a list in the form (A B HASH), where A and B are markers in `chronometrist-file' describing the region for which HASH was calculated." - (chronometrist-sexp-in-file chronometrist-file + (chronometrist-sexp-in-file file (let* ((start (cond ((number-or-marker-p start) start) ((eq :before-last start) (goto-char (point-max)) @@ -1389,13 +1391,12 @@ in `chronometrist-file' describing the region for which HASH was calculated." ***** tests #+BEGIN_SRC emacs-lisp :tangle chronometrist-tests.el :load test (ert-deftest file-hash () - (-let* ((chronometrist-file chronometrist-test-file) + (-let* ((file (chronometrist-backend-file chronometrist-plist-test-backend)) ((last-start last-end) - (chronometrist-file-hash :before-last nil)) + (chronometrist-file-hash file :before-last nil)) ((rest-start rest-end rest-hash) - (chronometrist-file-hash nil :before-last t))) - (message "chronometrist - file-hash test - file path is %s" - chronometrist-test-file) + (chronometrist-file-hash file nil :before-last t))) + (message "chronometrist - file-hash test - file path is %s" file) (should (= 1 rest-start)) (should (= 1254 rest-end)) (should (= 1256 last-start)) @@ -1428,8 +1429,8 @@ Possible states 5. t - rest changed #+BEGIN_SRC emacs-lisp -(defun chronometrist-file-change-type (state) - "Determine the type of change made to `chronometrist-file'. +(defun chronometrist-file-change-type (file state) + "Determine the type of change made to FILE. STATE must be a plist. (see `chronometrist--file-state') Return @@ -1444,9 +1445,9 @@ Return (last-expr-file (chronometrist-read-from last-start)) (last-expr-ht (chronometrist-events-last)) (last-same-p (equal last-expr-ht last-expr-file)) - (file-new-length (chronometrist-sexp-in-file chronometrist-file (point-max))) + (file-new-length (chronometrist-sexp-in-file file (point-max))) (rest-same-p (unless (< file-new-length rest-end) - (--> (chronometrist-file-hash rest-start rest-end t) + (--> (chronometrist-file-hash file rest-start rest-end t) (cl-third it) (equal rest-hash it))))) ;; (message "chronometrist - last-start\nlast-expr-file - %S\nlast-expr-ht - %S" @@ -1468,18 +1469,19 @@ Return ***** tests #+BEGIN_SRC emacs-lisp :tangle chronometrist-tests.el :load test (ert-deftest chronometrist-file-change-type () - (let* ((test-contents (with-current-buffer (find-file-noselect - (chronometrist-backend-file chronometrist-plist-test-backend)) + (let* ((file (chronometrist-backend-file chronometrist-plist-test-backend)) + (test-contents (with-current-buffer + (find-file-noselect file) (buffer-substring (point-min) (point-max)))) (chronometrist--file-state-old chronometrist--file-state) - (chronometrist--file-state (list :last (chronometrist-file-hash :before-last nil) - :rest (chronometrist-file-hash nil :before-last t))) + (chronometrist--file-state (list :last (chronometrist-file-hash file :before-last nil) + :rest (chronometrist-file-hash file nil :before-last t))) (chronometrist-events-old chronometrist-events)) (chronometrist-events-populate) (unwind-protect (progn (should - (eq nil (chronometrist-file-change-type chronometrist--file-state))) + (eq nil (chronometrist-file-change-type file chronometrist--file-state))) (should (eq :append (progn @@ -2404,9 +2406,10 @@ Re-read `chronometrist-file', update `chronometrist-events', and refresh the `chronometrist' buffer." (run-hooks 'chronometrist-file-change-hook) ;; (message "chronometrist - file %s" fs-event) - (-let* (((descriptor action _ _) fs-event) + (-let* ((file (chronometrist-backend-file (chronometrist-active-backend))) + ((descriptor action _ _) fs-event) (change (when chronometrist--file-state - (chronometrist-file-change-type chronometrist--file-state))) + (chronometrist-file-change-type file chronometrist--file-state))) (reset-watch (or (eq action 'deleted) (eq action 'renamed)))) ;; (message "chronometrist - file change type is %s" change) @@ -2445,8 +2448,8 @@ refresh the `chronometrist' buffer." (puthash date it chronometrist-events)))) ((pred null) nil))))) (setq chronometrist--file-state - (list :last (chronometrist-file-hash :before-last nil) - :rest (chronometrist-file-hash nil :before-last t))) + (list :last (chronometrist-file-hash file :before-last nil) + :rest (chronometrist-file-hash file nil :before-last t))) ;; REVIEW - can we move most/all of this to the `chronometrist-file-change-hook'? (chronometrist-refresh))) #+END_SRC @@ -3403,9 +3406,9 @@ LIST is either tags (a list of symbols) or a plist." (should (equal (chronometrist-details-rows-helper nil) "")) (should (equal (chronometrist-details-rows-helper nil) "")) (should (equal (chronometrist-details-rows-helper tags) - "a b c")) + "a, b, c")) (should (equal (chronometrist-details-rows-helper plist) - "1 2 3"))))) + "1, 2, 3"))))) #+END_SRC **** row-transformers :extension:variable: