Convert s-expression backend functions to methods

They are also renamed to match the names of the generic functions.
This commit is contained in:
contrapunctus 2020-11-09 14:08:42 +05:30
parent 8acf23b593
commit 1037cacd3c
10 changed files with 66 additions and 72 deletions

View File

@ -262,7 +262,7 @@ The next one was released in v0.5. Till then, any time the @uref{../elisp/chrono
After the optimization@dots{} After the optimization@dots{}
@enumerate @enumerate
@item @item
Two backend functions (@uref{../elisp/chronometrist-sexp.el, @samp{chronometrist-sexp-new}} and @uref{../elisp/chronometrist-sexp.el, @samp{chronometrist-sexp-replace-last}}) were modified to set a flag (@uref{../elisp/chronometrist.el, @samp{chronometrist--inhibit-read-p}}) before saving the file. Two backend functions (@uref{../elisp/chronometrist-sexp.el, @samp{chronometrist-new}} and @uref{../elisp/chronometrist-sexp.el, @samp{chronometrist-replace-last}}) were modified to set a flag (@uref{../elisp/chronometrist.el, @samp{chronometrist--inhibit-read-p}}) before saving the file.
@item @item
If this flag is non-nil, @uref{../elisp/chronometrist.el, @samp{chronometrist-refresh-file}} skips the expensive calls to @samp{chronometrist-events-populate}, @samp{chronometrist-tasks-from-table}, and @samp{chronometrist-tags-history-populate}, and resets the flag. If this flag is non-nil, @uref{../elisp/chronometrist.el, @samp{chronometrist-refresh-file}} skips the expensive calls to @samp{chronometrist-events-populate}, @samp{chronometrist-tasks-from-table}, and @samp{chronometrist-tags-history-populate}, and resets the flag.
@item @item
@ -1106,33 +1106,35 @@ Function - chronometrist-goal-on-file-change ()
@enumerate @enumerate
@item @item
Class - chronometrist-sexp
@item
Object - chronometrist-sexp
@item
Custom variable - chronometrist-sexp-pretty-print-function Custom variable - chronometrist-sexp-pretty-print-function
@item @item
Macro - chronometrist-sexp-in-file (file &rest body) Macro - chronometrist-sexp-in-file (file &rest body)
@item @item
Function - chronometrist-sexp-open-log () Method - chronometrist-to-hash ((backend chronometrist-sexp) table)
@item @item
Function - chronometrist-sexp-between (&optional (ts-beg (chronometrist-date)) (ts-end (ts-adjust 'day +1 (chronometrist-date)))) Method - chronometrist-from-hash ((backend chronometrist-sexp) table)
@item @item
Function - chronometrist-sexp-query-till (&optional (date (chronometrist-date))) Method - chronometrist-open-log ((backend chronometrist-sexp))
@item @item
Function - chronometrist-sexp-last () Method - chronometrist-last ((backend chronometrist-sexp))
@itemize @itemize
@item @item
-> plist -> plist
@end itemize @end itemize
@item @item
Function - chronometrist-sexp-current-task () Method - chronometrist-current-task ((backend chronometrist-sexp))
@item @item
Function - chronometrist-sexp-events-populate () Method - chronometrist-create-file ((backend chronometrist-sexp))
@item @item
Function - chronometrist-sexp-create-file () Method - chronometrist-new (((backend chronometrist-sexp)) plist)
@item
Function - chronometrist-sexp-new (plist &optional (buffer (find-file-noselect chronometrist-file)))
@item @item
Function - chronometrist-sexp-delete-list (&optional arg) Function - chronometrist-sexp-delete-list (&optional arg)
@item @item
Function - chronometrist-sexp-replace-last (plist) Method - chronometrist-replace-last (((backend chronometrist-sexp)) plist)
@item @item
Command - chronometrist-sexp-reindent-buffer () Command - chronometrist-sexp-reindent-buffer ()
@end enumerate @end enumerate

View File

@ -87,7 +87,7 @@ One of the earliest 'optimizations' of great importance turned out to simply be
The next one was released in v0.5. Till then, any time the [[file:../elisp/chronometrist-custom.el::defcustom chronometrist-file (][=chronometrist-file=]] was modified, we'd clear the [[file:../elisp/chronometrist-events.el::defvar chronometrist-events (][=chronometrist-events=]] hash table and read data into it again. The reading itself is nearly-instant, even with ~2 years' worth of data [fn:1] (it uses Emacs' [[elisp:(describe-function 'read)][=read=]], after all), but the splitting of [[* Midnight-spanning events][midnight-spanning events]] is the real performance killer. The next one was released in v0.5. Till then, any time the [[file:../elisp/chronometrist-custom.el::defcustom chronometrist-file (][=chronometrist-file=]] was modified, we'd clear the [[file:../elisp/chronometrist-events.el::defvar chronometrist-events (][=chronometrist-events=]] hash table and read data into it again. The reading itself is nearly-instant, even with ~2 years' worth of data [fn:1] (it uses Emacs' [[elisp:(describe-function 'read)][=read=]], after all), but the splitting of [[* Midnight-spanning events][midnight-spanning events]] is the real performance killer.
After the optimization... After the optimization...
1. Two backend functions ([[file:../elisp/chronometrist-sexp.el::cl-defun chronometrist-sexp-new (][=chronometrist-sexp-new=]] and [[file:../elisp/chronometrist-sexp.el::defun chronometrist-sexp-replace-last (][=chronometrist-sexp-replace-last=]]) were modified to set a flag ([[file:../elisp/chronometrist.el::defvar chronometrist--inhibit-read-p ][=chronometrist--inhibit-read-p=]]) before saving the file. 1. Two backend functions ([[file:../elisp/chronometrist-sexp.el::cl-defmethod chronometrist-new (][=chronometrist-new=]] and [[file:../elisp/chronometrist-sexp.el::cl-defmethod chronometrist-replace-last (][=chronometrist-replace-last=]]) were modified to set a flag ([[file:../elisp/chronometrist.el::defvar chronometrist--inhibit-read-p ][=chronometrist--inhibit-read-p=]]) before saving the file.
2. If this flag is non-nil, [[file:../elisp/chronometrist.el::defun chronometrist-refresh-file (][=chronometrist-refresh-file=]] skips the expensive calls to =chronometrist-events-populate=, =chronometrist-tasks-from-table=, and =chronometrist-tags-history-populate=, and resets the flag. 2. If this flag is non-nil, [[file:../elisp/chronometrist.el::defun chronometrist-refresh-file (][=chronometrist-refresh-file=]] skips the expensive calls to =chronometrist-events-populate=, =chronometrist-tasks-from-table=, and =chronometrist-tags-history-populate=, and resets the flag.
3. Instead, the aforementioned backend functions modify the relevant variables - =chronometrist-events=, =chronometrist-task-list=, and =chronometrist-tags-history= - via... 3. Instead, the aforementioned backend functions modify the relevant variables - =chronometrist-events=, =chronometrist-task-list=, and =chronometrist-tags-history= - via...
* =chronometrist-events-add= / =chronometrist-events-replace-last= * =chronometrist-events-add= / =chronometrist-events-replace-last=
@ -455,20 +455,21 @@ Each of these has a corresponding function to clear it and fill it with values -
13. Function - chronometrist-goal-on-file-change () 13. Function - chronometrist-goal-on-file-change ()
** chronometrist-sexp ** chronometrist-sexp
1. Custom variable - chronometrist-sexp-pretty-print-function 1. Class - chronometrist-sexp
2. Macro - chronometrist-sexp-in-file (file &rest body) 2. Object - chronometrist-sexp
3. Function - chronometrist-sexp-open-log () 3. Custom variable - chronometrist-sexp-pretty-print-function
4. Function - chronometrist-sexp-between (&optional (ts-beg (chronometrist-date)) (ts-end (ts-adjust 'day +1 (chronometrist-date)))) 4. Macro - chronometrist-sexp-in-file (file &rest body)
5. Function - chronometrist-sexp-query-till (&optional (date (chronometrist-date))) 5. Method - chronometrist-to-hash ((backend chronometrist-sexp) table)
6. Function - chronometrist-sexp-last () 5. Method - chronometrist-from-hash ((backend chronometrist-sexp) table)
* -> plist 5. Method - chronometrist-open-log ((backend chronometrist-sexp))
7. Function - chronometrist-sexp-current-task () 8. Method - chronometrist-last ((backend chronometrist-sexp))
8. Function - chronometrist-sexp-events-populate () * -> plist
9. Function - chronometrist-sexp-create-file () 9. Method - chronometrist-current-task ((backend chronometrist-sexp))
10. Function - chronometrist-sexp-new (plist &optional (buffer (find-file-noselect chronometrist-file))) 11. Method - chronometrist-create-file ((backend chronometrist-sexp))
11. Function - chronometrist-sexp-delete-list (&optional arg) 12. Method - chronometrist-new (((backend chronometrist-sexp)) plist)
12. Function - chronometrist-sexp-replace-last (plist) 13. Function - chronometrist-sexp-delete-list (&optional arg)
13. Command - chronometrist-sexp-reindent-buffer () 14. Method - chronometrist-replace-last (((backend chronometrist-sexp)) plist)
15. Command - chronometrist-sexp-reindent-buffer ()
# Local Variables: # Local Variables:
# org-link-file-path-type: relative # org-link-file-path-type: relative

View File

@ -19,6 +19,9 @@
"The extension used by a file of this backend, without a leading period." "The extension used by a file of this backend, without a leading period."
:initform ""))) :initform "")))
(defvar chronometrist-current-backend nil
"The current backend in use.")
(defvar chronometrist-backends nil (defvar chronometrist-backends nil
"List of enabled backends.") "List of enabled backends.")

View File

@ -61,7 +61,7 @@ file.")
(defun chronometrist-current-task () (defun chronometrist-current-task ()
"Return the name of the currently clocked-in task, or nil if not clocked in." "Return the name of the currently clocked-in task, or nil if not clocked in."
(chronometrist-sexp-current-task)) (chronometrist-current-task chronometrist-current-backend))
(cl-defun chronometrist-format-time (seconds &optional (blank " ")) (cl-defun chronometrist-format-time (seconds &optional (blank " "))
"Format SECONDS as a string suitable for display in Chronometrist buffers. "Format SECONDS as a string suitable for display in Chronometrist buffers.

View File

@ -96,7 +96,7 @@ be removed."
new-kvs new-kvs
`(:start ,old-start) `(:start ,old-start)
(when old-stop `(:stop ,old-stop))))) (when old-stop `(:stop ,old-stop)))))
(chronometrist-sexp-replace-last plist))) (chronometrist-replace-last chronometrist-current-backend plist)))
;;;; TAGS ;;;; ;;;; TAGS ;;;;
(defvar chronometrist-tags-history (make-hash-table :test #'equal) (defvar chronometrist-tags-history (make-hash-table :test #'equal)

View File

@ -22,7 +22,7 @@
(defun chronometrist-last () (defun chronometrist-last ()
"Return the last entry from `chronometrist-file' as a plist." "Return the last entry from `chronometrist-file' as a plist."
(chronometrist-sexp-last)) (chronometrist-last chronometrist-current-backend))
(cl-defun chronometrist-task-time-one-day (task &optional (ts (ts-now))) (cl-defun chronometrist-task-time-one-day (task &optional (ts (ts-now)))
"Return total time spent on TASK today or (if supplied) on timestamp TS. "Return total time spent on TASK today or (if supplied) on timestamp TS.

View File

@ -227,7 +227,7 @@ current week. Otherwise, display data from the week specified by
(kill-buffer buffer)) (kill-buffer buffer))
(t (unless keep-date (t (unless keep-date
(setq chronometrist-report--ui-date nil)) (setq chronometrist-report--ui-date nil))
(chronometrist-common-create-file) (chronometrist-create-file chronometrist-current-backend)
(chronometrist-report-mode) (chronometrist-report-mode)
(switch-to-buffer buffer) (switch-to-buffer buffer)
(chronometrist-report-refresh-file nil) (chronometrist-report-refresh-file nil)

View File

@ -33,32 +33,10 @@ neatly), or falls back to `pp' if it isn't."
`(with-current-buffer (find-file-noselect ,file) `(with-current-buffer (find-file-noselect ,file)
(save-excursion ,@body))) (save-excursion ,@body)))
;;;; Queries ;; # Migration #
(defun chronometrist-sexp-open-log () (cl-defmethod chronometrist-to-hash ((backend chronometrist-sexp) table))
"Open `chronometrist-file' in another window."
(find-file-other-window chronometrist-file)
(goto-char (point-max)))
(defun chronometrist-sexp-last () (cl-defmethod chronometrist-from-hash ((backend chronometrist-sexp) table)
"Return last s-expression from `chronometrist-file'."
(chronometrist-sexp-in-file chronometrist-file
(goto-char (point-max))
(backward-list)
(ignore-errors (read (current-buffer)))))
(defun chronometrist-sexp-current-task ()
"Return the name of the currently clocked-in task, or nil if not clocked in."
(let ((last-event (chronometrist-sexp-last)))
(if (plist-member last-event :stop)
nil
(plist-get last-event :name))))
(defun chronometrist-sexp-events-populate ()
"Populate hash table `chronometrist-events'.
The data is acquired from `chronometrist-file'.
Return final number of events read from file, or nil if there
were none."
(chronometrist-sexp-in-file chronometrist-file (chronometrist-sexp-in-file chronometrist-file
(goto-char (point-min)) (goto-char (point-min))
(let ((index 0) expr pending-expr) (let ((index 0) expr pending-expr)
@ -84,15 +62,30 @@ were none."
chronometrist-events))) chronometrist-events)))
(unless (zerop index) index)))) (unless (zerop index) index))))
;;;; Modifications ;; # Queries #
(defun chronometrist-sexp-create-file () (cl-defmethod chronometrist-open-log ((backend chronometrist-sexp))
"Create `chronometrist-file' if it doesn't already exist." (find-file-other-window chronometrist-file)
(goto-char (point-max)))
(cl-defmethod chronometrist-last ((backend chronometrist-sexp))
(chronometrist-sexp-in-file chronometrist-file
(goto-char (point-max))
(backward-list)
(ignore-errors (read (current-buffer)))))
(cl-defmethod chronometrist-current-task ((backend chronometrist-sexp))
(let ((last-event (chronometrist-last backend)))
(if (plist-member last-event :stop)
nil
(plist-get last-event :name))))
;; # Modifications #
(cl-defmethod chronometrist-create-file ((backend chronometrist-sexp))
(unless (file-exists-p chronometrist-file) (unless (file-exists-p chronometrist-file)
(with-current-buffer (find-file-noselect chronometrist-file) (with-current-buffer (find-file-noselect chronometrist-file)
(write-file chronometrist-file)))) (write-file chronometrist-file))))
(cl-defun chronometrist-sexp-new (plist) (cl-defmethod chronometrist-new ((backend chronometrist-sexp) plist)
"Add new PLIST at the end of `chronometrist-file'."
(chronometrist-sexp-in-file chronometrist-file (chronometrist-sexp-in-file chronometrist-file
(goto-char (point-max)) (goto-char (point-max))
;; If we're adding the first s-exp in the file, don't add a ;; If we're adding the first s-exp in the file, don't add a
@ -114,8 +107,7 @@ were none."
(forward-sexp (or arg 1)) (forward-sexp (or arg 1))
(delete-region point-1 (point)))) (delete-region point-1 (point))))
(defun chronometrist-sexp-replace-last (plist) (cl-defmethod chronometrist-replace-last ((backend chronometrist-sexp) plist)
"Replace the last s-expression in `chronometrist-file' with PLIST."
(chronometrist-sexp-in-file chronometrist-file (chronometrist-sexp-in-file chronometrist-file
(goto-char (point-max)) (goto-char (point-max))
(unless (and (bobp) (bolp)) (unless (and (bobp) (bolp))
@ -131,7 +123,7 @@ were none."
;; :name should be removed from `chronometrist-task-list', but to ascertain ;; :name should be removed from `chronometrist-task-list', but to ascertain
;; that condition we would have to either read the entire file or ;; that condition we would have to either read the entire file or
;; map over the hash table, defeating the optimization. Thus, we ;; map over the hash table, defeating the optimization. Thus, we
;; don't update `chronometrist-task-list' here (unlike `chronometrist-sexp-new') ;; don't update `chronometrist-task-list' here (unlike `chronometrist-new')
(chronometrist-tags-history-replace-last plist) (chronometrist-tags-history-replace-last plist)
(setq chronometrist--inhibit-read-p t) (setq chronometrist--inhibit-read-p t)
(save-buffer))) (save-buffer)))

View File

@ -260,7 +260,7 @@ specified by `chronometrist-statistics--ui-state'."
(setq chronometrist-statistics--ui-state `(:mode week (setq chronometrist-statistics--ui-state `(:mode week
:start ,week-start :start ,week-start
:end ,week-end))) :end ,week-end)))
(chronometrist-common-create-file) (chronometrist-create-file chronometrist-current-backend)
(chronometrist-statistics-mode) (chronometrist-statistics-mode)
(switch-to-buffer buffer) (switch-to-buffer buffer)
(chronometrist-statistics-refresh)))))) (chronometrist-statistics-refresh))))))

View File

@ -89,11 +89,7 @@
Argument _BUTTON is for the purpose of using this command as a Argument _BUTTON is for the purpose of using this command as a
button action." button action."
(interactive) (interactive)
(chronometrist-sexp-open-log)) (chronometrist-sexp-open-log chronometrist-current-backend))
(defun chronometrist-common-create-file ()
"Create `chronometrist-file' if it doesn't already exist."
(chronometrist-sexp-create-file))
(defun chronometrist-task-active? (task) (defun chronometrist-task-active? (task)
"Return t if TASK is currently clocked in, else nil." "Return t if TASK is currently clocked in, else nil."
@ -228,7 +224,7 @@ Argument _FS-EVENT is ignored."
TASK is the name of the task, a string. PREFIX is ignored." TASK is the name of the task, a string. PREFIX is ignored."
(interactive "P") (interactive "P")
(let ((plist `(:name ,task :start ,(chronometrist-format-time-iso8601)))) (let ((plist `(:name ,task :start ,(chronometrist-format-time-iso8601))))
(chronometrist-sexp-new plist) (chronometrist-new chronometrist-current-backend plist)
(chronometrist-refresh))) (chronometrist-refresh)))
(defun chronometrist-out (&optional _prefix) (defun chronometrist-out (&optional _prefix)
@ -236,7 +232,7 @@ TASK is the name of the task, a string. PREFIX is ignored."
PREFIX is ignored." PREFIX is ignored."
(interactive "P") (interactive "P")
(let ((plist (plist-put (chronometrist-last) :stop (chronometrist-format-time-iso8601)))) (let ((plist (plist-put (chronometrist-last) :stop (chronometrist-format-time-iso8601))))
(chronometrist-sexp-replace-last plist))) (chronometrist-replace-last plist)))
;; ## HOOKS ## ;; ## HOOKS ##
(defvar chronometrist-mode-hook nil (defvar chronometrist-mode-hook nil
@ -443,7 +439,7 @@ If numeric argument ARG is 2, run `chronometrist-statistics'."
(cond ((or (not (file-exists-p chronometrist-file)) (cond ((or (not (file-exists-p chronometrist-file))
(chronometrist-common-file-empty-p chronometrist-file)) (chronometrist-common-file-empty-p chronometrist-file))
;; first run ;; first run
(chronometrist-common-create-file) (chronometrist-create-file chronometrist-current-backend)
(let ((inhibit-read-only t)) (let ((inhibit-read-only t))
(chronometrist-common-clear-buffer buffer) (chronometrist-common-clear-buffer buffer)
(insert "Welcome to Chronometrist! Hit RET to ") (insert "Welcome to Chronometrist! Hit RET to ")