Rename backend generic functions

This commit is contained in:
contrapunctus 2020-11-09 20:11:43 +05:30
parent 3fc7109a95
commit 6b93d8183b
13 changed files with 94 additions and 57 deletions

View File

@ -1,7 +1,8 @@
;;; Directory Local Variables
;;; For more information see (info "(emacs) Directory Variables")
((emacs-lisp-mode . ((nameless-aliases . (("cc" . "chronometrist-common")
((emacs-lisp-mode . ((nameless-aliases . (("cb" . "chronometrist-backend")
("cc" . "chronometrist-common")
("cd" . "chronometrist-diary")
("ce" . "chronometrist-events")
("ck" . "chronometrist-kv")

View File

@ -65,6 +65,7 @@ Tags and Key-Values
Reference
* Legend of currently-used time formats::
* chronometrist-backend.el: chronometrist-backendel.
* chronometrist-common.el: chronometrist-commonel.
* chronometrist-custom.el: chronometrist-customel.
* chronometrist-diary-view.el: chronometrist-diary-viewel.
@ -133,7 +134,7 @@ From the project root, you can now run
@item
Subclass @samp{chronometrist-backend} to define your backend.
@item
Create methods for the nine generic functions in @samp{chronometrist-backend.el}
Create methods for the eight generic functions in @samp{chronometrist-backend.el}
@end enumerate
@node Explanation
@ -452,6 +453,7 @@ Each of these has a corresponding function to clear it and fill it with values -
@menu
* Legend of currently-used time formats::
* chronometrist-backend.el: chronometrist-backendel.
* chronometrist-common.el: chronometrist-commonel.
* chronometrist-custom.el: chronometrist-customel.
* chronometrist-diary-view.el: chronometrist-diary-viewel.
@ -545,6 +547,34 @@ Used for goals (chronometrist-goals-list, chronometrist-get-goal) - minutes seem
Only returned by chronometrist-seconds-to-hms, called by chronometrist-format-time
@end itemize
@node chronometrist-backendel
@section chronometrist-backend.el
@enumerate
@item
Class - chronometrist-backend ()
@item
Variable - chronometrist-backend-current
@item
Variable - chronometrist-backends
@item
Generic Function - chronometrist-backend-to-hash (backend table)
@item
Generic Function - chronometrist-backend-from-hash (backend table)
@item
Generic Function - chronometrist-backend-open-file (backend)
@item
Generic Function - chronometrist-backend-latest-record (backend)
@item
Generic Function - chronometrist-backend-current-task (backend)
@item
Generic Function - chronometrist-backend-create-file (backend)
@item
Generic Function - chronometrist-backend-add-new (backend plist)
@item
Generic Function - chronometrist-backend-replace-last (backend plist)
@end enumerate
@node chronometrist-commonel
@section chronometrist-common.el

View File

@ -18,7 +18,7 @@ All of these are optional, but recommended for the best experience.
2. =cask exec buttercup -L . --traceback pretty= to run tests.
** How to create a new backend
1. Subclass =chronometrist-backend= to define your backend.
2. Create methods for the nine generic functions in =chronometrist-backend.el=
2. Create methods for the eight generic functions in =chronometrist-backend.el=
* Explanation
:PROPERTIES:
:DESCRIPTION: The design, the implementation, and a little history
@ -218,6 +218,18 @@ Each of these has a corresponding function to clear it and fill it with values -
(hours minute seconds)
* Only returned by chronometrist-seconds-to-hms, called by chronometrist-format-time
** chronometrist-backend.el
1. Class - chronometrist-backend ()
2. Variable - chronometrist-backend-current
3. Variable - chronometrist-backends
4. Generic Function - chronometrist-backend-to-hash (backend table)
5. Generic Function - chronometrist-backend-from-hash (backend table)
6. Generic Function - chronometrist-backend-open-file (backend)
7. Generic Function - chronometrist-backend-latest-record (backend)
8. Generic Function - chronometrist-backend-current-task (backend)
9. Generic Function - chronometrist-backend-create-file (backend)
10. Generic Function - chronometrist-backend-add-new (backend plist)
11. Generic Function - chronometrist-backend-replace-last (backend plist)
** chronometrist-common.el
1. Variable - chronometrist-empty-time-string
2. Variable - chronometrist-date-re

View File

@ -19,44 +19,41 @@
"The extension used by a file of this backend, without a leading period."
:initform "")))
(defvar chronometrist-current-backend nil
(defvar chronometrist-backend-current nil
"The current backend in use.")
(defvar chronometrist-backends nil
"List of enabled backends.")
;; # Migration #
(cl-defgeneric chronometrist-to-hash (backend table)
"Clear TABLE and fill it using BACKEND.")
(cl-defgeneric chronometrist-backend-to-hash (backend table)
"Clear TABLE and fill it using BACKEND.
Return final number of intervals read from backend, or nil if
there were none.")
;; If the file for BACKEND exists and is not empty, signal an error or
;; prompt the user?
(cl-defgeneric chronometrist-from-hash (backend table)
(cl-defgeneric chronometrist-backend-from-hash (backend table)
"Fill BACKEND using TABLE.")
;; # Queries #
(cl-defgeneric chronometrist-open-file (backend)
(cl-defgeneric chronometrist-backend-open-file (backend)
"Open the storage file associated with BACKEND.")
(cl-defgeneric chronometrist-last (backend)
"Return the latest interval from BACKEND.")
(cl-defgeneric chronometrist-backend-latest-record (backend)
"Return the latest record from BACKEND.")
(cl-defgeneric chronometrist-current-task (backend)
(cl-defgeneric chronometrist-backend-current-task (backend)
"Return the name of the currently clocked-in task, or nil if not clocked in.")
(cl-defgeneric chronometrist-populate (backend table)
"Read data from BACKEND into hash table TABLE.
Return final number of intervals read from file, or nil if there
were none.")
;; # Modifications #
(cl-defgeneric chronometrist-create-file (backend)
(cl-defgeneric chronometrist-backend-create-file (backend)
"Create BACKEND file if it does not already exist.")
(cl-defgeneric chronometrist-new (backend plist)
(cl-defgeneric chronometrist-backend-add-new (backend plist)
"Use PLIST to add a new interval to BACKEND.")
(cl-defgeneric chronometrist-replace-last (backend plist)
(cl-defgeneric chronometrist-backend-replace-last (backend plist)
"Replace the latest record in BACKEND with PLIST.")
(provide 'chronometrist-backend)

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-current-task chronometrist-current-backend))
(chronometrist-current-task chronometrist-backend-current))
(cl-defun chronometrist-format-time (seconds &optional (blank " "))
"Format SECONDS as a string suitable for display in Chronometrist buffers.

View File

@ -125,7 +125,7 @@ The data is acquired from `chronometrist-file'.
Return final number of events read from file, or nil if there
were none."
(clrhash chronometrist-events)
(chronometrist-to-hash chronometrist-current-backend chronometrist-events))
(chronometrist-to-hash chronometrist-backend-current chronometrist-events))
(defun chronometrist-tasks-from-table ()
"Return a list of task names from `chronometrist-events'."

View File

@ -12,7 +12,6 @@
(require 'chronometrist-common)
(declare-function chronometrist-refresh "chronometrist.el")
(declare-function chronometrist-last "chronometrist-queries.el")
;; This is free and unencumbered software released into the public domain.
;;
@ -69,7 +68,7 @@ TAGS should be a list of symbols and/or strings.
PLIST should be a property list. Properties reserved by
Chronometrist - currently :name, :tags, :start, and :stop - will
be removed."
(let* ((old-expr (chronometrist-last))
(let* ((old-expr (chronometrist-backend-last chronometrist-backend-current))
(old-name (plist-get old-expr :name))
(old-start (plist-get old-expr :start))
(old-stop (plist-get old-expr :stop))
@ -96,7 +95,7 @@ be removed."
new-kvs
`(:start ,old-start)
(when old-stop `(:stop ,old-stop)))))
(chronometrist-replace-last chronometrist-current-backend plist)))
(chronometrist-replace-last chronometrist-backend-current plist)))
;;;; TAGS ;;;;
(defvar chronometrist-tags-history (make-hash-table :test #'equal)
@ -206,7 +205,7 @@ INITIAL-INPUT is as used in `completing-read'."
_ARGS are ignored. This function always returns t, so it can be
used in `chronometrist-before-out-functions'."
(unless chronometrist--skip-detail-prompts
(let* ((last-expr (chronometrist-last))
(let* ((last-expr (chronometrist-backend-last chronometrist-backend-current))
(last-name (plist-get last-expr :name))
(last-tags (plist-get last-expr :tags))
(input (->> last-tags
@ -353,7 +352,7 @@ It currently supports ido, ido-ubiquitous, ivy, and helm."
"Prompt the user to enter keys.
USED-KEYS are keys they have already added since the invocation
of `chronometrist-kv-add'."
(let ((key-suggestions (--> (chronometrist-last)
(let ((key-suggestions (--> (chronometrist-backend-last chronometrist-backend-current)
(plist-get it :name)
(gethash it chronometrist-key-history))))
(completing-read (format "Key (%s to quit): " (chronometrist-kv-completion-quit-key))
@ -397,7 +396,8 @@ used in `chronometrist-before-out-functions'."
(unless chronometrist--skip-detail-prompts
(let* ((buffer (get-buffer-create chronometrist-kv-buffer-name))
(first-key-p t)
(last-kvs (chronometrist-plist-remove (chronometrist-last) :name :tags :start :stop))
(last-kvs (chronometrist-plist-remove (chronometrist-backend-last chronometrist-backend-current)
:name :tags :start :stop))
(used-keys (->> (seq-filter #'keywordp last-kvs)
(mapcar #'symbol-name)
(--map (s-chop-prefix ":" it)))))
@ -405,7 +405,7 @@ used in `chronometrist-before-out-functions'."
(with-current-buffer buffer
(chronometrist-common-clear-buffer buffer)
(chronometrist-kv-read-mode)
(if (and (chronometrist-current-task) last-kvs)
(if (and (chronometrist-backend-current-task chronometrist-backend-current) last-kvs)
(progn
(funcall chronometrist-sexp-pretty-print-function last-kvs buffer)
(down-list -1)

View File

@ -20,10 +20,6 @@
(require 'chronometrist-common)
(require 'chronometrist-events)
(defun chronometrist-last ()
"Return the last entry from `chronometrist-file' as a plist."
(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.
The data is obtained from `chronometrist-file', via `chronometrist-events'.

View File

@ -154,7 +154,7 @@ If FIRSTONLY is non-nil, insert only the first keybinding found."
(defun chronometrist-report-refresh-file (_fs-event)
"Re-read `chronometrist-file' and refresh the `chronometrist-report' buffer.
Argument _FS-EVENT is ignored."
(chronometrist-events-populate)
(chronometrist-backend-to-hash chronometrist-backend-current chronometrist-events)
;; (chronometrist-events-clean)
(chronometrist-report-refresh))
@ -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-create-file chronometrist-current-backend)
(chronometrist-backend-create-file chronometrist-backend-current)
(chronometrist-report-mode)
(switch-to-buffer buffer)
(chronometrist-report-refresh-file nil)

View File

@ -34,7 +34,8 @@ neatly), or falls back to `pp' if it isn't."
(save-excursion ,@body)))
;; # Migration #
(cl-defmethod chronometrist-to-hash ((backend chronometrist-sexp) table)
(cl-defmethod chronometrist-backend-to-hash ((backend chronometrist-sexp) table)
(clrhash table)
(chronometrist-sexp-in-file chronometrist-file
(goto-char (point-min))
(let ((index 0) expr pending-expr)
@ -60,31 +61,31 @@ neatly), or falls back to `pp' if it isn't."
chronometrist-events)))
(unless (zerop index) index))))
(cl-defmethod chronometrist-from-hash ((backend chronometrist-sexp) table))
(cl-defmethod chronometrist-backend-from-hash ((backend chronometrist-sexp) table))
;; # Queries #
(cl-defmethod chronometrist-open-log ((backend chronometrist-sexp))
(cl-defmethod chronometrist-backend-open-file ((backend chronometrist-sexp))
(find-file-other-window chronometrist-file)
(goto-char (point-max)))
(cl-defmethod chronometrist-last ((backend chronometrist-sexp))
(cl-defmethod chronometrist-backend-latest-record ((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)))
(cl-defmethod chronometrist-backend-current-task ((backend chronometrist-sexp))
(let ((last-event (chronometrist-backend-latest-record backend)))
(unless (plist-member last-event :stop)
(plist-get last-event :name))))
;; # Modifications #
(cl-defmethod chronometrist-create-file ((backend chronometrist-sexp))
(cl-defmethod chronometrist-backend-create-file ((backend chronometrist-sexp))
(unless (file-exists-p chronometrist-file)
(with-current-buffer (find-file-noselect chronometrist-file)
(write-file chronometrist-file))))
(cl-defmethod chronometrist-new ((backend chronometrist-sexp) plist)
(cl-defmethod chronometrist-backend-add-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
@ -106,7 +107,7 @@ neatly), or falls back to `pp' if it isn't."
(forward-sexp (or arg 1))
(delete-region point-1 (point))))
(cl-defmethod chronometrist-replace-last ((backend chronometrist-sexp) plist)
(cl-defmethod chronometrist-backend-replace-last ((backend chronometrist-sexp) plist)
(chronometrist-sexp-in-file chronometrist-file
(goto-char (point-max))
(unless (and (bobp) (bolp))

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-create-file chronometrist-current-backend)
(chronometrist-backend-create-file chronometrist-backend-current)
(chronometrist-statistics-mode)
(switch-to-buffer buffer)
(chronometrist-statistics-refresh))))))

View File

@ -31,7 +31,7 @@
Buffers will be refreshed only if they are visible and the user
is clocked in to a task."
(when (chronometrist-current-task) ;; FIXME - This line is currently
(when (chronometrist-backend-current-task chronometrist-backend-current) ;; FIXME - This line is currently
;; resulting in no refresh at midnight. When `chronometrist-entries' is
;; optimized to consume less CPU and avoid unnecessary parsing,
;; remove this condition.

View File

@ -89,11 +89,11 @@
Argument _BUTTON is for the purpose of using this command as a
button action."
(interactive)
(chronometrist-sexp-open-log chronometrist-current-backend))
(chronometrist-backend-open-file chronometrist-backend-current))
(defun chronometrist-task-active? (task)
"Return t if TASK is currently clocked in, else nil."
(equal (chronometrist-current-task) task))
(equal (chronometrist-backend-current-task chronometrist-backend-current) task))
(defun chronometrist-activity-indicator ()
"Return a string to indicate that a task is active.
@ -107,7 +107,7 @@ See custom variable `chronometrist-activity-indicator'."
;; HACK - these calls are commented out, because `chronometrist-entries' is
;; called by both `chronometrist-refresh' and `chronometrist-refresh-file', and only the
;; latter should refresh from a file.
;; (chronometrist-events-populate)
;; (chronometrist-backend-to-hash chronometrist-backend-current chronometrist-events)
;; (chronometrist-events-clean)
(->> (-sort #'string-lessp chronometrist-task-list)
(--map-indexed
@ -134,7 +134,7 @@ See custom variable `chronometrist-activity-indicator'."
(defun chronometrist-goto-last-task ()
"In the `chronometrist' buffer, move point to the line containing the last active task."
(goto-char (point-min))
(re-search-forward (plist-get (chronometrist-last) :name) nil t)
(re-search-forward (plist-get (chronometrist-backend-last chronometrist-backend-current) :name) nil t)
(beginning-of-line))
(defun chronometrist-print-keybind (command &optional description firstonly)
@ -203,7 +203,7 @@ Argument _FS-EVENT is ignored."
;; REVIEW - can we move most/all of this to the `chronometrist-file-change-hook'?
(if chronometrist--inhibit-read-p
(setq chronometrist--inhibit-read-p nil)
(chronometrist-events-populate)
(chronometrist-backend-to-hash chronometrist-backend-current chronometrist-events)
(setq chronometrist-task-list (chronometrist-tasks-from-table))
(chronometrist-tags-history-populate chronometrist-events chronometrist-tags-history))
(chronometrist-key-history-populate chronometrist-events chronometrist-key-history)
@ -212,7 +212,7 @@ Argument _FS-EVENT is ignored."
(defun chronometrist-query-stop ()
"Ask the user if they would like to clock out."
(let ((task (chronometrist-current-task)))
(let ((task (chronometrist-backend-current-task chronometrist-backend-current)))
(and task
(yes-or-no-p (format "Stop tracking time for %s? " task))
(chronometrist-out))
@ -223,14 +223,14 @@ 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-new chronometrist-current-backend plist)
(chronometrist-new chronometrist-backend-current plist)
(chronometrist-refresh)))
(defun chronometrist-out (&optional _prefix)
"Record current moment as stop time to last s-exp in `chronometrist-file'.
PREFIX is ignored."
(interactive "P")
(let ((plist (plist-put (chronometrist-last) :stop (chronometrist-format-time-iso8601))))
(let ((plist (plist-put (chronometrist-backend-last chronometrist-backend-current) :stop (chronometrist-format-time-iso8601))))
(chronometrist-replace-last plist)))
;; ## HOOKS ##
@ -337,7 +337,7 @@ Argument _BUTTON is for the purpose of using this as a button
action, and is ignored."
(when current-prefix-arg
(chronometrist-goto-nth-task (prefix-numeric-value current-prefix-arg)))
(let ((current (chronometrist-current-task))
(let ((current (chronometrist-backend-current-task chronometrist-backend-current))
(at-point (chronometrist-task-at-point)))
;; clocked in + point on current = clock out
;; clocked in + point on some other task = clock out, clock in to task
@ -352,7 +352,7 @@ action, and is ignored."
Argument _BUTTON is for the purpose of using this as a button
action, and is ignored."
(let ((current (chronometrist-current-task)))
(let ((current (chronometrist-backend-current-task chronometrist-backend-current)))
(when current
(chronometrist-run-functions-and-clock-out current))
(let ((task (read-from-minibuffer "New task name: " nil nil nil nil nil t)))
@ -379,7 +379,7 @@ If INHIBIT-HOOKS is non-nil, the hooks
(nth (when prefix (chronometrist-goto-nth-task prefix)))
(at-point (chronometrist-task-at-point))
(target (or nth at-point))
(current (chronometrist-current-task))
(current (chronometrist-backend-current-task chronometrist-backend-current))
(in-function (if inhibit-hooks
#'chronometrist-in
#'chronometrist-run-functions-and-clock-in))
@ -438,7 +438,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-create-file chronometrist-current-backend)
(chronometrist-backend-create-file chronometrist-backend-current)
(let ((inhibit-read-only t))
(chronometrist-common-clear-buffer buffer)
(insert "Welcome to Chronometrist! Hit RET to ")