Compare commits

...
This repository has been archived on 2022-05-13. You can view files and clone it, but cannot push or open issues or pull requests.

3 Commits

Author SHA1 Message Date
contrapunctus 88b672f003 Update TODO 2022-01-06 23:11:42 +05:30
contrapunctus 9f59f938f4 Fix wrong number of arguments error 2022-01-06 23:11:42 +05:30
contrapunctus 44a97db410 Reduce successive file saves in interactive commands
Each save runs the file-notify callback, which may decrease
responsiveness.
2022-01-06 13:42:07 +05:30
3 changed files with 105 additions and 49 deletions

View File

@ -110,7 +110,11 @@
1. Add a new kind of plist - =(:name "NAME" :time "TIME" ...)=
To record events for which the time interval is not relevant. These won't be shown in =chronometrist= - perhaps in a different buffer.
* Optimization
** When clocking in/out, do not save file until hooks are run
** DONE Don't update task list from data when user has set their own
:PROPERTIES:
:CREATED: 2022-01-06T22:53:20+0530
:END:
** DONE When clocking in/out, do not save file until hooks are run
:PROPERTIES:
:CREATED: 2022-01-03T16:09:36+0530
:END:

View File

@ -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]]
@ -2218,44 +2231,46 @@ refresh the `chronometrist' buffer."
(let ((task (chronometrist-current-task)))
(and task
(yes-or-no-p (format "Stop tracking time for %s? " task))
(chronometrist-out))
(chronometrist-run-functions-and-clock-out task))
t))
;; 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)
"Record current moment as stop time to last s-exp in `chronometrist-file'.
PREFIX is ignored."
(cl-defun chronometrist-out (_task &key (save t))
"Record current moment as stop time for the latest record.
TASK 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 task :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]]

View File

@ -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:
@ -3181,45 +3196,52 @@ refresh the `chronometrist' buffer."
(let ((task (chronometrist-current-task)))
(and task
(yes-or-no-p (format "Stop tracking time for %s? " task))
(chronometrist-out))
(chronometrist-run-functions-and-clock-out task))
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)
"Record current moment as stop time to last s-exp in `chronometrist-file'.
PREFIX is ignored."
(cl-defun chronometrist-out (_task &key (save t))
"Record current moment as stop time for the latest record.
TASK 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 task :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 ()