Browse Source

ox: Fix bug where properties read from setupfile overwrite previous properties

* lisp/ox.el (org-export--get-inbuffer-options): Remove an optional
  argument.  Rewrite function.  Properties read from a setupfile do
  not overwrite anymore previously computed properties.
(org-export-get-environment): Apply changes to previous function.
* lisp/org.el (org-create-formula--latex-header): Apply arity change
  from `org-export--get-inbuffer-options'.
* testing/lisp/test-ox.el: Add test.
* testing/examples/setupfile.org: New file.
Nicolas Goaziou 12 years ago
parent
commit
677433d9b4
4 changed files with 140 additions and 107 deletions
  1. 2 4
      lisp/org.el
  2. 90 86
      lisp/ox.el
  3. 4 0
      testing/examples/setupfile.org
  4. 44 17
      testing/lisp/test-ox.el

+ 2 - 4
lisp/org.el

@@ -18101,7 +18101,7 @@ share a good deal of logic."
    string tofile options buffer))
 
 (declare-function org-export--get-global-options "org-export" (&optional backend))
-(declare-function org-export--get-inbuffer-options "org-export" (&optional backend files))
+(declare-function org-export--get-inbuffer-options "org-export" (&optional backend))
 (defun org-create-formula--latex-header ()
   "Return LaTeX header appropriate for previewing a LaTeX snippet."
   (org-latex-guess-inputenc
@@ -18112,9 +18112,7 @@ share a good deal of logic."
     (plist-get
      (org-combine-plists
       (org-export--get-global-options 'latex)
-      (org-export--get-inbuffer-options
-       'latex
-       (and buffer-file-name (org-remove-double-quotes buffer-file-name))))
+      (org-export--get-inbuffer-options 'latex))
      :latex-header-extra))))
 
 ;; This function borrows from Ganesh Swami's latex2png.el

+ 90 - 86
lisp/ox.el

@@ -1404,9 +1404,7 @@ inferior to file-local settings."
    ;; ... from an external property list...
    ext-plist
    ;; ... from in-buffer settings...
-   (org-export--get-inbuffer-options
-    backend
-    (and buffer-file-name (org-remove-double-quotes buffer-file-name)))
+   (org-export--get-inbuffer-options backend)
    ;; ... and from subtree, when appropriate.
    (and subtreep (org-export--get-subtree-options backend))
    ;; Eventually add misc. properties.
@@ -1531,106 +1529,112 @@ for export.  Return options as a plist."
      ;; Return value.
      plist)))
 
-(defun org-export--get-inbuffer-options (&optional backend files)
+(defun org-export--get-inbuffer-options (&optional backend)
   "Return current buffer export options, as a plist.
 
 Optional argument BACKEND, when non-nil, is a symbol specifying
 which back-end specific options should also be read in the
 process.
 
-Optional argument FILES is a list of setup files names read so
-far, used to avoid circular dependencies.
-
 Assume buffer is in Org mode.  Narrowing, if any, is ignored."
-  (org-with-wide-buffer
-   (goto-char (point-min))
-   (let ((case-fold-search t) plist)
-     ;; 1. Special keywords, as in `org-export-special-keywords'.
-     (let ((special-re
-	    (format "^[ \t]*#\\+%s:" (regexp-opt org-export-special-keywords))))
-       (while (re-search-forward special-re nil t)
-	 (let ((element (org-element-at-point)))
-	   (when (eq (org-element-type element) 'keyword)
-	     (let* ((key (org-element-property :key element))
-		    (val (org-element-property :value element))
-		    (prop
+  (let* (plist
+	 get-options			; For byte-compiler.
+	 (case-fold-search t)
+	 (options (append
+		   ;; Priority is given to back-end specific options.
+		   (and backend (org-export-backend-options backend))
+		   org-export-options-alist))
+	 (regexp (format "^[ \t]*#\\+%s:"
+			 (regexp-opt (nconc (delq nil (mapcar 'cadr options))
+					    org-export-special-keywords))))
+	 (find-opt
+	  (lambda (keyword)
+	    ;; Return property name associated to KEYWORD.
+	    (catch 'exit
+	      (mapc (lambda (option)
+		      (when (equal (nth 1 option) keyword)
+			(throw 'exit (car option))))
+		    options))))
+	 (get-options
+	  (lambda (&optional files plist)
+	    ;; Recursively read keywords in buffer.  FILES is a list
+	    ;; of files read so far.  PLIST is the current property
+	    ;; list obtained.
+	    (org-with-wide-buffer
+	     (goto-char (point-min))
+	     (while (re-search-forward regexp nil t)
+	       (let ((element (org-element-at-point)))
+		 (when (eq (org-element-type element) 'keyword)
+		   (let ((key (org-element-property :key element))
+			 (val (org-element-property :value element)))
 		     (cond
+		      ;; Options in `org-export-special-keywords'.
 		      ((equal key "SETUPFILE")
-		       (let ((file
-			      (expand-file-name
-			       (org-remove-double-quotes (org-trim val)))))
+		       (let ((file (expand-file-name
+				    (org-remove-double-quotes (org-trim val)))))
 			 ;; Avoid circular dependencies.
 			 (unless (member file files)
 			   (with-temp-buffer
 			     (insert (org-file-contents file 'noerror))
 			     (org-mode)
-			     (org-export--get-inbuffer-options
-			      backend (cons file files))))))
+			     (setq plist (funcall get-options
+						  (cons file files) plist))))))
 		      ((equal key "OPTIONS")
-		       (org-export--parse-option-keyword val backend))
+		       (setq plist
+			     (org-combine-plists
+			      plist
+			      (org-export--parse-option-keyword val backend))))
 		      ((equal key "FILETAGS")
-		       (list :filetags
-			     (org-uniquify
-			      (append (org-split-string val ":")
-				      (plist-get plist :filetags))))))))
-	       (setq plist (org-combine-plists plist prop)))))))
-     ;; 2. Standard options, as in `org-export-options-alist'.
-     (let* ((all (append
-		  ;; Priority is given to back-end specific options.
-		  (and backend (org-export-backend-options backend))
-		  org-export-options-alist)))
-       (dolist (option all)
-	 (let ((prop (car option)))
-	   (when (and (nth 1 option) (not (plist-member plist prop)))
-	     (goto-char (point-min))
-	     (let ((opt-re (format "^[ \t]*#\\+%s:" (nth 1 option)))
-		   (behaviour (nth 4 option)))
-	       (while (re-search-forward opt-re nil t)
-		 (let ((element (org-element-at-point)))
-		   (when (eq (org-element-type element) 'keyword)
-		     (let((key (org-element-property :key element))
-			  (val (org-element-property :value element)))
 		       (setq plist
-			     (plist-put
-			      plist (car option)
-			      ;; Handle value depending on specified
-			      ;; BEHAVIOUR.
-			      (case behaviour
-				(space
-				 (if (not (plist-get plist prop)) (org-trim val)
-				   (concat (plist-get plist prop)
-					   " "
-					   (org-trim val))))
-				(newline
-				 (org-trim (concat (plist-get plist prop)
-						   "\n"
-						   (org-trim val))))
-				(split `(,@(plist-get plist prop)
-					 ,@(org-split-string val)))
-				('t val)
-				(otherwise
-				 (if (not (plist-member plist prop)) val
-				   (plist-get plist prop))))))))))))))
-       ;; Parse keywords specified in
-       ;; `org-element-document-properties'.
-       (mapc
-	(lambda (key)
-	  ;; Find the property associated to the keyword.
-	  (let* ((prop (catch 'found
-			 (mapc (lambda (option)
-				 (when (equal (nth 1 option) key)
-				   (throw 'found (car option))))
-			       all)))
-		 (value (and prop (plist-get plist prop))))
-	    (when (stringp value)
-	      (setq plist
-		    (plist-put
-		     plist prop
-		     (org-element-parse-secondary-string
-		      value (org-element-restriction 'keyword)))))))
-	org-element-document-properties))
-     ;; 3. Return final value.
-     plist)))
+			     (org-combine-plists
+			      plist
+			      (list :filetags
+				    (org-uniquify
+				     (append (org-split-string val ":")
+					     (plist-get plist :filetags)))))))
+		      (t
+		       ;; Options in `org-export-options-alist'.
+		       (let* ((prop (funcall find-opt key))
+			      (behaviour (nth 4 (assq prop options))))
+			 (setq plist
+			       (plist-put
+				plist prop
+				;; Handle value depending on specified
+				;; BEHAVIOUR.
+				(case behaviour
+				  (space
+				   (if (not (plist-get plist prop))
+				       (org-trim val)
+				     (concat (plist-get plist prop)
+					     " "
+					     (org-trim val))))
+				  (newline
+				   (org-trim (concat (plist-get plist prop)
+						     "\n"
+						     (org-trim val))))
+				  (split `(,@(plist-get plist prop)
+					   ,@(org-split-string val)))
+				  ('t val)
+				  (otherwise
+				   (if (not (plist-member plist prop)) val
+				     (plist-get plist prop)))))))))))))
+	     ;; Return final value.
+	     plist))))
+    ;; Read options in the current buffer.
+    (setq plist (funcall get-options buffer-file-name nil))
+    ;; Parse keywords specified in `org-element-document-properties'.
+    (mapc (lambda (keyword)
+	    ;; Find the property associated to the keyword.
+	    (let* ((prop (funcall find-opt keyword))
+		   (value (and prop (plist-get plist prop))))
+	      (when (stringp value)
+		(setq plist
+		      (plist-put plist prop
+				 (org-element-parse-secondary-string
+				  value (org-element-restriction 'keyword)))))))
+	  org-element-document-properties)
+    ;; Return value.
+    plist))
 
 (defun org-export--get-buffer-attributes ()
   "Return properties related to buffer attributes, as a plist."

+ 4 - 0
testing/examples/setupfile.org

@@ -0,0 +1,4 @@
+#+DESCRIPTION: l2
+#+LANGUAGE: en
+#+SELECT_TAGS: b
+#+TITLE: b

+ 44 - 17
testing/lisp/test-ox.el

@@ -122,26 +122,53 @@ already filled in `info'."
 
 (ert-deftest test-org-export/get-inbuffer-options ()
   "Test reading all standard export keywords."
+  ;; Properties should follow buffer order.
   (should
    (equal
-    (org-test-with-temp-text "#+AUTHOR: Me, Myself and I
-#+CREATOR: Idem
-#+DATE: Today
-#+DESCRIPTION: Testing
-#+DESCRIPTION: with two lines
-#+EMAIL: some@email.org
-#+EXCLUDE_TAGS: noexport invisible
-#+KEYWORDS: test
-#+LANGUAGE: en
-#+SELECT_TAGS: export
-#+TITLE: Some title
-#+TITLE: with spaces"
+    (org-test-with-temp-text "#+LANGUAGE: fr\n#+CREATOR: Me\n#+EMAIL: email"
       (org-export--get-inbuffer-options))
-    '(:author
-      ("Me, Myself and I") :creator "Idem" :date ("Today")
-      :description "Testing\nwith two lines" :email "some@email.org"
-      :exclude-tags ("noexport" "invisible") :keywords "test" :language "en"
-      :select-tags ("export") :title ("Some title with spaces")))))
+    '(:language "fr" :creator "Me" :email "email")))
+  ;; Parse document keywords.
+  (should
+   (equal
+    (org-test-with-temp-text "#+AUTHOR: Me"
+      (org-export--get-inbuffer-options))
+    '(:author ("Me"))))
+  ;; Test `space' behaviour.
+  (should
+   (equal
+    (org-test-with-temp-text "#+TITLE: Some title\n#+TITLE: with spaces"
+      (org-export--get-inbuffer-options))
+    '(:title ("Some title with spaces"))))
+  ;; Test `newline' behaviour.
+  (should
+   (equal
+    (org-test-with-temp-text "#+DESCRIPTION: With\n#+DESCRIPTION: two lines"
+      (org-export--get-inbuffer-options))
+    '(:description "With\ntwo lines")))
+  ;; Test `split' behaviour.
+  (should
+   (equal
+    (org-test-with-temp-text "#+SELECT_TAGS: a\n#+SELECT_TAGS: b"
+      (org-export--get-inbuffer-options))
+    '(:select-tags ("a" "b"))))
+  ;; Options set through SETUPFILE.
+  (should
+   (equal
+    (org-test-with-temp-text
+	(format "#+DESCRIPTION: l1
+#+LANGUAGE: es
+#+SELECT_TAGS: a
+#+TITLE: a
+#+SETUPFILE: \"%s/examples/setupfile.org\"
+#+DESCRIPTION: l3
+#+LANGUAGE: fr
+#+SELECT_TAGS: c
+#+TITLE: c"
+		org-test-dir)
+      (org-export--get-inbuffer-options))
+    '(:description "l1\nl2\nl3":language "fr" :select-tags ("a" "b" "c")
+		   :title ("a b c")))))
 
 (ert-deftest test-org-export/get-subtree-options ()
   "Test setting options from headline's properties."