* [RFC] DOCT: Declarative Org Capture Templates

I've been working on an alternative syntax for org-capture-templates. The result is a package called "DOCT" (declarative org capture templates):

https://github.com/progfolio/doct

A brief list of what I believe to be improvements over the current syntax/implementation:

- DOCT validates templates before runtime execution.

For exmaple, you have a template with an entry type of `'entry' and you forget the leading star in the template string.
Days later you go to use that template. It's borked.
You have a number of options:
- forget about whatever you wanted to capture and press on with your original task - manually take a note about what you originally wanted to capture and another note to fix the broken template
- drop what you're doing and fix everything
None of these are ideal and all of them result in distraction.
DOCT performs a number of checks ahead of time when possible to prevent these types of errors.

- DOCT makes the parent/child relationship between templates explicit.

`org-capture-templates` is a flat list. The relationship between templates is hardcoded in each template's "keys" value. If you want to change the key for a top-level menu, you must then change it in each descendant's keys. DOCT uses nested plists
and implements property inheritance.

- DOCT manages per-template hooks/contexts.

Currently if you want to have a hook run for a particular template, you have to filter against `org-capture-plist' to check for the appropriate :key value. As stated above, this is fragile and you have to update that key value in numerous places if it ever changes. The same goes for `org-capture-templates-contexts`. DOCT allows specifying per-template contexts and hooks with the rest of the template's configuration.

- DOCT makes adding custom metadata to templates easy.

A common pattern for attaching data to a template is to add to `org-capture-plist'. This pollutes `org-capture-plist' more than necessary. DOCT adds custom data to `org-capture-plist' under a single :doct keyword and allows users to access that data in the template string with familiar %-escape syntax.

This example is one I use daily for taking notes in programming projects:

#+begin_src emacs-lisp
(doct
`("Note"
  :keys "n"
  :file ,(defun my/project-note-file ()
(let ((file (expand-file-name "notes.org" (when (functionp 'projectile-project-root)
                                                       
(projectile-project-root)))))
             (with-current-buffer (find-file-noselect file)
               (org-mode)
               ;;set to utf-8 because we may be visiting raw file
               (setq buffer-file-coding-system 'utf-8-unix)
               (when-let ((headline (doct-get :headline)))
(unless (org-find-exact-headline-in-buffer headline)
                   (goto-char (point-max))
                   (insert "* " headline)
                   (org-set-tags (downcase headline))))
               (unless (file-exists-p file) (write-file file))
               file)))
:template (lambda () (concat "* %{todo-state} " (when (y-or-n-p "link?") "%A\n") "%?"))
  :todo-state "TODO"
  :children (("bug"           :keys "b" :headline "Bugs")
("documentation" :keys "d" :headline "Documentation") ("enhancement" :keys "e" :headline "Enhancements" :todo-state "IDEA") ("feature" :keys "f" :headline "Features" :todo-state "IDEA") ("optimization" :keys "o" :headline "Optimizations") ("miscellaneous" :keys "m" :headline "Miscellaneous")
             ("security"      :keys "s" :headline "Security"))))
#+end_src

Each template inherits its parent's file finding function,template string, and a default :todo-state. The template string access the child's :todo-state keyword with the "%{KEYWORD}" syntax in the template string.

I would be happy to work on getting these features into Org if there is any interest.
Any feedback is welcome.

Thanks, nv.

Reply via email to