No Wayman <iarchivedmywholel...@gmail.com> writes:

The attached patch is the first step toward integrating DOCT[1] syntax into Org mode. It adds property options to org-capture-templates which make it easier to run template-specific hooks. The current approach for running such hooks involves adding to the desired global hook variable and filtering by the template's keys.
e.g.

#+begin_src emacs-lisp :lexical t
(defun +example-template-hook ()
  (when (eq (org-capture-get :key t) "e")
    (message "hook run")))

(add-hook 'org-capture-mode-hook #'+example-template-hook)

(let ((org-capture-templates '("e" "example" (file "") "")))
  (org-capture nil "e"))
#+end_src


The hook has to be maintained separately from the template declaration. The criteria to determine the selected template is baked into the hook function. This is fragile (change the binding for the template and you must update the hook function),
mixes concerns, and makes templates harder to share.

Contrast the above with the following syntax enabled by the attached patch:
#+begin_src emacs-lisp :lexical t
(let ((org-capture-templates
       '(("t" "test" plain (file "/tmp/test.org")
          "test %?"
          :hook ((lambda () (insert "mode-hook\n")))
:before-finalize ((lambda () (insert "before-finalize\n"))) ;; Only a message because this happens outside the context
          ;; of the capture buffer.
:after-finalize ((lambda () (message "after-finalize"))) :prepare-finalize ((lambda () (insert "prepare-finalize\n")))))))
  (org-capture nil "t"))
#+end_src

These template-specific hook functions run prior to their global counterparts.

Ihor, an implementation note: I have not used `run-hooks' with these because they have no associated symbol.
The functions are lists stored directly on `org-capture-plist'.

[1]: https://github.com/progfolio/doct

And, of course, the patch...

>From 790e8c517ba933025e50302e9c990ccf9265b55c Mon Sep 17 00:00:00 2001
From: Nicholas Vollmer <iarchivedmywholel...@gmail.com>
Date: Tue, 27 Sep 2022 05:44:33 -0400
Subject: [PATCH] org-capture: Add template hook properties

* lisp/org-capture.el (org-capture-templates): Document template hook properties.
(org-capture-finalize): execute :prepare/:before/:after-finalize functions.
(org-capture-place-template): execute :hook functions.

* doc/org-manual.org Document template hook properties.
---
 doc/org-manual.org  | 20 ++++++++++++++++++++
 lisp/org-capture.el | 21 +++++++++++++++++++++
 2 files changed, 41 insertions(+)

diff --git a/doc/org-manual.org b/doc/org-manual.org
index ab8a295e5..ed2322949 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -7838,6 +7838,26 @@ Now lets look at the elements of a template definition.  Each entry in
   - ~:refile-targets :: Temporarily set ~org-refile-targets~ to the
     value of this property.
 
+  - ~:hook~ ::
+
+    A list of functions run before `org-capture-mode-hook'
+    when the template is selected.
+
+ - ~:prepare-finalize~ ::
+
+    A list of functions run before `org-capture-prepare-finalize-hook'
+    when the template is selected.
+
+ - ~:before-finalize~ ::
+
+    A list of functions run before `org-capture-before-finalize-hook'
+    when the template is selected.
+
+ - ~:after-finalize~ ::
+
+    A list of functions run before `org-capture-after-finalize-hook'
+    when the template is selected.
+
 **** Template expansion
 :PROPERTIES:
 :DESCRIPTION: Filling in information about time and context.
diff --git a/lisp/org-capture.el b/lisp/org-capture.el
index 428d0ac0e..af1502ede 100644
--- a/lisp/org-capture.el
+++ b/lisp/org-capture.el
@@ -297,6 +297,21 @@ properties are:
 
  :no-save            Do not save the target file after finishing the capture.
 
+ :hook               A list of functions run before
+                     `org-capture-mode-hook' when the template is selected.
+
+ :prepare-finalize   A list of functions run before
+                     `org-capture-prepare-finalize-hook'
+                     when the template is selected.
+
+ :before-finalize    A list of functions run before
+                     `org-capture-before-finalize-hook'
+                     when the template is selected.
+
+ :after-finalize     A list of functions run before
+                     `org-capture-after-finalize-hook'
+                     when the template is selected.
+
 The template defines the text to be inserted.  Often this is an
 Org mode entry (so the first line should start with a star) that
 will be filed as a child of the target headline.  It can also be
@@ -751,6 +766,7 @@ captured item after finalizing."
 	       (buffer-base-buffer (current-buffer)))
     (error "This does not seem to be a capture buffer for Org mode"))
 
+  (mapc #'funcall (org-capture-get :prepare-finalize t))
   (run-hooks 'org-capture-prepare-finalize-hook)
 
   ;; Update `org-capture-plist' with the buffer-local value.  Since
@@ -823,6 +839,7 @@ captured item after finalizing."
       ;; the indirect buffer has been killed.
       (org-capture-store-last-position)
 
+      (mapc #'funcall (org-capture-get :before-finalize t))
       ;; Run the hook
       (run-hooks 'org-capture-before-finalize-hook))
 
@@ -871,6 +888,9 @@ captured item after finalizing."
       ;; Restore the window configuration before capture
       (set-window-configuration return-wconf))
 
+    ;; Do not use the local arg to `org-capture-get' here.
+    ;; The buffer-local has been stored on `org-capture-plist'.
+    (mapc #'funcall (org-capture-get :after-finalize))
     (run-hooks 'org-capture-after-finalize-hook)
     ;; Special cases
     (cond
@@ -1147,6 +1167,7 @@ may have been stored before."
     (`item (org-capture-place-item))
     (`checkitem (org-capture-place-item)))
   (setq-local org-capture-current-plist org-capture-plist)
+  (mapc #'funcall (org-capture-get :hook t))
   (org-capture-mode 1))
 
 (defun org-capture-place-entry ()
-- 
2.37.3

Reply via email to