diff --git a/contrib/lisp/org-export.el b/contrib/lisp/org-export.el index 6937bf284..1417c050e 100644 --- a/contrib/lisp/org-export.el +++ b/contrib/lisp/org-export.el @@ -2101,7 +2101,7 @@ Return code as a string." ;; visible part of buffer. (if noexpand (org-element-parse-buffer nil visible-only) (org-export-with-current-buffer-copy - (org-export-expand-include-keyword nil) + (org-export-expand-include-keyword) (let ((org-current-export-file (current-buffer))) (org-export-blocks-preprocess)) (org-element-parse-buffer nil visible-only))) @@ -2250,14 +2250,16 @@ Point is at buffer's beginning when BODY is applied." (progn ,@body)))))) (def-edebug-spec org-export-with-current-buffer-copy (body)) -(defun org-export-expand-include-keyword (included) +(defun org-export-expand-include-keyword (&optional included dir) "Expand every include keyword in buffer. -INCLUDED is a list of included file names along with their line -restriction, when appropriate. It is used to avoid infinite -recursion." - (let ((case-fold-search nil)) +Optional argument INCLUDED is a list of included file names along +with their line restriction, when appropriate. It is used to +avoid infinite recursion. Optional argument DIR is the current +working directory. It is used to properly resolve relative +paths." + (let ((case-fold-search t)) (goto-char (point-min)) - (while (re-search-forward "^[ \t]*#\\+include: \\(.*\\)" nil t) + (while (re-search-forward "^[ \t]*#\\+INCLUDE: \\(.*\\)" nil t) (when (eq (org-element-type (save-match-data (org-element-at-point))) 'keyword) (beginning-of-line) @@ -2265,7 +2267,7 @@ recursion." (let* ((value (match-string 1)) (ind (org-get-indentation)) (file (and (string-match "^\"\\(\\S-+\\)\"" value) - (prog1 (expand-file-name (match-string 1 value)) + (prog1 (expand-file-name (match-string 1 value) dir) (setq value (replace-match "" nil nil value))))) (lines (and (string-match @@ -2306,7 +2308,7 @@ recursion." "\\(^\\)\\([*]\\|[ \t]*#\\+\\)" "," (org-export-prepare-file-contents file lines) nil nil 1))) - (format "%s#+begin_example\n%s%s#+end_example\n" + (format "%s#+BEGIN_EXAMPLE\n%s%s#+END_EXAMPLE\n" ind-str contents ind-str)))) ((stringp env) (insert @@ -2318,7 +2320,7 @@ recursion." "\\(^\\)\\([*]\\|[ \t]*#\\+\\)") "," (org-export-prepare-file-contents file lines) nil nil 1))) - (format "%s#+begin_src %s\n%s%s#+end_src\n" + (format "%s#+BEGIN_SRC %s\n%s%s#+END_SRC\n" ind-str env contents ind-str)))) (t (insert @@ -2327,7 +2329,8 @@ recursion." (insert (org-export-prepare-file-contents file lines ind minlevel)) (org-export-expand-include-keyword - (cons (list file lines) included)) + (cons (list file lines) included) + (file-name-directory file)) (buffer-string)))))))))))) (defun org-export-prepare-file-contents (file &optional lines ind minlevel) diff --git a/testing/contrib/lisp/test-org-export.el b/testing/contrib/lisp/test-org-export.el index 8e878002e..9edc9c44a 100644 --- a/testing/contrib/lisp/test-org-export.el +++ b/testing/contrib/lisp/test-org-export.el @@ -247,3 +247,52 @@ text (should (equal (org-export-as 'test) "A\n"))) (let ((org-export-snippet-translation-alist '(("t" . "test")))) (should (equal (org-export-as 'test) "AB\n"))))))) + +(ert-deftest test-org-export/expand-include () + "Test file inclusion in an Org buffer." + ;; Full insertion with recursive inclusion. + (org-test-with-temp-text + (format "#+INCLUDE: \"%s/examples/include.org\"" org-test-dir) + (org-export-expand-include-keyword) + (should (equal (buffer-string) + "Small Org file with an include keyword. + +#+BEGIN_SRC emacs-lisp :exports results +(+ 2 1) +#+END_SRC + +Success! + +* Heading +body\n"))) + ;; Localized insertion. + (org-test-with-temp-text + (format "#+INCLUDE: \"%s/examples/include.org\" :lines \"1-2\"" + org-test-dir) + (org-export-expand-include-keyword) + (should (equal (buffer-string) + "Small Org file with an include keyword.\n"))) + ;; Insertion with constraints on headlines level. + (org-test-with-temp-text + (format + "* Top heading\n#+INCLUDE: \"%s/examples/include.org\" :lines \"9-\"" + org-test-dir) + (org-export-expand-include-keyword) + (should (equal (buffer-string) "* Top heading\n** Heading\nbody\n"))) + ;; Inclusion within an example block. + (org-test-with-temp-text + (format "#+INCLUDE: \"%s/examples/include.org\" :lines \"1-2\" example" + org-test-dir) + (org-export-expand-include-keyword) + (should + (equal + (buffer-string) + "#+BEGIN_EXAMPLE\nSmall Org file with an include keyword.\n#+END_EXAMPLE\n"))) + ;; Inclusion within a src-block. + (org-test-with-temp-text + (format + "#+INCLUDE: \"%s/examples/include.org\" :lines \"4-5\" src emacs-lisp" + org-test-dir) + (org-export-expand-include-keyword) + (should (equal (buffer-string) + "#+BEGIN_SRC emacs-lisp\n(+ 2 1)\n#+END_SRC\n")))) diff --git a/testing/examples/include.org b/testing/examples/include.org new file mode 100644 index 000000000..186facb26 --- /dev/null +++ b/testing/examples/include.org @@ -0,0 +1,10 @@ +Small Org file with an include keyword. + +#+BEGIN_SRC emacs-lisp :exports results +(+ 2 1) +#+END_SRC + +#+INCLUDE: "include2.org" + +* Heading +body diff --git a/testing/examples/include2.org b/testing/examples/include2.org new file mode 100644 index 000000000..f985b46af --- /dev/null +++ b/testing/examples/include2.org @@ -0,0 +1 @@ +Success!