Reorder TODO items in your org-mode agenda

I use org-mode to manage my to-do list with priorities and deadlines but inevitably I have multiple items without a specific deadline or scheduled date and that have the same priority. These appear in my agenda in the order in which they were added to my to-do list, but I’ll sometimes want to change that order. This can be done temporarily using M-UP or M-DOWN in the agenda view, but these changes are lost when the agenda is refreshed.

I came up with a two-part solution to this. The main part is a generic function to move the subtree at the current point to be the top item of all subtrees of the same level. Here is the function:

(defun bjm/org-headline-to-top ()
  "Move the current org headline to the top of its section"
  ;; check if we are at the top level
  (let ((lvl (org-current-level)))
     ;; above all headlines so nothing to do
     ((not lvl)
      (message "No headline to move"))
     ((= lvl 1)
      ;; if at top level move current tree to go above first headline
      ;; test if point is now at the first headline and if not then
      ;; move to the first headline
      (unless (looking-at-p "*")
        (org-next-visible-heading 1))
     ((> lvl 1)
      ;; if not at top level then get position of headline level above
      ;; current section and refile to that position. Inspired by
      (let* ((org-reverse-note-order t)
             (pos (save-excursion
                    (outline-up-heading 1)
             (filename (buffer-file-name))
             (rfloc (list nil filename nil pos)))
        (org-refile nil nil rfloc))))))

This will move any to-do item to the top of all of the items at the same level as that item. This is equivalent to putting the cursor on the headline you want to move and hitting M-UP until you reach the top of the section.

Now I want to be able to run this from the agenda-view, which is accomplished with the following function, which I then bind to the key 1 in the agenda view.

(defun bjm/org-agenda-item-to-top ()
    "Move the current agenda item to the top of the subtree in its file"
  ;; save buffers to preserve agenda
  ;; switch to buffer for current agenda item
  ;; move item to top
  ;; go back to agenda view
  (switch-to-buffer (other-buffer (current-buffer) 1))
  ;; refresh agenda

  ;; bind to key 1
  (define-key org-agenda-mode-map (kbd "1") 'bjm/org-agenda-item-to-top)

Now in my agenda view, I just hit 1 on a particular item and it is moved permanently to the top of its level (with deadlines and priorities still taking precedence in the final sorting order).


Prevent comments from breaking paragraphs in org-mode latex export

In an org-mode document, comments like this:

Some text forming a paragraph
# with some lines
# commented out
but I still want this to be a single paragraph.

are exported in latex like this:

Some text forming a paragraph

but I still want this to be a single paragraph.

which leads to a paragraph break between the two lines. This is the intended behaviour of the exporter, but I want it to export like this:

Some text forming a paragraph
but I still want this to be a single paragraph.

This was raised on stackexchange, and the mighty John Kitchin provided a quick solution with the following simple function to strip comments from the org file, which we then add to the org export hook:

;; remove comments from org document for use with export hook
(defun delete-org-comments (backend)
  (loop for comment in (reverse (org-element-map (org-element-parse-buffer)
                    'comment 'identity))
    (setf (buffer-substring (org-element-property :begin comment)
                (org-element-property :end comment))

;; add to export hook
(add-hook 'org-export-before-processing-hook 'delete-org-comments)

and then when we export an org-mode file, the comments are stripped out on-the-fly giving the desired result. The original org-mode file is not modified – the comments stay in place.

Insert internal org-mode links the ivy way

Org-mode is a fantastic way to organise information in simple text files. I often use internal links to other sections in a document for navigation, but I’ve found that there is not a great mechanism to quickly insert an internal link. I wanted an interface that would provide me a list of headlines in the document that I could use to select a link target, so I put together the functions below.

These simple functions leverage the fantastic ivy and worf packages, so install them first. Then put the code below into your emacs config file. Then, invoking M-x bjm/worf-insert-internal-link provides an ivy completion interface for the list of headlines in the document. Once you select the headline you want as the link target, the link is inserted for you.

;; use ivy to insert a link to a heading in the current document
;; based on `worf-goto`
(defun bjm/worf-insert-internal-link ()
  "Use ivy to insert a link to a heading in the current `org-mode' document. Code is based on `worf-goto'."
  (let ((cands (worf--goto-candidates)))
    (ivy-read "Heading: " cands
              :action 'bjm/worf-insert-internal-link-action)))

(defun bjm/worf-insert-internal-link-action (x)
  "Insert link for `bjm/worf-insert-internal-link'"
  ;; go to heading
    (goto-char (cdr x))
    ;; store link
    (call-interactively 'org-store-link)
  ;; return to original point and insert link
  (org-insert-last-stored-link 1)
  ;; org-insert-last-stored-link adds a newline so delete this
  (delete-backward-char 1)

A shorter shortcut to capture todo tasks

Org mode is wonderful for managing todo lists. Usually to add a task, I would run org-capture using the default C-c c and then select my todo template by hitting t. I do this so often that I wanted a shorter shortcut, so I defined a function to call org-capture with the specific template I wanted and bound this to a simple shortcut (I used C-9 after removing numeric prefixes):

;; function to capture a todo
(defun bjm/org-capture-todo ()
  "Capture a TODO item"
  (org-capture nil "t"))

;; bind
(define-key global-map (kbd "C-9") 'bjm/org-capture-todo)

Now C-9 takes me straight to my todo capture template.

Export org-mode headlines to separate files

I was recently managing a set of interviews and I had my notes on all of the candidates in a single org file, with each candidate under their own top-level headline:

* J Kepler
 - Nice work on orbits

* I Newton
 - Versatile
 - Hard to work with

* C Sagan
 - Good communication skills

However, I wanted to generate a separate pdf file for each candidate that I could circulate to interviewers (since each interviewer was only interviewing a subset of applicants).

I came across this stackexchange answer that demonstrated how to build a function to export top level headlines to separate files. There are a few variations on that page, and I put together the slightly tweaked version below. All of the credit goes to stackexchange user itsjeyd for a very detailed answer. In my version I hard code it to export to pdf, save the file first, and apply the export options from the parent file to each of the new files that are created. The new files have a name taken from the headline, with spaces replaced by underscores, unless the :EXPORT_FILE_NAME: property is set for a headline.

;; export headlines to separate files
(defun org-export-headlines-to-pdf ()
  "Export all subtrees that are *not* tagged with :noexport: to
separate files.

Subtrees that do not have the :EXPORT_FILE_NAME: property set
are exported to a filename derived from the headline text."
  (let ((modifiedp (buffer-modified-p)))
      (goto-char (point-min))
      (goto-char (re-search-forward "^*"))
      (set-mark (line-beginning-position))
      (goto-char (point-max))
       (lambda ()
         (let ((export-file (org-entry-get (point) "EXPORT_FILE_NAME")))
           (unless export-file
              (replace-regexp-in-string " " "_" (nth 4 (org-heading-components)))))
           (org-latex-export-to-pdf nil t)
           (unless export-file (org-delete-property "EXPORT_FILE_NAME"))
           (set-buffer-modified-p modifiedp)))
       "-noexport" 'region-start-level))))

A shortcut to my favourite org-mode agenda view

As I have mentioned before in my tutorial about todo lists and scheduled tasks in org-mode, my preferred way to view my agenda is to use C-c a n to view a list of my scheduled tasks with unscheduled tasks below that.

I wanted to make a shorter keybinding for this view and I found this advice on how to achieve this. We just need to define a simple helper function like so

;;keybinding for favourite agenda view
(defun org-agenda-show-agenda-and-todo (&optional arg)
  (interactive "P")
  (org-agenda arg "n"))

I then add this to my personal key map

(define-key bjm-map (kbd "a") 'org-agenda-show-agenda-and-todo)

and I can pull up my agenda with C-1 a. You might not want to use the same keybinding as me, but maybe you’ll find the idea helpful.

Sorting an org-mode table

You can quickly sort tables in org-mode by using C-c ^ with the point inside a table. You’ll be prompted for a sorting type, where you can choose e.g. a for alphabetic or n for numeric. You can use capital letter versions of these options to reverse the sort.

The table is sorted based on the column that the point is in, and from the documentation:

The range of lines is the range between the nearest horizontal separator lines, or the entire table of no such lines exist.

Use org-mode tables and structures in emails and elsewhere

I love the way that org-mode allows you to add simple clean structures to your text, with lists and tables. You can get some of that functionality in other modes by using orgstruct-mode and orgtbl-mode, which are part of org-mode.

Enable these minor modes in any major mode for one-off use with M-x orgstruct++-mode or M-x orgtbl-mode and you can use the normal org-mode commands to create lists and tables. I find this especially useful in emails, so I use this code in my emacs config file to automatically enable these for message-mode

;; use org structures and tables in message mode
(add-hook 'message-mode-hook 'turn-on-orgtbl)
(add-hook 'message-mode-hook 'turn-on-orgstruct++)