This repository has been archived on 2022-05-13. You can view files and clone it, but cannot push or open issues or pull requests.
chronometrist/doc/manual.info

1166 lines
38 KiB
Plaintext

\input texinfo @c -*- texinfo -*-
@c %**start of header
@setfilename manual.info
@settitle The Chronometrist Manual
@documentencoding UTF-8
@documentlanguage en
@c %**end of header
@finalout
@titlepage
@title The Chronometrist Manual
@author contrapunctus
@end titlepage
@contents
@ifnottex
@node Top
@top The Chronometrist Manual
The structure of this manual was inspired by @uref{https://documentation.divio.com/}
@end ifnottex
@menu
* How to@dots{}:: Step-by-step guides to achieve specific tasks
* Explanation:: The design, the implementation, and a little history
* Reference:: A list of definitions, with some type information
@detailmenu
--- The Detailed Node Listing ---
How to@dots{}
* How to set up Emacs to contribute::
* How to create a new backend::
Explanation
* Design goals:: Some vague objectives which guided the project
* Terminology:: Explanation of some terms used later
* Project overview:: A broad overview of the code
* Chronometrist:: The primary command and its associated buffer.
* Midnight-spanning events:: Events starting on one day and ending on another
* Point restore behaviour:: The desired behaviour of point in Chronometrist
* chronometrist-report date range logic:: Deriving dates in the current week
* Tags and Key-Values:: How tags and key-values are implemented
Chronometrist
* Optimization::
* Backends::
Midnight-spanning events
* Check the code of the first event of the day (timeclock format):: When the code of the first event in the day is "o", it's a midnight-spanning event.
* Split them at the file level::
* Split them at the hash-table-level::
* Split them at the data-consumer level (e.g. when calculating time for one day/getting events for one day): Split them at the data-consumer level (eg when calculating time for one day/getting events for one day).
Tags and Key-Values
* User input::
* History::
Reference
* Legend of currently-used time formats::
* chronometrist-backend.el: chronometrist-backendel.
* chronometrist-common.el: chronometrist-commonel.
* chronometrist-diary-view.el: chronometrist-diary-viewel.
* chronometrist.el: chronometristel.
* chronometrist-events.el: chronometrist-eventsel.
* chronometrist-migrate.el: chronometrist-migrateel.
* chronometrist-plist-pp.el: chronometrist-plist-ppel.
* chronometrist-report.el: chronometrist-reportel.
* chronometrist-key-values.el: chronometrist-key-valuesel.
* chronometrist-statistics.el: chronometrist-statisticsel.
* chronometrist-time.el: chronometrist-timeel.
* chronometrist-timer.el: chronometrist-timerel.
* chronometrist-goal::
* chronometrist-sexp::
Legend of currently-used time formats
* ts::
* iso-timestamp::
* iso-date::
* seconds::
* minutes::
* list-duration::
@end detailmenu
@end menu
@node How to@dots{}
@chapter How to@dots{}
@menu
* How to set up Emacs to contribute::
* How to create a new backend::
@end menu
@node How to set up Emacs to contribute
@section How to set up Emacs to contribute
All of these are optional, but recommended for the best experience.
@enumerate
@item
Use @uref{https://github.com/Malabarba/Nameless, nameless-mode} for easier reading of Emacs Lisp code, and
@item
Use @uref{https://github.com/joostkremers/visual-fill-column, visual-fill-column-mode} to soft-wrap lines in Org/Markdown files.
@samp{org-indent-mode} (for Org files) and @uref{https://elpa.gnu.org/packages/adaptive-wrap.html, adaptive-prefix-mode} (for Markdown and other files) will further enhance the experience.
@item
Get the sources from @uref{https://github.com/contrapunctus-1/chronometrist} and read this manual in the Org format (doc/manual.org), so links to identifiers can take you to their location in the source.
@item
Install @uref{https://github.com/cask/cask, Cask} to easily byte-compile and test the project.
From the project root, you can now run
@enumerate
@item
@samp{cask} to install the project dependencies in a sandbox
@item
@samp{cask exec buttercup -L . --traceback pretty} to run tests.
@end enumerate
@end enumerate
@node How to create a new backend
@section How to create a new backend
@enumerate
@item
Subclass @samp{chronometrist-backend} to define your backend.
@item
Create methods for the eight generic functions in @samp{chronometrist-backend.el}
@end enumerate
@node Explanation
@chapter Explanation
@menu
* Design goals:: Some vague objectives which guided the project
* Terminology:: Explanation of some terms used later
* Project overview:: A broad overview of the code
* Chronometrist:: The primary command and its associated buffer.
* Midnight-spanning events:: Events starting on one day and ending on another
* Point restore behaviour:: The desired behaviour of point in Chronometrist
* chronometrist-report date range logic:: Deriving dates in the current week
* Tags and Key-Values:: How tags and key-values are implemented
@end menu
@node Design goals
@section Design goals
@enumerate
@item
Don't make assumptions about the user's profession
@itemize
@item
e.g. timeclock seems to assume you're using it for a 9-to-5/contractor job
@end itemize
@item
Incentivize use
@itemize
@item
Hooks allow the time tracker to automate tasks and become a useful part of your workflow
@end itemize
@item
Make it easy to edit data using existing, familiar tools
@itemize
@item
We don't use an SQL database, where changing a single field is tricky @footnote{I still have doubts about this. Having SQL as a query language would be very useful in perusing the stored data. Maybe we should have tried to create a companion mode to edit SQL databases interactively?}
@item
We use a text file containing s-expressions (easy for humans to read and write)
@item
We use ISO-8601 for timestamps (easy for humans to read and write) rather than UNIX epoch time
@end itemize
@item
Reduce human errors in tracking
@item
Have a useful, informative, interactive interface
@item
Support mouse and keyboard use equally
@end enumerate
@node Terminology
@section Terminology
For lack of a better term, events are how we refer to time intervals. They are stored as plists; each contains at least a @samp{:name "<name>"}, a @samp{:start "<iso-timestamp>"}, and (except in case of an ongoing task) a @samp{:stop "<iso-timestamp>"}.
@node Project overview
@section Project overview
Chronometrist has three components, and each has a file containing major mode definitions and user-facing commands.
@enumerate
@item
@uref{../elisp/chronometrist.el, chronometrist.el}
@item
@uref{../elisp/chronometrist-report.el, chronometrist-report.el}
@item
@uref{../elisp/chronometrist-statistics.el, chronometrist-statistics.el}
@end enumerate
All three of these use @samp{(info "(elisp)Tabulated List Mode")}. Each of them also contains a "-print-non-tabular" function, which prints the non-tabular parts of the buffer.
Each of them has a corresponding @samp{-custom} file, which contain the Customize group and custom variable definitions for user-facing variables -
@itemize
@item
@uref{../elisp/chronometrist-custom.el, chronometrist-custom.el}
@item
@uref{../elisp/chronometrist-report-custom.el, chronometrist-report-custom.el}
@item
@uref{../elisp/chronometrist-statistics-custom.el, chronometrist-statistics-custom.el}
@end itemize
@uref{../elisp/chronometrist-common.el, chronometrist-common.el} contains definitions common to all components.
All three components use timers to keep their buffers updated. @uref{../elisp/chronometrist-timer.el, chronometrist-timer.el} contains all timer-related code.
Note - sometimes, when hacking or dealing with errors, timers may result in subtle bugs which are very hard to debug. Using @samp{chronometrist-force-restart-timer} or restarting Emacs can fix them, so try that as a first sanity check.
@node Chronometrist
@section Chronometrist
@menu
* Optimization::
* Backends::
@end menu
@node Optimization
@subsection Optimization
It is of great importance that Chronometrist be responsive -
@itemize
@item
A responsive program is more likely to be used; recall our design goal of 'incentivizing use'.
@item
Being an Emacs program, freezing the UI for any human-noticeable length of time is unacceptable - it prevents the user from working on anything in their environment.
@end itemize
Thus, I have considered various optimization strategies, and so far implemented two.
@enumerate
@item
Prevent excess creation of file watchers
One of the earliest 'optimizations' of great importance turned out to simply be a bug - turns out, if you run an identical call to @uref{(describe-function 'file-notify-add-watch), @samp{file-notify-add-watch}} twice, you create @emph{two} file watchers and your callback will be called @emph{twice.} We were creating a file watcher @emph{each time the chronometrist command was run.} 🤦 This was causing humongous slowdowns each time the file changed. 😅
@itemize
@item
It was fixed in v0.2.2 by making the watch creation conditional, using @uref{../elisp/chronometrist-common.el, @samp{chronometrist--fs-watch}} to store the watch object.
@end itemize
@item
Preserve state
The next one was released in v0.5. Till then, any time the @uref{../elisp/chronometrist-custom.el, @samp{chronometrist-file}} was modified, we'd clear the @uref{../elisp/chronometrist-events.el, @samp{chronometrist-events}} hash table and read data into it again. The reading itself is nearly-instant, even with ~2 years' worth of data @footnote{As indicated by exploratory work in the @samp{parsimonious-reading} branch, where I made a loop to only @samp{read} and collect s-expressions from the file. It was near-instant@dots{}until I added event splitting to it.} (it uses Emacs' @uref{(describe-function 'read), @samp{read}}, after all), but the splitting of @ref{Midnight-spanning events, , midnight-spanning events} is the real performance killer.
After the optimization@dots{}
@enumerate
@item
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
Instead, the aforementioned backend functions modify the relevant variables - @samp{chronometrist-events}, @samp{chronometrist-task-list}, and @samp{chronometrist-tags-history} - via@dots{}
@itemize
@item
@samp{chronometrist-events-add} / @samp{chronometrist-events-replace-last}
@item
@samp{chronometrist-task-list-add}, and
@item
@samp{chronometrist-tags-history-add} / @samp{chronometrist-tags-history-replace-last}, respectively.
@end itemize
@end enumerate
There are still some operations which @uref{../elisp/chronometrist.el, @samp{chronometrist-refresh-file}} runs unconditionally - which is to say there is scope for further optimization, if or when required.
@end enumerate
@node Backends
@subsection Backends
Chronometrist can currently store data in two ways -
@enumerate
@item
as plists in a plain text file, using the s-expression backend.
@item
as an SQL database, using the sqlite3 backend.
@end enumerate
Each of these has areas they excel in - plain text files are VCS-friendly, permit the use of tools like @samp{grep}, and are easy to make small, one-off edits in. SQL databases support a standard and popular query language, ACID guarantees, and are easy to make sweeping changes in.
To try and get the best of both, two approaches were considered -
@enumerate
@item
Support for multiple simultaneous backends
@itemize
@item
@samp{chronometrist-backend-list} could be a custom variable, containing a list of enabled backends
@item
A function like @samp{(call-with-backends generic-function &rest args)} could be created, which calls the generic-function with each backend as the first argument, and use this whenever Chronometrist makes a change to the files.
@item
When the user makes a change to one of the files outside of Chronometrist, we detect the change (via filesystem watchers) and propagate it to the other backends.
@end itemize
Consequently, a user can make quick, one-off edits to the SQL database by editing a text file, or make sweeping, structure-aware changes to the text file using SQL statements.
@item
SQL as the main backend, with viewing/editing UI + auto-export
@enumerate
@item
Write a 'record viewer/editor' for Chronometrist records. Basically, it reads in a record from the SQL database and displays it in an editable buffer. Forward/backward commands would be available, like in @samp{chronometrist-report} and @samp{chronometrist-statistics}. The record could be represented as anything - CSV, Org markup, Lisp data, etc. The user could edit and run a command to accept the changes, which would change the database.
@item
Automatically export from SQL to a plain text (or other) format, whenever there is a change in the database. (probably using hooks)
@end enumerate
These two put together result in the viewing and editing of individual records being easier, and the plain text format can be placed in a VCS, without dealing with any synchronization issues.
@end enumerate
Backends are defined as subclasses of the class @samp{chronometrist-backend} @footnote{I would have liked to define them as instances of @samp{chronometrist-backend}, but EIEIO, unlike CLOS, does not support EQL specialization.}.
@node Midnight-spanning events
@section Midnight-spanning events
A unique problem in working with Chronometrist, one I had never foreseen, was tasks which start on one day and end on another. These mess up data consumption (especially interval calculations and acquiring data for a specific date) in all sorts of unforeseen ways.
There are a few different approaches of dealing with them. (Currently, Chronometrist uses #3.)
@menu
* Check the code of the first event of the day (timeclock format):: When the code of the first event in the day is "o", it's a midnight-spanning event.
* Split them at the file level::
* Split them at the hash-table-level::
* Split them at the data-consumer level (e.g. when calculating time for one day/getting events for one day): Split them at the data-consumer level (eg when calculating time for one day/getting events for one day).
@end menu
@node Check the code of the first event of the day (timeclock format)
@subsection Check the code of the first event of the day (timeclock format)
@itemize
@item
Advantage - very simple to detect
@item
Disadvantage - "in" and "out" events must be represented separately
@end itemize
@node Split them at the file level
@subsection Split them at the file level
@itemize
@item
Advantage - operation is performed only once for each such event + simpler data-consuming code + reduced post-parsing load.
@item
What happens when the user changes their day-start-time? The split-up events are now split wrongly, and the second event may get split @emph{again.}
Possible solutions -
@enumerate
@item
Add function to check if, for two events A and B, the :stop of A is the same as the :start of B, and that all their other tags are identical. Then we can re-split them according to the new day-start-time.
@item
Add a :split tag to split events. It can denote that the next event was originally a part of this one.
@item
Re-check and update the file when the day-start-time changes.
@itemize
@item
Possible with @code{add-variable-watcher} or @code{:custom-set} in Customize (thanks bpalmer)
@end itemize
@end enumerate
@end itemize
@node Split them at the hash-table-level
@subsection Split them at the hash-table-level
Handled by @code{chronometrist-sexp-events-populate}
@itemize
@item
Advantage - simpler data-consuming code.
@end itemize
@node Split them at the data-consumer level (eg when calculating time for one day/getting events for one day)
@subsection Split them at the data-consumer level (e.g. when calculating time for one day/getting events for one day)
@itemize
@item
Advantage - reduced repetitive post-parsing load.
@end itemize
@node Point restore behaviour
@section Point restore behaviour
After hacking, always test for and ensure the following -
@enumerate
@item
Toggling the buffer via @samp{chronometrist=/=chronometrist-report=/=chronometrist-statistics} should preserve point
@item
The timer function should preserve point when the buffer is current
@item
The timer function should preserve point when the buffer is not current, but is visible in another window
@item
The next/previous week keys and buttons should preserve point.
@end enumerate
@node chronometrist-report date range logic
@section chronometrist-report date range logic
A quick description, starting from the first time @uref{../elisp/chronometrist-report.el, @samp{chronometrist-report}} is run in an Emacs session -
@enumerate
@item
We get the current date as a ts struct @samp{(chronometrist-date)}.
@item
The variable @samp{chronometrist-report-week-start-day} stores the day we consider the week to start with. The default is "Sunday".
We check if the date from #2 is on the week start day, else decrement it till we are, using @samp{(chronometrist-report-previous-week-start)}.
@item
We store the date from #3 in the global variable @samp{chronometrist-report--ui-date}.
@item
By counting up from @samp{chronometrist-report--ui-date}, we get dates for the days in the next 7 days using @samp{(chronometrist-report-date->dates-in-week)}. We store them in @samp{chronometrist-report--ui-week-dates}.
The dates in @samp{chronometrist-report--ui-week-dates} are what is finally used to query the data displayed in the buffer.
@item
To get data for the previous/next weeks, we decrement/increment the date in @samp{chronometrist-report--ui-date} by 7 days and repeat the above process (via @samp{(chronometrist-report-previous-week)=/=(chronometrist-report-next-week)}).
@end enumerate
@node Tags and Key-Values
@section Tags and Key-Values
@uref{../elisp/chronometrist-key-values.el, chronometrist-key-values.el} deals with adding additional information to events, in the form of key-values and tags.
Key-values are stored as plist keywords and values. The user can add any keywords except @samp{:name}, @samp{:tags}, @samp{:start}, and @samp{:stop}. @footnote{To remove this restriction, I had briefly considered making a keyword called @samp{:user}, whose value would be another plist containing all user-defined keyword-values. But in practice, this hasn't been a big enough issue yet to justify the work.} Values can be any readable Lisp values.
Similarly, tags are stored using a @samp{:tags (<tag>*)} keyword-value pair. The tags themselves (the elements of the list) can be any readable Lisp value.
@menu
* User input::
* History::
@end menu
@node User input
@subsection User input
The entry points are @uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-kv-add}} and @uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-tags-add}}. The user adds these to the desired hooks, and they prompt the user for tags/key-values.
Both have corresponding functions to create a prompt -
@itemize
@item
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-key-prompt}},
@item
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-value-prompt}}, and
@item
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-tags-prompt}}.
@end itemize
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-kv-add}}'s way of reading key-values from the user is somewhat different from most Emacs prompts - it creates a new buffer, and uses the minibuffer to alternatingly ask for keys and values in a loop. Key-values are inserted into the buffer as the user enters/selects them. The user can break out of this loop with an empty input (the keys to accept an empty input differ between completion systems, so we try to let the user know about them using @uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-kv-completion-quit-key}}). After exiting the loop, they can edit the key-values in the buffer, and use the commands @uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-kv-accept}} to accept the key-values (which uses @uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-append-to-last}} to add them to the last plist in @samp{chronometrist-file}) or @uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-kv-reject}} to discard them.
@node History
@subsection History
All prompts suggest past user inputs. These are queried from three history hash tables -
@itemize
@item
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-key-history}},
@item
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-value-history}}, and
@item
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-tags-history}}.
@end itemize
Each of these has a corresponding function to clear it and fill it with values -
@itemize
@item
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-key-history-populate}}
@item
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-value-history-populate}}, and
@item
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-tags-history-populate}}.
@end itemize
@node Reference
@chapter Reference
@menu
* Legend of currently-used time formats::
* chronometrist-backend.el: chronometrist-backendel.
* chronometrist-common.el: chronometrist-commonel.
* chronometrist-diary-view.el: chronometrist-diary-viewel.
* chronometrist.el: chronometristel.
* chronometrist-events.el: chronometrist-eventsel.
* chronometrist-migrate.el: chronometrist-migrateel.
* chronometrist-plist-pp.el: chronometrist-plist-ppel.
* chronometrist-report.el: chronometrist-reportel.
* chronometrist-key-values.el: chronometrist-key-valuesel.
* chronometrist-statistics.el: chronometrist-statisticsel.
* chronometrist-time.el: chronometrist-timeel.
* chronometrist-timer.el: chronometrist-timerel.
* chronometrist-goal::
* chronometrist-sexp::
@end menu
@node Legend of currently-used time formats
@section Legend of currently-used time formats
@menu
* ts::
* iso-timestamp::
* iso-date::
* seconds::
* minutes::
* list-duration::
@end menu
@node ts
@subsection ts
ts.el struct
@itemize
@item
Used by nearly all internal functions
@end itemize
@node iso-timestamp
@subsection iso-timestamp
"YYYY-MM-DDTHH:MM:SSZ"
@itemize
@item
Used in the s-expression file format
@item
Read by chronometrist-sexp-events-populate
@item
Used in the plists in the chronometrist-events hash table values
@end itemize
@node iso-date
@subsection iso-date
"YYYY-MM-DD"
@itemize
@item
Used as hash table keys in chronometrist-events - can't use ts structs for keys, you'd have to make a hash table predicate which uses ts=
@end itemize
@node seconds
@subsection seconds
integer seconds as duration
@itemize
@item
Used for most durations
@item
May be changed to floating point to allow larger durations. The minimum range of `most-positive-fixnum` is 536870911, which seems to be enough to represent durations of 17 years.
@item
Used for update intervals (chronometrist-update-interval, chronometrist-change-update-interval)
@end itemize
@node minutes
@subsection minutes
integer minutes as duration
@itemize
@item
Used for goals (chronometrist-goals-list, chronometrist-get-goal) - minutes seems like the ideal unit for users to enter
@end itemize
@node list-duration
@subsection list-duration
(hours minute seconds)
@itemize
@item
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-new-record (backend plist)
@item
Generic Function - chronometrist-backend-replace-last (backend plist)
@end enumerate
@node chronometrist-commonel
@section chronometrist-common.el
@enumerate
@item
Variable - chronometrist-empty-time-string
@item
Variable - chronometrist-date-re
@item
Variable - chronometrist-time-re-ui
@item
Variable - chronometrist-task-list
@item
Function - chronometrist-task-list-add (task)
@item
Internal Variable - chronometrist--fs-watch
@item
Function - chronometrist-current-task ()
@item
Function - chronometrist-format-time (seconds &optional (blank " "))
@itemize
@item
seconds -> "h:m:s"
@end itemize
@item
Function - chronometrist-common-file-empty-p (file)
@item
Function - chronometrist-common-clear-buffer (buffer)
@item
Function - chronometrist-format-keybinds (command map &optional firstonly)
@item
Function - chronometrist-events->ts-pairs (events)
@itemize
@item
(plist @dots{}) -> ((ts . ts) @dots{})
@end itemize
@item
Function - chronometrist-ts-pairs->durations (ts-pairs)
@itemize
@item
((ts . ts) @dots{}) -> seconds
@end itemize
@item
Function - chronometrist-previous-week-start (ts)
@itemize
@item
ts -> ts
@end itemize
@end enumerate
@node chronometrist-diary-viewel
@section chronometrist-diary-view.el
@enumerate
@item
Variable - chronometrist-diary-buffer-name
@item
Internal Variable - chronometrist-diary--current-date
@item
Function - chronometrist-intervals-on (date)
@item
Function - chronometrist-diary-tasks-reasons-on (date)
@item
Function - chronometrist-diary-refresh (&optional ignore-auto noconfirm date)
@item
Major Mode - chronometrist-diary-view-mode
@item
Command - chronometrist-diary-view (&optional date)
@end enumerate
@node chronometristel
@section chronometrist.el
@enumerate
@item
Custom variable - chronometrist-file
@item
Custom variable - chronometrist-buffer-name
@item
Custom variable - chronometrist-hide-cursor
@item
Custom variable - chronometrist-update-interval
@item
Custom variable - chronometrist-activity-indicator
@item
Custom variable - chronometrist-day-start-time
@item
Internal Variable - chronometrist--task-history
@item
Internal Variable - chronometrist--point
@item
Internal Variable - chronometrist--inhibit-read-p
@item
Keymap - chronometrist-mode-map
@item
Command - chronometrist-open-log (&optional button)
@item
Function - chronometrist-common-create-file ()
@item
Function - chronometrist-task-active? (task)
@itemize
@item
String -> Boolean
@end itemize
@item
Function - chronometrist-use-goals? ()
@item
Function - chronometrist-activity-indicator ()
@item
Function - chronometrist-entries ()
@item
Function - chronometrist-task-at-point ()
@item
Function - chronometrist-goto-last-task ()
@item
Function - chronometrist-print-keybind (command &optional description firstonly)
@item
Function - chronometrist-print-non-tabular ()
@item
Function - chronometrist-goto-nth-task (n)
@item
Function - chronometrist-refresh (&optional ignore-auto noconfirm)
@item
Function - chronometrist-refresh-file (fs-event)
@item
Command - chronometrist-query-stop ()
@item
Command - chronometrist-in (task &optional _prefix)
@item
Command - chronometrist-out (&optional _prefix)
@item
Variable - chronometrist-before-in-functions
@item
Variable - chronometrist-after-in-functions
@item
Variable - chronometrist-before-out-functions
@item
Variable - chronometrist-after-out-functions
@item
Function - chronometrist-run-functions-and-clock-in (task)
@item
Function - chronometrist-run-functions-and-clock-out (task)
@item
Keymap - chronometrist-mode-map
@item
Major Mode - chronometrist-mode
@item
Function - chronometrist-toggle-task-button (button)
@item
Function - chronometrist-add-new-task-button (button)
@item
Command - chronometrist-toggle-task (&optional prefix inhibit-hooks)
@item
Command - chronometrist-toggle-task-no-hooks (&optional prefix)
@item
Command - chronometrist-add-new-task ()
@item
Command - chronometrist (&optional arg)
@end enumerate
@node chronometrist-eventsel
@section chronometrist-events.el
@enumerate
@item
Variable - chronometrist-events
@itemize
@item
keys - iso-date
@end itemize
@item
Function - chronometrist-day-start (timestamp)
@itemize
@item
iso-timestamp -> encode-time
@end itemize
@item
Function - chronometrist-file-clean ()
@itemize
@item
commented out, unused
@end itemize
@item
Function - chronometrist-events-maybe-split (event)
@item
Function - chronometrist-events-populate ()
@item
Function - chronometrist-tasks-from-table ()
@item
Function - chronometrist-events-add (plist)
@item
Function - chronometrist-events-replace-last (plist)
@item
Function - chronometrist-events-subset (start end)
@itemize
@item
ts ts -> hash-table
@end itemize
@end enumerate
@node chronometrist-migrateel
@section chronometrist-migrate.el
@enumerate
@item
Variable - chronometrist-migrate-table
@item
Function - chronometrist-migrate-populate (in-file)
@item
Function - chronometrist-migrate-timelog-file->sexp-file (&optional in-file out-file)
@item
Function - chronometrist-migrate-check ()
@end enumerate
@node chronometrist-plist-ppel
@section chronometrist-plist-pp.el
@enumerate
@item
Variable - chronometrist-plist-pp-keyword-re
@item
Variable - chronometrist-plist-pp-whitespace-re
@item
Function - chronometrist-plist-pp-longest-keyword-length ()
@item
Function - chronometrist-plist-pp-buffer-keyword-helper ()
@item
Function - chronometrist-plist-pp-buffer ()
@item
Function - chronometrist-plist-pp-to-string (object)
@item
Function - chronometrist-plist-pp (object &optional stream)
@end enumerate
@node chronometrist-reportel
@section chronometrist-report.el
@enumerate
@item
Custom variable - chronometrist-report-buffer-name
@item
Custom variable - chronometrist-report-week-start-day
@item
Custom variable - chronometrist-report-weekday-number-alist
@item
Internal Variable - chronometrist-report--ui-date
@item
Internal Variable - chronometrist-report--ui-week-dates
@item
Internal Variable - chronometrist-report--point
@item
Function - chronometrist-report-date ()
@item
Function - chronometrist-report-date->dates-in-week (first-date-in-week)
@itemize
@item
ts-1 -> (ts-1 @dots{} ts-7)
@end itemize
@item
Function - chronometrist-report-date->week-dates ()
@item
Function - chronometrist-report-entries ()
@item
Function - chronometrist-report-print-keybind (command &optional description firstonly)
@item
Function - chronometrist-report-print-non-tabular ()
@item
Function - chronometrist-report-refresh (&optional _ignore-auto _noconfirm)
@item
Function - chronometrist-report-refresh-file (@math{_fs}-event)
@item
Keymap - chronometrist-report-mode-map
@item
Major Mode - chronometrist-report-mode
@item
Function - chronometrist-report (&optional keep-date)
@item
Function - chronometrist-report-previous-week (arg)
@item
Function - chronometrist-report-next-week (arg)
@end enumerate
@node chronometrist-key-valuesel
@section chronometrist-key-values.el
@enumerate
@item
Internal Variable - chronometrist--tag-suggestions
@item
Internal Variable - chronometrist--value-suggestions
@item
Function - chronometrist-plist-remove (plist &rest keys)
@item
Function - chronometrist-maybe-string-to-symbol (list)
@item
Function - chronometrist-maybe-symbol-to-string (list)
@item
Function - chronometrist-append-to-last (tags plist)
@item
Variable - chronometrist-tags-history
@item
Function - chronometrist-tags-history-populate (events-table history-table)
@item
Function - chronometrist-tags-history-add (plist)
@item
Function - chronometrist-tags-history-replace-last (plist)
@item
Function - chronometrist-tags-history-combination-strings (task)
@item
Function - chronometrist-tags-history-individual-strings (task)
@item
Function - chronometrist-tags-prompt (task &optional initial-input)
@item
Function - chronometrist-tags-add (&rest args)
@item
Custom Variable - chronometrist-kv-buffer-name
@item
Variable - chronometrist-key-history
@item
Variable - chronometrist-value-history
@item
Function - chronometrist-ht-history-prep (table)
@item
Function - chronometrist-key-history-populate (events-table history-table)
@item
Function - chronometrist-value-history-populate (events-table history-table)
@item
Keymap - chronometrist-kv-read-mode-map
@item
Major Mode - chronometrist-kv-read-mode
@item
Function - chronometrist-kv-completion-quit-key ()
@item
Function - chronometrist-string-has-whitespace-p (string)
@item
Function - chronometrist-key-prompt (used-keys)
@item
Function - chronometrist-value-prompt (key)
@item
Function - chronometrist-value-insert (value)
@item
Function - chronometrist-kv-add (&rest args)
@item
Command - chronometrist-kv-accept ()
@item
Command - chronometrist-kv-reject ()
@item
Internal Variable - chronometrist--skip-detail-prompts
@item
Function - chronometrist-skip-query-prompt (task)
@item
Function - chronometrist-skip-query-reset (@math{_task})
@end enumerate
@node chronometrist-statisticsel
@section chronometrist-statistics.el
@enumerate
@item
Custom variable - chronometrist-statistics-buffer-name
@item
Internal Variable - chronometrist-statistics--ui-state
@item
Internal Variable - chronometrist-statistics--point
@item
Function - chronometrist-statistics-count-average-time-spent (task &optional (table chronometrist-events))
@itemize
@item
string &optional hash-table -> seconds
@end itemize
@item
Function - chronometrist-statistics-entries-internal (table)
@item
Function - chronometrist-statistics-entries ()
@item
Function - chronometrist-statistics-print-keybind (command &optional description firstonly)
@item
Function - chronometrist-statistics-print-non-tabular ()
@item
Function - chronometrist-statistics-refresh (&optional ignore-auto noconfirm)
@item
Keymap - chronometrist-statistics-mode-map
@item
Major Mode - chronometrist-statistics-mode
@item
Command - chronometrist-statistics (&optional preserve-state)
@item
Command - chronometrist-statistics-previous-range (arg)
@item
Command - chronometrist-statistics-next-range (arg)
@end enumerate
@node chronometrist-timeel
@section chronometrist-time.el
@enumerate
@item
Function - chronometrist-iso-timestamp->ts (timestamp)
@itemize
@item
iso-timestamp -> ts
@end itemize
@item
Function - chronometrist-iso-date->ts (date)
@itemize
@item
iso-date -> ts
@end itemize
@item
Function - chronometrist-date (&optional (ts (ts-now)))
@itemize
@item
&optional ts -> ts (with time 00:00:00)
@end itemize
@item
Function - chronometrist-format-time-iso8601 (&optional unix-time)
@item
Function - chronometrist-midnight-spanning-p (start-time stop-time)
@item
Function - chronometrist-seconds-to-hms (seconds)
@itemize
@item
seconds -> list-duration
@end itemize
@item
Function - chronometrist-interval (event)
@itemize
@item
event -> duration
@end itemize
@end enumerate
@node chronometrist-timerel
@section chronometrist-timer.el
@enumerate
@item
Internal Variable - chronometrist--timer-object
@item
Function - chronometrist-timer ()
@item
Command - chronometrist-stop-timer ()
@item
Command - chronometrist-maybe-start-timer (&optional interactive-test)
@item
Command - chronometrist-force-restart-timer ()
@item
Command - chronometrist-change-update-interval (arg)
@end enumerate
@node chronometrist-goal
@section chronometrist-goal
@enumerate
@item
Internal Variable - chronometrist-goal--timers-list
@item
Custom Variable - chronometrist-goal-list nil
@item
Function - chronometrist-goal-run-at-time (time repeat function &rest args)
@item
Function - chronometrist-goal-seconds->alert-string (seconds)
@itemize
@item
seconds -> string
@end itemize
@item
Function - chronometrist-goal-approach-alert (task goal spent)
@itemize
@item
string minutes minutes
@end itemize
@item
Function - chronometrist-goal-complete-alert (task goal spent)
@itemize
@item
string minutes minutes
@end itemize
@item
Function - chronometrist-goal-exceed-alert (task goal spent)
@itemize
@item
string minutes minutes
@end itemize
@item
Function - chronometrist-goal-no-goal-alert (task goal spent)
@itemize
@item
string minutes minutes
@end itemize
@item
Custom Variable - chronometrist-goal-alert-functions
@itemize
@item
each function is passed - string minutes minutes
@end itemize
@item
Function - chronometrist-goal-get (task &optional (goal-list chronometrist-goal-list))
@itemize
@item
String &optional List -> minutes
@end itemize
@item
Function - chronometrist-goal-run-alert-timers (task)
@item
Function - chronometrist-goal-stop-alert-timers (&optional _task)
@item
Function - chronometrist-goal-on-file-change ()
@end enumerate
@node chronometrist-sexp
@section chronometrist-sexp
@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
Method - chronometrist-to-hash ((backend chronometrist-sexp) table)
@item
Method - chronometrist-from-hash ((backend chronometrist-sexp) table)
@item
Method - chronometrist-open-log ((backend chronometrist-sexp))
@item
Method - chronometrist-last ((backend chronometrist-sexp))
@itemize
@item
-> plist
@end itemize
@item
Method - chronometrist-current-task ((backend chronometrist-sexp))
@item
Method - chronometrist-create-file ((backend chronometrist-sexp))
@item
Method - chronometrist-backend-new-record (((backend chronometrist-sexp)) plist)
@item
Function - chronometrist-sexp-delete-list (&optional arg)
@item
Method - chronometrist-replace-last (((backend chronometrist-sexp)) plist)
@item
Method - chronometrist-task-time-one-day (task &optional (ts (ts-now)))
@itemize
@item
String &optional ts -> seconds
@end itemize
@item
Method - chronometrist-active-time-one-day (&optional ts)
@itemize
@item
&optional ts -> seconds
@end itemize
@item
Method - chronometrist-statistics-count-active-days (task &optional (table chronometrist-events))
@itemize
@item
String &optional table -> integer
@end itemize
@item
Method - chronometrist-task-events-in-day (task ts)
@itemize
@item
String ts -> events
@end itemize
@item
Command - chronometrist-sexp-reindent-buffer ()
@end enumerate
@bye