Posts Tagged ‘org-mode’

Org-mode

Wednesday, October 28th, 2009

I recently started to use org-mode, a emacs mode used for scheduling and organizing your tasks. I’ve really started to like it; I now do all my scheduling using it, instead of my previous Google Calendar.

Org-mode files are essentially trees, with * as the separator. An example is:

* Heading 1
** Subheading
some notes
more notes
** Subheading
* Heading 2

You can easily cycle the visibility using TAB. This is it’s basic note-taking mode; if you go far enough in, it’s quite complicated. For example, tasks can be scheduled, occur at a time, or have a deadline. You can insert deadlines for the current task with C-c C-d, schedule a task to begin with C-c C-s, or just add a timestamp to an item manually. Any of these can be recurring. These will show up on your agenda, which I’ll talk about shortly. An example with all of these types of timestamps is:

* Heading 1
** Subheading
<2009-10-19 Mon +1w>
some notes
more notes
** Subheading
TODO: <2009-10-21 Wed>
** Subheading 3
SCHEDULED: <2009-10-23 Fri>
* Heading 2

For a more detailed explanation of times with org-mode, read this.

Org-mode also has an ‘agenda’ view, which takes all the org files you’ve specified as agenda files and displays a timeline of them. This is one of my most used features; It allows you to easily see your schedule for a given day, see the list of items you have to do, and in general acts as a calendar. You can customize the various views; I haven’t done much of this, but you should read the org-mode manual in order to learn about this and other uses of org-mode.

To get started with org mode, you should require it:

(require 'org)

I set a few keybindings in order to make org easier to use. I remap left and right to raise the tree one level and indent the tree one level respectively. I map RETURN to add a new item; If i want a linebreak, I can still use C-j. I needed to use <, and it was bound to something I didn't care about, so I bound it to self-insert-command. Org-agenda switches to agenda mode, and I wanted it to be easily accessed.

(define-key org-mode-map (kbd "RET") 'org-meta-return)
(define-key org-mode-map (kbd "<left>") 'org-metaleft)
(define-key org-mode-map (kbd "<right>") 'org-metaright)
(define-key org-mode-map (kbd "<" ) 'self-insert-command)
(global-set-key "\C-ca" 'org-agenda)

There’s a few customizations I add to org to make it a smoother experience. The first thing is to turn off flyspell-mode. This is unfortunate; it could be useful, but as-is it just interferes with org-mode’s highlighting. I also set org-mode to log the time when I mark items as done. I set the list of my agenda files, and the amount of time in advance to start warning me of deadlines as 10 days. The last one is to set pabbrev to not activate; it interferes with org’s expanding and hiding of trees.

(add-hook 'org-mode-hook #'(lambda () (flyspell-mode -1)))
(setq org-log-done 'time)
(setq org-agenda-files '("~/org/technical.org" "~/org/designproject.org" "~/org/personal.org" "~/org/jobs.org" "~/org/martialarts.org" "~/org/emacs.org" "~/org/school.org" "~/org/financial.org"))
(setq org-deadline-warning-days 10)
 
(defadvice pabbrev-global-mode (around org-stop-pabbrev activate)
  (unless (eq major-mode 'org-mode)
    ad-do-it))

These functions are utilities used in publishing my agenda buffer. They work to generate a string describing the section number of the current bullet in an org buffer. These are required because exporting an org file to HTML will use these values as section numbers - to hyperlink to them in the agenda buffer, we will need to know this. There may already be functions to perform this, but I couldn’t find them.

    )))
 
(defun org-full-sections (&optional pos)
  "Returns a list coresponding to the full section number at pos"
  (interactive)
  (save-excursion
    (if pos (goto-char pos))
    (let* ((retn 1)
           (curnum (org-current-section-number))
           (retlist (list curnum)))
      (condition-case nil
          (while t
            (progn
              (outline-up-heading 1)
              (setq retlist (append (list (org-current-section-number)) retlist))))
        (error retlist))
      retlist)))
 
(defun org-full-sections-string (&optional pos)
  "Returns a string corresponding to the section at pos"
  (interactive)
  (substring (reduce (lambda (x y) (concat x "." (number-to-string y)))
                     (org-full-sections)
                     :initial-value "") 1))

Sometimes, I’m not on my primary computer, which has all of my agenda-files, and still want to be able to look at my schedule. To do this, the logical way would be to store my agenda as a HTML file that I can access from anywhere. Org has a few ways to publish to HTML, but I could not find how to export the agenda buffer and files, hyperlinked so that it emulates the actual agenda. I ended up writing a rather ugly method to do it myself; the results are below.

(defun org-publish-agenda ()
  (interactive)
  (save-window-excursion
    (mapcar (lambda (file)
              (find-file file)
              (org-export-as-html 3)
              (kill-buffer))
            org-agenda-files)
    (org-agenda 0 "a")
    (org-agenda-month-view)
    (let ((html-buffer (htmlize-buffer (get-buffer "*Org Agenda*")))
          (agenda-buffer (get-buffer "*Org Agenda*")))
      (switch-to-buffer html-buffer)
      (goto-char (point-min))
      (search-forward "<body>")
      (let ((line-start (line-number-at-pos)))
        (while (< (point) (point-max))
          (beginning-of-line)
          (cond
           ((line-matches "org-agenda-structure") (forward-line))
           ((line-matches "org-agenda-dat") (forward-line))
           ((line-matches "org-time-grid") (forward-line))
           ((line-matches " *\\([^:]+\\):")
            (let ((calendar (after-last " "(match-string 1))))
              (let ((agenda-line-no (1- (- (line-number-at-pos) line-start))))
                (switch-to-buffer agenda-buffer)
                (goto-line agenda-line-no)
                (let* ((marker (or (get-text-property (point) 'org-marker)
                                   (org-agenda-error)))
                       (buffer (marker-buffer marker))
                       (pos (marker-position marker)))
                  (switch-to-buffer buffer)
                  (goto-char pos)
                  (setq sec-string (concat "sec-" (org-full-sections-string)))
                  (switch-to-buffer html-buffer)
                  ))
              (insert (concat "<a href=\"" calendar ".html#" sec-string "\">"))
              (end-of-line)
              (insert "</a>")
              (forward-line)))
           (t (forward-line))
           )))
      (write-file "~/org/agenda.html")
      (let ((default-directory "~/org/"))
        (shell-command "mv *.html publish/")
        (kill-buffer "agenda.html")
        ))))
 
(defun org-publish-agenda-to-site ()
  (interactive)
  (save-window-excursion
    (org-publish-agenda)
    (shell-command "scp -r ~/org/publish nflath.com:/home/nflath/public_html/nflath.com/ &")))
 
(setq agenda-update-timer (run-with-timer 0 3600 'org-publish-agenda-to-site))

These functions require org-publish-location to be defined to the value you wish the HTML to be stored. This isn’t the best code; it assumes that your agenda files are in ~/org, and that the directory ~/org/publish exists, but this should be easy to fix if this is not where you store your agenda files.

org-publish-agenda works by modifying the results of calling htmlize-buffer on the org-agenda; htmlize.el is a package that can turn arbitrary buffers into HTML. It is sometimes useful when you want to export your view of a file, such as in this case. To use it, you have to put the following in your initialization file:

(require 'htmlize)