Skip to content

Latest commit

 

History

History
440 lines (356 loc) · 19.5 KB

org-setup.org

File metadata and controls

440 lines (356 loc) · 19.5 KB

Org setup

Introduction

This file provides details on my current org setup. It is intended as a literate programming approach to structure my org setup. It may work, or it may not (!) but I thought I’d give it a go.

If you want to use some (all) of this setup, you’ll need to change directories appropriately. You can tangle this file to Emacs lisp using C-c C-v C-t, which will create org-setup.el in the same folder as this file. You can then source this in your init.el file.

This setup has been greatly influenced by Gregory Stein (GS) and in turn by Bernt Hansen (BH). You should check out those great resources.

Initial setup

First up, we’ll add a block to load all this org config after spacemacs starts, else we’ll get conflicts. If you’re following along at home, don’t forget to complete the block at the end of the file.

Where do I store stuff?

The default directory for org files is ~/org, and I’m going to stick with that convention, but make it explicit. When I capture tasks/notes (C-c t / C-c n), I’m going to have them sent to the ~/org/refile.org file, when I can refile them at my convenience. This saves time when capturing TODO items, as it means I don’t have to decide which project they’ll go into straight away. Speaking of, when it comes time to refile, these can be put into separate project files; the refile-targets allows the nesting to be searched. Finally, I set the agenda from the default org directory.

(setq
 ;; Default directory for org files
 org-directory "~/github/org"
 ;; Directory for notes/tasks to be refiled
 org-default-notes-file (concat org-directory "/refile.org")
 org-journal-file (concat org-directory "/journal-club.org")
 ;; Allows to store agenda files in their appropriate files.
 ;; This is useful when per project task lists are used.
 ;; Only show level 1 headings for refiling (level 2 are the task headers)
 org-refile-targets (quote ((nil :maxlevel . 2)
            (org-agenda-files :maxlevel . 2)))
 ;; Org agenda files read from here
 org-agenda-files '(
                    "~/github/org/waiting-for.org" "~/github/org/refile.org" "~/github/org/issue-tracking.org" "~/github/org/habits-and-schedules.org" "~/github/org/general.org" "~/github/org/analysis.org" "~/github/org/someday.org" "~/github/org/meetings.org"
                    )
 )

I spoke above about refiling. Here’s where I set that up. I’m telling org to use the filename and the headers (so a level 2 header will be filename/level1/level2). complete-in-steps nil means that we can use helm/ivy to provide nice completion, and finally allow the creation of parent headers (with confirmation).

(setq
 ;; Be sure to use the full path for refile setup
 org-refile-use-outline-path 'file
 ;; Set this to nil to use helm/ivy for completion
 org-outline-path-complete-in-steps nil
 ;; Allow refile to create parent tasks with confirmation
 org-refile-allow-creating-parent-nodes 'confirm
 )

How do I capture stuff?

I now need to set up capture templates. When I go to capture a task/note etc, I am presented with the options listed below.

Task categories

I’m migrating/supplementing a BuJo. The following provides my current ‘bullets’ from my BuJo, translated to an org setup. What the code does is define the TODO keywords, and the transition between them. Keywords to the left of the | are in a state of ‘not done’ whilst those on the right are done.

(setq org-todo-keywords
(quote ((sequence "TODO(t)" "|" "DONE(d)")
        (sequence "TODAY(T)" "|" "DONE(d)")
        (sequence "PROJECT(p)" "|" "DONE(d)" "CANCELLED(c)")
        (sequence "WAITING(w)" "|")
        (sequence "|" "CANCELLED(c)")
        (sequence "SOMEDAY(s)" "|" "CANCELLED(c)")
        (sequence "|" "MEETING")
        )
       )
)

My BuJo is pretty simple, I’ve mapped it as follows:

todo
(t) this is the standard task/todo item, which can be marked done
waiting
(w) this keyword is added to a task when I’m waiting to hear back from someone before progressing the task
cancelled
(c) pretty self-explanatory …
someday
(s) this is for tasks that I may want to do at some stage, but not just yet. I still want to keep track of them though.
project
(p) I’m using as a top-level to keep a track of projects.

On top of this, I’m going to try and use GS/BH’s keyword:

meeting
This will be used for ‘true’ meetings, as well as any other interruptions that may carry on for a long time…

Colours for the agenda

When we display these in the agenda, we’d also like to have different colours for different keywords:

(setq
 ;; Coloured faces for agenda/todo items
 org-todo-keyword-faces
 '(
   ("DONE" . (:foreground "#2B4450" :weight bold))
   ("TODO" . (:foreground "#ff3030" :weight bold))
   ("WAITING" . (:foreground "#fe2f92" :weight bold))
   ("CANCELLED" . (:foreground "#999999" :weight bold))
   ("SOMEDAY" . (:foreground "#ab82ff" :weight bold))
   ("MEETING" . (:foreground "#1874cd" :weight bold))
   )
 )

Capture templates

The following code provides the capture templates when I add TODO tasks. Best place to check how these work is the help for that variable. You can find the help for a variable by placing the cursor over the variable and entering C-h v (help for variable). To make sure that folded views keep some space between headers, we set the separator-lines variable.

(setq
 ;; Define the custum capture templates
 org-capture-templates
 '(("t" "Todo's and other general notes")
   ("tt" "Todo (inbox/to refile)" entry (file org-default-notes-file)
    "* TODO %?\nCaptured: %u\n%a\n" :clock-in t :clock-resume t)
   ("tT" "Todo (with headline choice)" entry (function sprazza/org-get-target-headline)
    "* TODO %?\nCaptured: %u\n%a\n" :clock-in t :clock-resume t)
   ("tr" "Ticklers/Reminders" entry (file+headline (lambda() (concat org-directory "/general.org")) "Ticklers")
    "* TODO %?\nCaptured: %u\n%a\n" :clock-in t :clock-resume t)
   ("m" "Meeting templates")
   ("mp" "People Leaders" entry (file+headline (lambda() (concat org-directory "/meetings.org")) "People Leaders")
    "* MEETING :MEETING:\n** Date: %^u\n** Attendees: MN, RN, SL\n** SL Updates\n*** %?\n** RN Updates\n** MN Updates\n** Mood assessment\n" :clock-in nil :clock-resume t)
   ("mg" "General Meeting" entry (file+headline (lambda() (concat org-directory "/meetings.org")) "General Meetings")
    "* MEETING Topic: %? :MEETING:\n** Date: %^u\n** Attendees: Steve Lane\n** Notes Prior\n** Notes During\n*** Mood assessment\n" :clock-in nil :clock-resume t)
   ("mr" "Regular One-on-one's" entry (file+function (lambda() (concat org-directory "/meetings.org")) sprazza/org-get-target-headline)
    "* MEETING Regular meeting with %? :MEETING:\n** Date: %^u\n** Notes Prior\n** Notes During\n*** Mood assessment\n" :clock-in nil :clock-resume t)
   ("mw" "Weekly Review" entry (file+headline (lambda() (concat org-directory "/meetings.org")) "Weekly Review")
    "* MEETING Weekly Review - %^u :MEETING:\n** Attendees: Steve, Sally, Ryan, Jason, Seb\n** Notes Prior\n*** All\n*** Ryan\n*** Sally\n*** Jason\n*** Seb\n** Notes During\n*** Mood assessment\n" :clock-in nil :clock-resume t)
   ("j" "Journal Club" entry (file org-journal-file)
    "* DETAILS: %? :JOURNAL CLUB: \n%u\n" :clock-in t :clock-resume t :empty-lines 1)
   ("P" "Professional development templates")
   ("Ps" "Seminar" entry (file+headline (lambda() (concat org-directory "/professional-development.org")) "Seminars")
    "* Title: %?\n** Date: %^u\n** Speaker: \n** Notes\n" :clock-in t :clock-resume t)
   ("Pw" "Workshop" entry (file+headline (lambda() (concat org-directory "/professional-development.org")) "Workshops")
    "* Title: %?\n** Date: %^u\n** Presenter: " :clock-in t :clock-resume t)
   ("i" "Issue tracking" entry (file (lambda() (concat org-directory "/issue-tracking.org")))
    "* TODO %^{Title}\n%^{Type}p\nCaptured: %u\n** Description\n" :clock-in nil :clock-resume t)
   ("J" "Journal wins/losses" entry (file+function (lambda() (concat org-directory "/wins-and-losses.org")) sprazza/org-get-target-headline)
  "* %u\n** %U\n%?" :clock-in nil)
   )
 ;; Keep a line between headers
 org-cycle-separator-lines 1
 )

For the regular people meetings, we can search for the org target of interest, using this function (called in the above template):

(defun sprazza/org-get-target-headline (&optional targets prompt)
  "Prompt for a location in an org file and jump to it.

This is for promping for refile targets when doing captures.
Targets are selected from `org-refile-targets'. If TARGETS is
given it temporarily overrides `org-refile-targets'. PROMPT will
replace the default prompt message.

"
  (let ((org-refile-targets (or targets org-refile-targets))
        (prompt (or prompt "Capture Location")))
      (org-refile t nil nil prompt))
  )

Source for this function is this stack.

Tagging tasks

Extensibility is provided by tagging. You can tag on the fly, or use a predefined list. Below I set a predefined list. Everything between :startgroup :endgroup below is a single tag for that task—i.e. you can only choose one. ? gives the shortcut to add the tag.

;; Custom tags
(setq org-tag-alist
      '(
        ("meeting" . ?m)
        ("review" . ?r)
        ("analysis" . ?a)
        ("statistical" . ?s)
        ("documenting" . ?d)
        ("coding" . ?c)
        )
      )

Automatic tagging of tasks.

Tasks can have automatic actions taken to tag them. Following BH, I’m using the following triggers:

  • moving a task to CANCELLED adds a :CANCELLED: tag
  • moving a task to a done state removes cancelled tags
(setq
 ;; Triggers for state changes
 org-todo-state-tags-triggers
 (quote (
	   ;; Move to cancelled adds the cancelled tag
	   ("CANCELLED" ("CANCELLED" . t))
	   ;; Move to waiting adds the waiting tag
	   ("WAITING" ("WAITING" . t))
	   ;; Move to a done state removes waiting/cancelled
	   (done ("WAITING") ("CANCELLED"))
	   ("DONE" ("WAITING") ("CANCELLED"))
	   ;; Move to todo, removes waiting/cancelled
	   ("TODO" ("WAITING") ("CANCELLED"))
	   )
	  )
 )

Finishing tasks

To make sure that tasks with child tasks are not completed prematurely:

(setq
 ;; Ensure child dependencies complete before parents can be marked complete
 org-enforce-todo-dependencies t
 )

Archiving

We’ll want to move stuff out of our agenda tree at some stage. This sets up our archiving to go into the archive sub-directory:

(setq
 ;; Where I'm going to archive stuff
 org-archive-location "archive/%s_archive::"
 )

;; How archive files will appear
(defvar org-archive-file-header-format "#+FILETAGS: ARCHIVE\nArchived entries from file %s\n")

Effort

When setting up a task, you can add effort estimates for billing/budgeting, and tracking how you’re going with your tasks. Apparently the best way to set effort is by using column view. Next I define the columns to display (and their widths), and also provide default effort values.

Related to this is how tasks are clocked. If I clock-in and clock-out immediately (such as when capturing an email), I shouldn’t record that clock.

(setq
 ;; Set column view headings
 org-columns-default-format "%50ITEM(Task) %10Effort(Effort){:} %10CLOCKSUM"
 ;; Set default effort values
 org-global-properties (quote (("Effort_ALL" . "0:15 0:30 1:00 2:00 4:00 6:00 8:00 16:00")))
 ;; When there's 0 time spent, remove the entry
 org-clock-out-remove-zero-time-clocks t
 )

Bling

I wanted some bling! I added the all-the-icons requirement above, now the next block of code sets some bling in the agenda:

 (setq org-agenda-category-icon-alist
	`(("TODO" (list (all-the-icons-faicon "tasks")) nil nil :ascent center)))
 ;; (setq
  ;; Add fancy icons to the agenda...
  ;; org-agenda-category-icon-alist
  ;; '(
  ;;   (("TODO" (#("" 0 1 (font-lock-ignore t rear-nonsticky t display (raise -0.24) face (:family "FontAwesome" :height 1.2)))) nil nil :ascent center))
  ;;   ;; (`(("MEETING" ,(list (all-the-icons-faicon "tasks")) nil nil :ascent center)))
  ;;   )
  ;; )

Habits

Habits allows some recurring tasks to reappear when marked done. See here for some good info. To enable habits, you need to load the org-habits module into org, which is achieved with the following:

(add-to-list 'org-modules 'org-habit t)

Agenda View

The default agenda lacks a little oomph. What I’d like to see is collections such as:

  • tasks for today
  • tasks to be refiled
  • tasks for next week
  • tasks that are unscheduled
  • tasks that are waiting/someday

This next bit of setup uses ~org-super-agenda~ to set up a nicely grouped agenda:

(setq org-agenda-custom-commands
      '(("a" "Super Agenda"
         ((agenda "" ((org-agenda-span 'day)
                      (org-agenda-overriding-header "Today's Items")
                      (org-agenda-skip-scheduled-if-deadline-is-shown t)
                      (org-super-agenda-groups
                       '((:discard (:todo ("DONE" "CANCELLED")))
                         (:discard (:tag "MEETING"))
                         (:name "Today"
                                :time-grid t
                                :todo "TODAY"
                                :scheduled today
                                :order 0)
                         (:habit t)
                         (:name "Due Today"
                                :deadline today
                                :order 2)
                         (:name "Due Soon"
                                :deadline future
                                :order 8)
                         (:name "Overdue"
                                :deadline past
                                :order 7)
                         ))))
          (todo "" ((org-agenda-overriding-header "All Other TODOs")
                    (org-super-agenda-groups
                     '((:discard (:category ("Issues" "Refile" "Waiting")))
                       (:discard (:todo ("SOMEDAY")))
                       (:and (:deadline nil :scheduled nil))
                       (:discard (:scheduled t))
                       (:auto-category t :order 9)
                       ))))
          (todo "" ((org-agenda-overriding-header "Waiting for")
                    (org-super-agenda-groups
                     '((:category "Waiting")
                       (:discard (:anything t))
                       ))))
          (todo "" ((org-agenda-overriding-header "Issues")
                  (org-super-agenda-groups
                   '((:category "Issues")
                     (:discard (:anything t))
                     ))))))
        ("r" "Daily review"
         (
          (todo "" ((org-agenda-overriding-header "Daily review")
                    (org-super-agenda-groups
                     '((:category "Refile")
                       (:discard (:anything t))
                       ))))))
        ("i" "Issues Tracking"
         (
          (todo "" ((org-agenda-overriding-header "Issues tracking")
                    (org-super-agenda-groups
                     '((:category "Issues")
                       (:discard (:anything t))
                       ))))))
        ("w" "Waiting for"
         (
          (todo "" ((org-agenda-overriding-header "Waiting for")
                    (org-super-agenda-groups
                     '((:category "Waiting")
                       (:discard (:anything t))
                       ))))))
        ("s" "Someday"
         (
          (todo "" ((org-agenda-overriding-header "Someday")
                    (org-super-agenda-groups
                     '(
                       (:name "Someday"
                              :todo "SOMEDAY")
                       (:discard (:anything t))
                       )
                     )))))
        ("d" "Done in the last week" tags "+TODO=\"DONE\"+CLOSED>=\"<-6d>\"")
        )
      )

(org-super-agenda-mode);; Custom agenda views

The last line only shows one copy of the task—without it, both the scheduled, and the deadline task will show up.

2019-01-14: I’ve now added a new ‘task’ view in the custom agenda above. This is to list all projects that I have a hand in, just to keep me on track. I’ve added a new keyword ‘PROJECT’ to do this, which can be marked as DONE once the whole project is completed or cancelled.

Finally close off the with=eval-after-load from earlier:

Searching through your notes

To make it a little easier to search, use ripgrep. It replaces the current org-search-view which is not helpful (imo).

(defun sprazza/org-search ()
  "Search in the org directory with `rg'."
  (interactive)
  (spacemacs/compleseus-search nil (expand-file-name org-directory))
  )

(progn
  (spacemacs/set-leader-keys
    "aos" 'sprazza/org-search
    )

  ;; Is not as helpful in major mode, but can be done...
  ;; (spacemacs/set-leader-keys-for-major-mode 'org-mode
  ;;   "q" 'sprazza/org-search
  ;;   )
  )