Reduce successive file saves in interactive commands
Each save runs the file-notify callback, which may decrease responsiveness.
This commit is contained in:
parent
e8137651d0
commit
44a97db410
|
@ -932,6 +932,11 @@ OLD-PATH and NEW-PATH are the old and new values of
|
|||
`chronometrist-file', respectively.")
|
||||
;; on-file-path-change:1 ends here
|
||||
|
||||
;; [[file:chronometrist.org::*save-backend][save-backend:1]]
|
||||
(cl-defgeneric chronometrist-save-backend (backend)
|
||||
"Save any unsaved data in BACKEND.")
|
||||
;; save-backend:1 ends here
|
||||
|
||||
;; [[file:chronometrist.org::*reset-backend][reset-backend:1]]
|
||||
(cl-defgeneric chronometrist-reset-backend (backend)
|
||||
"Reset data structures for BACKEND.")
|
||||
|
@ -1203,6 +1208,8 @@ Return
|
|||
(-let*
|
||||
(((last-start last-end) (plist-get state :last))
|
||||
((rest-start rest-end rest-hash) (plist-get state :rest))
|
||||
;; may be nil if 'rest' content has changed such that
|
||||
;; last-start is now in the middle of an s-expression
|
||||
(last-expr-file (chronometrist-read-from last-start))
|
||||
(last-expr-ht (chronometrist-events-last))
|
||||
(file (chronometrist-backend-file (chronometrist-active-backend)))
|
||||
|
@ -1210,8 +1217,8 @@ Return
|
|||
(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)
|
||||
(cl-third it)
|
||||
(equal rest-hash it)))))
|
||||
(cl-third it)
|
||||
(equal rest-hash it)))))
|
||||
;; (message "chronometrist - last-start\nlast-expr-file - %S\nlast-expr-ht - %S"
|
||||
;; last-expr-file
|
||||
;; last-expr-ht)
|
||||
|
@ -1322,6 +1329,12 @@ FS-EVENT is the event passed by the `filenotify' library (see `file-notify-add-w
|
|||
:rest (chronometrist-file-hash nil :before-last t))))))
|
||||
;; on-change:1 ends here
|
||||
|
||||
;; [[file:chronometrist.org::*save-backend][save-backend:1]]
|
||||
(cl-defmethod chronometrist-save-backend ((backend chronometrist-file-backend-mixin))
|
||||
(with-slots (file) backend
|
||||
(chronometrist-sexp-in-file file (save-buffer))))
|
||||
;; save-backend:1 ends here
|
||||
|
||||
;; [[file:chronometrist.org::*backend][backend:1]]
|
||||
(defclass chronometrist-plist-backend (chronometrist-elisp-sexp-backend chronometrist-file-backend-mixin)
|
||||
((extension :initform "plist"
|
||||
|
@ -1745,14 +1758,14 @@ Return value is either a list in the form
|
|||
;; task-records-for-date:1 ends here
|
||||
|
||||
;; [[file:chronometrist.org::*replace-last][replace-last:1]]
|
||||
(cl-defmethod chronometrist-replace-last ((backend chronometrist-plist-group-backend) plist)
|
||||
(cl-defmethod chronometrist-replace-last ((backend chronometrist-plist-group-backend) plist &key (save t))
|
||||
(cl-check-type plist chronometrist-plist)
|
||||
(when (chronometrist-backend-empty-p backend)
|
||||
(error "No record to replace in %s" (eieio-object-class-name backend)))
|
||||
(chronometrist-sexp-in-file (chronometrist-backend-file backend)
|
||||
(chronometrist-remove-last backend :save nil)
|
||||
(chronometrist-insert backend plist :save nil)
|
||||
(save-buffer)))
|
||||
(when save (save-buffer))))
|
||||
;; replace-last:1 ends here
|
||||
|
||||
;; [[file:chronometrist.org::*remove-prefix][remove-prefix:1]]
|
||||
|
@ -2223,39 +2236,41 @@ refresh the `chronometrist' buffer."
|
|||
;; query-stop:1 ends here
|
||||
|
||||
;; [[file:chronometrist.org::*chronometrist-in][chronometrist-in:1]]
|
||||
(defun chronometrist-in (task &optional _prefix)
|
||||
(cl-defun chronometrist-in (task &key (save t))
|
||||
"Clock in to TASK; record current time in `chronometrist-file'.
|
||||
TASK is the name of the task, a string. PREFIX is ignored."
|
||||
(interactive "P")
|
||||
(let ((plist `(:name ,task :start ,(chronometrist-format-time-iso8601))))
|
||||
(chronometrist-insert (chronometrist-active-backend) plist)
|
||||
(let ((plist (list :name task :start (chronometrist-format-time-iso8601))))
|
||||
(chronometrist-insert (chronometrist-active-backend) plist :save save)
|
||||
(chronometrist-refresh)))
|
||||
;; chronometrist-in:1 ends here
|
||||
|
||||
;; [[file:chronometrist.org::*chronometrist-out][chronometrist-out:1]]
|
||||
(defun chronometrist-out (&optional _prefix)
|
||||
(cl-defun chronometrist-out (&key (save t))
|
||||
"Record current moment as stop time to last s-exp in `chronometrist-file'.
|
||||
PREFIX is ignored."
|
||||
(interactive "P")
|
||||
(let* ((latest (chronometrist-latest-record (chronometrist-active-backend)))
|
||||
(plist (plist-put latest :stop (chronometrist-format-time-iso8601))))
|
||||
(chronometrist-replace-last (chronometrist-active-backend) plist)))
|
||||
(chronometrist-replace-last (chronometrist-active-backend) plist :save save)))
|
||||
;; chronometrist-out:1 ends here
|
||||
|
||||
;; [[file:chronometrist.org::*run-functions-and-clock-in][run-functions-and-clock-in:1]]
|
||||
(defun chronometrist-run-functions-and-clock-in (task)
|
||||
"Run hooks and clock in to TASK."
|
||||
(run-hook-with-args 'chronometrist-before-in-functions task)
|
||||
(chronometrist-in task)
|
||||
(run-hook-with-args 'chronometrist-after-in-functions task))
|
||||
(chronometrist-in task :save nil)
|
||||
(run-hook-with-args 'chronometrist-after-in-functions task)
|
||||
(chronometrist-save-backend (chronometrist-active-backend)))
|
||||
;; run-functions-and-clock-in:1 ends here
|
||||
|
||||
;; [[file:chronometrist.org::*run-functions-and-clock-out][run-functions-and-clock-out:1]]
|
||||
(defun chronometrist-run-functions-and-clock-out (task)
|
||||
"Run hooks and clock out of TASK."
|
||||
(when (run-hook-with-args-until-failure 'chronometrist-before-out-functions task)
|
||||
(chronometrist-out)
|
||||
(run-hook-with-args 'chronometrist-after-out-functions task)))
|
||||
(chronometrist-out :save nil)
|
||||
(run-hook-with-args 'chronometrist-after-out-functions task)
|
||||
(chronometrist-save-backend (chronometrist-active-backend))))
|
||||
;; run-functions-and-clock-out:1 ends here
|
||||
|
||||
;; [[file:chronometrist.org::*chronometrist-mode-map][chronometrist-mode-map:1]]
|
||||
|
@ -2410,14 +2425,16 @@ INHIBIT-HOOKS is non-nil or prefix argument is supplied.
|
|||
Has no effect if no task is active."
|
||||
(interactive "P")
|
||||
(if (chronometrist-current-task)
|
||||
(let* ((latest (chronometrist-latest-record (chronometrist-active-backend)))
|
||||
(let* ((b (chronometrist-active-backend))
|
||||
(latest (chronometrist-latest-record b))
|
||||
(plist (plist-put latest :start (chronometrist-format-time-iso8601)))
|
||||
(task (plist-get plist :name)))
|
||||
(unless inhibit-hooks
|
||||
(run-hook-with-args 'chronometrist-before-in-functions task))
|
||||
(chronometrist-replace-last (chronometrist-active-backend) plist)
|
||||
(run-hook-with-args 'chronometrist-before-in-functions task))
|
||||
(chronometrist-replace-last (chronometrist-active-backend) plist :save nil)
|
||||
(unless inhibit-hooks
|
||||
(run-hook-with-args 'chronometrist-after-in-functions task)))
|
||||
(run-hook-with-args 'chronometrist-after-in-functions task))
|
||||
(chronometrist-save-backend b))
|
||||
(message "Can only restart an active task - use this when clocked in.")))
|
||||
;; restart-task:1 ends here
|
||||
|
||||
|
@ -2432,14 +2449,16 @@ Has no effect if a task is active."
|
|||
(interactive "P")
|
||||
(if (chronometrist-current-task)
|
||||
(message "Cannot extend an active task - use this after clocking out.")
|
||||
(let* ((latest (chronometrist-latest-record (chronometrist-active-backend)))
|
||||
(let* ((b (chronometrist-active-backend))
|
||||
(latest (chronometrist-latest-record b))
|
||||
(plist (plist-put latest :stop (chronometrist-format-time-iso8601)))
|
||||
(task (plist-get plist :name)))
|
||||
(unless inhibit-hooks
|
||||
(run-hook-with-args-until-failure 'chronometrist-before-out-functions task))
|
||||
(chronometrist-replace-last (chronometrist-active-backend) plist)
|
||||
(run-hook-with-args-until-failure 'chronometrist-before-out-functions task))
|
||||
(chronometrist-replace-last b plist :save nil)
|
||||
(unless inhibit-hooks
|
||||
(run-hook-with-args 'chronometrist-after-out-functions task)))))
|
||||
(run-hook-with-args 'chronometrist-after-out-functions task))
|
||||
(chronometrist-save-backend b))))
|
||||
;; extend-task:1 ends here
|
||||
|
||||
;; [[file:chronometrist.org::*discard-active][discard-active:1]]
|
||||
|
|
|
@ -1526,6 +1526,12 @@ OLD-PATH and NEW-PATH are the old and new values of
|
|||
`chronometrist-file', respectively.")
|
||||
#+END_SRC
|
||||
|
||||
***** save-backend
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(cl-defgeneric chronometrist-save-backend (backend)
|
||||
"Save any unsaved data in BACKEND.")
|
||||
#+END_SRC
|
||||
|
||||
**** memory operations
|
||||
***** reset-backend :generic:function:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
|
@ -1859,6 +1865,8 @@ Return
|
|||
(-let*
|
||||
(((last-start last-end) (plist-get state :last))
|
||||
((rest-start rest-end rest-hash) (plist-get state :rest))
|
||||
;; may be nil if 'rest' content has changed such that
|
||||
;; last-start is now in the middle of an s-expression
|
||||
(last-expr-file (chronometrist-read-from last-start))
|
||||
(last-expr-ht (chronometrist-events-last))
|
||||
(file (chronometrist-backend-file (chronometrist-active-backend)))
|
||||
|
@ -1866,8 +1874,8 @@ Return
|
|||
(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)
|
||||
(cl-third it)
|
||||
(equal rest-hash it)))))
|
||||
(cl-third it)
|
||||
(equal rest-hash it)))))
|
||||
;; (message "chronometrist - last-start\nlast-expr-file - %S\nlast-expr-ht - %S"
|
||||
;; last-expr-file
|
||||
;; last-expr-ht)
|
||||
|
@ -2042,6 +2050,13 @@ FS-EVENT is the event passed by the `filenotify' library (see `file-notify-add-w
|
|||
:rest (chronometrist-file-hash nil :before-last t))))))
|
||||
#+END_SRC
|
||||
|
||||
**** save-backend :writer:method:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(cl-defmethod chronometrist-save-backend ((backend chronometrist-file-backend-mixin))
|
||||
(with-slots (file) backend
|
||||
(chronometrist-sexp-in-file file (save-buffer))))
|
||||
#+END_SRC
|
||||
|
||||
*** plist backend
|
||||
In this format, user data is stored as Elisp plists in a plain text file. A basic plist in this file looks like this -
|
||||
|
||||
|
@ -2642,14 +2657,14 @@ Return value is either a list in the form
|
|||
We apply the same hack as in the [[<<hack-note-plist-group-insert>>][insert]] method, removing and inserting the plist group instead of just the specific plist, to avoid having to update the pretty printer.
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(cl-defmethod chronometrist-replace-last ((backend chronometrist-plist-group-backend) plist)
|
||||
(cl-defmethod chronometrist-replace-last ((backend chronometrist-plist-group-backend) plist &key (save t))
|
||||
(cl-check-type plist chronometrist-plist)
|
||||
(when (chronometrist-backend-empty-p backend)
|
||||
(error "No record to replace in %s" (eieio-object-class-name backend)))
|
||||
(chronometrist-sexp-in-file (chronometrist-backend-file backend)
|
||||
(chronometrist-remove-last backend :save nil)
|
||||
(chronometrist-insert backend plist :save nil)
|
||||
(save-buffer)))
|
||||
(when save (save-buffer))))
|
||||
#+END_SRC
|
||||
|
||||
***** count-records :reader:method:NOEXPORT:
|
||||
|
@ -3184,42 +3199,49 @@ refresh the `chronometrist' buffer."
|
|||
(chronometrist-out))
|
||||
t))
|
||||
#+END_SRC
|
||||
|
||||
**** chronometrist-in :command:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun chronometrist-in (task &optional _prefix)
|
||||
(cl-defun chronometrist-in (task &key (save t))
|
||||
"Clock in to TASK; record current time in `chronometrist-file'.
|
||||
TASK is the name of the task, a string. PREFIX is ignored."
|
||||
(interactive "P")
|
||||
(let ((plist `(:name ,task :start ,(chronometrist-format-time-iso8601))))
|
||||
(chronometrist-insert (chronometrist-active-backend) plist)
|
||||
(let ((plist (list :name task :start (chronometrist-format-time-iso8601))))
|
||||
(chronometrist-insert (chronometrist-active-backend) plist :save save)
|
||||
(chronometrist-refresh)))
|
||||
#+END_SRC
|
||||
|
||||
**** chronometrist-out :command:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun chronometrist-out (&optional _prefix)
|
||||
(cl-defun chronometrist-out (&key (save t))
|
||||
"Record current moment as stop time to last s-exp in `chronometrist-file'.
|
||||
PREFIX is ignored."
|
||||
(interactive "P")
|
||||
(let* ((latest (chronometrist-latest-record (chronometrist-active-backend)))
|
||||
(plist (plist-put latest :stop (chronometrist-format-time-iso8601))))
|
||||
(chronometrist-replace-last (chronometrist-active-backend) plist)))
|
||||
(chronometrist-replace-last (chronometrist-active-backend) plist :save save)))
|
||||
#+END_SRC
|
||||
|
||||
**** run-functions-and-clock-in :writer:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun chronometrist-run-functions-and-clock-in (task)
|
||||
"Run hooks and clock in to TASK."
|
||||
(run-hook-with-args 'chronometrist-before-in-functions task)
|
||||
(chronometrist-in task)
|
||||
(run-hook-with-args 'chronometrist-after-in-functions task))
|
||||
(chronometrist-in task :save nil)
|
||||
(run-hook-with-args 'chronometrist-after-in-functions task)
|
||||
(chronometrist-save-backend (chronometrist-active-backend)))
|
||||
#+END_SRC
|
||||
|
||||
**** run-functions-and-clock-out :writer:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun chronometrist-run-functions-and-clock-out (task)
|
||||
"Run hooks and clock out of TASK."
|
||||
(when (run-hook-with-args-until-failure 'chronometrist-before-out-functions task)
|
||||
(chronometrist-out)
|
||||
(run-hook-with-args 'chronometrist-after-out-functions task)))
|
||||
(chronometrist-out :save nil)
|
||||
(run-hook-with-args 'chronometrist-after-out-functions task)
|
||||
(chronometrist-save-backend (chronometrist-active-backend))))
|
||||
#+END_SRC
|
||||
|
||||
**** chronometrist-mode-map :keymap:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defvar chronometrist-mode-map
|
||||
|
@ -3240,6 +3262,7 @@ PREFIX is ignored."
|
|||
map)
|
||||
"Keymap used by `chronometrist-mode'.")
|
||||
#+END_SRC
|
||||
|
||||
**** chronometrist-menu :menu:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(easy-menu-define chronometrist-menu chronometrist-mode-map
|
||||
|
@ -3277,6 +3300,7 @@ PREFIX is ignored."
|
|||
(setq revert-buffer-function #'chronometrist-refresh)
|
||||
(run-hooks 'chronometrist-mode-hook))
|
||||
#+END_SRC
|
||||
|
||||
**** toggle-task-button :writer:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun chronometrist-toggle-task-button (_button)
|
||||
|
@ -3295,6 +3319,7 @@ action, and is ignored."
|
|||
(unless (equal at-point current)
|
||||
(chronometrist-run-functions-and-clock-in at-point))))
|
||||
#+END_SRC
|
||||
|
||||
**** add-new-task-button :writer:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun chronometrist-add-new-task-button (_button)
|
||||
|
@ -3307,6 +3332,7 @@ action, and is ignored."
|
|||
(let ((task (read-from-minibuffer "New task name: " nil nil nil nil nil t)))
|
||||
(chronometrist-run-functions-and-clock-in task))))
|
||||
#+END_SRC
|
||||
|
||||
**** toggle-task :command:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
;; TODO - if clocked in and point not on a task, just clock out
|
||||
|
@ -3345,6 +3371,7 @@ If INHIBIT-HOOKS is non-nil, the hooks
|
|||
(unless (equal target current)
|
||||
(funcall in-function target))))))
|
||||
#+END_SRC
|
||||
|
||||
**** toggle-task-no-hooks :command:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun chronometrist-toggle-task-no-hooks (&optional prefix)
|
||||
|
@ -3373,16 +3400,19 @@ INHIBIT-HOOKS is non-nil or prefix argument is supplied.
|
|||
Has no effect if no task is active."
|
||||
(interactive "P")
|
||||
(if (chronometrist-current-task)
|
||||
(let* ((latest (chronometrist-latest-record (chronometrist-active-backend)))
|
||||
(let* ((b (chronometrist-active-backend))
|
||||
(latest (chronometrist-latest-record b))
|
||||
(plist (plist-put latest :start (chronometrist-format-time-iso8601)))
|
||||
(task (plist-get plist :name)))
|
||||
(unless inhibit-hooks
|
||||
(run-hook-with-args 'chronometrist-before-in-functions task))
|
||||
(chronometrist-replace-last (chronometrist-active-backend) plist)
|
||||
(run-hook-with-args 'chronometrist-before-in-functions task))
|
||||
(chronometrist-replace-last (chronometrist-active-backend) plist :save nil)
|
||||
(unless inhibit-hooks
|
||||
(run-hook-with-args 'chronometrist-after-in-functions task)))
|
||||
(run-hook-with-args 'chronometrist-after-in-functions task))
|
||||
(chronometrist-save-backend b))
|
||||
(message "Can only restart an active task - use this when clocked in.")))
|
||||
#+END_SRC
|
||||
|
||||
**** extend-task :command:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun chronometrist-extend-task (&optional inhibit-hooks)
|
||||
|
@ -3395,15 +3425,18 @@ Has no effect if a task is active."
|
|||
(interactive "P")
|
||||
(if (chronometrist-current-task)
|
||||
(message "Cannot extend an active task - use this after clocking out.")
|
||||
(let* ((latest (chronometrist-latest-record (chronometrist-active-backend)))
|
||||
(let* ((b (chronometrist-active-backend))
|
||||
(latest (chronometrist-latest-record b))
|
||||
(plist (plist-put latest :stop (chronometrist-format-time-iso8601)))
|
||||
(task (plist-get plist :name)))
|
||||
(unless inhibit-hooks
|
||||
(run-hook-with-args-until-failure 'chronometrist-before-out-functions task))
|
||||
(chronometrist-replace-last (chronometrist-active-backend) plist)
|
||||
(run-hook-with-args-until-failure 'chronometrist-before-out-functions task))
|
||||
(chronometrist-replace-last b plist :save nil)
|
||||
(unless inhibit-hooks
|
||||
(run-hook-with-args 'chronometrist-after-out-functions task)))))
|
||||
(run-hook-with-args 'chronometrist-after-out-functions task))
|
||||
(chronometrist-save-backend b))))
|
||||
#+END_SRC
|
||||
|
||||
**** discard-active :command:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun chronometrist-discard-active ()
|
||||
|
|
Reference in New Issue