[breaking] update org-roam capture-template syntax

This commit is contained in:
Jethro Kuan
2021-04-09 15:54:26 +08:00
parent 39bb939dc2
commit 7a6b885b99
4 changed files with 65 additions and 233 deletions

View File

@ -47,157 +47,35 @@
This variable is populated dynamically, and is only non-nil
during the Org-roam capture process.")
(defvar org-roam-capture-additional-template-props nil
"Additional props to be added to the Org-roam template.")
(defconst org-roam-capture--template-keywords '(:file-name :head :olp)
(defconst org-roam-capture--template-keywords '(:file-path :head :olp)
"Keywords used in `org-roam-capture-templates' specific to Org-roam.")
(defcustom org-roam-capture-templates
`(("d" "default" plain (function org-roam-capture--get-point)
"%?"
:file-name "%<%Y%m%d%H%M%S>-${slug}.org"
'((:key "d"
:desc "default"
:body "%?"
:file-path "%<%Y%m%d%H%M%S>-${slug}.org"
:head "#+title: ${title}\n"
:unnarrowed t))
"Capture templates for Org-roam.
The Org-roam capture-templates builds on the default behaviours of
`org-capture-templates' by expanding them in 3 areas:
1. Template-expansion capabilities are extended with additional
custom syntax. See `org-roam-capture--fill-template' for more
details.
2. The `:file-name' key is added, which defines the naming format
to use when creating new notes. This file-name is relative to
`org-roam-directory', and is without the file-extension.
3. The `:head' key is added, which contains the template that is
inserted upon the creation of a new file. This is where you
your note metadata should go.
Each template should have the following structure:
\(KEY DESCRIPTION `plain' `(function org-roam-capture--get-point)'
TEMPLATE
`:file-name' FILENAME-FORMAT
`:head' HEADER-FORMAT
`:unnarrowed t'
OPTIONS-PLIST)
The elements of a template-entry and their placement are the same
as in `org-capture-templates', except that the entry type must
always be the symbol `plain', and that the target must always be
the list `(function org-roam-capture--get-point)'.
Org-roam requires the plist elements `:file-name' and `:head' to
be present, and its recommended that `:unnarrowed' be set to t."
TODO: Document this"
:group 'org-roam
;; Adapted from `org-capture-templates'
:type
'(repeat
(choice :value ("d" "default" plain (function org-roam-capture--get-point)
"%?"
:file-name "%<%Y%m%d%H%M%S>-${slug}"
:head "#+title: ${title}\n"
:unnarrowed t)
(list :tag "Multikey description"
(string :tag "Keys ")
(string :tag "Description"))
(list :tag "Template entry"
(string :tag "Keys ")
(string :tag "Description ")
(const :format "" plain)
(const :format "" (function org-roam-capture--get-point))
(choice :tag "Template "
(string :tag "String"
:format "String:\n \
Template string :\n%v")
(list :tag "File"
(const :format "" file)
(file :tag "Template file "))
(list :tag "Function"
(const :format "" function)
(function :tag "Template function ")))
(const :format "File name format :" :file-name)
(string :format " %v" :value "#+title: ${title}\n")
(const :format "Header format :" :head)
(string :format "\n%v" :value "%<%Y%m%d%H%M%S>-${slug}")
(const :format "" :unnarrowed) (const :format "" t)
(plist :inline t
:tag "Options"
;; Give the most common options as checkboxes
:options
(((const :format "%v " :prepend) (const t))
((const :format "%v " :immediate-finish) (const t))
((const :format "%v " :jump-to-captured) (const t))
((const :format "%v " :empty-lines) (const 1))
((const :format "%v " :empty-lines-before) (const 1))
((const :format "%v " :empty-lines-after) (const 1))
((const :format "%v " :clock-in) (const t))
((const :format "%v " :clock-keep) (const t))
((const :format "%v " :clock-resume) (const t))
((const :format "%v " :time-prompt) (const t))
((const :format "%v " :tree-type) (const week))
((const :format "%v " :table-line-pos) (string))
((const :format "%v " :kill-buffer) (const t))))))))
:type '(repeat plist) ;; TODO: add :type properly
)
(defcustom org-roam-capture-ref-templates
'(("r" "ref" plain #'org-roam-capture--get-point
"%?"
:file-name "${slug}"
:head "#+title: ${title}\n#+roam_key: ${ref}"
'((:key "r"
:desc "ref"
:body "%?"
:file-path "${slug}.org"
:head "#+title: ${title}\n#+roam_key: ${ref}" ;TODO: auto insert ref instead
:unnarrowed t))
"The Org-roam templates used during a capture from the roam-ref protocol.
Details on how to specify for the template is given in `org-roam-capture-templates'."
:group 'org-roam
;; Adapted from `org-capture-templates'
:type
'(repeat
(choice :value ("d" "default" plain (function org-roam-capture--get-point)
"%?"
:file-name "${slug}"
:head "#+title: ${title}\n#+roam_key: ${ref}\n"
:unnarrowed t)
(list :tag "Multikey description"
(string :tag "Keys ")
(string :tag "Description"))
(list :tag "Template entry"
(string :tag "Keys ")
(string :tag "Description ")
(const :format "" plain)
(const :format "" (function org-roam-capture--get-point))
(choice :tag "Template "
(string :tag "String"
:format "String:\n \
Template string :\n%v")
(list :tag "File"
(const :format "" file)
(file :tag "Template file "))
(list :tag "Function"
(const :format "" function)
(function :tag "Template function ")))
(const :format "File name format :" :file-name)
(string :format " %v" :value "#+title: ${title}\n")
(const :format "Header format :" :head)
(string :format "\n%v" :value "%<%Y%m%d%H%M%S>-${slug}")
(const :format "" :unnarrowed) (const :format "" t)
(plist :inline t
:tag "Options"
;; Give the most common options as checkboxes
:options
(((const :format "%v " :prepend) (const t))
((const :format "%v " :immediate-finish) (const t))
((const :format "%v " :jump-to-captured) (const t))
((const :format "%v " :empty-lines) (const 1))
((const :format "%v " :empty-lines-before) (const 1))
((const :format "%v " :empty-lines-after) (const 1))
((const :format "%v " :clock-in) (const t))
((const :format "%v " :clock-keep) (const t))
((const :format "%v " :clock-resume) (const t))
((const :format "%v " :time-prompt) (const t))
((const :format "%v " :tree-type) (const week))
((const :format "%v " :table-line-pos) (string))
((const :format "%v " :kill-buffer) (const t))))))))
:type '(repeat plist) ;; TODO: add :type properly
)
(defun org-roam-capture-p ()
"Return t if the current capture process is an Org-roam capture.
@ -340,8 +218,8 @@ aborted, we do the following:
3. Add a function on `org-capture-before-finalize-hook' that saves
the file if the original value of :no-save is not t and
`org-note-abort' is not t."
(let* ((name-templ (or (org-roam-capture--get :file-name)
(user-error "Template needs to specify `:file-name'")))
(let* ((name-templ (or (org-roam-capture--get :file-path)
(user-error "Template needs to specify `:file-path'")))
(rel-filename (s-trim (org-roam-capture--fill-template name-templ)))
(file-path (expand-file-name rel-filename org-roam-directory))
(roam-head (or (org-roam-capture--get :head) ""))
@ -437,14 +315,10 @@ This function is used solely in Org-roam's capture templates: see
(org-roam-capture--put :file-path file-path
:finalize (or (org-capture-get :finalize)
(org-roam-capture--get :finalize)))
(while org-roam-capture-additional-template-props
(let ((prop (pop org-roam-capture-additional-template-props))
(val (pop org-roam-capture-additional-template-props)))
(org-roam-capture--put prop val)))
(set-buffer (org-capture-target-buffer file-path))
(widen)
(if-let* ((olp (org-roam-capture--get :olp)))
(condition-case err
(condition-case errg
(when-let ((marker (org-roam-capture-find-or-create-olp olp)))
(goto-char marker)
(set-marker marker nil))
@ -454,33 +328,43 @@ This function is used solely in Org-roam's capture templates: see
(signal (car err) (cdr err))))
(goto-char (point-max)))))
(defun org-roam-capture--convert-template (template)
"Convert TEMPLATE from Org-roam syntax to `org-capture-templates' syntax."
(pcase template
(`(,_key ,_description) template)
(`(,key ,description ,type ,target . ,rest)
(let ((converted `(,key ,description ,type ,target
,(unless (keywordp (car rest)) (pop rest))))
org-roam-plist
options)
(while rest
(let* ((key (pop rest))
(val (pop rest))
(custom (member key org-roam-capture--template-keywords)))
(when (and custom
(not val))
(user-error "Invalid capture template format: %s\nkey %s cannot be nil" template key))
(push val (if custom org-roam-plist options))
(push key (if custom org-roam-plist options))))
(append converted options `(:org-roam ,org-roam-plist))))
(_ (user-error "Invalid capture template format: %s" template))))
(defun org-roam-capture--convert-template (template &optional props)
"Convert TEMPLATE from Org-roam syntax to `org-capture-templates' syntax.
PROPS is a plist containing additional Org-roam specific
properties to be added to the template."
(let* ((key (or (plist-get template :key)
(user-error "template has no :key")))
(desc (or (plist-get template :desc)
(user-error "template has no :desc")))
(body (or (plist-get template :body)
(user-error "template has no :body")))
(rest (org-plist-delete template :key))
(rest (org-plist-delete rest :desc))
(rest (org-plist-delete rest :body))
(org-roam-plist props)
options)
(while rest
(let* ((key (pop rest))
(val (pop rest))
(custom (member key org-roam-capture--template-keywords)))
(when (and custom
(not val))
(user-error "Invalid capture template format: %s\nkey %s cannot be nil" template key))
(push val (if custom org-roam-plist options))
(push key (if custom org-roam-plist options))))
(append `(,key ,desc plain #'org-roam-capture--get-point ,body)
options
(list :org-roam org-roam-plist))))
(cl-defun org-roam-capture--capture (&key goto keys info)
(cl-defun org-roam-capture--capture (&key goto keys info props)
"Main entry point.
GOTO and KEYS correspond to `org-capture' arguments.
INFO is an alist for filling up Org-roam's capture templates."
INFO is an alist for filling up Org-roam's capture templates.
PROPS is a plist containing additional Org-roam properties for each template."
(let* ((org-capture-templates
(mapcar #'org-roam-capture--convert-template org-roam-capture-templates))
(mapcar (lambda (t)
(org-roam-capture--convert-template t props))
org-roam-capture-templates))
(org-roam-capture--info info)
(one-template-p (= (length org-capture-templates) 1)))
(when one-template-p

View File

@ -61,67 +61,15 @@
:type 'hook)
(defcustom org-roam-dailies-capture-templates
`(("d" "default" entry (function org-roam-capture--get-point)
"* %?"
:file-name ,(concat org-roam-dailies-directory "%<%Y-%m-%d>")
'((:key "d"
:desc "default"
:body "* %?"
:file-path ,(concat org-roam-dailies-directory "%<%Y-%m-%d>")
:head "#+title: %<%Y-%m-%d>\n"))
"Capture templates for daily-notes in Org-roam."
:group 'org-roam
;; Adapted from `org-capture-templates'
:type
`(repeat
(choice :value ("d" "default" plain (function org-roam-capture--get-point)
"%?"
:file-name ,(concat org-roam-dailies-directory
"%<%Y-%m-%d>")
:head "#+title: %<%Y-%m-%d>\n"
:unnarrowed t)
(list :tag "Multikey description"
(string :tag "Keys ")
(string :tag "Description"))
(list :tag "Template entry"
(string :tag "Keys ")
(string :tag "Description ")
(choice :tag "Type "
(const :tag "Plain" plain)
(const :tag "Entry (for creating headlines)" entry))
(const :format "" #'org-roam-capture--get-point)
(choice :tag "Template "
(string :tag "String"
:format "String:\n \
Template string :\n%v")
(list :tag "File"
(const :format "" file)
(file :tag "Template file "))
(list :tag "Function"
(const :format "" function)
(function :tag "Template function ")))
(const :format "File name format :" :file-name)
(string :format " %v" :value ,(concat org-roam-dailies-directory
"%<%Y-%m-%d>"))
(const :format "Header format :" :head)
(string :format " %v" :value "#+title: ${title}\n")
(plist :inline t
:tag "Options"
;; Give the most common options as checkboxes
:options
(((const :tag "Outline path" :olp)
(repeat :tag "Headings"
(string :tag "Heading")))
((const :format "%v " :unnarrowed) (const t))
((const :format "%v " :prepend) (const t))
((const :format "%v " :immediate-finish) (const t))
((const :format "%v " :jump-to-captured) (const t))
((const :format "%v " :empty-lines) (const 1))
((const :format "%v " :empty-lines-before) (const 1))
((const :format "%v " :empty-lines-after) (const 1))
((const :format "%v " :clock-in) (const t))
((const :format "%v " :clock-keep) (const t))
((const :format "%v " :clock-resume) (const t))
((const :format "%v " :time-prompt) (const t))
((const :format "%v " :tree-type) (const week))
((const :format "%v " :table-line-pos) (string))
((const :format "%v " :kill-buffer) (const t))))))))
'(repeat plist))
;;;; Utilities
(defun org-roam-dailies-directory--get-absolute-path ()

View File

@ -82,6 +82,7 @@ It opens or creates a note with the given ref.
(org-capture-link-is-already-stored t)
(template (cdr (assoc 'template decoded-alist))))
(raise-frame)
;; TODO: FIX
(org-roam-capture--capture nil template)
(org-roam-message "Item captured.")))
nil)

View File

@ -687,10 +687,10 @@ If OTHER-WINDOW, visit the NODE in another window."
(let ((node (org-roam-node-read initial-input filter-fn)))
(if (org-roam-node-file node)
(org-roam-node-visit node other-window)
(setq org-roam-capture-additional-template-props (list :finalize 'find-file))
(org-roam-capture--capture
:info `((title . ,(org-roam-node-title node))
(slug . ,(funcall org-roam-title-to-slug-function (org-roam-node-title node))))))))
(slug . ,(funcall org-roam-title-to-slug-function (org-roam-node-title node))))
:props (list :finalize 'find-file)))))
(defun org-roam-node-insert (&optional filter-fn)
"Find an Org-roam file, and insert a relative org link to it at point.
@ -720,15 +720,14 @@ which takes as its argument an alist of path-completions."
(insert (org-link-make-string
(concat "id:" (org-roam-node-id node))
description)))
(setq org-roam-capture-additional-template-props
(list :region (when (and beg end)
(cons beg end))
:insert-at (point-marker)
:link-description description
:finalize 'insert-link))
(org-roam-capture--capture
:info `((title . ,(org-roam-node-title node))
(slug . ,(funcall org-roam-title-to-slug-function (org-roam-node-title node))))))))
(slug . ,(funcall org-roam-title-to-slug-function (org-roam-node-title node))))
:props (list :region (when (and beg end)
(cons beg end))
:insert-at (point-marker)
:link-description description
:finalize 'insert-link)))))
(deactivate-mark)))
;;;###autoload