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:
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:
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.
`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.
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.
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:
(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"))))
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.