Merge branch 'dev' into literate
This commit is contained in:
commit
203838aa49
|
@ -4,9 +4,11 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
## [0.6.5] - 2021-02-11
|
||||
### Added
|
||||
* Major mode (syntax highlighting, hook) for Chronometrist s-expression files, derived from emacs-lisp-mode
|
||||
### Fixed
|
||||
* Bug in updating the task list in some cases of the latest interval being modified.
|
||||
|
||||
## [0.6.4] - 2021-01-27
|
||||
### Fixed
|
||||
|
|
2
Cask
2
Cask
|
@ -3,7 +3,7 @@
|
|||
|
||||
(package
|
||||
"Chronometrist"
|
||||
"0.6.4"
|
||||
"0.6.5"
|
||||
"A time tracker for Emacs with a nice interface")
|
||||
|
||||
(depends-on "cl")
|
||||
|
|
16
TODO.org
16
TODO.org
|
@ -131,7 +131,17 @@ Some options and ideas -
|
|||
1. find the difference between ISO-8601 timestamps
|
||||
2. compare ISO-8601 timestamps, and
|
||||
3. do 1 and 2 faster than Elisp.
|
||||
8. [ ] Change data structure - instead of storing each plist as-is, split each into two, one with the =:start= and one with the =:end=. (Tags and key values could be added to either one.) Now we have the elegance of the one-plist-is-a-complete-interval schema in the file, and the ease and speed of detection of midnight spanning intervals in memory.
|
||||
8. [ ] Change data structure - instead of storing each plist as-is, split each into two, one with the =:start= and one with the =:end=. Now we have the elegance of the one-plist-is-a-complete-interval schema in the file, and the ease and speed of detection of midnight spanning intervals in memory.
|
||||
|
||||
So this
|
||||
: (:name "Task" ... :start "<timestamp>" :stop "<timestamp>")
|
||||
is stored in hash table values as
|
||||
#+BEGIN_SRC
|
||||
((:name "Task" ... :start "<timestamp>")
|
||||
(:stop "<timestamp>")
|
||||
...)
|
||||
#+END_SRC
|
||||
9. [ ] Change file timestamp format to =("<iso-date>" "<iso-time>")=
|
||||
** Cache
|
||||
+ Lessons from the parsimonious-reading branch - iterating =read= over the whole file is fast; splitting the intervals is not.
|
||||
+ Things we need to read the whole file for - task list, tag/key/value history.
|
||||
|
@ -200,7 +210,7 @@ ppp.el doesn't align plist values along the same column. It's also GPL, and I'm
|
|||
#+END_SRC
|
||||
...is displayed as 1:53:52 (rather than 00:54:52) after clocking out. :\
|
||||
|
||||
** chronometrist [7%]
|
||||
** chronometrist [6%]
|
||||
1. [ ] Add =:stop= time when we call =chronometrist-kv-accept=, not when we quit the key-value prompt with a blank input.
|
||||
* It might be nice to be able to quit =chronometrist-kv-add= with C-g instead, actually.
|
||||
+ =C-g= stops execution of =chronometrist-run-functions-and-clock-in=/=chronometrist-run-functions-and-clock-out=, so they can't reach the calls for =chronometrist-in=/=chronometrist-out=.
|
||||
|
@ -334,6 +344,8 @@ ppp.el doesn't align plist values along the same column. It's also GPL, and I'm
|
|||
2. Not asking for tags and/or key-values for a particular task, or having a special behaviour for a task. (e.g. some tasks I use follow certain patterns, which I'd like to automate away)
|
||||
3. [ ] Completion for sub-plists - if the value of a user keyword-value pair is a plist (heuristic - list with keyword as first element), can we reuse the keyword-value prompt for it? 🤔
|
||||
* Maybe generate the completion hash table when the plist is created, since this is likely to be less-used.
|
||||
4. [ ] Create a debug mode
|
||||
5. [ ] Create a verification command to test =chronometrist-file= for errors.
|
||||
|
||||
** plist-pp
|
||||
1. Represent sublists by depth (integer) instead of a boolean.
|
||||
|
|
|
@ -681,6 +681,8 @@ Function - chronometrist-task-list ()
|
|||
-> List
|
||||
@end itemize
|
||||
@item
|
||||
Function - chronometrist-reset-task-list ()
|
||||
@item
|
||||
Function - chronometrist-add-to-task-list (task)
|
||||
@item
|
||||
Function - chronometrist-remove-from-task-list (task)
|
||||
|
|
|
@ -280,26 +280,27 @@ Each of these has a corresponding function to clear it and fill it with values -
|
|||
19. Function - chronometrist-file-change-type (state)
|
||||
20. Function - chronometrist-task-list ()
|
||||
* -> List
|
||||
21. Function - chronometrist-add-to-task-list (task)
|
||||
22. Function - chronometrist-remove-from-task-list (task)
|
||||
23. Function - chronometrist-refresh-file (fs-event)
|
||||
24. Command - chronometrist-query-stop ()
|
||||
25. Command - chronometrist-in (task &optional _prefix)
|
||||
26. Command - chronometrist-out (&optional _prefix)
|
||||
27. Variable - chronometrist-before-in-functions
|
||||
28. Variable - chronometrist-after-in-functions
|
||||
29. Variable - chronometrist-before-out-functions
|
||||
30. Variable - chronometrist-after-out-functions
|
||||
31. Function - chronometrist-run-functions-and-clock-in (task)
|
||||
32. Function - chronometrist-run-functions-and-clock-out (task)
|
||||
33. Keymap - chronometrist-mode-map
|
||||
34. Major Mode - chronometrist-mode
|
||||
35. Function - chronometrist-toggle-task-button (button)
|
||||
36. Function - chronometrist-add-new-task-button (button)
|
||||
37. Command - chronometrist-toggle-task (&optional prefix inhibit-hooks)
|
||||
38. Command - chronometrist-toggle-task-no-hooks (&optional prefix)
|
||||
39. Command - chronometrist-add-new-task ()
|
||||
40. Command - chronometrist (&optional arg)
|
||||
21. Function - chronometrist-reset-task-list ()
|
||||
22. Function - chronometrist-add-to-task-list (task)
|
||||
23. Function - chronometrist-remove-from-task-list (task)
|
||||
24. Function - chronometrist-refresh-file (fs-event)
|
||||
25. Command - chronometrist-query-stop ()
|
||||
26. Command - chronometrist-in (task &optional _prefix)
|
||||
27. Command - chronometrist-out (&optional _prefix)
|
||||
28. Variable - chronometrist-before-in-functions
|
||||
29. Variable - chronometrist-after-in-functions
|
||||
30. Variable - chronometrist-before-out-functions
|
||||
31. Variable - chronometrist-after-out-functions
|
||||
32. Function - chronometrist-run-functions-and-clock-in (task)
|
||||
33. Function - chronometrist-run-functions-and-clock-out (task)
|
||||
34. Keymap - chronometrist-mode-map
|
||||
35. Major Mode - chronometrist-mode
|
||||
36. Function - chronometrist-toggle-task-button (button)
|
||||
37. Function - chronometrist-add-new-task-button (button)
|
||||
38. Command - chronometrist-toggle-task (&optional prefix inhibit-hooks)
|
||||
39. Command - chronometrist-toggle-task-no-hooks (&optional prefix)
|
||||
40. Command - chronometrist-add-new-task ()
|
||||
41. Command - chronometrist (&optional arg)
|
||||
|
||||
** chronometrist-events.el
|
||||
1. Variable - chronometrist-events
|
||||
|
|
|
@ -10,7 +10,16 @@
|
|||
;; (s "1.12.0")
|
||||
;; (ts "0.2")
|
||||
;; (anaphora "1.0.4"))
|
||||
;; Version: 0.6.4
|
||||
;; Version: 0.6.5
|
||||
|
||||
;; This is free and unencumbered software released into the public domain.
|
||||
;;
|
||||
;; Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
;; distribute this software, either in source code form or as a compiled
|
||||
;; binary, for any purpose, commercial or non-commercial, and by any
|
||||
;; means.
|
||||
;;
|
||||
;; For more information, please refer to <https://unlicense.org>
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
@ -581,6 +590,9 @@ which span midnights. (see `chronometrist-events-clean')"
|
|||
(defvar chronometrist-task-list nil
|
||||
"List of tasks in `chronometrist-file'.")
|
||||
|
||||
(defun chronometrist-reset-task-list ()
|
||||
(setq chronometrist-task-list (chronometrist-task-list)))
|
||||
|
||||
(defun chronometrist-add-to-task-list (task)
|
||||
(unless (cl-member task chronometrist-task-list :test #'equal)
|
||||
(setq chronometrist-task-list
|
||||
|
@ -599,7 +611,8 @@ which span midnights. (see `chronometrist-events-clean')"
|
|||
when (equal task (plist-get interval :name))
|
||||
return t)
|
||||
return count)))
|
||||
(when (and position (= position count))
|
||||
(when (or (not position)
|
||||
(= position count))
|
||||
;; The only interval for TASK is the last expression
|
||||
(setq chronometrist-task-list (remove task chronometrist-task-list)))))
|
||||
|
||||
|
@ -1111,52 +1124,48 @@ Return
|
|||
:modify))))
|
||||
|
||||
(defun chronometrist-refresh-file (fs-event)
|
||||
"Re-read `chronometrist-file' and refresh the `chronometrist' buffer.
|
||||
Argument _FS-EVENT is ignored."
|
||||
(run-hooks 'chronometrist-file-change-hook)
|
||||
;; (message "chronometrist - file %s" fs-event)
|
||||
;; `chronometrist-file-change-type' must be run /before/ we update `chronometrist--file-state'
|
||||
;; (the latter represents the old state of the file, which
|
||||
;; `chronometrist-file-change-type' compares with the new one)
|
||||
(-let* (((descriptor action file ...) fs-event)
|
||||
(change (when chronometrist--file-state
|
||||
(chronometrist-file-change-type chronometrist--file-state)))
|
||||
(reset-watch (or (eq action 'deleted) (eq action 'renamed))))
|
||||
;; (message "chronometrist - file change type is %s" change)
|
||||
(cond ((or reset-watch (not chronometrist--file-state) (eq change t))
|
||||
(when reset-watch
|
||||
(file-notify-rm-watch chronometrist--fs-watch)
|
||||
(setq chronometrist--fs-watch nil chronometrist--file-state nil))
|
||||
(chronometrist-events-populate)
|
||||
(setq chronometrist-task-list (chronometrist-task-list)))
|
||||
(chronometrist--file-state
|
||||
(let ((task (plist-get (chronometrist-last) :name)))
|
||||
(pcase change
|
||||
(:append
|
||||
(chronometrist-events-update (chronometrist-sexp-last))
|
||||
(chronometrist-add-to-task-list task))
|
||||
(:modify
|
||||
(chronometrist-events-update (chronometrist-sexp-last) t)
|
||||
(chronometrist-remove-from-task-list task)
|
||||
(chronometrist-add-to-task-list task))
|
||||
(:remove
|
||||
(let* ((date (--> (hash-table-keys chronometrist-events)
|
||||
(last it)
|
||||
(car it)))
|
||||
(old-task (--> (gethash date chronometrist-events)
|
||||
(last it)
|
||||
(car it)
|
||||
(plist-get it :name))))
|
||||
(chronometrist-remove-from-task-list old-task)
|
||||
(--> (gethash date chronometrist-events)
|
||||
(-drop-last 1 it)
|
||||
(puthash date it chronometrist-events))))
|
||||
((pred null) nil)))))
|
||||
(setq chronometrist--file-state
|
||||
(list :last (chronometrist-file-hash :before-last nil)
|
||||
:rest (chronometrist-file-hash nil :before-last t)))
|
||||
;; REVIEW - can we move most/all of this to the `chronometrist-file-change-hook'?
|
||||
(chronometrist-refresh)))
|
||||
"Re-read `chronometrist-file' and refresh the `chronometrist' buffer.
|
||||
Argument _FS-EVENT is ignored."
|
||||
(run-hooks 'chronometrist-file-change-hook)
|
||||
;; (message "chronometrist - file %s" fs-event)
|
||||
;; `chronometrist-file-change-type' must be run /before/ we update `chronometrist--file-state'
|
||||
;; (the latter represents the old state of the file, which
|
||||
;; `chronometrist-file-change-type' compares with the new one)
|
||||
(-let* (((descriptor action file ...) fs-event)
|
||||
(change (when chronometrist--file-state
|
||||
(chronometrist-file-change-type chronometrist--file-state)))
|
||||
(reset-watch (or (eq action 'deleted) (eq action 'renamed))))
|
||||
;; (message "chronometrist - file change type is %s" change)
|
||||
(cond ((or reset-watch (not chronometrist--file-state) (eq change t))
|
||||
(when reset-watch
|
||||
(file-notify-rm-watch chronometrist--fs-watch)
|
||||
(setq chronometrist--fs-watch nil chronometrist--file-state nil))
|
||||
(chronometrist-events-populate)
|
||||
(chronometrist-reset-task-list))
|
||||
(chronometrist--file-state
|
||||
(let* ((old-plist (chronometrist-events-last))
|
||||
(old-task (plist-get old-plist :name))
|
||||
(new-task (plist-get (chronometrist-sexp-last) :name)))
|
||||
(pcase change
|
||||
(:append
|
||||
(chronometrist-events-update (chronometrist-sexp-last))
|
||||
(chronometrist-add-to-task-list new-task))
|
||||
(:modify
|
||||
(chronometrist-events-update (chronometrist-sexp-last) t)
|
||||
(chronometrist-remove-from-task-list old-task)
|
||||
(chronometrist-add-to-task-list new-task))
|
||||
(:remove
|
||||
(let ((date (chronometrist-events-last-date)))
|
||||
(chronometrist-remove-from-task-list old-task)
|
||||
(--> (gethash date chronometrist-events)
|
||||
(-drop-last 1 it)
|
||||
(puthash date it chronometrist-events))))
|
||||
((pred null) nil)))))
|
||||
(setq chronometrist--file-state
|
||||
(list :last (chronometrist-file-hash :before-last nil)
|
||||
:rest (chronometrist-file-hash nil :before-last t)))
|
||||
;; REVIEW - can we move most/all of this to the `chronometrist-file-change-hook'?
|
||||
(chronometrist-refresh)))
|
||||
|
||||
(defun chronometrist-query-stop ()
|
||||
"Ask the user if they would like to clock out."
|
||||
|
|
|
@ -26,7 +26,16 @@ This is displayed when the user clicks on the package's entry in =M-x list-packa
|
|||
;; (s "1.12.0")
|
||||
;; (ts "0.2")
|
||||
;; (anaphora "1.0.4"))
|
||||
;; Version: 0.6.4
|
||||
;; Version: 0.6.5
|
||||
|
||||
;; This is free and unencumbered software released into the public domain.
|
||||
;;
|
||||
;; Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
;; distribute this software, either in source code form or as a compiled
|
||||
;; binary, for any purpose, commercial or non-commercial, and by any
|
||||
;; means.
|
||||
;;
|
||||
;; For more information, please refer to <https://unlicense.org>
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
@ -696,6 +705,11 @@ which span midnights. (see `chronometrist-events-clean')"
|
|||
(defvar chronometrist-task-list nil
|
||||
"List of tasks in `chronometrist-file'.")
|
||||
#+END_SRC
|
||||
**** reset-task-list :function:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun chronometrist-reset-task-list ()
|
||||
(setq chronometrist-task-list (chronometrist-task-list)))
|
||||
#+END_SRC
|
||||
**** add-to-task-list :function:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun chronometrist-add-to-task-list (task)
|
||||
|
@ -718,7 +732,8 @@ which span midnights. (see `chronometrist-events-clean')"
|
|||
when (equal task (plist-get interval :name))
|
||||
return t)
|
||||
return count)))
|
||||
(when (and position (= position count))
|
||||
(when (or (not position)
|
||||
(= position count))
|
||||
;; The only interval for TASK is the last expression
|
||||
(setq chronometrist-task-list (remove task chronometrist-task-list)))))
|
||||
#+END_SRC
|
||||
|
@ -1338,52 +1353,48 @@ Return
|
|||
***** refresh-file :function:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun chronometrist-refresh-file (fs-event)
|
||||
"Re-read `chronometrist-file' and refresh the `chronometrist' buffer.
|
||||
Argument _FS-EVENT is ignored."
|
||||
(run-hooks 'chronometrist-file-change-hook)
|
||||
;; (message "chronometrist - file %s" fs-event)
|
||||
;; `chronometrist-file-change-type' must be run /before/ we update `chronometrist--file-state'
|
||||
;; (the latter represents the old state of the file, which
|
||||
;; `chronometrist-file-change-type' compares with the new one)
|
||||
(-let* (((descriptor action file ...) fs-event)
|
||||
(change (when chronometrist--file-state
|
||||
(chronometrist-file-change-type chronometrist--file-state)))
|
||||
(reset-watch (or (eq action 'deleted) (eq action 'renamed))))
|
||||
;; (message "chronometrist - file change type is %s" change)
|
||||
(cond ((or reset-watch (not chronometrist--file-state) (eq change t))
|
||||
(when reset-watch
|
||||
(file-notify-rm-watch chronometrist--fs-watch)
|
||||
(setq chronometrist--fs-watch nil chronometrist--file-state nil))
|
||||
(chronometrist-events-populate)
|
||||
(setq chronometrist-task-list (chronometrist-task-list)))
|
||||
(chronometrist--file-state
|
||||
(let ((task (plist-get (chronometrist-last) :name)))
|
||||
(pcase change
|
||||
(:append
|
||||
(chronometrist-events-update (chronometrist-sexp-last))
|
||||
(chronometrist-add-to-task-list task))
|
||||
(:modify
|
||||
(chronometrist-events-update (chronometrist-sexp-last) t)
|
||||
(chronometrist-remove-from-task-list task)
|
||||
(chronometrist-add-to-task-list task))
|
||||
(:remove
|
||||
(let* ((date (--> (hash-table-keys chronometrist-events)
|
||||
(last it)
|
||||
(car it)))
|
||||
(old-task (--> (gethash date chronometrist-events)
|
||||
(last it)
|
||||
(car it)
|
||||
(plist-get it :name))))
|
||||
(chronometrist-remove-from-task-list old-task)
|
||||
(--> (gethash date chronometrist-events)
|
||||
(-drop-last 1 it)
|
||||
(puthash date it chronometrist-events))))
|
||||
((pred null) nil)))))
|
||||
(setq chronometrist--file-state
|
||||
(list :last (chronometrist-file-hash :before-last nil)
|
||||
:rest (chronometrist-file-hash nil :before-last t)))
|
||||
;; REVIEW - can we move most/all of this to the `chronometrist-file-change-hook'?
|
||||
(chronometrist-refresh)))
|
||||
"Re-read `chronometrist-file' and refresh the `chronometrist' buffer.
|
||||
Argument _FS-EVENT is ignored."
|
||||
(run-hooks 'chronometrist-file-change-hook)
|
||||
;; (message "chronometrist - file %s" fs-event)
|
||||
;; `chronometrist-file-change-type' must be run /before/ we update `chronometrist--file-state'
|
||||
;; (the latter represents the old state of the file, which
|
||||
;; `chronometrist-file-change-type' compares with the new one)
|
||||
(-let* (((descriptor action file ...) fs-event)
|
||||
(change (when chronometrist--file-state
|
||||
(chronometrist-file-change-type chronometrist--file-state)))
|
||||
(reset-watch (or (eq action 'deleted) (eq action 'renamed))))
|
||||
;; (message "chronometrist - file change type is %s" change)
|
||||
(cond ((or reset-watch (not chronometrist--file-state) (eq change t))
|
||||
(when reset-watch
|
||||
(file-notify-rm-watch chronometrist--fs-watch)
|
||||
(setq chronometrist--fs-watch nil chronometrist--file-state nil))
|
||||
(chronometrist-events-populate)
|
||||
(chronometrist-reset-task-list))
|
||||
(chronometrist--file-state
|
||||
(let* ((old-plist (chronometrist-events-last))
|
||||
(old-task (plist-get old-plist :name))
|
||||
(new-task (plist-get (chronometrist-sexp-last) :name)))
|
||||
(pcase change
|
||||
(:append
|
||||
(chronometrist-events-update (chronometrist-sexp-last))
|
||||
(chronometrist-add-to-task-list new-task))
|
||||
(:modify
|
||||
(chronometrist-events-update (chronometrist-sexp-last) t)
|
||||
(chronometrist-remove-from-task-list old-task)
|
||||
(chronometrist-add-to-task-list new-task))
|
||||
(:remove
|
||||
(let ((date (chronometrist-events-last-date)))
|
||||
(chronometrist-remove-from-task-list old-task)
|
||||
(--> (gethash date chronometrist-events)
|
||||
(-drop-last 1 it)
|
||||
(puthash date it chronometrist-events))))
|
||||
((pred null) nil)))))
|
||||
(setq chronometrist--file-state
|
||||
(list :last (chronometrist-file-hash :before-last nil)
|
||||
:rest (chronometrist-file-hash nil :before-last t)))
|
||||
;; REVIEW - can we move most/all of this to the `chronometrist-file-change-hook'?
|
||||
(chronometrist-refresh)))
|
||||
#+END_SRC
|
||||
***** query-stop :function:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
|
|
Reference in New Issue