This package provides functions for creating reverse
date trees, which are similar to date trees as supported by built-in
functions of Org mode (e.g. org-capture
) but in a
reversed order. Since newer contents come first in reverse date trees,
they are more useful in situations where you want to find latest
activities on a particular subject using a search tool like
helm-org-rifle.
- Reverse date trees, where latest contents are shown first.
- You can customize the format of the date tree.
- Week trees are also supported. You can even create date trees with four levels (year-month-week-date) or any number of levels.
- Configurations of date trees are stored in file headers, so each file is ensured to have a single date tree with a consistent structure.
- Configuration is done interactively on first creation.
You can install this package from MELPA.
The following functions retrieve a configuration from the file header:
- Use
org-reverse-datetree-goto-date-in-file
to jump to a date in the date tree. If this function is called non-interactively and the time argument is nil, it jumps to the current date. This can be used fororg-capture
. org-reverse-datetree-goto-read-date-in-file
is similar as above, but it always prompts for a date even if the function is called non-interactively.org-reverse-datetree-refile-to-file
is a function that refiles the current entry into a date tree. This can be used to build a custom command for refiling an entry to a particular file.
The format configuration is stored in the file header of each Org file, as shown in the following example:
#+REVERSE_DATETREE_DATE_FORMAT: %Y-%m-%d %A
#+REVERSE_DATETREE_WEEK_FORMAT: %Y W%W
#+REVERSE_DATETREE_YEAR_FORMAT: %Y
#+REVERSE_DATETREE_USE_WEEK_TREE: t
These attributes are added by functions in this package on initial creation of the date tree, so you usually don’t have to manually edit them.
You can customize the default format by setting
org-reverse-datetree-{year,month,week,date}-format
.
Note that the formats should be basically numeric and zero-prefixed, since
date-tree headings are ordered lexicographically by their texts.
You should avoid a month format starting with a string like “Feb” or “February”. If you want to contain one, you should append it to a zero-prefixed numeric month.
Another way to configure the structure is to set org-reverse-datetree-level-formats
variable as a file-local variable. Through the variable, you can define a structure with any number of levels.
For example, the following configuration enables date trees consisting of four levels (year-month-week-date) in all files (thanks @samspo for reporting):
(setq-default org-reverse-datetree-level-formats
'("%Y" ; year
(lambda (time) (format-time-string "%Y-%m %B" (org-reverse-datetree-monday time))) ; month
"%Y W%W" ; week
"%Y-%m-%d %A" ; date
))
Even though this package is named org-reverse-datetree
, it is now possible to create a non-reverse date tree, i.e. a normal ascending date tree.
To enable the feature, set org-reverse-datetree-non-reverse
variable to non-nil. It is a file-local variable. The default continues to be a reverse date tree.
You can define an org-capture
template which inserts an entry into a date tree with org-starter package as follows:
(org-starter-def-capture "p"
"Commonplace book plain entry"
entry
(file+function "cpb.org" org-reverse-datetree-goto-date-in-file)
"* %?"
:clock-in t :clock-resume t :empty-lines 1)
Or with doct:
(setq org-capture-templates
(doct '(("Commonplace book" :keys "c"
:file "~/org/cpb.org"
:function org-reverse-datetree-goto-date-in-file
:template ("* %?")))))
Warning: This is an experimental feature, so advanced features such as refiling, archiving, and cleaning up (which are described later) are not supported for it.
If you want a date tree under an outline path (like file+olp+datetree
target in org-capture
), call the function with :olp
option:
(org-reverse-datetree-goto-date-in-file nil :olp '("Group" "Subgroup 1"))
which you could use in a capture template like this:
(setq org-capture-templates
'(("c" "Commonplace book" entry
(file+function "cpb.org"
(lambda ()
(org-reverse-datetree-goto-date-in-file
nil :olp '("Group" "Subgroup 1"))))
"* %?"
:clock-in t :clock-resume t)))
When a new olp is created, it is ordered alphabetically (or lexicographically).
Use org-reverse-datetree-goto-date-in-file
command to jump to a particular date in the date tree of the current file.
If you run org-reverse-datetree-calendar
from an Org file, calendar
is shown with dates in the date tree highlighted.
If you run org-reverse-datetree-display-entry
in the calendar, a corresponding date entry in the date tree will be displayed in a window. If the date doesn’t exist in the date tree, a new entry will be created.
To navigate between highlighted dates in the calendar, use org-reverse-datetree-calendar-next
and org-reverse-datetree-calendar-previous
.
The following is an example configuration for calendar-mode-map
:
(define-key calendar-mode-map "]" #'org-reverse-datetree-calendar-next)
(define-key calendar-mode-map "[" #'org-reverse-datetree-calendar-previous)
(define-key calendar-mode-map (kbd "RET") #'org-reverse-datetree-display-entry)
If you want to remove the highlights, run org-reverse-datetree-unlink-calendar
command.
With org-reverse-datetree-guess-date
function, you can retrieve the date of the entry at point where a date tree is effective.
Note that this function may not work in certain situations, so it should be considered experimental.
With org-reverse-datetree-refile-to-file
, you can define a function which can be used to refile entries to the date tree in a particular file:
(defun akirak/org-refile-to-cpb (arg)
(interactive "P")
(org-reverse-datetree-refile-to-file "~/org/cpb.org" arg))
The date is determined according to org-reverse-datetree-entry-time
custom variable.
If a C-u
prefix argument is given, the user is asked to pick a date manually.
The second argument can be an Emacs time. The following example refiles the current entry to today:
(defun akirak/org-refile-to-cpb-today (arg)
(interactive "P")
(org-reverse-datetree-refile-to-file "~/org/cpb.org" (current-time)))
The second argument can also take the same format as org-reverse-datetree-entry-time
.
The following function refile the current entry according to CREATED_AT
property or the earliest clock:
(defun akirak/org-refile-to-cpb-2 (arg)
(interactive "P")
(org-reverse-datetree-refile-to-file "~/org/cpb.org"
'((property "CREATED_AT")
(clock earliest))))
You can use this function both in org-mode
(either on a single entry or on multiple entries under selection) and in org-agenda-mode
(either on a single entry or on bulk entries). It retrieves a date for each entry if it operates on multiple entries.
org-starter package integrates with this function well. For example, you can define the following function:
(defun akirak/org-refile-to-cpb (&optional arg)
(interactive "P")
(org-reverse-datetree-refile-to-file (org-starter-locate-file "cpb.org" nil t)
arg))
A recommended way to invoke this command is to add an entry to org-starter-extra-refile-map
in org-starter package:
(add-to-list 'org-starter-extra-refile-map
'("p" akirak/org-refile-to-cpb "cpb"))
Then you can run org-starter-refile-by-key
and press p
key to refile the selected entries to cpb.org
.
The following snippet is a naive implementation of a function which migrates entries in a date-tree file (the current buffer) to another date-tree file (dest-file
argument):
(defun org-reverse-datetree-migrate-to-file (dest-file)
(let ((depth (length (org-reverse-datetree--get-level-formats)))
;; Prevent from showing the contexts for better performance
(org-reverse-datetree-show-context-detail nil))
(save-restriction
(widen)
(while (re-search-forward (rx-to-string `(and bol
,(make-string depth ?\*)
space))
nil t)
(let ((date (thread-last (seq-drop (parse-time-string
(org-get-heading t t t t))
3)
(append '(0 0 0))
(encode-time))))
(if date
(progn
(outline-next-heading)
(while (= (1+ depth) (org-outline-level))
(org-reverse-datetree-refile-to-file dest-file date)))
(user-error "Date is unavailable")))))))
org-reverse-datetree-archive-subtree
command.
It also works on multiple trees in an active region.
The destination is specified in either REVERSE_DATETREE_ARCHIVE_FILE
property (inherited) or REVERSE_DATETREE_ARCHIVE_FILE
file header. It should be a file path.
For now, the target file cannot contain multiple date trees.
From inside org-agenda
, you can use org-reverse-datetree-agenda-archive
.
It doesn’t work on bulk entries for now.
With org-ql package, you can define a function for browsing entries in a reverse date tree:
(org-ql-search "~/org/cpb.org"
(level 4)
:sort priority)
You can also define a custom org-agenda command:
(setq org-agenda-custom-commands
'(("c" "Browse entries in cpb.org"
org-ql-block '(level 4)
((org-super-agenda-groups
'((:todo "DONE")
(:todo t)))
(org-agenda-files '("~/org/cpb.org"))))))
org-super-agenda-groups
is an option for org-super-agenda for grouping the contents. If you don’t activate org-super-agenda-mode
, that option is simply ignoerd.
You can use org-reverse-datetree-cleanup-empty-dates
command to clean up date entries that contains no children.
org-reverse-datetree-date-child-p
function returns non-nil if and only if the heading is a direct child of a date heading in the date tree.
You can use this function to define an org-ql predicate that matches direct children of date trees:
(org-ql-defpred datetree ()
"Return non-nil if the entry is a direct child of a date entry."
:body
(org-reverse-datetree-date-child-p))
The following code displays entries in the date tree using org-ql-search
:
(org-ql-search (current-buffer)
'(datetree))
- An Annotated Spacemacs - For an org-mode workflow ·: Using the package in some of his
org-capture
templates for clippling URLs, notes, and tasks.
- Allow overriding org-read-date-prefer-future for the package. (See
org-reverse-datetree-prefer-future
custom variable) - If
org-use-effective-time
is non-nil, considerorg-extend-today-until
for determining the target date - Bugfix: Don’t depend on org-time-stamp-formats to support Org 9.6
- Bugfix: Don’t throw an error when run in a file without a datetree
- Bugfix: Restore the archive time
- Bugfix: Skip the property drawer when inserting a new header
- Require Emacs 28.1 and Org 9.5 as minimum dependencies
- Add
org-reverse-datetree-calendar-next
andorg-reverse-datetree-calendar-previous
commands. - Fix the face of calendar highlights to support light background.
- Fix quotes in
cl-case
patterns.
- Add
org-reverse-datetree-date-child-p
function. - Add
org-reverse-datetree-default-entry-time
function. org-reverse-datetree-refile-to-file
function has been changed to return the time, unless a region is active in theorg-mode
buffer or bulk mode is active inorg-agenda-mode
.
- Add calendar integration.
- Add
org-reverse-datetree-dates
function.
- Optimize the header reading by narrowing.
- Add
org-reverse-datetree-num-levels
function.
- Add
org-reverse-datetree-guess-date
function.
- Add
org-reverse-datetree-map-entries
function.
- Add
match
entry type toorg-reverse-datetree-entry-time
custom variable.
- Add
org-reverse-datetree-entry-time
to allow customizing how to determine the date. - Make
org-reverse-datetree-refile-to-file
taket
or patterns as the time argument.
- Add
org-reverse-datetree-show-context-detail
to allow customization of the behavior.
- Add
org-reverse-datetree-show-context
option.
- Fix bugs with
org-reverse-datetree-cleanup-empty-dates
. - Switch to elinter for CI.
Add a function for archiving from org-agenda, org-reverse-datetree-agenda-archive
.
Add an initial support for archiving.
Add support for a non-reverse date tree.
- Fix a bunch of issues with
org-reverse-datetree-cleanup-empty-dates
. Explicitly documented the function in README. - Switch to GitHub Actions on running CI.
GPL v3