> On Jul 6, 2026, at 21:20, Derek Chen-Becker <[email protected]> wrote:
>
> Hi Perry, thanks for the patch. In the docstring for the
> `org-export-after-includes-function' you have a typo with "Bable". Also,
> would it be possible to add a unit test?
diff --git a/doc/org-manual.org b/doc/org-manual.org
index bda84c49c..5706380b1 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -16964,12 +16964,15 @@ for usage and configuration details.
#+vindex: org-export-before-processing-hook
#+vindex: org-export-before-processing-functions
+#+vindex: org-export-after-includes-functions
#+vindex: org-export-before-parsing-hook
#+vindex: org-export-before-parsing-functions
-The export process executes two hooks before the actual exporting
+The export process executes three hooks before the actual exporting
begins. The first hook, ~org-export-before-processing-functions~,
runs before any expansions of macros, Babel code, and include keywords
in the buffer. The second hook,
+~org-export-after-includes-functions~, runs after the include keywords
+but before the macros and Babel code. The last hook,
~org-export-before-parsing-functions~, runs before the buffer is
parsed.
@@ -17109,10 +17112,12 @@ export keywords, does not contribute to the export
output.]:
3. Remove commented subtrees in the whole buffer (see [[*Comment
Lines]]);
-4. Replace macros in the whole buffer (see [[*Macro Replacement]]),
+4. Execute ~org-export-after-includes-functions~ (see [[*Export hooks]]);
+
+5. Replace macros in the whole buffer (see [[*Macro Replacement]]),
unless ~org-export-replace-macros~ is nil;
-5. When ~org-export-use-babel~ is non-nil (default), process code
+6. When ~org-export-use-babel~ is non-nil (default), process code
blocks:
- Leave code blocks inside archived subtrees (see [[*Internal
diff --git a/lisp/org-table.el b/lisp/org-table.el
index 9aeb53822..1f180bf53 100644
--- a/lisp/org-table.el
+++ b/lisp/org-table.el
@@ -5867,6 +5867,7 @@ This may be either a string or a function of two
arguments:
;; We _do not_ disable `org-export-filter-parse-tree-functions'
;; (historically).
(let ((org-export-before-processing-functions nil)
+ (org-export-after-includes-functions nil)
(org-export-replace-macros nil)
(org-export-use-babel nil)
(org-export-before-parsing-functions nil)
diff --git a/lisp/ox.el b/lisp/ox.el
index 90c853d76..9fe88b617 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -2175,10 +2175,11 @@ keywords before output."
;; Filters properties are installed in communication channel with
;; `org-export-install-filters' function.
;;
-;; Eventually, two hooks (`org-export-before-processing-functions' and
-;; `org-export-before-parsing-functions') are run at the beginning of the
-;; export process and just before parsing to allow for heavy structure
-;; modifications.
+;; Eventually, three hooks (`org-export-before-processing-functions',
+;; `org-export-after-includes-functions' and
+;; `org-export-before-parsing-functions') are run at the beginning of
+;; the export process and just before parsing to allow for heavy
+;; structure modifications.
;;;; Hooks
@@ -2194,6 +2195,14 @@ is at the beginning of the buffer.
Every function in this hook will be called with one argument: the
backend currently used, as a symbol.")
+(defvar org-export-after-includes-functions nil
+ "Abnormal hook run after includes but before macros.
+
+This is run after include keywords but before macros have been
+expanded and Babel code blocks executed, on a copy of the original
+buffer being exported. Visibility and narrowing are preserved. Point
+is at the beginning of the buffer.")
+
(defvar org-export-before-parsing-functions nil
"Abnormal hook run before parsing an export buffer.
@@ -3104,6 +3113,10 @@ still inferior to file-local settings."
(org-export-backend-name backend))
(org-export-expand-include-keyword nil nil nil nil (plist-get info
:expand-links))
(org-export--delete-comment-trees)
+ (save-excursion
+ (goto-char (point-min))
+ (run-hook-with-args 'org-export-after-includes-functions
+ (org-export-backend-name backend)))
(when org-export-replace-macros
(org-macro-initialize-templates org-export-global-macros)
(org-macro-replace-all org-macro-templates parsed-keywords))
diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el
index 79365ad29..e58c776e9 100644
--- a/testing/lisp/test-ox.el
+++ b/testing/lisp/test-ox.el
@@ -1879,6 +1879,24 @@ Footnotes[fn:2], foot[fn:test] and [fn:inline:inline
footnote]
(org-element-property :end object)))))))))
(org-export-as (org-test-default-backend)))))))
+(ert-deftest test-org-export/after-includes-hook ()
+ "Test `org-export-after-includes-functions'."
+ (should
+ (equal "success\n"
+ (org-test-with-temp-text
+ (format "#+INCLUDE: \"%s/examples/macro-templates.org\"\n"
org-test-dir)
+ (let* ((org-export-after-includes-functions
+ '((lambda (backend)
+ (goto-char (point-min))
+ ;; will fail if hook done before the include
+ (if (search-forward "Macro templates")
+ (progn
+ (goto-char (point-max))
+ ;; will not expand if hook executes after macros
+ (insert "{{{included-macro}}}"))))))
+ (output (org-export-as (org-test-default-backend))))
+ (substring output (string-match ".*\n\\'" output)))))))
+
(ert-deftest test-org-export/before-parsing-functions ()
"Test `org-export-before-parsing-functions'."
(should