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{}
@enumerate
@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
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
@ -1106,33 +1106,35 @@ Function - chronometrist-goal-on-file-change ()
@enumerate
@item
Class - chronometrist-sexp
@item
Object - chronometrist-sexp
@item
Custom variable - chronometrist-sexp-pretty-print-function
@item
Macro - chronometrist-sexp-in-file (file &rest body)
@item
Function - chronometrist-sexp-open-log ()
Method - chronometrist-to-hash ((backend chronometrist-sexp) table)
@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
Function - chronometrist-sexp-query-till (&optional (date (chronometrist-date)))
Method - chronometrist-open-log ((backend chronometrist-sexp))
@item
Function - chronometrist-sexp-last ()
Method - chronometrist-last ((backend chronometrist-sexp))
@itemize
@item
-> plist
@end itemize
@item
Function - chronometrist-sexp-current-task ()
Method - chronometrist-current-task ((backend chronometrist-sexp))
@item
Function - chronometrist-sexp-events-populate ()
Method - chronometrist-create-file ((backend chronometrist-sexp))
@item
Function - chronometrist-sexp-create-file ()
@item
Function - chronometrist-sexp-new (plist &optional (buffer (find-file-noselect chronometrist-file)))
Method - chronometrist-new (((backend chronometrist-sexp)) plist)
@item
Function - chronometrist-sexp-delete-list (&optional arg)
@item
Function - chronometrist-sexp-replace-last (plist)
Method - chronometrist-replace-last (((backend chronometrist-sexp)) plist)
@item
Command - chronometrist-sexp-reindent-buffer ()
@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.
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.
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=
@ -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 ()
** chronometrist-sexp
1. Custom variable - chronometrist-sexp-pretty-print-function
2. Macro - chronometrist-sexp-in-file (file &rest body)
3. Function - chronometrist-sexp-open-log ()
4. Function - chronometrist-sexp-between (&optional (ts-beg (chronometrist-date)) (ts-end (ts-adjust 'day +1 (chronometrist-date))))
5. Function - chronometrist-sexp-query-till (&optional (date (chronometrist-date)))
6. Function - chronometrist-sexp-last ()
* -> plist
7. Function - chronometrist-sexp-current-task ()
8. Function - chronometrist-sexp-events-populate ()
9. Function - chronometrist-sexp-create-file ()
10. Function - chronometrist-sexp-new (plist &optional (buffer (find-file-noselect chronometrist-file)))
11. Function - chronometrist-sexp-delete-list (&optional arg)
12. Function - chronometrist-sexp-replace-last (plist)
13. Command - chronometrist-sexp-reindent-buffer ()
1. Class - chronometrist-sexp
2. Object - chronometrist-sexp
3. Custom variable - chronometrist-sexp-pretty-print-function
4. Macro - chronometrist-sexp-in-file (file &rest body)
5. Method - chronometrist-to-hash ((backend chronometrist-sexp) table)
5. Method - chronometrist-from-hash ((backend chronometrist-sexp) table)
5. Method - chronometrist-open-log ((backend chronometrist-sexp))
8. Method - chronometrist-last ((backend chronometrist-sexp))
* -> plist
9. Method - chronometrist-current-task ((backend chronometrist-sexp))
11. Method - chronometrist-create-file ((backend chronometrist-sexp))
12. Method - chronometrist-new (((backend chronometrist-sexp)) plist)
13. Function - chronometrist-sexp-delete-list (&optional arg)
14. Method - chronometrist-replace-last (((backend chronometrist-sexp)) plist)
15. Command - chronometrist-sexp-reindent-buffer ()
# Local Variables:
# 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."
:initform "")))
(defvar chronometrist-current-backend nil
"The current backend in use.")
(defvar chronometrist-backends nil
"List of enabled backends.")

View File

@ -61,7 +61,7 @@ file.")
(defun chronometrist-current-task ()
"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 " "))
"Format SECONDS as a string suitable for display in Chronometrist buffers.

View File

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

View File

@ -22,7 +22,7 @@
(defun chronometrist-last ()
"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)))
"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))
(t (unless keep-date
(setq chronometrist-report--ui-date nil))
(chronometrist-common-create-file)
(chronometrist-create-file chronometrist-current-backend)
(chronometrist-report-mode)
(switch-to-buffer buffer)
(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)
(save-excursion ,@body)))
;;;; Queries
(defun chronometrist-sexp-open-log ()
"Open `chronometrist-file' in another window."
(find-file-other-window chronometrist-file)
(goto-char (point-max)))
;; # Migration #
(cl-defmethod chronometrist-to-hash ((backend chronometrist-sexp) table))
(defun chronometrist-sexp-last ()
"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."
(cl-defmethod chronometrist-from-hash ((backend chronometrist-sexp) table)
(chronometrist-sexp-in-file chronometrist-file
(goto-char (point-min))
(let ((index 0) expr pending-expr)
@ -84,15 +62,30 @@ were none."
chronometrist-events)))
(unless (zerop index) index))))
;;;; Modifications
(defun chronometrist-sexp-create-file ()
"Create `chronometrist-file' if it doesn't already exist."
;; # Queries #
(cl-defmethod chronometrist-open-log ((backend chronometrist-sexp))
(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)
(with-current-buffer (find-file-noselect chronometrist-file)
(write-file chronometrist-file))))
(cl-defun chronometrist-sexp-new (plist)
"Add new PLIST at the end of `chronometrist-file'."
(cl-defmethod chronometrist-new ((backend chronometrist-sexp) plist)
(chronometrist-sexp-in-file chronometrist-file
(goto-char (point-max))
;; 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))
(delete-region point-1 (point))))
(defun chronometrist-sexp-replace-last (plist)
"Replace the last s-expression in `chronometrist-file' with PLIST."
(cl-defmethod chronometrist-replace-last ((backend chronometrist-sexp) plist)
(chronometrist-sexp-in-file chronometrist-file
(goto-char (point-max))
(unless (and (bobp) (bolp))
@ -131,7 +123,7 @@ were none."
;; :name should be removed from `chronometrist-task-list', but to ascertain
;; that condition we would have to either read the entire file or
;; 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)
(setq chronometrist--inhibit-read-p t)
(save-buffer)))

View File

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

View File

@ -89,11 +89,7 @@
Argument _BUTTON is for the purpose of using this command as a
button action."
(interactive)
(chronometrist-sexp-open-log))
(defun chronometrist-common-create-file ()
"Create `chronometrist-file' if it doesn't already exist."
(chronometrist-sexp-create-file))
(chronometrist-sexp-open-log chronometrist-current-backend))
(defun chronometrist-task-active? (task)
"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."
(interactive "P")
(let ((plist `(:name ,task :start ,(chronometrist-format-time-iso8601))))
(chronometrist-sexp-new plist)
(chronometrist-new chronometrist-current-backend plist)
(chronometrist-refresh)))
(defun chronometrist-out (&optional _prefix)
@ -236,7 +232,7 @@ TASK is the name of the task, a string. PREFIX is ignored."
PREFIX is ignored."
(interactive "P")
(let ((plist (plist-put (chronometrist-last) :stop (chronometrist-format-time-iso8601))))
(chronometrist-sexp-replace-last plist)))
(chronometrist-replace-last plist)))
;; ## HOOKS ##
(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))
(chronometrist-common-file-empty-p chronometrist-file))
;; first run
(chronometrist-common-create-file)
(chronometrist-create-file chronometrist-current-backend)
(let ((inhibit-read-only t))
(chronometrist-common-clear-buffer buffer)
(insert "Welcome to Chronometrist! Hit RET to ")