1 #+OPTIONS: H:3 num:nil toc:t \n:nil @:t ::t |:t ^:t -:t f:t *:t TeX:t LaTeX:t skip:nil d:(HIDE) tags:not-in-toc
2 #+STARTUP: align fold nodlcheck hidestars oddeven lognotestate
3 #+SEQ_TODO: TODO(t) INPROGRESS(i) WAITING(w@) | DONE(d) CANCELED(c@)
4 #+TAGS: Write(w) Update(u) Fix(f) Check(c)
5 #+TITLE: Org ad hoc code, quick hacks and workarounds
7 #+EMAIL: bzg AT altern DOT org
12 # This file is the default header for new Org files in Worg. Feel free
13 # to tailor it to your needs.
15 [[file:index.org][{Back to Worg's index}]]
17 This page is for ad hoc bits of code. Feel free to add quick hacks and
20 * Automatically add an appointment when clocking in a task
22 #+BEGIN_SRC emacs-lisp
23 ;; Make sure you have a sensible value for `appt-message-warning-time'
24 (defvar bzg-org-clock-in-appt-delay 100
25 "Number of minutes for setting an appointment by clocking-in")
28 This function let's you add an appointment for the current entry.
29 This can be useful when you need a reminder.
31 #+BEGIN_SRC emacs-lisp
32 (defun bzg-org-clock-in-add-appt (&optional n)
33 "Add an appointment for the Org entry at point in N minutes."
36 (org-back-to-heading t)
37 (looking-at org-complex-heading-regexp)
38 (let* ((msg (match-string-no-properties 4))
39 (ct-time (decode-time))
40 (appt-min (+ (cadr ct-time)
41 (or n bzg-org-clock-in-appt-delay)))
42 (appt-time ; define the time for the appointment
43 (progn (setf (cadr ct-time) appt-min) ct-time)))
44 (appt-add (format-time-string
45 "%H:%M" (apply 'encode-time appt-time)) msg)
46 (if (interactive-p) (message "New appointment for %s" msg)))))
49 You can advise =org-clock-in= so that =C-c C-x C-i= will automatically
52 #+BEGIN_SRC emacs-lisp
53 (defadvice org-clock-in (after org-clock-in-add-appt activate)
54 "Add an appointment when clocking a task in."
55 (bzg-org-clock-in-add-appt))
58 You may also want to delete the associated appointment when clocking
59 out. This function does this:
61 #+BEGIN_SRC emacs-lisp
62 (defun bzg-org-clock-out-delete-appt nil
63 "When clocking out, delete any associated appointment."
66 (org-back-to-heading t)
67 (looking-at org-complex-heading-regexp)
68 (let* ((msg (match-string-no-properties 4)))
69 (setq appt-time-msg-list
73 (if (not (string-match (regexp-quote msg)
79 And here is the advice for =org-clock-out= (=C-c C-x C-o=)
81 #+BEGIN_SRC emacs-lisp
82 (defadvice org-clock-out (before org-clock-out-delete-appt activate)
83 "Delete an appointment when clocking a task out."
84 (bzg-org-clock-out-delete-appt))
87 *IMPORTANT*: You can add appointment by clocking in in both an
88 =org-mode= and an =org-agenda-mode= buffer. But clocking out from
89 agenda buffer with the advice above will bring an error.
91 * Use Org-mode with Screen [Andrew Hyatt]
93 "The general idea is that you start a task in which all the work will
94 take place in a shell. This usually is not a leaf-task for me, but
95 usually the parent of a leaf task. From a task in your org-file, M-x
96 ash-org-screen will prompt for the name of a session. Give it a name,
97 and it will insert a link. Open the link at any time to go the screen
98 session containing your work!"
100 http://article.gmane.org/gmane.emacs.orgmode/5276
102 #+BEGIN_SRC emacs-lisp
105 (defun ash-org-goto-screen (name)
106 "Open the screen with the specified name in the window"
107 (interactive "MScreen name: ")
108 (let ((screen-buffer-name (ash-org-screen-buffer-name name)))
109 (if (member screen-buffer-name
110 (mapcar 'buffer-name (buffer-list)))
111 (switch-to-buffer screen-buffer-name)
112 (switch-to-buffer (ash-org-screen-helper name "-dr")))))
114 (defun ash-org-screen-buffer-name (name)
115 "Returns the buffer name corresponding to the screen name given."
116 (concat "*screen " name "*"))
118 (defun ash-org-screen-helper (name arg)
119 ;; Pick the name of the new buffer.
120 (let ((term-ansi-buffer-name
121 (generate-new-buffer-name
122 (ash-org-screen-buffer-name name))))
123 (setq term-ansi-buffer-name
125 term-ansi-buffer-name "/usr/bin/screen" nil arg name))
126 (set-buffer term-ansi-buffer-name)
129 (term-set-escape-char ?\C-x)
130 term-ansi-buffer-name))
132 (defun ash-org-screen (name)
133 "Start a screen session with name"
134 (interactive "MScreen name: ")
136 (ash-org-screen-helper name "-S"))
137 (insert-string (concat "[[screen:" name "]]")))
139 ;; And don't forget to add ("screen" . "elisp:(ash-org-goto-screen
140 ;; \"%s\")") to org-link-abbrev-alist.
143 * Org Agenda + Appt + Zenity
145 Russell Adams posted this setup [[http://article.gmane.org/gmane.emacs.orgmode/5806][on the list]]. It make sure your agenda
146 appointments are known by Emacs, and it displays warnings in a [[http://live.gnome.org/Zenity][zenity]]
149 #+BEGIN_SRC emacs-lisp
150 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
151 ; For org appointment reminders
153 ;; Get appointments for today
154 (defun my-org-agenda-to-appt ()
156 (setq appt-time-msg-list nil)
157 (let ((org-deadline-warning-days 0)) ;; will be automatic in org 5.23
158 (org-agenda-to-appt)))
160 ;; Run once, activate and schedule refresh
161 (my-org-agenda-to-appt)
163 (run-at-time "24:01" nil 'my-org-agenda-to-appt)
166 (setq appt-message-warning-time 15)
167 (setq appt-display-interval 5)
169 ; Update appt each time agenda opened.
170 (add-hook 'org-finalize-agenda-hook 'my-org-agenda-to-appt)
172 ; Setup zenify, we tell appt to use window, and replace default function
173 (setq appt-display-format 'window)
174 (setq appt-disp-window-function (function my-appt-disp-window))
176 (defun my-appt-disp-window (min-to-app new-time msg)
177 (save-window-excursion (shell-command (concat
178 "/usr/bin/zenity --info --title='Appointment' --text='"
179 msg "' &") nil nil)))
182 * Org-Mode + gnome-osd
184 Richard Riley uses gnome-osd in interaction with Org-Mode to display
185 appointments. You can look at the code on the [[http://www.emacswiki.org/emacs-en/OrgMode-OSD][emacswiki]].
191 http://article.gmane.org/gmane.emacs.orgmode/5073
194 Remind (http://www.roaringpenguin.com/products/remind) is a very powerful
195 command line calendaring program. Its features superseed the possibilities
196 of orgmode in the area of date specifying, so that I want to use it
197 combined with orgmode.
199 Using the script below I'm able use remind and incorporate its output in my
200 agenda views. The default of using 13 months look ahead is easily
201 changed. It just happens I sometimes like to look a year into the
205 * org-remember-anything
207 [[http://www.emacswiki.org/cgi-bin/wiki/Anything][Anything]] users may find the snippet below interesting:
209 #+BEGIN_SRC emacs-lisp
210 (defvar org-remember-anything
211 '((name . "Org Remember")
212 (candidates . (lambda () (mapcar 'car org-remember-templates)))
213 (action . (lambda (name)
214 (let* ((orig-template org-remember-templates)
215 (org-remember-templates
216 (list (assoc name orig-template))))
217 (call-interactively 'org-remember))))))
220 You can add it to your 'anything-sources' variable and open remember directly
221 from anything. I imagine this would be more interesting for people with many
222 remember templatesm, so that you are out of keys to assign those to. You should
223 get something like this:
225 [[file:images/thumbs/org-remember-anything.png]]
229 As of Org version 6.23b (released Sunday Feb 22, 2009) there is a new
230 function to reload org files.
232 Normally you want to use the compiled files since they are faster.
233 If you update your org files you can easily reload them with
237 If you run into a bug and want to generate a useful backtrace you can
238 reload the source files instead of the compiled files with
242 and turn on the "Enter Debugger On Error" option. Redo the action
243 that generates the error and cut and paste the resulting backtrace.
244 To switch back to the compiled version just reload again with
248 * Split horizontally for agenda
250 If you would like to split the frame into two side-by-side windows when
251 displaying the agenda, try this hack from Jan Rehders, which uses the
252 `toggle-window-split' from
254 http://www.emacswiki.org/cgi-bin/wiki/ToggleWindowSplit
256 #+BEGIN_SRC emacs-lisp
257 ;; Patch org-mode to use vertical splitting
258 (defadvice org-prepare-agenda (after org-fix-split)
259 (toggle-window-split))
260 (ad-activate 'org-prepare-agenda)
262 * Highlight the agenda line under cursor
264 This is useful to make sure what task you are operating on.
266 #+BEGIN_SRC emacs-lisp
267 (add-hook 'org-agenda-mode-hook '(lambda () (hl-line-mode 1)))
272 #+BEGIN_SRC emacs-lisp
273 ;; hl-line seems to be only for emacs
275 (add-hook 'org-agenda-mode-hook '(lambda () (highline-mode 1)))
277 ;; highline-mode does not work straightaway in tty mode.
278 ;; I use a black background
280 '(highline-face ((((type tty) (class color))
281 (:background "white" :foreground "black")))))
284 * Remove time grid lines that are in an appointment
286 The agenda shows lines for the time grid. Some people think that
287 these lines are a distraction when there are appointments at those
288 times. You can get rid of the lines which coincide exactly with the
289 beginning of an appointment. Michael Ekstrand has written a piece of
290 advice that also removes lines that are somewhere inside an
293 #+begin_src emacs-lisp
294 (defun org-time-to-minutes (time)
295 "Convert an HHMM time to minutes"
296 (+ (* (/ time 100) 60) (% time 100)))
298 (defun org-time-from-minutes (minutes)
299 "Convert a number of minutes to an HHMM time"
300 (+ (* (/ minutes 60) 100) (% minutes 60)))
302 (defadvice org-agenda-add-time-grid-maybe (around mde-org-agenda-grid-tweakify
304 (if (member 'remove-match (car org-agenda-time-grid))
305 (flet ((extract-window
307 (let ((start (get-text-property 1 'time-of-day line))
308 (dur (get-text-property 1 'duration line)))
312 (org-time-from-minutes
313 (+ dur (org-time-to-minutes start)))))
316 (let* ((windows (delq nil (mapcar 'extract-window list)))
317 (org-agenda-time-grid
318 (list (car org-agenda-time-grid)
319 (cadr org-agenda-time-grid)
325 (and (>= time (car w))
328 (caddr org-agenda-time-grid)))))
331 (ad-activate 'org-agenda-add-time-grid-maybe)
334 * Group task list by a property
336 This advice allows you to group a task list in Org-Mode. To use it,
337 set the variable =org-agenda-group-by-property= to the name of a
338 property in the option list for a TODO or TAGS search. The resulting
339 agenda view will group tasks by that property prior to searching.
341 #+begin_src emacs-lisp
342 (defvar org-agenda-group-by-property nil
343 "Set this in org-mode agenda views to group tasks by property")
345 (defun org-group-bucket-items (prop items)
348 (let* ((marker (get-text-property 0 'org-marker item))
349 (pvalue (org-entry-get marker prop t))
350 (cell (assoc pvalue buckets)))
352 (setcdr cell (cons item (cdr cell)))
353 (setq buckets (cons (cons pvalue (list item))
355 (setq buckets (mapcar (lambda (bucket)
357 (reverse (cdr bucket))))
359 (sort buckets (lambda (i1 i2)
360 (string< (car i1) (car i2))))))
362 (defadvice org-finalize-agenda-entries (around org-group-agenda-finalize
363 (list &optional nosort))
364 "Prepare bucketed agenda entry lists"
365 (if org-agenda-group-by-property
366 ;; bucketed, handle appropriately
368 (dolist (bucket (org-group-bucket-items
369 org-agenda-group-by-property
371 (let ((header (concat "Property "
372 org-agenda-group-by-property
374 (or (car bucket) "<nil>") ":\n")))
375 (add-text-properties 0 (1- (length header))
376 (list 'face 'org-agenda-structure)
380 ;; recursively process
381 (let ((org-agenda-group-by-property nil))
382 (org-finalize-agenda-entries
383 (cdr bucket) nosort))
385 (setq ad-return-value text))
387 (ad-activate 'org-finalize-agenda-entries)
389 * Link to Gnus messages by Message-Id
391 In a [[http://thread.gmane.org/gmane.emacs.orgmode/8860][recent thread]] on the Org-Mode mailing list, there was some
392 discussion about linking to Gnus messages without encoding the folder
393 name in the link. The following code hooks in to the store-link
394 function in Gnus to capture links by Message-Id when in nnml folders,
395 and then provides a link type "mid" which can open this link. The
396 =mde-org-gnus-open-message-link= function uses the
397 =mde-mid-resolve-methods= variable to determine what Gnus backends to
398 scan. It will go through them, in order, asking each to locate the
399 message and opening it from the first one that reports success.
401 It has only been tested with a single nnml backend, so there may be
402 bugs lurking here and there.
404 The logic for finding the message was adapted from [[http://www.emacswiki.org/cgi-bin/wiki/FindMailByMessageId][an Emacs Wiki
407 #+begin_src emacs-lisp
408 ;; Support for saving Gnus messages by Message-ID
409 (defun mde-org-gnus-save-by-mid ()
410 (when (memq major-mode '(gnus-summary-mode gnus-article-mode))
411 (when (eq major-mode 'gnus-article-mode)
412 (gnus-article-show-summary))
413 (let* ((group gnus-newsgroup-name)
414 (method (gnus-find-method-for-group group)))
415 (when (eq 'nnml (car method))
416 (let* ((article (gnus-summary-article-number))
417 (header (gnus-summary-article-header article))
418 (from (mail-header-from header))
421 (let ((mid (mail-header-id header)))
422 (if (string-match "<\\(.*\\)>" mid)
424 (error "Malformed message ID header %s" mid)))))
425 (date (mail-header-date header))
426 (subject (gnus-summary-subject-string)))
427 (org-store-link-props :type "mid" :from from :subject subject
428 :message-id message-id :group group
429 :link (org-make-link "mid:" message-id))
430 (apply 'org-store-link-props
431 :description (org-email-link-description)
432 org-store-link-plist)
435 (defvar mde-mid-resolve-methods '()
436 "List of methods to try when resolving message ID's. For Gnus,
437 it is a cons of 'gnus and the select (type and name).")
438 (setq mde-mid-resolve-methods
441 (defvar mde-org-gnus-open-level 1
442 "Level at which Gnus is started when opening a link")
443 (defun mde-org-gnus-open-message-link (msgid)
444 "Open a message link with Gnus"
448 (message "[MID linker] Resolving %s" msgid)
449 (dolist (method mde-mid-resolve-methods)
451 ((and (eq (car method) 'gnus)
452 (eq (cadr method) 'nnml))
453 (funcall (cdr (assq 'gnus org-link-frame-setup))
454 mde-org-gnus-open-level)
455 (when gnus-other-frame-object
456 (select-frame gnus-other-frame-object))
457 (let* ((msg-info (nnml-find-group-number
458 (concat "<" msgid ">")
460 (group (and msg-info (car msg-info)))
461 (message (and msg-info (cdr msg-info)))
463 (if (gnus-methods-equal-p
467 (gnus-group-full-name group (cdr method))))))
469 (gnus-summary-read-group qname nil t)
470 (gnus-summary-goto-article message nil t))
471 (throw 'method-found t)))
472 (t (error "Unknown link type"))))))
474 (eval-after-load 'org-gnus
476 (add-to-list 'org-store-link-functions 'mde-org-gnus-save-by-mid)
477 (org-add-link-type "mid" 'mde-org-gnus-open-message-link)))
479 * Dynamically adjust tag position
480 Here is a bit of code that allows you to have the tags always
481 right-adjusted in the buffer.
483 This is useful when you have bigger window than default window-size
484 and you dislike the aesthetics of having the tag in the middle of the
487 This hack solves the problem of adjusting it whenever you change the
489 Before saving it will revert the file to having the tag position be
490 left-adjusted so that if you track your files with version control,
491 you won't run into artificial diffs just because the window-size
494 *IMPORTANT*: This is probably slow on very big files.
496 #+begin_src emacs-lisp
497 (setq ba/org-adjust-tags-column t)
499 (defun ba/org-adjust-tags-column-reset-tags ()
500 "In org-mode buffers it will reset tag position according to
503 (not (string= (buffer-name) "*Remember*"))
504 (eql major-mode 'org-mode))
505 (let ((b-m-p (buffer-modified-p)))
508 (goto-char (point-min))
509 (command-execute 'outline-next-visible-heading)
510 ;; disable (message) that org-set-tags generates
511 (flet ((message (&rest ignored) nil))
513 (set-buffer-modified-p b-m-p))
516 (defun ba/org-adjust-tags-column-now ()
517 "Right-adjust `org-tags-column' value, then reset tag position."
518 (set (make-local-variable 'org-tags-column)
519 (- (- (window-width) 3)))
520 (ba/org-adjust-tags-column-reset-tags))
522 (defun ba/org-adjust-tags-column-maybe ()
523 "If `ba/org-adjust-tags-column' is set to non-nil, adjust tags."
524 (when ba/org-adjust-tags-column
525 (ba/org-adjust-tags-column-now)))
527 (defun ba/org-adjust-tags-column-before-save ()
528 "Tags need to be left-adjusted when saving."
529 (when ba/org-adjust-tags-column
530 (setq org-tags-column 1)
531 (ba/org-adjust-tags-column-reset-tags)))
533 (defun ba/org-adjust-tags-column-after-save ()
534 "Revert left-adjusted tag position done by before-save hook."
535 (ba/org-adjust-tags-column-maybe)
536 (set-buffer-modified-p nil))
538 ; automatically align tags on right-hand side
539 (add-hook 'window-configuration-change-hook
540 'ba/org-adjust-tags-column-maybe)
541 (add-hook 'before-save-hook 'ba/org-adjust-tags-column-before-save)
542 (add-hook 'after-save-hook 'ba/org-adjust-tags-column-after-save)
544 * Compiling Org without make
546 :CUSTOM_ID: compiling-org-without-make
549 This file is the result of [[http://article.gmane.org/gmane.emacs.orgmode/15264][one of our discussions]] on the mailing list.
550 Enhancements wellcome.
552 To use this function, adjust the variables =my/org-lisp-directory= and
553 =my/org-compile-sources= to suite your needs.
555 #+BEGIN_SRC emacs-lisp
556 (defvar my/org-lisp-directory "~/.emacs.d/org/lisp"
557 "Directory where your org-mode files live.")
559 (defvar my/org-compile-sources t
560 "If `nil', never compile org-sources. `my/compile-org' will only create
561 the autoloads file `org-install.el' then. If `t', compile the sources, too.")
564 (setq my/org-lisp-directory "~/.emacs.d/org/lisp")
567 (setq my/org-compile-sources t)
569 (defun my/compile-org(&optional directory)
570 "Compile all *.el files that come with org-mode."
572 (setq directory (concat
574 (or directory my/org-lisp-directory)) "/"))
576 (add-to-list 'load-path directory)
578 (let ((list-of-org-files (file-expand-wildcards (concat directory "*.el"))))
580 ;; create the org-install file
582 (setq esf/org-install-file (concat directory "org-install.el"))
583 (find-file esf/org-install-file)
586 (generate-file-autoloads x))
588 (insert "\n(provide (quote org-install))\n")
591 (byte-compile-file esf/org-install-file t)
593 (dolist (f list-of-org-files)
594 (if (file-exists-p (concat f "c")) ; delete compiled files
595 (delete-file (concat f "c")))
596 (if my/org-compile-sources ; Compile, if `my/org-compile-sources' is t
597 (byte-compile-file f)))))
599 * Customize the size of the frame for remember
602 On emacs-orgmode, Ryan C. Thompson suggested this:
605 I am using org-remember set to open a new frame when used,
606 and the default frame size is much too large. To fix this, I have
607 designed some advice and a custom variable to implement custom
608 parameters for the remember frame:
611 #+begin_src emacs-lisp
612 (defcustom remember-frame-alist nil
613 "Additional frame parameters for dedicated remember frame."
617 (defadvice remember (around remember-frame-parameters activate)
618 "Set some frame parameters for the remember frame."
619 (let ((default-frame-alist (append remember-frame-alist
620 default-frame-alist)))
624 Setting remember-frame-alist to =((width . 80) (height . 15)))= give a
625 reasonable size for the frame.
628 * Add an effort estimate on the fly when clocking in
630 You can use =org-clock-in-prepare-hook= to add an effort estimate.
631 This way you can easily have a "tea-timer" for your tasks when they
632 don't already have an effort estimate.
634 #+begin_src emacs-lisp
635 (add-hook 'org-clock-in-prepare-hook
636 'my-org-mode-ask-effort)
638 (defun my-org-mode-ask-effort ()
639 "Ask for an effort estimate when clocking in."
640 (unless (org-entry-get (point) "Effort")
644 (org-entry-get-multivalued-property (point) "Effort"))))
645 (unless (equal effort "")
646 (org-set-property "Effort" effort)))))
649 Or you can use a default effort for such a timer:
651 #+begin_src emacs-lisp
652 (add-hook 'org-clock-in-prepare-hook
653 'my-org-mode-add-default-effort)
655 (defvar org-clock-default-effort "1:00")
657 (defun my-org-mode-add-default-effort ()
658 "Add a default effort estimation."
659 (unless (org-entry-get (point) "Effort")
660 (org-set-property "Effort" org-clock-default-effort)))
662 * Dates computation in an Org table
664 ** Question ([[http://article.gmane.org/gmane.emacs.orgmode/15692][Xin Shi]])
666 I have a table in org which stores the date, I'm wondering if there is
667 any function to calculate the duration? For example:
669 | Start Date | End Date | Duration |
670 |------------+------------+----------|
671 | 2004.08.07 | 2005.07.08 | |
673 I tried to use B&-C&, but failed ...
675 ** Answer ([[http://article.gmane.org/gmane.emacs.orgmode/15694][Nick Dokos]])
679 | Start Date | End Date | Duration |
680 |------------+------------+----------|
681 | 2004.08.07 | 2005.07.08 | 335 |
682 :#+TBLFM: $3=(date(<$2>)-date(<$1>))
686 http://thread.gmane.org/gmane.emacs.orgmode/7741
688 as well as this post (which is really a followup on the
691 http://article.gmane.org/gmane.emacs.orgmode/7753
693 The problem that this last article pointed out was solved
696 http://article.gmane.org/gmane.emacs.orgmode/8001
698 and Chris Randle's original musings are at
700 http://article.gmane.org/gmane.emacs.orgmode/6536/
701 * Org-mode and saveplace.el
703 Fix a problem with saveplace.el putting you back in a folded position:
705 #+begin_src emacs-lisp
706 (add-hook 'org-mode-hook
708 (when (outline-invisible-p)
710 (outline-previous-visible-heading 1)
711 (org-show-subtree)))))
713 * Easy customization of TODO colors
716 Here is some code I came up with some code to make it easier to
717 customize the colors of various TODO keywords. As long as you just
718 want a different color and nothing else, you can customize the
719 variable org-todo-keyword-faces and use just a string color (i.e. a
720 string of the color name) as the face, and then org-get-todo-face
721 will convert the color to a face, inheriting everything else from
722 the standard org-todo face.
724 To demonstrate, I currently have org-todo-keyword-faces set to
726 #+BEGIN_SRC emacs-lisp
727 (("IN PROGRESS" . "dark orange")
729 ("CANCELED" . "saddle brown"))
732 Here's the code, in a form you can put in your =.emacs=
734 #+BEGIN_SRC emacs-lisp
735 (eval-after-load 'org-faces
737 (defcustom org-todo-keyword-faces nil
738 "Faces for specific TODO keywords.
739 This is a list of cons cells, with TODO keywords in the car and
740 faces in the cdr. The face can be a symbol, a color, or a
741 property list of attributes, like (:foreground \"blue\" :weight
747 (string :tag "Keyword")
748 (choice color (sexp :tag "Face")))))))
750 (eval-after-load 'org
752 (defun org-get-todo-face-from-color (color)
753 "Returns a specification for a face that inherits from org-todo
754 face and has the given color as foreground. Returns nil if
757 `(:inherit org-warning :foreground ,color)))
759 (defun org-get-todo-face (kwd)
760 "Get the right face for a TODO keyword KWD.
761 If KWD is a number, get the corresponding match group."
762 (if (numberp kwd) (setq kwd (match-string kwd)))
763 (or (let ((face (cdr (assoc kwd org-todo-keyword-faces))))
765 (org-get-todo-face-from-color face)
767 (and (member kwd org-done-keywords) 'org-done)