mirror of
https://github.com/org-roam/org-roam
synced 2025-08-01 12:17:21 -05:00
[breaking] update org-roam capture-template syntax
This commit is contained in:
@ -47,157 +47,35 @@
|
|||||||
This variable is populated dynamically, and is only non-nil
|
This variable is populated dynamically, and is only non-nil
|
||||||
during the Org-roam capture process.")
|
during the Org-roam capture process.")
|
||||||
|
|
||||||
(defvar org-roam-capture-additional-template-props nil
|
(defconst org-roam-capture--template-keywords '(:file-path :head :olp)
|
||||||
"Additional props to be added to the Org-roam template.")
|
|
||||||
|
|
||||||
(defconst org-roam-capture--template-keywords '(:file-name :head :olp)
|
|
||||||
"Keywords used in `org-roam-capture-templates' specific to Org-roam.")
|
"Keywords used in `org-roam-capture-templates' specific to Org-roam.")
|
||||||
|
|
||||||
(defcustom org-roam-capture-templates
|
(defcustom org-roam-capture-templates
|
||||||
`(("d" "default" plain (function org-roam-capture--get-point)
|
'((:key "d"
|
||||||
"%?"
|
:desc "default"
|
||||||
:file-name "%<%Y%m%d%H%M%S>-${slug}.org"
|
:body "%?"
|
||||||
|
:file-path "%<%Y%m%d%H%M%S>-${slug}.org"
|
||||||
:head "#+title: ${title}\n"
|
:head "#+title: ${title}\n"
|
||||||
:unnarrowed t))
|
:unnarrowed t))
|
||||||
"Capture templates for Org-roam.
|
"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
|
TODO: Document this"
|
||||||
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 it’s recommended that `:unnarrowed' be set to t."
|
|
||||||
:group 'org-roam
|
:group 'org-roam
|
||||||
;; Adapted from `org-capture-templates'
|
:type '(repeat plist) ;; TODO: add :type properly
|
||||||
: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))))))))
|
|
||||||
|
|
||||||
(defcustom org-roam-capture-ref-templates
|
(defcustom org-roam-capture-ref-templates
|
||||||
'(("r" "ref" plain #'org-roam-capture--get-point
|
'((:key "r"
|
||||||
"%?"
|
:desc "ref"
|
||||||
:file-name "${slug}"
|
:body "%?"
|
||||||
:head "#+title: ${title}\n#+roam_key: ${ref}"
|
:file-path "${slug}.org"
|
||||||
|
:head "#+title: ${title}\n#+roam_key: ${ref}" ;TODO: auto insert ref instead
|
||||||
:unnarrowed t))
|
:unnarrowed t))
|
||||||
"The Org-roam templates used during a capture from the roam-ref protocol.
|
"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'."
|
Details on how to specify for the template is given in `org-roam-capture-templates'."
|
||||||
:group 'org-roam
|
:group 'org-roam
|
||||||
;; Adapted from `org-capture-templates'
|
:type '(repeat plist) ;; TODO: add :type properly
|
||||||
: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))))))))
|
|
||||||
|
|
||||||
(defun org-roam-capture-p ()
|
(defun org-roam-capture-p ()
|
||||||
"Return t if the current capture process is an Org-roam capture.
|
"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
|
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
|
the file if the original value of :no-save is not t and
|
||||||
`org-note-abort' is not t."
|
`org-note-abort' is not t."
|
||||||
(let* ((name-templ (or (org-roam-capture--get :file-name)
|
(let* ((name-templ (or (org-roam-capture--get :file-path)
|
||||||
(user-error "Template needs to specify `:file-name'")))
|
(user-error "Template needs to specify `:file-path'")))
|
||||||
(rel-filename (s-trim (org-roam-capture--fill-template name-templ)))
|
(rel-filename (s-trim (org-roam-capture--fill-template name-templ)))
|
||||||
(file-path (expand-file-name rel-filename org-roam-directory))
|
(file-path (expand-file-name rel-filename org-roam-directory))
|
||||||
(roam-head (or (org-roam-capture--get :head) ""))
|
(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
|
(org-roam-capture--put :file-path file-path
|
||||||
:finalize (or (org-capture-get :finalize)
|
:finalize (or (org-capture-get :finalize)
|
||||||
(org-roam-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))
|
(set-buffer (org-capture-target-buffer file-path))
|
||||||
(widen)
|
(widen)
|
||||||
(if-let* ((olp (org-roam-capture--get :olp)))
|
(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)))
|
(when-let ((marker (org-roam-capture-find-or-create-olp olp)))
|
||||||
(goto-char marker)
|
(goto-char marker)
|
||||||
(set-marker marker nil))
|
(set-marker marker nil))
|
||||||
@ -454,14 +328,20 @@ This function is used solely in Org-roam's capture templates: see
|
|||||||
(signal (car err) (cdr err))))
|
(signal (car err) (cdr err))))
|
||||||
(goto-char (point-max)))))
|
(goto-char (point-max)))))
|
||||||
|
|
||||||
(defun org-roam-capture--convert-template (template)
|
(defun org-roam-capture--convert-template (template &optional props)
|
||||||
"Convert TEMPLATE from Org-roam syntax to `org-capture-templates' syntax."
|
"Convert TEMPLATE from Org-roam syntax to `org-capture-templates' syntax.
|
||||||
(pcase template
|
PROPS is a plist containing additional Org-roam specific
|
||||||
(`(,_key ,_description) template)
|
properties to be added to the template."
|
||||||
(`(,key ,description ,type ,target . ,rest)
|
(let* ((key (or (plist-get template :key)
|
||||||
(let ((converted `(,key ,description ,type ,target
|
(user-error "template has no :key")))
|
||||||
,(unless (keywordp (car rest)) (pop rest))))
|
(desc (or (plist-get template :desc)
|
||||||
org-roam-plist
|
(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)
|
options)
|
||||||
(while rest
|
(while rest
|
||||||
(let* ((key (pop rest))
|
(let* ((key (pop rest))
|
||||||
@ -472,15 +352,19 @@ This function is used solely in Org-roam's capture templates: see
|
|||||||
(user-error "Invalid capture template format: %s\nkey %s cannot be nil" template key))
|
(user-error "Invalid capture template format: %s\nkey %s cannot be nil" template key))
|
||||||
(push val (if custom org-roam-plist options))
|
(push val (if custom org-roam-plist options))
|
||||||
(push key (if custom org-roam-plist options))))
|
(push key (if custom org-roam-plist options))))
|
||||||
(append converted options `(:org-roam ,org-roam-plist))))
|
(append `(,key ,desc plain #'org-roam-capture--get-point ,body)
|
||||||
(_ (user-error "Invalid capture template format: %s" template))))
|
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.
|
"Main entry point.
|
||||||
GOTO and KEYS correspond to `org-capture' arguments.
|
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
|
(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)
|
(org-roam-capture--info info)
|
||||||
(one-template-p (= (length org-capture-templates) 1)))
|
(one-template-p (= (length org-capture-templates) 1)))
|
||||||
(when one-template-p
|
(when one-template-p
|
||||||
|
@ -61,67 +61,15 @@
|
|||||||
:type 'hook)
|
:type 'hook)
|
||||||
|
|
||||||
(defcustom org-roam-dailies-capture-templates
|
(defcustom org-roam-dailies-capture-templates
|
||||||
`(("d" "default" entry (function org-roam-capture--get-point)
|
'((:key "d"
|
||||||
"* %?"
|
:desc "default"
|
||||||
:file-name ,(concat org-roam-dailies-directory "%<%Y-%m-%d>")
|
:body "* %?"
|
||||||
|
:file-path ,(concat org-roam-dailies-directory "%<%Y-%m-%d>")
|
||||||
:head "#+title: %<%Y-%m-%d>\n"))
|
:head "#+title: %<%Y-%m-%d>\n"))
|
||||||
"Capture templates for daily-notes in Org-roam."
|
"Capture templates for daily-notes in Org-roam."
|
||||||
:group 'org-roam
|
:group 'org-roam
|
||||||
;; Adapted from `org-capture-templates'
|
|
||||||
:type
|
:type
|
||||||
`(repeat
|
'(repeat plist))
|
||||||
(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))))))))
|
|
||||||
|
|
||||||
;;;; Utilities
|
;;;; Utilities
|
||||||
(defun org-roam-dailies-directory--get-absolute-path ()
|
(defun org-roam-dailies-directory--get-absolute-path ()
|
||||||
|
@ -82,6 +82,7 @@ It opens or creates a note with the given ref.
|
|||||||
(org-capture-link-is-already-stored t)
|
(org-capture-link-is-already-stored t)
|
||||||
(template (cdr (assoc 'template decoded-alist))))
|
(template (cdr (assoc 'template decoded-alist))))
|
||||||
(raise-frame)
|
(raise-frame)
|
||||||
|
;; TODO: FIX
|
||||||
(org-roam-capture--capture nil template)
|
(org-roam-capture--capture nil template)
|
||||||
(org-roam-message "Item captured.")))
|
(org-roam-message "Item captured.")))
|
||||||
nil)
|
nil)
|
||||||
|
15
org-roam.el
15
org-roam.el
@ -687,10 +687,10 @@ If OTHER-WINDOW, visit the NODE in another window."
|
|||||||
(let ((node (org-roam-node-read initial-input filter-fn)))
|
(let ((node (org-roam-node-read initial-input filter-fn)))
|
||||||
(if (org-roam-node-file node)
|
(if (org-roam-node-file node)
|
||||||
(org-roam-node-visit node other-window)
|
(org-roam-node-visit node other-window)
|
||||||
(setq org-roam-capture-additional-template-props (list :finalize 'find-file))
|
|
||||||
(org-roam-capture--capture
|
(org-roam-capture--capture
|
||||||
:info `((title . ,(org-roam-node-title node))
|
: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)
|
(defun org-roam-node-insert (&optional filter-fn)
|
||||||
"Find an Org-roam file, and insert a relative org link to it at point.
|
"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
|
(insert (org-link-make-string
|
||||||
(concat "id:" (org-roam-node-id node))
|
(concat "id:" (org-roam-node-id node))
|
||||||
description)))
|
description)))
|
||||||
(setq org-roam-capture-additional-template-props
|
(org-roam-capture--capture
|
||||||
(list :region (when (and beg end)
|
:info `((title . ,(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))
|
(cons beg end))
|
||||||
:insert-at (point-marker)
|
:insert-at (point-marker)
|
||||||
:link-description description
|
:link-description description
|
||||||
:finalize 'insert-link))
|
: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))))))))
|
|
||||||
(deactivate-mark)))
|
(deactivate-mark)))
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
|
Reference in New Issue
Block a user