Merge branch 'dev' into literate

This commit is contained in:
contrapunctus 2021-02-11 12:08:50 +05:30
commit 203838aa49
7 changed files with 157 additions and 120 deletions

View File

@ -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
View File

@ -3,7 +3,7 @@
(package
"Chronometrist"
"0.6.4"
"0.6.5"
"A time tracker for Emacs with a nice interface")
(depends-on "cl")

View File

@ -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.

View File

@ -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)

View File

@ -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

View File

@ -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."

View File

@ -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