(feat!)capture: unify template variables

Instead of using various capture template variables, like
org-roam-dailies-capture-templates or org-roam-capture-ref-templates,
just use org-roam-capture-templates for everything.

From now on, each individual template will able to optionally specify a
more detailed context for which they're dedicated for, using :kind and
:action properties. Both of them accept either, a symbol or a list of
symbols.

If it's a list, then it can optionally start with :not symbol to
indicate inverse logic, otherwise it will act akin to memq. If it's a
symbol, then it will work akin to eq, but with support for special 'any
value, which indicates that it will work in any contexts; 'any can be
also used inside of the list based values.

Org-roam and the built-in extension will provide the following contexts:
- :action {capture,goto,any}
- :kind {normal,daily,protocol,any}
but third party extensions / users can easily extend them for their own
needs.

If :action isn't specified it will implicitly default to 'capture, while
:kind will default to 'normal contexts.

BREAKING CHANGE: org-roam-dailies-capture-templates and
org-roam-capture-ref-templates are now removed in the favor of unified
approach.
This commit is contained in:
Wetlize
2021-08-25 00:36:17 +03:00
parent e9ae19c01c
commit b6a898d6d6
5 changed files with 156 additions and 165 deletions

View File

@@ -36,6 +36,18 @@
;; One can use dailies for various purposes, e.g. journaling, fleeting notes,
;; scratch notes and whatever else you can came up with.
;;
;; To add new capture templates dedicated for dailies, specify ":kind daily" for
;; each of such template in `org-roam-capture-templates', e.g.
;;
;; (setq org-roam-capture-templates
;; '(("d" "daily" entry "* %?" :kind daily
;; :if-new (file+head "%<%Y-%m-%d>.org"
;; "#+title: %<%Y-%m-%d>\n"))))
;;
;; Note that in order for your daily files to properly integrate with the
;; calendar, each daily file should be named with a format understood by
;; `org-parse-time-string'.
;;
;;; Code:
(require 'f)
(require 'dash)
@@ -49,8 +61,9 @@
;;; Options
(defcustom org-roam-dailies-directory "daily/"
"Path to daily-notes.
This path is relative to `org-roam-directory'."
"Path to daily-notes. This path is relative to `org-roam-directory'.
Daily based capture templates will automatically start from this
path."
:group 'org-roam
:type 'string)
@@ -59,74 +72,6 @@ This path is relative to `org-roam-directory'."
:group 'org-roam
:type 'hook)
(defcustom org-roam-dailies-capture-templates
`(("d" "default" entry
"* %?"
:if-new (file+head "%<%Y-%m-%d>.org"
"#+title: %<%Y-%m-%d>\n")))
"Capture templates for daily-notes in Org-roam.
Note that for daily files to show up in the calendar, they have to be of format
\"org-time-string.org\".
See `org-roam-capture-templates' for the template documentation."
:group 'org-roam
:type '(repeat
(choice (list :tag "Multikey description"
(string :tag "Keys ")
(string :tag "Description"))
(list :tag "Template entry"
(string :tag "Keys ")
(string :tag "Description ")
(choice :tag "Capture Type " :value entry
(const :tag "Org entry" entry)
(const :tag "Plain list item" item)
(const :tag "Checkbox item" checkitem)
(const :tag "Plain text" plain)
(const :tag "Table line" table-line))
(choice :tag "Template "
(string)
(list :tag "File"
(const :format "" file)
(file :tag "Template file"))
(list :tag "Function"
(const :format "" function)
(function :tag "Template function")))
(plist :inline t
;; Give the most common options as checkboxes
:options (((const :format "%v " :if-new)
(choice :tag "Node location"
(list :tag "File"
(const :format "" file)
(string :tag " File"))
(list :tag "File & Head Content"
(const :format "" file+head)
(string :tag " File")
(string :tag " Head Content"))
(list :tag "File & Outline path"
(const :format "" file+olp)
(string :tag " File")
(list :tag "Outline path"
(repeat (string :tag "Headline"))))
(list :tag "File & Head Content & Outline path"
(const :format "" file+head+olp)
(string :tag " File")
(string :tag " Head Content")
(list :tag "Outline path"
(repeat (string :tag "Headline"))))))
((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 " :unnarrowed) (const t))
((const :format "%v " :table-line-pos) (string))
((const :format "%v " :kill-buffer) (const t))))))))
;;; Commands
;;;; Today
;;;###autoload
@@ -306,8 +251,8 @@ When GOTO is non-nil, go the note without creating an entry."
(let ((org-roam-directory (expand-file-name org-roam-dailies-directory org-roam-directory)))
(org-roam-capture- :goto (when goto '(4))
:node (org-roam-node-create)
:templates org-roam-dailies-capture-templates
:props (list :override-default-time time)))
:props (list :kind 'daily
:override-default-time time)))
(when goto (run-hooks 'org-roam-dailies-find-file-hook)))
(add-hook 'org-roam-capture-preface-hook #'org-roam-dailies--override-capture-time-h)
@@ -317,6 +262,12 @@ When GOTO is non-nil, go the note without creating an entry."
(when (org-roam-capture--get :override-default-time)
(org-capture-put :default-time (org-roam-capture--get :override-default-time)))))
(when (org-roam-capture--load-templates-p 'org-roam-dailies)
(push '("d" "daily" entry "* %?" :kind daily
:if-new (file+head "%<%Y-%m-%d>.org"
"#+title: %<%Y-%m-%d>\n"))
org-roam-capture-templates))
;;; Bindings
(defvar org-roam-dailies-map (make-sparse-keymap)
"Keymap for `org-roam-dailies'.")

View File

@@ -32,12 +32,21 @@
;; 1. "roam-node": This protocol simply opens the node given by the node ID
;; 2. "roam-ref": This protocol creates or opens the node with the given REF
;;
;; You can find detailed instructions on how to setup the protocol in the
;; documentation for Org-roam.
;; To add new capture templates dedicated for the protocol, specify ":kind
;; protocol" for each of such template in `org-roam-capture-templates', e.g.
;;
;; (setq org-roam-capture-templates
;; '(("r" "ref" plain "%?" :kind protocol
;; :if-new (file+head "${slug}.org"
;; "#+title: ${title}")
;; :unnarrowed t)))
;;
;; You can find a detailed instruction on how to setup the protocol in the
;; manual for Org-roam.
;;
;;; Code:
(require 'org-protocol)
(require 'ol) ;; for org-link-decode
(require 'ol) ; to use `org-link-decode'
(require 'org-roam)
;;; Options
@@ -46,73 +55,12 @@
:type 'boolean
:group 'org-roam)
(defcustom org-roam-capture-ref-templates
'(("r" "ref" plain "%?"
:if-new (file+head "${slug}.org"
"#+title: ${title}")
:unnarrowed t))
"The Org-roam templates used during a capture from the roam-ref protocol.
See `org-roam-capture-templates' for the template documentation."
:group 'org-roam
:type '(repeat
(choice (list :tag "Multikey description"
(string :tag "Keys ")
(string :tag "Description"))
(list :tag "Template entry"
(string :tag "Keys ")
(string :tag "Description ")
(choice :tag "Capture Type " :value entry
(const :tag "Org entry" entry)
(const :tag "Plain list item" item)
(const :tag "Checkbox item" checkitem)
(const :tag "Plain text" plain)
(const :tag "Table line" table-line))
(choice :tag "Template "
(string)
(list :tag "File"
(const :format "" file)
(file :tag "Template file"))
(list :tag "Function"
(const :format "" function)
(function :tag "Template function")))
(plist :inline t
;; Give the most common options as checkboxes
:options (((const :format "%v " :if-new)
(choice :tag "Node location"
(list :tag "File"
(const :format "" file)
(string :tag " File"))
(list :tag "File & Head Content"
(const :format "" file+head)
(string :tag " File")
(string :tag " Head Content"))
(list :tag "File & Outline path"
(const :format "" file+olp)
(string :tag " File")
(list :tag "Outline path"
(repeat (string :tag "Headline"))))
(list :tag "File & Head Content & Outline path"
(const :format "" file+head+olp)
(string :tag " File")
(string :tag " Head Content")
(list :tag "Outline path"
(repeat (string :tag "Headline"))))))
((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 " :unnarrowed) (const t))
((const :format "%v " :table-line-pos) (string))
((const :format "%v " :kill-buffer) (const t))))))))
;;; Protocols
(mapc (lambda (spec) (cl-pushnew spec org-protocol-protocol-alist :test #'equal))
'(("org-roam-ref" :protocol "roam-ref" :function org-roam-protocol-open-ref)
("org-roam-node" :protocol "roam-node" :function org-roam-protocol-open-node)))
;;; Handlers
;;;; roam-ref
(defun org-roam-protocol-open-ref (info)
"Process an org-protocol://roam-ref?ref= style url with INFO.
@@ -146,29 +94,9 @@ It opens or creates a note with the given ref.
:node (org-roam-node-create :title (plist-get info :title))
:info (list :ref (plist-get info :ref)
:body (plist-get info :body))
:templates org-roam-capture-ref-templates)
:props '(:kind protocol))
nil)
(defun org-roam-protocol-open-node (info)
"This handler simply opens the file with emacsclient.
INFO is a plist containing additional information passed by the protocol URL.
It should contain the FILE key, pointing to the path of the file to open.
Example protocol string:
org-protocol://roam-node?node=uuid"
(when-let ((node (plist-get info :node)))
(raise-frame)
(org-roam-node-visit (org-roam-populate (org-roam-node-create :id node)) nil 'force))
nil)
(push '("org-roam-ref" :protocol "roam-ref" :function org-roam-protocol-open-ref)
org-protocol-protocol-alist)
(push '("org-roam-node" :protocol "roam-node" :function org-roam-protocol-open-node)
org-protocol-protocol-alist)
;;; Capture implementation
(add-hook 'org-roam-capture-preface-hook #'org-roam-protocol--try-capture-to-ref-h)
(defun org-roam-protocol--try-capture-to-ref-h ()
"Try to capture to an existing node that match the ref."
@@ -186,6 +114,27 @@ org-protocol://roam-node?node=uuid"
(when-let ((ref (plist-get org-roam-capture--info :ref)))
(org-roam-ref-add ref)))
(when (org-roam-capture--load-templates-p 'org-roam-protocol)
(push '("r" "ref" plain "%?" :kind protocol
:if-new (file+head "${slug}.org"
"#+title: ${title}")
:unnarrowed t)
org-roam-capture-templates))
;;;; roam-node
(defun org-roam-protocol-open-node (info)
"This handler simply opens the file with emacsclient.
INFO is a plist containing additional information passed by the protocol URL.
It should contain the FILE key, pointing to the path of the file to open.
Example protocol string:
org-protocol://roam-node?node=uuid"
(when-let ((node (plist-get info :node)))
(raise-frame)
(org-roam-node-visit (org-roam-populate (org-roam-node-create :id node)) nil 'force))
nil)
(provide 'org-roam-protocol)