(feat): enable nested captures (#966)
This PR enables the long-awaited nested-captures. 1. Adds a hook to org-capture-prepare-finalize-hook, which installs org-roam-capture--finalize into org-capture-after-finalize-hook if the capture is an Org-roam capture. This function contains key functionality for Org-roam to Do The Right Thing after specific interactive functions, such as finding the file, or inserting a link. 2. A patch for org-capture-finalize. Specifically, we make org-capture-plist valid during org-capture-finalize. 3. Many hacks that were originally in place are now replaced with nicer alternatives. Co-authored-by: Leo Vivier <zaephon@gmail.com>
This commit is contained in:
parent
863ae2427e
commit
20f876aa6b
4 changed files with 67 additions and 50 deletions
|
@ -15,6 +15,7 @@
|
|||
- [#863](https://github.com/org-roam/org-roam/pull/863) Display outline hierarchy in backlinks buffer
|
||||
- [#898](https://github.com/org-roam/org-roam/pull/898) Add `org-roam-random-note` to browse a random note.
|
||||
- [#900](https://github.com/org-roam/org-roam/pull/900) Support and index all valid org links
|
||||
- [#966](https://github.com/org-roam/org-roam/pull/966) Enable nested captures
|
||||
|
||||
### Bugfixes
|
||||
|
||||
|
|
|
@ -280,30 +280,76 @@ Template string :\n%v")
|
|||
((const :format "%v " :table-line-pos) (string))
|
||||
((const :format "%v " :kill-buffer) (const t))))))))
|
||||
|
||||
(defun org-roam-capture-p ()
|
||||
"Return t if the current capture process is an Org-roam capture.
|
||||
This function is to only be called when org-capture-plist is
|
||||
valid for the capture (i.e. initialization, and finalization of
|
||||
the capture)."
|
||||
(plist-get org-capture-plist :org-roam))
|
||||
|
||||
(defun org-roam-capture--get (keyword)
|
||||
"Gets the value for KEYWORD from the `org-roam-capture-template'."
|
||||
"Get the value for KEYWORD from the `org-roam-capture-template'."
|
||||
(plist-get (plist-get org-capture-plist :org-roam) keyword))
|
||||
|
||||
(defun org-roam-capture--put (&rest stuff)
|
||||
"Puts properties from STUFF into the `org-roam-capture-template'."
|
||||
"Put properties from STUFF into the `org-roam-capture-template'."
|
||||
(let ((p (plist-get org-capture-plist :org-roam)))
|
||||
(while stuff
|
||||
(setq p (plist-put p
|
||||
(pop stuff) (pop stuff))))
|
||||
(setq p (plist-put p (pop stuff) (pop stuff))))
|
||||
(setq org-capture-plist
|
||||
(plist-put org-capture-plist :org-roam p))))
|
||||
|
||||
(defun org-roam-capture--in-process-p ()
|
||||
"Return non-nil if a `org-roam-capture' buffer exists."
|
||||
(cl-some (lambda (buffer)
|
||||
(and (eq (buffer-local-value 'major-mode buffer)
|
||||
'org-mode)
|
||||
(plist-get (buffer-local-value 'org-capture-current-plist buffer)
|
||||
:org-roam)))
|
||||
(buffer-list)))
|
||||
;; FIXME: Pending upstream patch
|
||||
;; https://orgmode.org/list/87h7tv9pkm.fsf@hidden/T/#u
|
||||
;;
|
||||
;; Org-capture's behaviour right now is that `org-capture-plist' is valid only
|
||||
;; during the initialization of the Org-capture buffer. The value of
|
||||
;; `org-capture-plist' is saved into buffer-local `org-capture-current-plist'.
|
||||
;; However, the value for that particular capture is no longer accessible for
|
||||
;; hooks in `org-capture-after-finalize-hook', since the capture buffer has been
|
||||
;; cleaned up.
|
||||
;;
|
||||
;; This advice restores the global `org-capture-plist' during finalization, so
|
||||
;; the plist is valid during both initialization and finalization of the
|
||||
;; capture.
|
||||
(defun org-roam-capture--update-plist (&optional _)
|
||||
"Update global plist from local var."
|
||||
(setq org-capture-plist org-capture-current-plist))
|
||||
|
||||
(advice-add 'org-capture-finalize :before #'org-roam-capture--update-plist)
|
||||
|
||||
(defun org-roam-capture--finalize ()
|
||||
"Finalize the `org-roam-capture' process."
|
||||
(unless org-note-abort
|
||||
(pcase (org-roam-capture--get :finalize)
|
||||
('find-file
|
||||
(when-let ((file-path (org-roam-capture--get :file-path)))
|
||||
(org-roam--find-file file-path)
|
||||
(run-hooks 'org-roam-capture-after-find-file-hook)))
|
||||
('insert-link
|
||||
(when-let* ((mkr (org-roam-capture--get :insert-at))
|
||||
(buf (marker-buffer mkr)))
|
||||
(with-current-buffer buf
|
||||
(when-let ((region (org-roam-capture--get :region))) ;; Remove previously selected text.
|
||||
(delete-region (car region) (cdr region)))
|
||||
(let ((path (org-roam-capture--get :file-path))
|
||||
(desc (org-roam-capture--get :link-description)))
|
||||
(if (eq (point) (marker-position mkr))
|
||||
(insert (org-roam--format-link path desc))
|
||||
(org-with-point-at mkr
|
||||
(insert (org-roam--format-link path desc))))))))))
|
||||
(org-roam-capture--save-file-maybe)
|
||||
(remove-hook 'org-capture-after-finalize-hook #'org-roam-capture--finalize))
|
||||
|
||||
(defun org-roam-capture--install-finalize ()
|
||||
"Install `org-roam-capture--finalize' if the capture is an Org-roam capture."
|
||||
(when (org-roam-capture-p)
|
||||
(add-hook 'org-capture-after-finalize-hook #'org-roam-capture--finalize)))
|
||||
|
||||
(add-hook 'org-capture-prepare-finalize-hook #'org-roam-capture--install-finalize)
|
||||
|
||||
(defun org-roam-capture--fill-template (str)
|
||||
"Expands the template STR, returning the string.
|
||||
"Expand the template STR, returning the string.
|
||||
This is an extension of org-capture's template expansion.
|
||||
|
||||
First, it expands ${var} occurrences in STR, using `org-roam-capture--info'.
|
||||
|
@ -320,19 +366,7 @@ Next, it expands the remaining template string using
|
|||
val))) nil)
|
||||
(org-capture-fill-template)))
|
||||
|
||||
(defun org-roam-capture--insert-link-h ()
|
||||
"Insert the link into the original buffer, after the capture process is done.
|
||||
This is added as a hook to `org-capture-after-finalize-hook'."
|
||||
(when (and (not org-note-abort)
|
||||
(eq (org-roam-capture--get :capture-fn)
|
||||
'org-roam-insert))
|
||||
(when-let ((region (org-roam-capture--get :region))) ;; Remove previously selected text.
|
||||
(delete-region (car region) (cdr region)))
|
||||
(insert (org-roam--format-link (org-roam-capture--get :file-path)
|
||||
(org-roam-capture--get :link-description))))
|
||||
(remove-hook 'org-capture-after-finalize-hook #'org-roam-capture--insert-link-h))
|
||||
|
||||
(defun org-roam-capture--save-file-maybe-h ()
|
||||
(defun org-roam-capture--save-file-maybe ()
|
||||
"Save the file conditionally.
|
||||
The file is saved if the original value of :no-save is not t and
|
||||
`org-note-abort' is not t. It is added to
|
||||
|
@ -346,8 +380,7 @@ The file is saved if the original value of :no-save is not t and
|
|||
((and (not (org-roam-capture--get :orig-no-save))
|
||||
(not org-note-abort))
|
||||
(with-current-buffer (org-capture-get :buffer)
|
||||
(save-buffer))))
|
||||
(remove-hook 'org-capture-after-finalize-hook #'org-roam-capture--save-file-maybe-h))
|
||||
(save-buffer)))))
|
||||
|
||||
(defun org-roam-capture--new-file ()
|
||||
"Return the path to the new file during an Org-roam capture.
|
||||
|
@ -367,7 +400,7 @@ aborted, we do the following:
|
|||
|
||||
2. Set the capture template's :no-save to t.
|
||||
|
||||
3. Add a function on `org-capture-after-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
|
||||
`org-note-abort' is not t."
|
||||
(let* ((name-templ (or (org-roam-capture--get :file-name)
|
||||
|
@ -455,16 +488,6 @@ This function is used solely in Org-roam's capture templates: see
|
|||
(append converted options `(:org-roam ,org-roam-plist))))
|
||||
(_ (user-error "Invalid capture template format: %s" template))))
|
||||
|
||||
(defun org-roam-capture--find-file-h ()
|
||||
"Opens the newly created template file.
|
||||
This is added as a hook to `org-capture-after-finalize-hook'.
|
||||
Run the hooks defined in `org-roam-capture-after-find-file-hook'."
|
||||
(unless org-note-abort
|
||||
(when-let ((file-path (org-roam-capture--get :file-path)))
|
||||
(org-roam--find-file file-path))
|
||||
(run-hooks 'org-roam-capture-after-find-file-hook))
|
||||
(remove-hook 'org-capture-after-finalize-hook #'org-roam-capture--find-file-h))
|
||||
|
||||
(defcustom org-roam-capture-after-find-file-hook nil
|
||||
"Hook that is run right after an Org-roam capture process is finalized.
|
||||
Suitable for moving point."
|
||||
|
@ -486,7 +509,6 @@ GOTO and KEYS argument have the same functionality as
|
|||
org-capture-templates-contexts)
|
||||
(when one-template-p
|
||||
(setq keys (caar org-capture-templates)))
|
||||
(add-hook 'org-capture-after-finalize-hook #'org-roam-capture--save-file-maybe-h)
|
||||
(if (or one-template-p
|
||||
(eq org-roam-capture-function 'org-capture))
|
||||
(org-capture goto keys)
|
||||
|
@ -498,8 +520,6 @@ GOTO and KEYS argument have the same functionality as
|
|||
This uses the templates defined at `org-roam-capture-templates'."
|
||||
(interactive)
|
||||
(unless org-roam-mode (org-roam-mode))
|
||||
(when (org-roam-capture--in-process-p)
|
||||
(user-error "Nested Org-roam capture processes not supported"))
|
||||
(let* ((completions (org-roam--get-title-path-completions))
|
||||
(title-with-keys (org-roam-completion--completing-read "File: "
|
||||
completions))
|
||||
|
@ -510,7 +530,6 @@ This uses the templates defined at `org-roam-capture-templates'."
|
|||
(cons 'slug (funcall org-roam-title-to-slug-function title))
|
||||
(cons 'file file-path)))
|
||||
(org-roam-capture--context 'capture))
|
||||
(setq org-roam-capture-additional-template-props (list :capture-fn 'org-roam-capture))
|
||||
(condition-case err
|
||||
(org-roam-capture--capture)
|
||||
(error (user-error "%s. Please adjust `org-roam-capture-templates'"
|
||||
|
|
|
@ -103,7 +103,7 @@ Template string :\n%v")
|
|||
(let ((org-roam-capture-templates org-roam-dailies-capture-templates)
|
||||
(org-roam-capture--info (list (cons 'time time)))
|
||||
(org-roam-capture--context 'dailies))
|
||||
(add-hook 'org-capture-after-finalize-hook #'org-roam-capture--find-file-h)
|
||||
(setq org-roam-capture-additional-template-props (list :finalize 'find-file))
|
||||
(org-roam--with-template-error 'org-roam-dailies-capture-templates
|
||||
(org-roam-capture--capture))))
|
||||
|
||||
|
|
|
@ -1356,7 +1356,6 @@ which takes as its argument an alist of path-completions. See
|
|||
`org-roam--get-title-path-completions' for details."
|
||||
(interactive)
|
||||
(unless org-roam-mode (org-roam-mode))
|
||||
(when (org-roam-capture--in-process-p) (user-error "Org-roam capture in process"))
|
||||
(let* ((completions (funcall (or filter-fn #'identity)
|
||||
(or completions (org-roam--get-title-path-completions))))
|
||||
(title-with-tags (org-roam-completion--completing-read "File: " completions
|
||||
|
@ -1368,7 +1367,7 @@ which takes as its argument an alist of path-completions. See
|
|||
(let ((org-roam-capture--info `((title . ,title-with-tags)
|
||||
(slug . ,(funcall org-roam-title-to-slug-function title-with-tags))))
|
||||
(org-roam-capture--context 'title))
|
||||
(add-hook 'org-capture-after-finalize-hook #'org-roam-capture--find-file-h)
|
||||
(setq org-roam-capture-additional-template-props (list :finalize 'find-file))
|
||||
(org-roam--with-template-error 'org-roam-capture-templates
|
||||
(org-roam-capture--capture))))))
|
||||
|
||||
|
@ -1445,15 +1444,13 @@ If DESCRIPTION is provided, use this as the link label. See
|
|||
(when region ;; Remove previously selected text.
|
||||
(delete-region (car region) (cdr region)))
|
||||
(insert (org-roam--format-link target-file-path link-description)))
|
||||
(when (org-roam-capture--in-process-p)
|
||||
(user-error "Nested Org-roam capture processes not supported"))
|
||||
(let ((org-roam-capture--info `((title . ,title-with-tags)
|
||||
(slug . ,(funcall org-roam-title-to-slug-function title-with-tags))))
|
||||
(org-roam-capture--context 'title))
|
||||
(add-hook 'org-capture-after-finalize-hook #'org-roam-capture--insert-link-h)
|
||||
(setq org-roam-capture-additional-template-props (list :region region
|
||||
:insert-at (point-marker)
|
||||
:link-description link-description
|
||||
:capture-fn 'org-roam-insert))
|
||||
:finalize 'insert-link))
|
||||
(org-roam--with-template-error 'org-roam-capture-templates
|
||||
(org-roam-capture--capture))))
|
||||
res))
|
||||
|
|
Loading…
Reference in a new issue