https://www.labri.fr/perso/nrougier/GTD/index.html
Get Things Done with Emacs
Nicolas P. Rougier - September 2020
Table of Contents
* Introduction
+ Get Things Done
+ About this document
+ License
+ Resources
* Inbox
+ Basic setup
+ Working with mail
+ Capturing tasks
+ Summary
* Agenda
+ Basic setup (I)
+ Recurrent events
+ Meetings & notes
+ Summary
* Review
+ Basic setup
+ Moving things
+ Activating tasks
+ Summary
* Doing things
+ Agenda setup (II)
+ Completing a task
+ Summary
* Additional changes
+ Automatic saving after refilling
* Conclusion
+ Going further
Introduction
[?] Back to top
Before I start describing how I organize my professional life using
Emacs and org-mode, let me introduce myself. I'm a researcher in
computational neuroscience working at the Institute of
Neurodegenerative Diseases in Bordeaux (France). As such, my main
tasks during a typical day are meetings with my PhD students
(physical or online), team and grant meetings (physical or online),
development (GitHub), reading (online), reviewing (online) and
writing (books, papers, grants, notes, etc). I'm also co-founder and
editor in chief for ReScience C and ReScience X and I'm involved in
several academic and open source projects. I receive approximately
100 mails and 10 GitHub notifications a day.
As of September 2020, I'm supervising five PhD students (two first
year, one second year, one third year and one finishing). I'm
currently writing a book (Scientific Visualization -- Python &
Matplotlib), handling the simulatenous writing of 6 academic papers
(at various stages), while preparing a SciFi festival (Hypermondes),
a hackaton (ReproHack @ Bordeaux), a workshop (Ten Years
Reproducibility Challenge) and a summer school (ASPP 2021) to be held
in Bordeaux in 2021.
All these activities requires some organisation.
Get Things Done
I've decided some weeks ago to (try to) adopt the method of David
Allen named "Get Things Done" (GTD). A lot of people were giving very
positive feedback about this method and there are a lot of related
resources online, including several Emacs/org-mode setup. This helped
me a lot to design my own setup since I did not read the book, but I
found a nice summary:
1. Capture anything that crosses your mind, nothing is too big or
small.
2. Clarify what you've captured into clear and concrete action
steps.
3. Organize and put everything into the right place.
4. Review, update, and revise your lists.
5. Engage Get to work on the important stuff.
Then I read a lot of other Emacs specific resources that were
incredibly useful but also a bit complex because people introduced
their full setup making it difficult to understand it sometime. Since
I'm still in the setup phase, I've decided to document the process
while designing it, hoping it will be helpful for others.
If you want to read my current full setup, just go the end of this
document. Else, you can read each section and discover the process
step by step. Note that this means we'll write some parts only to be
rewritten later with some improvements, just for the sake of clarity.
About this document
In all the following section, I'll suppose you're familiar with emacs
(of course) and knows bit of org-mode. If this is not the case, you
can follow the different links I've inserted. They point to the
relevant section in the org-mode manual. We'll start with a Vanilla
emacs without any initial configuration ("emacs -q - GTD.el") such
that you should obtain the same result as me. GTD.el will be given in
each section.
The address of this document is: https://www.labri.fr/perso/nrougier/
GTD/index.html
The sources of this document is: https://github.com/rougier/emacs-GTD
License
This document has been written in org mode (GTD.org) using a
customized CSS stylesheet (GTD.css) released (both) under a CC-BY 4.0
international license. Feel free to re-use it if you like it.
Resources
Here are various resources that helped me to design my workflow:
* Org-mode Workflow: A Preview by Jethro Kuan (2020)
This is going to be a multi-part series on Emacs and
org-mode. This is also going to be a really long series, so
before we begin I want to give you an idea of what to expect.
What I'm about to present is a workflow I've tweaked over
several years. It is a workflow that has constantly evolved
to adapt to my varying needs. At the time of writing, I have
completed 3478 todo items, and written over 29000 lines in my
personal knowledge base.
* Orgmode for GTD by Nicolas Petton (2017)
I've been using Orgmode to implement the GTD methodology for
the last 4 years. Rather than explaining the GTD methodology
itself or how Orgmode works, in this post I'll detail how I
use Orgmode to implement GTD. If you don't know Orgmode and
are curious about it, you should head to its website first.
* Org Mode - Organize Your Life In Plain Text! by Bernt Hansen
(2015)
I have been using org-mode as my personal information manager
for years now. I started small with just the default TODO and
DONE keywords. I added small changes to my workflow and over
time it evolved into what is described by this document. I
still change my workflow and try new things regularly. This
document describes mature workflows in my current org-mode
setup. I tend to document changes to my workflow 30 days
after implementing them (assuming they are still around at
that point) so that the new workflow has a chance to mature.
* How I use Emacs and Org-mode to implement GTD by Charles Cave
(2009)
"Getting Things Done" is a book by David Allen in which he
describes a work-life management system developed after
twenty years of consulting work. The promise of GTD is
"Stress-free productivity" and developing a calm state of
mind in a state of readiness. Allen's second book, "Ready for
Anything" elaborates on these principles in a series of
essays. The effectiveness of GTD lies in taking a complete
and current inventory of all your commitments, then
organizing and reviewing this information regularly in a
systematic way. Your work and life can then be viewed from
different levels of detail allowing you to make choices about
what to do (and not do) at any moment.
And of course, the org-mode documentation helped me alot.
Org is a mode for keeping notes, maintaining TODO lists, and
doing project planning with a fast and effective plain-text
system. It is also an authoring and publishing system, and it
supports working with source code for literal programming and
reproducible research.
Inbox
[?] Back to top
Basic setup
The first step I was interested in was a way to quickly capture any
idea that I may have during the day. My goal was to have a
non-disruptive process, that is, type a key sequence to enter capture
mode, type some text and then just forget about it. More
specifically, I did not want to have to think where I would store
this text nor thinking about any related information such as tags or
date. I chose to store theses ideas in a ~/Documents/org/inbox.org
file:
(setq org-directory "~/Documents/org")
(setq org-agenda-files (list "inbox.org"))
My initial inbox.org reads (we'll modify it later):
#+STARTUP: content showstars indent
#+FILETAGS: inbox
The STARTUP line defines some buffer settings (initial visibility,
indent mode and star visibility) while the FILETAGS line defines a
common tag that will be inherited by all entries (inbox in this
case).
then we can setup a specific capture template for inbox:
(setq org-capture-templates
`(("i" "Inbox" entry (file "inbox.org")
,(concat "* TODO %?\n"
"/Entered on/ %U"))))
and setup a keyboard shortcut (C-c c):
(define-key global-map (kbd "C-c c") 'org-capture)
Note that this binding will invoke the org capture menu and to add
something to the inbox, the full sequence is C-c c i. Since I intend
to use it quite often, I prefer to have shorter bindings:
(defun org-capture-inbox ()
(interactive)
(call-interactively 'org-store-link)
(org-capture nil "i"))
(define-key global-map (kbd "C-c i") 'org-capture-inbox)
Working with mail
For a long time, I've been using my mail inbox as a todo box, i.e. a
collection of emails that call for action. Any mail that was not
archived requested an answer in the short or long term. I doubt this
is the recommended way of handling the inbox (but is there any?) but
since I decided to move to GTD, it was also the opportunity to handle
my mail differently. A few months back, I moved away from the default
system email client on my OSX machine and started to use the almighty
mu4e (mu for emacs) that provides a very smooth capture integration.
We can thus add a new template for quickly capturing a "Reply to"
action to be filed in the inbox.
(setq org-capture-templates
`(("i" "Inbox" entry (file "inbox.org")
,(concat "* TODO %?\n"
"/Entered on/ %U"))
("@" "Inbox [mu4e]" entry (file "inbox.org")
,(concat "* TODO Process \"%a\" %?\n"
"/Entered on/ %U"))))
We can now define a binding for headers and view mode:
(define-key mu4e-headers-mode-map (kbd "C-c c") 'mu4e-org-store-and-capture)
(define-key mu4e-view-mode-map (kbd "C-c c") 'mu4e-org-store-and-capture)
To register a reply action, the full sequence is thus C-c c @. Since
I'm lazy, let's shorten it to C-c i:
(defun org-capture-mail ()
(interactive)
(call-interactively 'org-store-link)
(org-capture nil "@"))
(define-key mu4e-headers-mode-map (kbd "C-c i") 'org-capture-mail)
(define-key mu4e-view-mode-map (kbd "C-c i") 'org-capture-mail)
Last thing is a small hook to tell org-capture to use the full window
instead of splitting the current window. This is not mandatory at all
and mostly depends on your taste.
(add-hook 'org-capture-mode-hook 'delete-other-windows)
Capturing tasks
Any time you want to enter something in your inbox, be it a note or
something related to a mail, just type C-c i and you should see the
following capture window:
Capture buffer. Finish 'C-c C-c', refile 'C-c C-w', abort 'C-c C-k'.
* TODO _
Entered on [2020-09-11 Fri 07:53]
For mail related note, you don't have to do anything:
Capture buffer. Finish 'C-c C-c', refile 'C-c C-w', abort 'C-c C-k'.
* TODO Process "Adaptive Computation Time ..." _
Entered on [2020-09-08 Tue 08:51]
My inbox notes are generally short (verb + subject) because I use a
daily review and it is generally enough information for later
processing. To give you an idea, here is my current inbox (2020-09-11
Fri). Note that all the Process entries links to the relevant mail.
#+STARTUP: content showstars indent
#+FILETAGS: inbox
* TODO Organize bibliography ...
* TODO Process "Adaptive Computation Time ..." ...
* TODO Process "Relevant paper" ...
* TODO Mail F.Wagner about team talk ...
* TODO Buy "Canon printer" ...
* TODO Process "ICDL oral presentation" ...
* TODO Write discussion (plasticity paper) ...
* TODO Compute VSOM dxdy representation ...
During the review, some of these notes will be filed directly under
an existing project, some will be split in several subtasks and some
others will lead to the creation of a new project.
Summary
Our first (incomplete) GTD configuration is:
GTD.el (version 1)
(require 'org)
(setq org-directory "~/Documents/org")
(setq org-agenda-files (list "inbox.org"))
(setq org-capture-templates
`(("i" "Inbox" entry (file "inbox.org")
,(concat "* TODO %?\n"
"/Entered on/ %U"))
("@" "Inbox [mu4e]" entry (file "inbox.org")
,(concat "* TODO Reply to \"%a\" %?\n"
"/Entered on/ %U"))))
(defun org-capture-inbox ()
(interactive)
(call-interactively 'org-store-link)
(org-capture nil "i"))
(defun org-capture-mail ()
(interactive)
(call-interactively 'org-store-link)
(org-capture nil "@"))
;; Use full window for org-capture
(add-hook 'org-capture-mode-hook 'delete-other-windows)
(define-key global-map (kbd "C-c c") 'org-capture)
(define-key global-map (kbd "C-c i") 'org-capture-inbox)
;; Only if you use mu4e
;; (require 'mu4e)
;; (define-key mu4e-headers-mode-map (kbd "C-c i") 'org-capture-mail)
;; (define-key mu4e-view-mode-map (kbd "C-c i") 'org-capture-mail)
The set of available commands at this point is:
+-------------------------------------------------------------------+
| Command | Bindings | Mode + where |
|--------------------------+-----------------+----------------------|
| Capture menu | C-c c | any |
|--------------------------+-----------------+----------------------|
| Capture a new TODO | C-c i or C-c c | any |
| | i | |
|--------------------------+-----------------+----------------------|
| Capture a mail-related | C-c i or C-c c | mu4e view/headers |
| TODO | @ | mode |
+-------------------------------------------------------------------+
Agenda
[?] Back to top
Basic setup (I)
Before heading to the review process, we need first to setup the
agenda that will help us to track daily meetings and tasks. Using the
previous inbox configuration, you can actually already have a view of
your inbox using the M-x org-agenda command that will bring up a menu
where you can choose what to display. Before using it, let me first
define a new global key binding (C-c a) for quick access to the
agenda:
(define-key global-map (kbd "C-c a") 'org-agenda)
Now, let's see what our tasks look like by invoking the "List of All
TODO entries" using the t menu (full sequence is thus C-c a t):
Global list of TODO items of type: ALL
Press 'N r' (e.g. '0 r') to search again: (0)[ALL] (1)TODO (2)DONE
inbox: TODO Organize bibliography :inbox:
inbox: TODO Process "Adaptive Computation Time..." :inbox:
inbox: TODO Process "Relevant paper" :inbox:
inbox: TODO Mail F.Wagner about team talk :inbox:
inbox: TODO Buy "Canon printer" :inbox:
inbox: TODO Process "ICDL oral presentation" :inbox:
inbox: TODO Write discution (plasticity paper) :inbox:
inbox: TODO Compute VSOM dxdy representation :inbox:
The display of the task list is bit redundant because we have an
"inbox" on the left and an "inbox" on the right. However, they do not
have the same origin. The one on the left is the name of the file
where the related TODO is stored while the one on the right is a tag.
If you remember our inbox file setup header, there was a #+FILETAGS:
inbox line that assign the "inbox" tag to each entry. Since tags are
redundant, let's just remove them by filtering them out:
(setq org-agenda-hide-tags-regexp ".")
With this line, we actually ask the agenda to hide any tag (.) that
may be present. Of course, you can choose to be more specific and
hide only some tags, it's up to you. Now we still have to choose what
to do with the left part displaying "inbox". This is displayed
because there is no category assigned to the entry and when the
agenda display a TODO item, the default behavior is to display the
category or the filename if there is no category. You can of course
change what is actually displayed by modifying the
org-agenda-prefix-format:
(setq org-agenda-prefix-format
'((agenda . " %i %-12:c%?-12t% s")
(todo . " %i %-12:c")
(tags . " %i %-12:c")
(search . " %i %-12:c")))
We could also add a #+CATEGORY: INBOX to the header of our inbox file
but I prefer not to have anything displayed for now. So let's get's
rid of the category display for todo items:
(setq org-agenda-prefix-format
'((agenda . " %i %-12:c%?-12t% s")
(todo . " ")
(tags . " %i %-12:c")
(search . " %i %-12:c")))
And the result is:
Global list of TODO items of type: ALL
Press 'N r' (e.g. '0 r') to search again: (0)[ALL] (1)TODO (2)DONE
TODO Organize bibliography
TODO Process "Adaptive Computation Time..."
TODO Process "Relevant paper"
TODO Mail F.Wagner about team talk
TODO Buy "Canon printer"
TODO Process "ICDL oral presentation"
TODO Write discution (plasticity paper)
TODO Compute VSOM dxdy representation
Recurrent events
Beside notes taking, I send or received a number of appointments
(generally through mail) and I need to save them. I prefer not to
file them in my inbox since generally I know both the topic and when
they will occur such that I can directly store them in a dedicated
agenda.org file that will be used to store all my scheduled events
and meetings. Let's first add this new file to the list of org files:
(setq org-agenda-files (list "inbox.org" "agenda.org"))
The initial file has a basic startup header configuration and I also
defined a set of default tags using the #+TAGS syntax. This will ease
the assignment of tags when adding a new entry or modifying an entry
(we'll that later):
#+STARTUP: hideall showstars indent
#+TAGS: event(e) meeting(m) deadline(d)
#+TAGS: @outside(o) @imn(i) @inria(r) @online(l) @canceled(c)
The structure of my agenda is split into birthdays and recurrent /
past / future events. This quite arbitrary and you can use any a
different structure.
* Birthdays
* Recurrent
* Scheduled
* People
* Team
* Past
* Future
Let's first start filling the birthdays part. For any yearly event
such as birthdays, we can add special entries using the
org-anniversary syntax (from the diary).
* Birthdays
%%(org-anniversary 1976 6 1) Emacs is %d years old
%%(org-anniversary 1953 3 16) Richard Stallman is %d years old
Each time month/day is displayed in the agenda, the corresponding
text will be displayed as well.
For the recurrent events, I make a separation between scheduled,
people and team. In my case, scheduled events are mostly daily events
and I use the agenda as a reminder. I could have used the repeating
tasks feature (using syntax such as [2020-09-12 Sat 09:00 +1d], but
the problem is that I wanted to have some daily repeating task only
for workdays and the previous syntax does not offer such choice.
Instead, I've used the following code:
* Recurrent
* Scheduled
* GTD Review 18:00-18:20 :review:@home:
SCHEDULED: <%%(memq (calendar-day-of-week date) '(1 2 3 4 5))>
* Notes cleaning 18:20-18:30 :review:@home:
SCHEDULED: <%%(memq (calendar-day-of-week date) '(1 2 3 4 5))>
* Agenda cleaning :review:@home:
SCHEDULED: <2020-09-01 Tue +1m>
For recurrent events with people, I do not use the repeating task
feature because I want to be able to cancel a specific meeting if it
did not happen and also to use the entry for taking notes (we will
see that below).
* Recurrent
* People
* Student A (weekly meeting) :meeting:
* Student A :@canceled:
<2020-09-14 Mon 10:00-11:00>
* Student A :@online:
<2020-09-07 Mon 10:00-11:00>
* Student A :@imn:
<2020-09-14 Mon 10:00-11:00>
For these recurrent events, I write them directly into the agenda
file at the end of a meeting where we generally agree on next
meeting. Here is quick reminder by Greg Lucas on how to enter date
and their definition:
PLAIN timestamp (C-c .)
This is used for things like appointments where the entry occurs
at a specific date/time. Such an entry will show up in the agenda
on the specified day, and will not show up after that day has
passed. Note that an appointment in the past won't keep showing
up on your agenda regardless of whether you mark it DONE: if you
didn't go to your doctor's appointment yesterday, that doesn't
mean you still have one today!
SCHEDULED timestamp (C-c C-s)
This is used to indicate when you intend to do the task. It will
show up on the agenda on the scheduled day. If you don't complete
the task at that time, it will continue to show up on the agenda
on the following days to show you that you have not completed
something that you planned to do.
DEADLINE timestamp (C-c C-d)
This is used to indicate when something must be completed.
Typically you want to see deadlines ahead of time, so that you
can do whatever it is that must be done to meet them. Like a
scheduled entry, if you miss a deadline it will continue to
appear on the agenda as past due.
INACTIVE timestamp (C-c !)
This is when you want to attach a date to an entry but do not
want it to show up in the agenda at all. Inactive timestamps have
no special behavior.
Meetings & notes
Now it is time to define a new capture template for registering a
meeting.
("m" "Meeting" entry (file+headline "agenda.org" "Future")
,(concat "* %? :meeting:\n"
"<%<%Y-%m-%d %a %H:00>>"))
It is not very complicated: any meeting will be filed in agenda.org
under the Future header (and will be refiled later, during agenda
review). By default, the timestamp is the current day with a rounded
clock since my meetings generally starts at plain hours. When
invoked, this template looks like:
Capture buffer. Finish 'C-c C-c', refile 'C-c C-w', abort 'C-c C-k'.
* _ :meeting:
<2020-09-11 Fri 11:00>
When I enter a meeting, I generally use the name of people that will
be present at the meeting (if there are not too many people) or the
topic of the meeting. The goal is to have short but useful
information when the meeting shows up in my agenda.
The next step is to define a "Note" template that will be used to
take notes during a meeting:
("n" "Note" entry (file "notes.org")
,(concat "* Note (%a)\n"
"/Entered on/ %U\n" "\n" "%?"))
We will (temporarily) store these notes in a notes.org dedicated
file. Maybe you've noticed the "%a", in the template. This is a
template extension that will be resolved to the content created with
the org-store-link command. However, if you capture a note while your
cursor is on the same line of an agenda entry, the content will be
filled with this entry. This is super convenient to link your note
with the agenda entry.
My initial notes.org is as follows:
#+STARTUP: content showstars indent
#+FILETAGS: notes
Nothing fancy here since it is actually a temporary storage, notes
will be later moved to the relevant project they belong to.
Summary
Here is our updated configuration:
GTD.el (version 2)
(require 'org)
(setq org-directory "~/Documents/org")
(setq org-agenda-files (list "inbox.org" "agenda.org" "notes.org"))
(setq org-capture-templates
`(("i" "Inbox" entry (file "inbox.org")
,(concat "* TODO %?\n"
"/Entered on/ %U"))
("m" "Meeting" entry (file+headline "agenda.org" "Future")
,(concat "* %? :meeting:\n"
"<%<%Y-%m-%d %a %H:00>>"))
("n" "Note" entry (file "notes.org")
,(concat "* Note (%a)\n"
"/Entered on/ %U\n" "\n" "%?"))
("@" "Inbox [mu4e]" entry (file "inbox.org")
,(concat "* TODO Reply to \"%a\" %?\n"
"/Entered on/ %U"))))
(defun org-capture-inbox ()
(interactive)
(call-interactively 'org-store-link)
(org-capture nil "i"))
(defun org-capture-mail ()
(interactive)
(call-interactively 'org-store-link)
(org-capture nil "@"))
;; Use full window for org-capture
(add-hook 'org-capture-mode-hook 'delete-other-windows)
(define-key global-map (kbd "C-c a") 'org-agenda)
(define-key global-map (kbd "C-c c") 'org-capture)
(define-key global-map (kbd "C-c i") 'org-capture-inbox)
;; Only if you use mu4e
;; (require 'mu4e)
;; (define-key mu4e-headers-mode-map (kbd "C-c i") 'org-capture-mail)
;; (define-key mu4e-view-mode-map (kbd "C-c i") 'org-capture-mail)
The set of available commands is now:
+-------------------------------------------------------------------+
| Command | Bindings | Mode + where |
|-----------------------------+---------------+---------------------|
| Agenda | C-c a | any |
|-----------------------------+---------------+---------------------|
| Agenda for today | C-c a a | any |
|-----------------------------+---------------+---------------------|
| | | |
|-----------------------------+---------------+---------------------|
| Capture menu | C-c c | any |
|-----------------------------+---------------+---------------------|
| Capture meeting | C-c c m | any |
| (agenda.org) | | |
|-----------------------------+---------------+---------------------|
| Capture meeting note | C-c c n | any |
| (notes.org) | | |
|-----------------------------+---------------+---------------------|
| Capture generic TODO | C-c i or C-c | any |
| (inbox.org) | c i | |
|-----------------------------+---------------+---------------------|
| Capture mail TODO | C-c i or C-c | mu4e view/headers |
| (inbox.org) | c @ | mode |
|-----------------------------+---------------+---------------------|
| | | |
|-----------------------------+---------------+---------------------|
| Add/Remove tag | C-c C-c | org-mode on |
| | | headline |
|-----------------------------+---------------+---------------------|
| | | |
|-----------------------------+---------------+---------------------|
| Plain timestamp | C-c . | org-mode |
|-----------------------------+---------------+---------------------|
| Scheduled timestamp | C-c C-s | org-mode |
|-----------------------------+---------------+---------------------|
| Deadline timestamp | C-c C-d | org-mode |
|-----------------------------+---------------+---------------------|
| Inactive timestamp | C-c ! | org-mode |
+-------------------------------------------------------------------+
Review
[?] Back to top
The actual review process is probably the less documented aspect of
GTD from all the resources I've read so far. When it is mentioned,
some people explain they have a weekly review while some others uses
a daily review. To get things further complicated, it is not clear if
this process is a matter of a few minutes or a matter of hours. My
personal point of view so far (and this may change in the future) is
that a daily process is probably the way to go in my case since I'm
adding from 5 to 10 entries to my inbox each day (mail included). For
me, the end of the work day seems to be the best time to do the
review since my memory is still fresh with things I've done during
the day, and what may come next for each project.
Basic setup
My review process consists mostly in moving things out of the inbox
in order to file them in the corresponding project if it exists or to
create one if this is not the case. To do that, we need first to
create a new projects.org file that will hold all our projects. The
structure of this file is quite important because you'll interact
with it a lot of times. I chose to split first my projects into
global categories:
#+STARTUP: content showstars indent
* Students :students:
* Team :team:
* Collaboratorive projects :collaborative:project:
* Events organization :events:
* Academic papers :article:
* Personal projects :personal:project:
* ReScience :rescience:
* Home :home:
I then add projects under one of these categories using a specific
structure. Let me show you a typical student "project":
* Students :students:
* J.Doe (PhD) [/] :phd:j.doe:
:PROPERTIES:
:CATEGORY: J.DOE
:VISIBILITY: hide
:COOKIE_DATA: recursive todo
:END:
* Information :info:
:PROPERTIES:
:VISIBILITY: hide
:END:
* Notes :notes:
:PROPERTIES:
:VISIBILITY: hide
:END:
* Tasks :tasks:
:PROPERTIES:
:VISIBILITY: content
:END:
The information section contains information related to the project
and you're free to organize the way you want. The notes section is
where I move meeting notes related to this project during my notes
review. Finally, the tasks is where I will move related inbox
entries. I also set different visibility properties for each section:
information and notes are hidden while tasks section is open. You
might have also noticed the COOKIE_DATA property at the top and the
[/] at the end of the project name. These two components will help us
to track the number of pending tasks vs the number of completed
tasks. Each time we'll move a task from the TODO state to the DONE
state, the header will be updated accordingly (you can also refresh
it with C-c C-c when the cursor is over it).
The overall number of projects is very dependent on you. In my case,
I've about 40 opened projects. Some will last a few months (e.g.
academic papers), some will last a few years (e.g. grants and phd
students) and some others do not have foreseeable end (house, garden,
car).
Moving things
Before refiling inbox entries into projects, I usually try to set an
estimated time (effort) needed to complete the tasks as well as some
contextual information (using tags). To ease the process, we'll first
modify the inbox header to add typical estimated efforts and some
tags that will speed the overall processing of each entry.
#+STARTUP: content showstars indent
#+TAGS: @home(h) @work(w) @mail(m) @comp(c) @web(b)
#+PROPERTY: Effort_ALL 0 0:05 0:10 0:15 0:30 0:45 1:00 2:00 4:00
Before refiling (i.e. moving) an entry, I will set some tags using
the C-c C-c keybinding and if the entry is an atomic task (i.e. that
can be done independently of any others tasks), I'll assign an
estimated effort using the existing C-c C-x e key binding. For
example, let's consider the following entry before review:
* TODO Write review section (GTD.org)
Entered on [2020-09-12 Sat 09:20]
After having set effort and tags, the entry reads:
* TODO Write review section (GTD.org) :@comp:
:PROPERTIES:
:Effort: 0:30
:END:
Entered on [2020-09-12 Sat 09:20]
Tags are supposed to give some contextual information on where the
task can be completed. However, I did not really use them partly due
to the 2020 sanitary crisis that tends to blur the line between work
and home. Setting the estimated is however quite important for me
because when I'll activate a task, the estimated effort will be
displayed in the agenda and will help me to decide if I can engage in
task depending on the amount of free time I have. This is especially
useful for small tasks (5 minutes) that can be completed any time.
Now it's time to move the entry.
I explained that inbox entries have to be moved into the relevant
project under the Tasks headline. To do that, we'll use the
org-refile function (bound to C-c C-w when on a headline) and specify
where the entry can be refiled in the projects.org. If you remember,
we also have notes that needs to be refiled in projects such that
targets in projects.org are either Notes or Tasks. We thus need to
define a refile target using regexp. One easy way to do that is to
use the regexp-opt function:
(regexp-opt '("Tasks" "Notes"))
You can evaluate the expression by placing the cursor at the end of
the line and type C-u C-x C-e. The optimized regex ("\\(?:\\(?:Note\\
|Task\\)s\\)") should appear at the end of the line. We can now use
it so specifiy our targets:
(setq org-refile-targets
'(("projects.org" :regexp . "\\(?:\\(?:Note\\|Task\\)s\\)")))
Last step is to tell org-mode we want to specify a refile target
using the file path.
(setq org-refile-use-outline-path 'file)
(setq org-outline-path-complete-in-steps nil)
To refile a J.Doe related inbox entry, you can then type:
C-c C-w + "J.Doe" + tab + "T" + tab
and this will be resolved to "projects.org/Students/J.Doe/Tasks".
Activating tasks
After having emptied the inbox, it's time to have a look at the
different projects to decide what are the next tasks to be activated.
Before doing that, we need to define what is an active task. Org-mode
defines two different states for TODO items: TODO and DONE. We need
to modify this in order to introduce two new non-terminal state: NEXT
to express this is the next task to be completed and HOLD to express
this task is on hold (for whaterver reason):
(setq org-todo-keywords
'((sequence "TODO(t)" "NEXT(n)" "HOLD(h)" "|" "DONE(d)")))
Thanks to Erik Anderson, we can also add a hook that will log when we
activate a task by creating an "ACTIVATED" property the first time
the task enters the NEXT state:
(defun log-todo-next-creation-date (&rest ignore)
"Log NEXT creation time in the property drawer under the key 'ACTIVATED'"
(when (and (string= (org-get-todo-state) "NEXT")
(not (org-entry-get nil "ACTIVATED")))
(org-entry-put nil "ACTIVATED" (format-time-string "[%Y-%m-%d]"))))
(add-hook 'org-after-todo-state-change-hook #'log-todo-next-creation-date)
We'll see in the next section how to exploit this property. Next step
is to update the trailing [/] behind each headline. To do that, you
can type C-u C-c #. For each projects, you now have something like [x
/y] where x is the number of closed tasks and y is the total number
of tasks. Any project with x < y means that there are some tasks that
can be activated. You can now selectively open a project and decide
if you want to activate one of the non closed task. The most easy way
to do that is to go over the TODO keyword and use S-right to advance
to the NEXT state.
Summary
Here is our updated configuration:
GTD.el (version 3)
(require 'org)
;; Files
(setq org-directory "~/Documents/org")
(setq org-agenda-files (list "inbox.org" "agenda.org" "notes.org"))
;; Capture
(setq org-capture-templates
`(("i" "Inbox" entry (file "inbox.org")
,(concat "* TODO %?\n"
"/Entered on/ %U"))
("m" "Meeting" entry (file+headline "agenda.org" "Future")
,(concat "* %? :meeting:\n"
"<%<%Y-%m-%d %a %H:00>>"))
("n" "Note" entry (file "notes.org")
,(concat "* Note (%a)\n"
"/Entered on/ %U\n" "\n" "%?"))
("@" "Inbox [mu4e]" entry (file "inbox.org")
,(concat "* TODO Reply to \"%a\" %?\n"
"/Entered on/ %U"))))
(defun org-capture-inbox ()
(interactive)
(call-interactively 'org-store-link)
(org-capture nil "i"))
(defun org-capture-mail ()
(interactive)
(call-interactively 'org-store-link)
(org-capture nil "@"))
;; Use full window for org-capture
(add-hook 'org-capture-mode-hook 'delete-other-windows)
;; Key bindings
(define-key global-map (kbd "C-c a") 'org-agenda)
(define-key global-map (kbd "C-c c") 'org-capture)
(define-key global-map (kbd "C-c i") 'org-capture-inbox)
;; Only if you use mu4e
;; (require 'mu4e)
;; (define-key mu4e-headers-mode-map (kbd "C-c i") 'org-capture-mail)
;; (define-key mu4e-view-mode-map (kbd "C-c i") 'org-capture-mail)
;; Refile
(setq org-refile-use-outline-path 'file)
(setq org-outline-path-complete-in-steps nil)
(setq org-refile-targets
'(("projects.org" :regexp . "\\(?:\\(?:Note\\|Task\\)s\\)")))
;; TODO
(setq org-todo-keywords
'((sequence "TODO(t)" "NEXT(n)" "HOLD(h)" "|" "DONE(d)")))
(defun log-todo-next-creation-date (&rest ignore)
"Log NEXT creation time in the property drawer under the key 'ACTIVATED'"
(when (and (string= (org-get-todo-state) "NEXT")
(not (org-entry-get nil "ACTIVATED")))
(org-entry-put nil "ACTIVATED" (format-time-string "[%Y-%m-%d]"))))
(add-hook 'org-after-todo-state-change-hook #'log-todo-next-creation-date)
The set of available commands is now:
+-------------------------------------------------------------------+
| Command | Bindings | Mode + where |
|-----------------------------+---------------+---------------------|
| Agenda | C-c a | any |
|-----------------------------+---------------+---------------------|
| Agenda for today | C-c a a | any |
|-----------------------------+---------------+---------------------|
| | | |
|-----------------------------+---------------+---------------------|
| Capture menu | C-c c | any |
|-----------------------------+---------------+---------------------|
| Capture meeting | C-c c m | any |
| (agenda.org) | | |
|-----------------------------+---------------+---------------------|
| Capture meeting note | C-c c n | any |
| (notes.org) | | |
|-----------------------------+---------------+---------------------|
| Capture generic TODO | C-c i or C-c | any |
| (inbox.org) | c i | |
|-----------------------------+---------------+---------------------|
| Capture mail TODO | C-c i or C-c | mu4e view/headers |
| (inbox.org) | c @ | mode |
|-----------------------------+---------------+---------------------|
| | | |
|-----------------------------+---------------+---------------------|
| Add/Remove tag | C-c C-c | org-mode on |
| | | headline |
|-----------------------------+---------------+---------------------|
| Update progress indicator | C-c C-c | org-mode on [/] |
|-----------------------------+---------------+---------------------|
| Update all progress | C-u C-c # | org-mode |
| indicators | | |
|-----------------------------+---------------+---------------------|
| Enter estimated effort | C-c C-x e | org-mode on |
| | | headline |
|-----------------------------+---------------+---------------------|
| Refile section | C-c C-w | org-mode on |
| | | headline |
|-----------------------------+---------------+---------------------|
| Move to next TODO state | S-right | org-mode on TODO |
|-----------------------------+---------------+---------------------|
| | | |
|-----------------------------+---------------+---------------------|
| Plain timestamp | C-c . | org-mode |
|-----------------------------+---------------+---------------------|
| Scheduled timestamp | C-c s | org-mode |
|-----------------------------+---------------+---------------------|
| Deadline timestamp | C-c d | org-mode |
|-----------------------------+---------------+---------------------|
| Inactive timestamp | C-c ! | org-mode |
+-------------------------------------------------------------------+
Doing things
[?] Back to top
Agenda setup (II)
It's now time to work on custom agenda view that will display
meetings for the day, task that need to be done and deadlines. We'll
also add the content of the inbox as a reminder to file entries, and
the tasks we've completed today. So overall, our custom agenda
headers will be:
Day-agenda (W37): ...
Sunday 13 September 2020 ...
Tasks ...
Deadlines ...
Inbox ...
Completed today ...
Let's write a new agenda customn command (g) using the
org-custom-agenda-commands variable.
(setq org-agenda-custom-commands
'(("g" "Get Things Done (GTD)"
((agenda ""
((org-agenda-skip-function
'(org-agenda-skip-entry-if 'deadline))
(org-deadline-warning-days 0)))
(todo "NEXT"
((org-agenda-skip-function
'(org-agenda-skip-entry-if 'deadline))
(org-agenda-prefix-format " %i %-12:c [%e] ")
(org-agenda-overriding-header "\nTasks\n")))
(agenda nil
((org-agenda-entry-types '(:deadline))
(org-agenda-format-date "")
(org-deadline-warning-days 7)
(org-agenda-skip-function
'(org-agenda-skip-entry-if 'notregexp "\\* NEXT"))
(org-agenda-overriding-header "\nDeadlines")))
(tags-todo "inbox"
((org-agenda-prefix-format " %?-12t% s")
(org-agenda-overriding-header "\nInbox\n")))
(tags "CLOSED>=\"\""
((org-agenda-overriding-header "\nCompleted today\n")))))))
I won't detail every line because you'll find a lot of information
online, in the documentation and inside emacs as well.
Agenda view (2020-09-13)
Day-agenda (W37):
Sunday 13 September 2020
8:00...... ----------------
8:45- 9:00 Scheduled: Keyboard training
10:00...... ----------------
12:00...... ----------------
14:00...... ----------------
14:19...... > now <
16:00...... ----------------
18:00...... ----------------
20:00...... ----------------
Sched.12x: Agenda cleaning
Tasks
VIZBOOK: [0:30] NEXT Organize book writing (4d.)
RESCIENCE-X: [0:30] NEXT ReScience X DOI registrar (4d.)
DNFSOM: [0:20] NEXT [#A] Read section 4 on DNFSOM (2d.)
X.YYYYY: [0:05] NEXT Mail X.Yyyyy (PhD) (1d.)
Deadlines
3 d. ago: NEXT [#A] Read "Chapitre RNN 2020-09-08" (4d.)
3 d. ago: NEXT [#A] Read "Chapitre WMEXP 2020-09-08]]" (4d.)
Inbox
TODO Organize bibliography
TODO Read to "Adaptive Computation Time for Recurrent Neural Networks"
TODO Process "Relevant paper"
TODO Mail X.Yyyyy about team talk
TODO Buy "Canon printer"
TODO Process "ICDL oral presentation"
TODO Write discution (plasticity paper)
TODO Compute VSOM dxdy representation
TODO Process "Internships for Students at Ecole Polytechnique"
Completed today
DONE Write GTD Review section
You can see that entries in the Task section display a duration in
front of the description. May you've guessed that this duration
corresponds to the estimated effort we entered during the review
process. Now, each time you have some free time ahead, you can which
task you want to engage based on this estimation. Very convenient.
Completing a task
Once you've chosen a task to do, and before starting the task, you
can choose to log the time it will actually take to complete the task
such that you can later refine your estimation. Just type C-c C-x C-i
(clock in) to start the clock and C-c C-x C-o (clock out) to stop the
clock and to add the duration in the logbook. Once a task is
completed, you can change its state from NEXT to DONE (using S-right
while the cursor is over the NEXT word). In order to keep track of
when the task was completed, we can ask org-mode to log that:
(setq org-log-done 'time)
This will add a CLOSED: [2020-09-13 Sun 19:24] line under the
corresponding entry. Using this time, we can thus display the tasks
that have been completed during the day (see the last line of our
agenda custom commands).
To summarize, for a given task:
* TODO Some task
Entered on [2020-09-13 Sun 19:23]
It will be first modified be when the estimated effort is set:
* TODO Some task
:PROPERTIES:
:EFFORT: 0:30
:END:
Entered on [2020-09-13 Sun 19:23]
and further modified when activated:
* NEXT Some task
:PROPERTIES:
:EFFORT: 0:30
:ACTIVATED: [2020-09-13 Sun]
:END:
Entered on [2020-09-13 Sun 19:23]
Finally, once completed, we have:
* DONE Some task
CLOSED: [2020-09-13 Sun 20:14]
:PROPERTIES:
:EFFORT: 0:30
:ACTIVATED: [2020-08-30]
:END:
:LOGBOOK:
CLOCK: [2020-09-13 Sun 19:55]--[2020-09-13 Sun 20:14] => 0:19
:END:
Entered on [2020-09-13 Sun 19:23]
Next time we have to estimate the effort for this specific task, and
based on the above log, we'll probably set it to 20 minutes instead
of 30.
Summary
Our final configuration is thus
GTD.el (version 4)
(require 'org)
;; Files
(setq org-directory "~/Documents/org")
(setq org-agenda-files (list "inbox.org" "agenda.org"
"notes.org" "projects.org"))
;; Capture
(setq org-capture-templates
`(("i" "Inbox" entry (file "inbox.org")
,(concat "* TODO %?\n"
"/Entered on/ %U"))
("m" "Meeting" entry (file+headline "agenda.org" "Future")
,(concat "* %? :meeting:\n"
"<%<%Y-%m-%d %a %H:00>>"))
("n" "Note" entry (file "notes.org")
,(concat "* Note (%a)\n"
"/Entered on/ %U\n" "\n" "%?"))
("@" "Inbox [mu4e]" entry (file "inbox.org")
,(concat "* TODO Reply to \"%a\" %?\n"
"/Entered on/ %U"))))
(defun org-capture-inbox ()
(interactive)
(call-interactively 'org-store-link)
(org-capture nil "i"))
(defun org-capture-mail ()
(interactive)
(call-interactively 'org-store-link)
(org-capture nil "@"))
;; Use full window for org-capture
(add-hook 'org-capture-mode-hook 'delete-other-windows)
;; Key bindings
(define-key global-map (kbd "C-c a") 'org-agenda)
(define-key global-map (kbd "C-c c") 'org-capture)
(define-key global-map (kbd "C-c i") 'org-capture-inbox)
;; Only if you use mu4e
;; (require 'mu4e)
;; (define-key mu4e-headers-mode-map (kbd "C-c i") 'org-capture-mail)
;; (define-key mu4e-view-mode-map (kbd "C-c i") 'org-capture-mail)
;; Refile
(setq org-refile-use-outline-path 'file)
(setq org-outline-path-complete-in-steps nil)
(setq org-refile-targets
'(("projects.org" :regexp . "\\(?:\\(?:Note\\|Task\\)s\\)")))
;; TODO
(setq org-todo-keywords
'((sequence "TODO(t)" "NEXT(n)" "HOLD(h)" "|" "DONE(d)")))
(defun log-todo-next-creation-date (&rest ignore)
"Log NEXT creation time in the property drawer under the key 'ACTIVATED'"
(when (and (string= (org-get-todo-state) "NEXT")
(not (org-entry-get nil "ACTIVATED")))
(org-entry-put nil "ACTIVATED" (format-time-string "[%Y-%m-%d]"))))
(add-hook 'org-after-todo-state-change-hook #'log-todo-next-creation-date)
;; Agenda
(setq org-agenda-custom-commands
'(("g" "Get Things Done (GTD)"
((agenda ""
((org-agenda-skip-function
'(org-agenda-skip-entry-if 'deadline))
(org-deadline-warning-days 0)))
(todo "NEXT"
((org-agenda-skip-function
'(org-agenda-skip-entry-if 'deadline))
(org-agenda-prefix-format " %i %-12:c [%e] ")
(org-agenda-overriding-header "\nTasks\n")))
(agenda nil
((org-agenda-entry-types '(:deadline))
(org-agenda-format-date "")
(org-deadline-warning-days 7)
(org-agenda-skip-function
'(org-agenda-skip-entry-if 'notregexp "\\* NEXT"))
(org-agenda-overriding-header "\nDeadlines")))
(tags-todo "inbox"
((org-agenda-prefix-format " %?-12t% s")
(org-agenda-overriding-header "\nInbox\n")))
(tags "CLOSED>=\"\""
((org-agenda-overriding-header "\nCompleted today\n")))))))
The set of available commands is now:
+-------------------------------------------------------------------+
| Command | Bindings | Mode + where |
|-----------------------------+---------------+---------------------|
| Agenda | C-c a | any |
|-----------------------------+---------------+---------------------|
| Agenda for today | C-c a a | any |
|-----------------------------+---------------+---------------------|
| | | |
|-----------------------------+---------------+---------------------|
| Capture menu | C-c c | any |
|-----------------------------+---------------+---------------------|
| Capture meeting | C-c c m | any |
| (agenda.org) | | |
|-----------------------------+---------------+---------------------|
| Capture meeting note | C-c c n | any |
| (notes.org) | | |
|-----------------------------+---------------+---------------------|
| Capture generic TODO | C-c i or C-c | any |
| (inbox.org) | c i | |
|-----------------------------+---------------+---------------------|
| Capture mail TODO | C-c i or C-c | mu4e view/headers |
| (inbox.org) | c @ | mode |
|-----------------------------+---------------+---------------------|
| | | |
|-----------------------------+---------------+---------------------|
| Add/Remove tag | C-c C-c | org-mode on |
| | | headline |
|-----------------------------+---------------+---------------------|
| Update progress indicator | C-c C-c | org-mode on [/] |
|-----------------------------+---------------+---------------------|
| Update all progress | C-u C-c # | org-mode |
| indicators | | |
|-----------------------------+---------------+---------------------|
| Enter estimated effort | C-c C-x e | org-mode on |
| | | headline |
|-----------------------------+---------------+---------------------|
| Refile section | C-c C-w | org-mode on |
| | | headline |
|-----------------------------+---------------+---------------------|
| Move to next TODO state | S-right | org-mode on TODO |
|-----------------------------+---------------+---------------------|
| | | |
|-----------------------------+---------------+---------------------|
| Clock in | C-c C-x C-i | org-mode on |
| | | headline |
|-----------------------------+---------------+---------------------|
| Clock out | C-c C-x C-o | org-mode on |
| | | headline |
|-----------------------------+---------------+---------------------|
| | | |
|-----------------------------+---------------+---------------------|
| Plain timestamp | C-c . | org-mode |
|-----------------------------+---------------+---------------------|
| Scheduled timestamp | C-c s | org-mode |
|-----------------------------+---------------+---------------------|
| Deadline timestamp | C-c d | org-mode |
|-----------------------------+---------------+---------------------|
| Inactive timestamp | C-c ! | org-mode |
+-------------------------------------------------------------------+
Additional changes
Automatic saving after refilling
After refilling, you will have to save manually your opened org
files, which is not really convenient. Fortunately, a small change in
the code will save the files automatically.
First, you need to get the files you want to save with their
fullpath. Replace the previous definition of org-agenda-files with
the following:
(setq org-agenda-files
(mapcar 'file-truename
(file-expand-wildcards "~/Documents/org/*.org")))
Now, we create a new function to save those files, using the model of
the org-save-all-org-buffers function and finally we add it after the
org-refile action:
;; Save the corresponding buffers
(defun gtd-save-org-buffers ()
"Save `org-agenda-files' buffers without user confirmation.
See also `org-save-all-org-buffers'"
(interactive)
(message "Saving org-agenda-files buffers...")
(save-some-buffers t (lambda ()
(when (member (buffer-file-name) org-agenda-files)
t)))
(message "Saving org-agenda-files buffers... done"))
;; Add it after refile
(advice-add 'org-refile :after
(lambda (&rest _)
(gtd-save-org-buffers)))
Conclusion
[?] Back to top
I'm now done describing my GTD setup and I hope it will be useful for
some readers. I'm rather new to GTD and there are certainly better
ways to implement GTD (see Resources) and probably I'll modify some
parts in the future.
If you spot errors or typos, feel free to open an issue at https://
github.com/rougier/emacs-GTD.
Going further
As explained in the introduction, there are plenty of resources
online regarding org-mode in general and GTD with org-mode in
particular. There are also plenty of interactive places where you ask
for help and find some code:
* Emacs Stack Exchange
* IRC freenode (#org-mode)
* Reddit (orgmode)
* Mailing list