org-export: Fix bug with recursive file inclusion and relative paths

* contrib/lisp/org-export.el (org-export-expand-include-keyword): Use
  another optional argument to specify the current working directory.
(org-export-as): Apply changes.
* testing/contrib/lisp/test-org-export.el: Add tests.
* testing/examples/include.org: New test file.
* testing/examples/include2.org: New test file.
This commit is contained in:
Nicolas Goaziou 2012-02-23 21:06:17 +01:00
parent 186f0f7594
commit 94185eac92
4 changed files with 74 additions and 11 deletions

View File

@ -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)

View File

@ -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"))))

View File

@ -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

View File

@ -0,0 +1 @@
Success!