Compare commits
11 Commits
88aa54c082
...
998ea0f94e
Author | SHA1 | Date |
---|---|---|
contrapunctus | 998ea0f94e | |
contrapunctus | 8f14fbb546 | |
contrapunctus | bb4b2121c9 | |
contrapunctus | dbc7be41d2 | |
contrapunctus | a1c79d8faa | |
contrapunctus | d63bc11c8a | |
contrapunctus | dcca2632fb | |
contrapunctus | bbf5a2e048 | |
contrapunctus | 73be9670b7 | |
contrapunctus | 1bf960b6e0 | |
contrapunctus | ca20b3edda |
37
TODO.org
37
TODO.org
|
@ -840,6 +840,14 @@ Chronometrist allows the user to add arbitrary key-values. There are a few ways
|
|||
https://github.com/projecthamster/hamster
|
||||
+ https://github.com/projecthamster/hamster/wiki/Our-datamodel
|
||||
|
||||
** [[https://gitlab.com/Harag/cl-naive-store][cl-naive-store]]
|
||||
:PROPERTIES:
|
||||
:CREATED: 2022-04-16T21:11:07+0530
|
||||
:END:
|
||||
Reimplementing the plist/plist-group backends in Common Lisp, without the buffer visualisation and buffer-movement commands provided by Emacs/Elisp, is not something I'm very enthusiastic about. Even if those turn out to not be major factors, implementing these backends in Elisp taught me that making your own database can be a lot of work. Especially if you just care about your application, and writing a database was not what you set out to do (I severely underestimated the work involved).
|
||||
|
||||
Yet, some may prefer the plist-group backend to an SQLite database. I trust that a cl-naive-store backend could have the same capabilities as the plist-group backend, but without the maintenance burden of an /ad hoc/ s-expression store.
|
||||
|
||||
* Functions doing similar things :code:
|
||||
1. =task-time-one-day=
|
||||
2. =interval= (unused)
|
||||
|
@ -1086,11 +1094,10 @@ A task can be part of any number of categories. A category may also contain othe
|
|||
+ Some backends may not be installed - "see MELPA for more formats"
|
||||
2. [ ] Don't suggest nil when asking for first project on first run
|
||||
|
||||
* Common Lisp port
|
||||
* STARTED Common Lisp port
|
||||
:PROPERTIES:
|
||||
:CREATED: 2022-04-03T09:36:53+0530
|
||||
:END:
|
||||
|
||||
** DONE Replace =add-variable-watcher=
|
||||
Common Lisp does not have Elisp's =add-variable-watcher=. The Elisp code used that to automatically update the =file= slot of the active backend object when the user changes the value of =chronometrist-file=. The new solution is to remove direct accesses to the =file= slot, define a =backend-file= method which returns the value of =file= if non-nil (it's usually nil), or derives a file path from =*user-data-file*= and the backend's extension.
|
||||
|
||||
|
@ -1198,3 +1205,29 @@ Default view is a tiled composition of the one-day task-duration table, one-day
|
|||
:CREATED: 2022-04-08T19:56:38+0530
|
||||
:END:
|
||||
some way to view/edit the database in a different format, e.g. Lisp plists
|
||||
|
||||
* Documentation restructuring :doc:
|
||||
:PROPERTIES:
|
||||
:CREATED: 2022-04-14T18:04:21+0530
|
||||
:END:
|
||||
1. Move documentation from LPs to manual.org
|
||||
2. Revamp tutorials - https://diataxis.fr/tutorials/#the-language-of-tutorials
|
||||
3. Make how-to guides more general?
|
||||
4. Move some how-to guide code into a contrib directory?
|
||||
|
||||
** tutorial and how-to topic revamp
|
||||
tutorials
|
||||
1. installation (MELPA) -> first run
|
||||
2. adding the first task
|
||||
3. clocking in, clocking out
|
||||
4. customization - open a file when starting a task (move from how-to)
|
||||
5. customization - warn when exiting with an active task
|
||||
6. clocking in/out without running hooks
|
||||
7. restarting or discarding an activity
|
||||
8. key-value extension - installation -> customization -> testing/results
|
||||
9. graph extension - installation -> customization -> testing/results
|
||||
|
||||
how-to guides
|
||||
1. how to install (Git, MELPA, Quelpa, etc)
|
||||
2. how to attach tags and properties to time intervals (cover different commands)
|
||||
3. how to configure Emacs for Chronometrist development
|
||||
|
|
|
@ -672,7 +672,9 @@ treated as though their time is 00:00:00."
|
|||
|
||||
*** task-time-one-day :reader:
|
||||
#+BEGIN_SRC lisp
|
||||
(defun task-time-one-day (task &optional (date (date-ts)) (backend (chronometrist-active-backend)))
|
||||
(defun task-time-one-day (task &optional
|
||||
(date (date-ts))
|
||||
(backend (chronometrist-active-backend)))
|
||||
"Return total time spent on TASK today or on DATE, an ISO-8601 date.
|
||||
The return value is seconds, as an integer."
|
||||
(let ((task-events (chronometrist-task-records-for-date backend task date)))
|
||||
|
@ -680,8 +682,8 @@ The return value is seconds, as an integer."
|
|||
(->> (plists-to-durations task-events)
|
||||
(-reduce #'+)
|
||||
(truncate))
|
||||
;; no events for this task on DATE, i.e. no time spent
|
||||
0)))
|
||||
;; no events for this task on DATE, i.e. no time spent
|
||||
0)))
|
||||
#+END_SRC
|
||||
|
||||
*** active-time-on :reader:
|
||||
|
@ -948,6 +950,56 @@ return a list of tasks from the active backend."
|
|||
(or *task-list* task-list (setf task-list (list-tasks backend))))))
|
||||
#+END_SRC
|
||||
|
||||
**** day :class:
|
||||
#+BEGIN_SRC lisp
|
||||
(defclass day ()
|
||||
((properties :initarg :properties :accessor properties)
|
||||
(date :initarg :date
|
||||
:accessor date
|
||||
:type integer
|
||||
:documentation "The date as an integer representing the UNIX epoch time.")
|
||||
(intervals :initarg :intervals
|
||||
:accessor intervals
|
||||
:documentation "The intervals associated with this day.")
|
||||
(events :initarg :events
|
||||
:accessor events
|
||||
:documentation "The events associated with this day.")))
|
||||
#+END_SRC
|
||||
|
||||
**** interval :class:
|
||||
#+BEGIN_SRC lisp
|
||||
(defclass interval ()
|
||||
((properties :initarg :properties :accessor properties)
|
||||
(activity :initarg :activity
|
||||
:accessor activity
|
||||
:type string
|
||||
:documentation "The name of the task executed during this interval.")
|
||||
(start :initarg :start
|
||||
:accessor start
|
||||
:type integer
|
||||
:documentation "The time at which this interval started, as an integer representing the UNIX epoch time.")
|
||||
(stop :initarg :stop
|
||||
:accessor stop
|
||||
:type integer
|
||||
:documentation "The time at which this interval ended, as an integer representing the UNIX epoch time."))
|
||||
(:documentation "A time range spent on a specific task, with optional properties."))
|
||||
#+END_SRC
|
||||
|
||||
**** event :class:
|
||||
#+BEGIN_SRC lisp
|
||||
(defclass event ()
|
||||
((properties :initarg :properties :accessor properties)
|
||||
(name :initarg :name
|
||||
:accessor name
|
||||
:type string
|
||||
:documentation "The name of this event.")
|
||||
(time :initarg :time
|
||||
:accessor time
|
||||
:type integer
|
||||
:documentation "The time at which this interval started, as an integer representing the UNIX epoch time."))
|
||||
(:documentation "A named timestamp with optional properties."))
|
||||
#+END_SRC
|
||||
|
||||
**** run-assertions :generic:function:
|
||||
#+BEGIN_SRC lisp
|
||||
(defgeneric backend-run-assertions (backend)
|
||||
|
@ -981,8 +1033,7 @@ For instance, a file-based backend could be undergoing editing by
|
|||
a user."))
|
||||
#+END_SRC
|
||||
|
||||
**** file operations
|
||||
***** create-file :generic:function:
|
||||
**** create-file :generic:function:
|
||||
[[file:../tests/tests.org::#tests-backend-create-file][tests]]
|
||||
|
||||
#+BEGIN_SRC lisp
|
||||
|
@ -992,14 +1043,13 @@ Use FILE as a path, if provided.
|
|||
Return path of new file if successfully created, and nil if it already exists."))
|
||||
#+END_SRC
|
||||
|
||||
***** latest-date-records :generic:function:
|
||||
**** get :generic:function:
|
||||
#+BEGIN_SRC lisp
|
||||
(defgeneric latest-date-records (backend)
|
||||
(:documentation "Return intervals of latest day in BACKEND as a tagged list (\"DATE\" PLIST*).
|
||||
Return nil if BACKEND contains no records."))
|
||||
(defgeneric get (date backend)
|
||||
(:documentation "Return day associated with DATE from BACKEND, or nil if no such day exists."))
|
||||
#+END_SRC
|
||||
|
||||
***** insert :generic:function:
|
||||
**** insert :generic:function:
|
||||
#+BEGIN_SRC lisp
|
||||
(defgeneric insert (backend plist &key &allow-other-keys)
|
||||
(:documentation "Insert PLIST as new record in BACKEND.
|
||||
|
@ -1013,7 +1063,7 @@ PLIST may be an interval which crosses days."))
|
|||
(error "Not a valid plist: %S" plist)))
|
||||
#+END_SRC
|
||||
|
||||
***** remove-last :generic:function:
|
||||
**** remove-last :generic:function:
|
||||
#+BEGIN_SRC lisp
|
||||
(defgeneric remove-last (backend &key &allow-other-keys)
|
||||
(:documentation "Remove last record from BACKEND.
|
||||
|
@ -1021,7 +1071,7 @@ Return non-nil if record is successfully removed.
|
|||
Signal an error if there is no record to remove."))
|
||||
#+END_SRC
|
||||
|
||||
***** latest-record :generic:function:
|
||||
**** latest-record :generic:function:
|
||||
#+BEGIN_SRC lisp
|
||||
(defgeneric latest-record (backend)
|
||||
(:documentation "Return the latest record from BACKEND as a plist, or nil if BACKEND contains no records.
|
||||
|
@ -1032,7 +1082,7 @@ If the latest record starts on one day and ends on another, the
|
|||
entire (unsplit) record must be returned."))
|
||||
#+END_SRC
|
||||
|
||||
***** task-records-for-date :generic:function:
|
||||
**** task-records-for-date :generic:function:
|
||||
#+BEGIN_SRC lisp
|
||||
(defgeneric task-records-for-date (backend task date-ts &key &allow-other-keys)
|
||||
(:documentation "From BACKEND, return records for TASK on DATE-TS as a list of plists.
|
||||
|
@ -1047,7 +1097,7 @@ Return nil if BACKEND contains no records."))
|
|||
(error "date-ts %S is not a `ts' struct" date-ts)))
|
||||
#+END_SRC
|
||||
|
||||
***** replace-last :generic:function:
|
||||
**** replace-last :generic:function:
|
||||
#+BEGIN_SRC lisp
|
||||
(defgeneric replace-last (backend plist &key &allow-other-keys)
|
||||
(:documentation "Replace last record in BACKEND with PLIST.
|
||||
|
@ -1058,14 +1108,14 @@ Return non-nil if successful."))
|
|||
(error "Not a valid plist: %S" plist)))
|
||||
#+END_SRC
|
||||
|
||||
***** to-file :generic:function:
|
||||
**** to-file :generic:function:
|
||||
#+BEGIN_SRC lisp
|
||||
(defgeneric to-file (input-hash-table output-backend output-file)
|
||||
(:documentation "Save data from INPUT-HASH-TABLE to OUTPUT-FILE, in OUTPUT-BACKEND format.
|
||||
Any existing data in OUTPUT-FILE is overwritten."))
|
||||
#+END_SRC
|
||||
|
||||
***** on-add :generic:function:
|
||||
**** on-add :generic:function:
|
||||
#+BEGIN_SRC lisp
|
||||
(defgeneric on-add (backend)
|
||||
(:documentation "Function called when data is added to BACKEND.
|
||||
|
@ -1076,7 +1126,7 @@ backend file).
|
|||
NEW-DATA is the data that was added."))
|
||||
#+END_SRC
|
||||
|
||||
***** on-modify :generic:function:
|
||||
**** on-modify :generic:function:
|
||||
#+BEGIN_SRC lisp
|
||||
(defgeneric on-modify (backend)
|
||||
(:documentation "Function called when data in BACKEND is modified (rather than added or removed).
|
||||
|
@ -1088,7 +1138,7 @@ OLD-DATA and NEW-DATA is the data before and after the changes,
|
|||
respectively."))
|
||||
#+END_SRC
|
||||
|
||||
***** on-remove :generic:function:
|
||||
**** on-remove :generic:function:
|
||||
#+BEGIN_SRC lisp
|
||||
(defgeneric on-remove (backend)
|
||||
(:documentation "Function called when data is removed from BACKEND.
|
||||
|
@ -1099,7 +1149,7 @@ the backend file).
|
|||
OLD-DATA is the data that was modified."))
|
||||
#+END_SRC
|
||||
|
||||
***** on-change :generic:function:
|
||||
**** on-change :generic:function:
|
||||
#+BEGIN_SRC lisp
|
||||
(defgeneric on-change (backend &rest args)
|
||||
(:documentation "Function to be run when BACKEND changes on disk.
|
||||
|
@ -1108,7 +1158,7 @@ This may happen within Chronometrist (e.g. via
|
|||
backend file)."))
|
||||
#+END_SRC
|
||||
|
||||
***** verify :generic:function:
|
||||
**** verify :generic:function:
|
||||
#+BEGIN_SRC lisp
|
||||
(defgeneric verify (backend)
|
||||
(:documentation "Check BACKEND for errors in data.
|
||||
|
@ -1117,7 +1167,7 @@ Return nil if no errors are found.
|
|||
If an error is found, return (LINE-NUMBER . COLUMN-NUMBER) for file-based backends."))
|
||||
#+END_SRC
|
||||
|
||||
***** on-file-path-change :generic:function:
|
||||
**** on-file-path-change :generic:function:
|
||||
#+BEGIN_SRC lisp
|
||||
(defgeneric on-file-path-change (backend old-path new-path)
|
||||
(:documentation "Function run when the value of `file' is changed.
|
||||
|
@ -1125,14 +1175,13 @@ OLD-PATH and NEW-PATH are the old and new values of
|
|||
`file', respectively."))
|
||||
#+END_SRC
|
||||
|
||||
**** memory operations
|
||||
***** reset-backend :generic:function:
|
||||
**** reset-backend :generic:function:
|
||||
#+BEGIN_SRC lisp
|
||||
(defgeneric reset-backend (backend)
|
||||
(:documentation "Reset data structures for BACKEND."))
|
||||
#+END_SRC
|
||||
|
||||
***** to-hash-table :generic:function:
|
||||
**** to-hash-table :generic:function:
|
||||
#+BEGIN_SRC lisp
|
||||
(defgeneric to-hash-table (backend)
|
||||
(:documentation "Return data in BACKEND as a hash table in chronological order.
|
||||
|
@ -1141,13 +1190,13 @@ lists of records, represented by plists. Both hash table keys and
|
|||
hash table values must be in chronological order."))
|
||||
#+END_SRC
|
||||
|
||||
***** to-list :generic:function:
|
||||
**** to-list :generic:function:
|
||||
#+BEGIN_SRC lisp
|
||||
(defgeneric to-list (backend)
|
||||
(:documentation "Return all records in BACKEND as a list of plists."))
|
||||
#+END_SRC
|
||||
|
||||
***** memory-layer-empty-p :generic:function:
|
||||
**** memory-layer-empty-p :generic:function:
|
||||
#+BEGIN_SRC lisp
|
||||
(defgeneric memory-layer-empty-p (backend)
|
||||
(:documentation "Return non-nil if memory layer of BACKEND contains no records, else nil."))
|
||||
|
@ -2419,13 +2468,13 @@ s-expressions in a text column.")
|
|||
|
||||
**** table-function :function:
|
||||
#+BEGIN_SRC lisp
|
||||
#+(or)
|
||||
(defun table-function (table-specification)
|
||||
(loop for col-spec in *table-specification*
|
||||
for (sym str) in *table-specification*
|
||||
(loop ;; for col-spec in *table-specification*
|
||||
;; for (sym str) in *table-specification*
|
||||
with date = ()
|
||||
for task in (chronometrist:task-list)
|
||||
for index from 1
|
||||
collect (cond ((eq sym 'index) index)
|
||||
(()))))
|
||||
collect (list index task)))
|
||||
#+END_SRC
|
||||
|
||||
**** display
|
||||
|
|
|
@ -55,16 +55,6 @@ Chronometrist records /time intervals/ (earlier called "events") as plists. Each
|
|||
|
||||
See also [[#explanation-time-formats][Currently-Used Time Formats]]
|
||||
|
||||
** Overview
|
||||
At its most basic, we read data from a [[#program-backend][backend]], store it in a [[#program-data-structures][hash table]], and [[#program-frontend-chronometrist][display it]] as a [[elisp:(find-library "tabulated-list-mode")][=tabulated-list-mode=]] buffer. When the file is changed—whether by the program or the user—we [[refresh-file][update the hash table]] and the [[#program-frontend-chronometrist-refresh][buffer]].
|
||||
|
||||
In addition, we implement a [[#program-pretty-printer][plist pretty-printer]] and some [[#program-migration][migration commands]].
|
||||
|
||||
Extensions exist for -
|
||||
1. [[file:chronometrist-key-values.org][attaching arbitrary metadata]] to time intervals, and
|
||||
2. support for the [[file:chronometrist-third.org][Third Time system]]
|
||||
3. [[https://tildegit.org/contrapunctus/chronometrist-goal][time goals and alerts]]
|
||||
|
||||
** Optimization
|
||||
It is of great importance that Chronometrist be responsive -
|
||||
+ A responsive program is more likely to be used; recall our design goal of 'incentivizing use'.
|
||||
|
|
89
manual.org
89
manual.org
|
@ -24,11 +24,10 @@ Chronometrist is a friendly and powerful personal time tracker and analyzer. It
|
|||
:CUSTOM_ID: benefits
|
||||
:END:
|
||||
1. Extremely simple and efficient to use
|
||||
2. Displays useful information about your time usage
|
||||
2. Displays useful information about your time usage (including fancy graphs with the =chronometrist-spark= extension)
|
||||
3. Support for both mouse and keyboard
|
||||
4. Human errors in tracking are easily fixed by editing a plain text file
|
||||
5. Hooks to let you perform arbitrary actions when starting/stopping tasks
|
||||
6. Fancy graphs with the =chronometrist-spark= extension
|
||||
4. Human errors in tracking can be easily fixed by editing a plain text file
|
||||
5. Hooks to integrate time tracking into your workflow
|
||||
|
||||
** Limitations
|
||||
:PROPERTIES:
|
||||
|
@ -75,28 +74,56 @@ In March 2022, work began on the long-awaited Common Lisp port of Chronometrist,
|
|||
|
||||
The port was also driven by the desire to have access to Common Lisp's better performance, and features such as namespaces, a /de facto/ standard build system, multithreading, SQLite bindings, a more fully-featured implementation of CLOS and MOP, and type annotations, checking, and inference.
|
||||
|
||||
Currently, this port can -
|
||||
The literate sources for the Common Lisp port may be found in [[file:cl/chronometrist.org][cl/chronometrist.org]]. Currently, this port can -
|
||||
1. import from a plist-group file and export to an SQLite database
|
||||
#+BEGIN_SRC lisp
|
||||
(chronometrist:to-file (chronometrist:to-hash-table
|
||||
(make-instance 'chronometrist.plist-group:plist-group-backend
|
||||
:file "/path/to/file.plg"))
|
||||
(make-instance 'chronometrist.sqlite:sqlite-backend)
|
||||
"/path/to/file.sqlite")
|
||||
(chronometrist:to-file (chronometrist:to-hash-table
|
||||
(make-instance 'chronometrist.plist-group:plist-group-backend
|
||||
:file "/path/to/file.plg"))
|
||||
(make-instance 'chronometrist.sqlite:sqlite-backend)
|
||||
"/path/to/file.sqlite")
|
||||
#+END_SRC
|
||||
2. display a (WIP) CLIM GUI - =(chronometrist.clim:run-chronometrist)=
|
||||
|
||||
The Emacs Lisp codebase will probably become an Emacs frontend to a future Common Lisp CLI client.
|
||||
|
||||
[fn:1] McCLIM also has an incomplete ncurses backend - when completed, a CLIM frontend could provide a TUI "for free".
|
||||
|
||||
** Literate Program
|
||||
** Literate program
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: explanation-literate-program
|
||||
:END:
|
||||
Chronometrist is a literate program, made using Org - the canonical source is the =chronometrist.org= file, which contains source blocks. These are provided to users after /tangling/ (extracting the source into an Emacs Lisp file).
|
||||
Chronometrist is written as an Org literate program, which makes it easy to obtain different views of the program source, thanks to tree- and source-block folding, tags, properties, and the =org-match= command.
|
||||
|
||||
The Org file can also be loaded directly using the [[https://github.com/jingtaozf/literate-elisp][literate-elisp]] package, so that all source links (e.g. =xref=, =describe-function=) lead to the Org file, within the context of the concerned documentation. See [[#how-to-literate-elisp][How to load the program using literate-elisp]].
|
||||
The canonical source file is [[file:elisp/chronometrist.org][elisp/chronometrist.org]], which contains source blocks. These are provided to users after /tangling/ (extracting the source into an Emacs Lisp file). [fn:2]
|
||||
|
||||
=chronometrist.org= is also included in MELPA installs, although not used directly by default, since doing so would interfere with automatic generation of autoloads.
|
||||
The Org literate program can also be loaded directly using the [[https://github.com/jingtaozf/literate-elisp][literate-elisp]] package, so that all source links (e.g. =xref=, =describe-function=) lead to the Org file. See [[#how-to-literate-elisp][How to load the program using literate-elisp]].
|
||||
|
||||
[fn:2] the literate source is also included in MELPA installs, although not loaded through =literate-elisp-load= by default, since doing so would interfere with automatic generation of autoloads.
|
||||
|
||||
** Source code overview
|
||||
At its most basic, we read data from a [[file:elisp/chronometrist.org::#program-backend][backend]] and [[file:elisp/chronometrist.org::#program-frontend-chronometrist][display it]] as a [[elisp:(find-library "tabulated-list")][=tabulated-list-mode=]] buffer.
|
||||
|
||||
The plist and plist-group backends (collectively known as the s-expression backends) =read= a text file containing s-expressions into a [[file:elisp/chronometrist.org::#program-data-structures][hash table]], and query that. When the file is changed—whether by the program or the user—they [[file:elisp/chronometrist.org::refresh-file][update the hash table]] and the [[file:elisp/chronometrist.org::#program-frontend-chronometrist-refresh][buffer]]. The s-expression backends also make use of a [[file:elisp/chronometrist.org::#program-pretty-printer][plist pretty-printer]] of their own.
|
||||
|
||||
There are also some [[file:elisp/chronometrist.org::#program-migration][migration commands]].
|
||||
|
||||
Extensions exist for -
|
||||
1. [[file:elisp/chronometrist-key-values.org][attaching arbitrary metadata]] to time intervals,
|
||||
2. [[https://tildegit.org/contrapunctus/chronometrist-goal][time goals and alerts]], and
|
||||
3. support for the [[file:elisp/chronometrist-third.org][Third Time system]]
|
||||
|
||||
** Contributions and contact
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: contributions-contact
|
||||
:END:
|
||||
Feedback and MRs are very welcome. 🙂
|
||||
+ [[file:TODO.org]] has a long list of tasks
|
||||
+ [[file:elisp/chronometrist.org]] contains all developer-oriented documentation
|
||||
|
||||
If you have tried using Chronometrist, I'd love to hear your experiences! Get in touch with the author and other Emacs users in the Emacs channel on the Jabber network - [[https://conversations.im/j/emacs@salas.suchat.org][xmpp:emacs@salas.suchat.org?join]] ([[https://inverse.chat/#converse/room?jid=emacs@salas.suchat.org][web chat]])
|
||||
|
||||
(For help in getting started with Jabber, [[https://xmpp.org/getting-started/][click here]])
|
||||
|
||||
** License
|
||||
:PROPERTIES:
|
||||
|
@ -220,6 +247,9 @@ Evaluate or add to your init.el the following -
|
|||
:PROPERTIES:
|
||||
:CUSTOM_ID: how-to-literate-elisp
|
||||
:END:
|
||||
The literate Org document will automatically =literate-elisp-load= itself when opened, if =literate-elisp= is installed via =package.el=.
|
||||
|
||||
If you want it to be loaded with =literate-elisp-load= on Emacs startup, add the following to your init.el -
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(add-to-list 'load-path "<directory containing chronometrist.org>")
|
||||
|
||||
|
@ -231,7 +261,7 @@ Evaluate or add to your init.el the following -
|
|||
:PROPERTIES:
|
||||
:CUSTOM_ID: how-to-tags
|
||||
:END:
|
||||
1. Add =chronometrist-tags-add= to one or more of these hooks [fn:2] -
|
||||
1. Add =chronometrist-tags-add= to one or more of these hooks [fn:3] -
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(add-to-list 'chronometrist-after-in-functions 'chronometrist-tags-add)
|
||||
|
@ -242,18 +272,18 @@ Evaluate or add to your init.el the following -
|
|||
|
||||
The prompt suggests past combinations you used for the current task, which you can browse with =M-p=/=M-n=. You can leave it blank by pressing =RET=.
|
||||
|
||||
[fn:2] but not =chronometrist-before-in-functions=
|
||||
[fn:3] but not =chronometrist-before-in-functions=
|
||||
|
||||
** How to attach key-values to time intervals
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: how-to-key-value-pairs
|
||||
:END:
|
||||
1. Add =chronometrist-kv-add= to one or more of these hooks [fn:2] -
|
||||
1. Add =chronometrist-kv-add= to one or more of these hooks [fn:3] -
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(add-to-list 'chronometrist-after-in-functions 'chronometrist-kv-add)
|
||||
(add-to-list 'chronometrist-before-out-functions 'chronometrist-kv-add)
|
||||
(add-to-list 'chronometrist-after-out-functions 'chronometrist-kv-add)
|
||||
(add-to-list 'chronometrist-after-in-functions 'chronometrist-kv-add)
|
||||
(add-to-list 'chronometrist-before-out-functions 'chronometrist-kv-add)
|
||||
(add-to-list 'chronometrist-after-out-functions 'chronometrist-kv-add)
|
||||
#+END_SRC
|
||||
|
||||
To exit the prompt, press the key it indicates for quitting - you can then edit the resulting key-values by hand if required. Press =C-c C-c= to accept the key-values, or =C-c C-k= to cancel.
|
||||
|
@ -323,7 +353,7 @@ Return nil (and run `magit-status') if the user answers no."
|
|||
:PROPERTIES:
|
||||
:CUSTOM_ID: how-to-backup
|
||||
:END:
|
||||
I suggest backing up Chronometrist data on each save using the [[https://tildegit.org/contrapunctus/async-backup][async-backup]] package.[fn:3] Here's how you can do that.
|
||||
I suggest backing up Chronometrist data on each save using the [[https://tildegit.org/contrapunctus/async-backup][async-backup]] package.[fn:4] Here's how you can do that.
|
||||
|
||||
1. Add the following to your init.
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
|
@ -332,9 +362,10 @@ I suggest backing up Chronometrist data on each save using the [[https://tildegi
|
|||
2. Open your Chronometrist file and add =async-backup= to a buffer-local =after-save-hook=.
|
||||
: M-x chronometrist-open-log
|
||||
: M-x add-file-local-variable-prop-line RET eval RET (add-hook 'after-save-hook #'async-backup nil t) RET
|
||||
3. Optionally, configure =backup-directory-alist= to set a specific directory for the backups.
|
||||
3. Optionally, configure =async-backup-location= to set a specific directory for the backups -
|
||||
: (setq async-backup-location "/path/to/backup/dir/")
|
||||
|
||||
[fn:3] It is possible to use Emacs' built-in backup system to do it, but since it is synchronous, doing so will greatly slow down saving of the Chronometrist file.
|
||||
[fn:4] It is possible to use Emacs' built-in backup system to do it, but since it is synchronous, doing so will greatly slow down saving of the Chronometrist file.
|
||||
|
||||
** How to configure Vertico for use with Chronometrist
|
||||
:PROPERTIES:
|
||||
|
@ -385,18 +416,6 @@ Hooks
|
|||
8. =chronometrist-file-change-hook=
|
||||
9. =chronometrist-timer-hook=
|
||||
|
||||
* Contributions and contact
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: contributions-contact
|
||||
:END:
|
||||
Feedback and MRs are very welcome. 🙂
|
||||
+ [[file:TODO.org]] has a long list of tasks
|
||||
+ [[file:elisp/chronometrist.org]] contains all developer-oriented documentation
|
||||
|
||||
If you have tried using Chronometrist, I'd love to hear your experiences! Get in touch with the author and other Emacs users in the Emacs channel on the Jabber network - [[https://conversations.im/j/emacs@salas.suchat.org][xmpp:emacs@salas.suchat.org?join]] ([[https://inverse.chat/#converse/room?jid=emacs@salas.suchat.org][web chat]])
|
||||
|
||||
(For help in getting started with Jabber, [[https://xmpp.org/getting-started/][click here]])
|
||||
|
||||
* Local variables :noexport:
|
||||
# Local Variables:
|
||||
# my-org-src-default-lang: "emacs-lisp"
|
||||
|
|
Reference in New Issue