I've implemented the following functions to implement this feature:
my-org-capture-under-headline
: the main command.
- Some advice around
org-capture
to provide a new C-u 2
prefix argument
;; Create ‘C-u 2 M-x org-capture’ command to refile org-capture template under
;; headline at point.
(defun my-org-capture-under-headline (&optional _goto keys)
"Capture under the headline at current point.
This finds an appropriate point at the end of the subtree to capture the new
entry to, then call ‘org-capture’ with \"C-u 0\" to capture at this new point,
and finally fixes the level of the newly-captured entry to be a child of the
headline at which this function was called.
KEYS are passed to ‘org-capture’ in its KEYS argument. _GOTO corresponds to the
GOTO argument of ‘org-capture’, but is is ignored because we intentionally place
the point where we want it."
(interactive "P")
(unwind-protect
(progn
(unless (eq major-mode 'org-mode)
(user-error "Must be called from an Org-mode buffer"))
(org-with-point-at (point)
(org-back-to-heading t)
(let* ((headline-marker (point-marker))
(headline-level (org-current-level)))
(org-end-of-subtree t)
(unless (org--line-empty-p 1)
(end-of-line)
(open-line 1)
(forward-line 1))
(cl-letf*
(((symbol-function 'my-org-capture-under-headline--fixup-level)
(lambda ()
;; If :begin-marker doesn’t point to a buffer, the capture
;; has already been finalized, so don’t try to do any work
;; in that case.
(when-let* ((begin-marker (org-capture-get :begin-marker))
((marker-buffer begin-marker)))
;; Go to the capture buffer.
(org-goto-marker-or-bmk begin-marker)
;; Make the captured headline one level below the original headline.
(while (< (org-current-level) (+ 1 headline-level))
(save-excursion (org-demote-subtree)))
(while (> (org-current-level) (+ 1 headline-level))
(save-excursion (org-promote-subtree))))))
;; Store the original ‘org-capture-finalize’ for the override before.
((symbol-function 'org-capture-finalize-orig) (symbol-function 'org-capture-finalize))
;; Need to fixup level in case ‘:immediate-finish’ set in ‘org-capture-templates’
((symbol-function 'org-capture-finalize)
(lambda (&optional stay-with-capture)
(my-org-capture-under-headline--fixup-level)
(org-capture-finalize-orig stay-with-capture))))
(apply #'org-capture 0 keys)
;; If the template didn’t contain ‘:immediate-finish’, we fix up
;; levels now.
(my-org-capture-under-headline--fixup-level)))))))
(defun my-org-capture-under-headline-prefix (_orig-fn &rest _args)
"Provide an additional “C-u 2” prefix arg to `org-capture'.
When ‘org-capture’ is called with this prefix argument, it will capture a
headline according to the template you select, and then immediately refile that
headline under the headline at the current point."
(let ((goto (nth 0 _args)))
(if (equal goto 2)
(apply #'my-org-capture-under-headline _args)
(apply _orig-fn _args))))
(advice-add 'org-capture :around #'my-org-capture-under-headline-prefix)
Update 2022-02-22: make the function more robust.