From 3b71b82e857267be72f9e073718a750e70fd697e Mon Sep 17 00:00:00 2001 From: Perry Smith Date: Thu, 23 Jul 2026 06:47:44 -0600 Subject: [PATCH] Add new after-includes hook for org export Provide a new hook that runs after include keywords, but before macros and Babel code. * doc/org-manual.org: Update the org-export hooks section to describe the new hook in relation to the other existing hooks. * lisp/org-table.el (orgtbl-to-generic): Disable the new hook during table export just as we do with other hooks. * lisp/ox.el (org-export-after-includes-functions, org-export--annotate-info): Add description and new variable for `org-export-after-includes-functions' and wire the new hook into `org-export--annotate-info'. * testing/lisp/test-ox.el (test-org-export/after-includes-hook): Add a unit test to exercise the new `org-export-after-includes-functions' hook. --- doc/org-manual.org | 11 ++++++++--- lisp/org-table.el | 1 + lisp/ox.el | 21 +++++++++++++++++---- testing/lisp/test-ox.el | 18 ++++++++++++++++++ 4 files changed, 44 insertions(+), 7 deletions(-) diff --git a/doc/org-manual.org b/doc/org-manual.org index cd8903a7e..2fab4e48d 100644 --- a/doc/org-manual.org +++ b/doc/org-manual.org @@ -16975,12 +16975,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. @@ -17120,10 +17123,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 -- 2.43.0