ox: Ignore INCLUDE keywords in commented headlines
* lisp/ox.el (org-export-expand-include-keyword): Ignore INCLUDE keywords in commented headlines. * testing/lisp/test-ox.el (test-org-export/expand-include): Add test.
This commit is contained in:
parent
942b6267a0
commit
5e1f7ff04b
|
@ -313,6 +313,7 @@ you use "/!" markup when filtering TODO keywords.
|
|||
This variable is a ~defcustom~ and replaces the variable
|
||||
~org-babel-capitalize-example-region-markers~, which is a ~defvar~ and
|
||||
is now obselete.
|
||||
*** =INCLUDE= keywords in commented trees are now ignored.
|
||||
|
||||
* Version 9.0
|
||||
|
||||
|
|
219
lisp/ox.el
219
lisp/ox.el
|
@ -3281,116 +3281,119 @@ storing and resolving footnotes. It is created automatically."
|
|||
;; Expand INCLUDE keywords.
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward include-re nil t)
|
||||
(let ((element (save-match-data (org-element-at-point))))
|
||||
(when (eq (org-element-type element) 'keyword)
|
||||
(beginning-of-line)
|
||||
;; Extract arguments from keyword's value.
|
||||
(let* ((value (org-element-property :value element))
|
||||
(ind (org-get-indentation))
|
||||
location
|
||||
(file
|
||||
(and (string-match
|
||||
"^\\(\".+?\"\\|\\S-+\\)\\(?:\\s-+\\|$\\)" value)
|
||||
(prog1
|
||||
(save-match-data
|
||||
(let ((matched (match-string 1 value)))
|
||||
(when (string-match "\\(::\\(.*?\\)\\)\"?\\'"
|
||||
matched)
|
||||
(setq location (match-string 2 matched))
|
||||
(setq matched
|
||||
(replace-match "" nil nil matched 1)))
|
||||
(expand-file-name
|
||||
(org-unbracket-string "\"" "\"" matched)
|
||||
dir)))
|
||||
(setq value (replace-match "" nil nil value)))))
|
||||
(only-contents
|
||||
(and (string-match ":only-contents *\\([^: \r\t\n]\\S-*\\)?"
|
||||
value)
|
||||
(prog1 (org-not-nil (match-string 1 value))
|
||||
(setq value (replace-match "" nil nil value)))))
|
||||
(lines
|
||||
(and (string-match
|
||||
":lines +\"\\(\\(?:[0-9]+\\)?-\\(?:[0-9]+\\)?\\)\""
|
||||
value)
|
||||
(prog1 (match-string 1 value)
|
||||
(setq value (replace-match "" nil nil value)))))
|
||||
(env (cond
|
||||
((string-match "\\<example\\>" value) 'literal)
|
||||
((string-match "\\<export\\(?: +\\(.*\\)\\)?" value)
|
||||
'literal)
|
||||
((string-match "\\<src\\(?: +\\(.*\\)\\)?" value)
|
||||
'literal)))
|
||||
;; Minimal level of included file defaults to the child
|
||||
;; level of the current headline, if any, or one. It
|
||||
;; only applies is the file is meant to be included as
|
||||
;; an Org one.
|
||||
(minlevel
|
||||
(and (not env)
|
||||
(if (string-match ":minlevel +\\([0-9]+\\)" value)
|
||||
(prog1 (string-to-number (match-string 1 value))
|
||||
(setq value (replace-match "" nil nil value)))
|
||||
(get-text-property (point)
|
||||
:org-include-induced-level))))
|
||||
(args (and (eq env 'literal) (match-string 1 value)))
|
||||
(block (and (string-match "\\<\\(\\S-+\\)\\>" value)
|
||||
(match-string 1 value))))
|
||||
;; Remove keyword.
|
||||
(delete-region (point) (line-beginning-position 2))
|
||||
(cond
|
||||
((not file) nil)
|
||||
((not (file-readable-p file))
|
||||
(error "Cannot include file %s" file))
|
||||
;; Check if files has already been parsed. Look after
|
||||
;; inclusion lines too, as different parts of the same file
|
||||
;; can be included too.
|
||||
((member (list file lines) included)
|
||||
(error "Recursive file inclusion: %s" file))
|
||||
(t
|
||||
(unless (org-in-commented-heading-p)
|
||||
(let ((element (save-match-data (org-element-at-point))))
|
||||
(when (eq (org-element-type element) 'keyword)
|
||||
(beginning-of-line)
|
||||
;; Extract arguments from keyword's value.
|
||||
(let* ((value (org-element-property :value element))
|
||||
(ind (org-get-indentation))
|
||||
location
|
||||
(file
|
||||
(and (string-match
|
||||
"^\\(\".+?\"\\|\\S-+\\)\\(?:\\s-+\\|$\\)" value)
|
||||
(prog1
|
||||
(save-match-data
|
||||
(let ((matched (match-string 1 value)))
|
||||
(when (string-match "\\(::\\(.*?\\)\\)\"?\\'"
|
||||
matched)
|
||||
(setq location (match-string 2 matched))
|
||||
(setq matched
|
||||
(replace-match "" nil nil matched 1)))
|
||||
(expand-file-name
|
||||
(org-unbracket-string "\"" "\"" matched)
|
||||
dir)))
|
||||
(setq value (replace-match "" nil nil value)))))
|
||||
(only-contents
|
||||
(and (string-match ":only-contents *\\([^: \r\t\n]\\S-*\\)?"
|
||||
value)
|
||||
(prog1 (org-not-nil (match-string 1 value))
|
||||
(setq value (replace-match "" nil nil value)))))
|
||||
(lines
|
||||
(and (string-match
|
||||
":lines +\"\\(\\(?:[0-9]+\\)?-\\(?:[0-9]+\\)?\\)\""
|
||||
value)
|
||||
(prog1 (match-string 1 value)
|
||||
(setq value (replace-match "" nil nil value)))))
|
||||
(env (cond
|
||||
((string-match "\\<example\\>" value) 'literal)
|
||||
((string-match "\\<export\\(?: +\\(.*\\)\\)?" value)
|
||||
'literal)
|
||||
((string-match "\\<src\\(?: +\\(.*\\)\\)?" value)
|
||||
'literal)))
|
||||
;; Minimal level of included file defaults to the
|
||||
;; child level of the current headline, if any, or
|
||||
;; one. It only applies is the file is meant to be
|
||||
;; included as an Org one.
|
||||
(minlevel
|
||||
(and (not env)
|
||||
(if (string-match ":minlevel +\\([0-9]+\\)" value)
|
||||
(prog1 (string-to-number (match-string 1 value))
|
||||
(setq value (replace-match "" nil nil value)))
|
||||
(get-text-property (point)
|
||||
:org-include-induced-level))))
|
||||
(args (and (eq env 'literal) (match-string 1 value)))
|
||||
(block (and (string-match "\\<\\(\\S-+\\)\\>" value)
|
||||
(match-string 1 value))))
|
||||
;; Remove keyword.
|
||||
(delete-region (point) (line-beginning-position 2))
|
||||
(cond
|
||||
((eq env 'literal)
|
||||
(insert
|
||||
(let ((ind-str (make-string ind ?\s))
|
||||
(arg-str (if (stringp args) (format " %s" args) ""))
|
||||
(contents
|
||||
(org-escape-code-in-string
|
||||
(org-export--prepare-file-contents file lines))))
|
||||
(format "%s#+BEGIN_%s%s\n%s%s#+END_%s\n"
|
||||
ind-str block arg-str contents ind-str block))))
|
||||
((stringp block)
|
||||
(insert
|
||||
(let ((ind-str (make-string ind ?\s))
|
||||
(contents
|
||||
(org-export--prepare-file-contents file lines)))
|
||||
(format "%s#+BEGIN_%s\n%s%s#+END_%s\n"
|
||||
ind-str block contents ind-str block))))
|
||||
((not file) nil)
|
||||
((not (file-readable-p file))
|
||||
(error "Cannot include file %s" file))
|
||||
;; Check if files has already been parsed. Look after
|
||||
;; inclusion lines too, as different parts of the same
|
||||
;; file can be included too.
|
||||
((member (list file lines) included)
|
||||
(error "Recursive file inclusion: %s" file))
|
||||
(t
|
||||
(insert
|
||||
(with-temp-buffer
|
||||
(let ((org-inhibit-startup t)
|
||||
(lines
|
||||
(if location
|
||||
(org-export--inclusion-absolute-lines
|
||||
file location only-contents lines)
|
||||
lines)))
|
||||
(org-mode)
|
||||
(insert
|
||||
(org-export--prepare-file-contents
|
||||
file lines ind minlevel
|
||||
(or (gethash file file-prefix)
|
||||
(puthash file (cl-incf current-prefix) file-prefix))
|
||||
footnotes)))
|
||||
(org-export-expand-include-keyword
|
||||
(cons (list file lines) included)
|
||||
(file-name-directory file)
|
||||
footnotes)
|
||||
(buffer-string)))))
|
||||
;; Expand footnotes after all files have been included.
|
||||
;; Footnotes are stored at end of buffer.
|
||||
(unless included
|
||||
(org-with-wide-buffer
|
||||
(goto-char (point-max))
|
||||
(maphash (lambda (k v) (insert (format "\n[fn:%s] %s\n" k v)))
|
||||
footnotes)))))))))))
|
||||
(cond
|
||||
((eq env 'literal)
|
||||
(insert
|
||||
(let ((ind-str (make-string ind ?\s))
|
||||
(arg-str (if (stringp args) (format " %s" args) ""))
|
||||
(contents
|
||||
(org-escape-code-in-string
|
||||
(org-export--prepare-file-contents file lines))))
|
||||
(format "%s#+BEGIN_%s%s\n%s%s#+END_%s\n"
|
||||
ind-str block arg-str contents ind-str block))))
|
||||
((stringp block)
|
||||
(insert
|
||||
(let ((ind-str (make-string ind ?\s))
|
||||
(contents
|
||||
(org-export--prepare-file-contents file lines)))
|
||||
(format "%s#+BEGIN_%s\n%s%s#+END_%s\n"
|
||||
ind-str block contents ind-str block))))
|
||||
(t
|
||||
(insert
|
||||
(with-temp-buffer
|
||||
(let ((org-inhibit-startup t)
|
||||
(lines
|
||||
(if location
|
||||
(org-export--inclusion-absolute-lines
|
||||
file location only-contents lines)
|
||||
lines)))
|
||||
(org-mode)
|
||||
(insert
|
||||
(org-export--prepare-file-contents
|
||||
file lines ind minlevel
|
||||
(or
|
||||
(gethash file file-prefix)
|
||||
(puthash file (cl-incf current-prefix) file-prefix))
|
||||
footnotes)))
|
||||
(org-export-expand-include-keyword
|
||||
(cons (list file lines) included)
|
||||
(file-name-directory file)
|
||||
footnotes)
|
||||
(buffer-string)))))
|
||||
;; Expand footnotes after all files have been
|
||||
;; included. Footnotes are stored at end of buffer.
|
||||
(unless included
|
||||
(org-with-wide-buffer
|
||||
(goto-char (point-max))
|
||||
(maphash (lambda (k v)
|
||||
(insert (format "\n[fn:%s] %s\n" k v)))
|
||||
footnotes))))))))))))
|
||||
|
||||
(defun org-export--inclusion-absolute-lines (file location only-contents lines)
|
||||
"Resolve absolute lines for an included file with file-link.
|
||||
|
|
|
@ -1055,6 +1055,11 @@ Text"
|
|||
(should-error
|
||||
(org-test-with-temp-text "#+INCLUDE: dummy.org"
|
||||
(org-export-expand-include-keyword)))
|
||||
;; Refuse to expand keywords in commented headings.
|
||||
(should
|
||||
(org-test-with-temp-text "* COMMENT H1\n#+INCLUDE: dummy.org"
|
||||
(org-export-expand-include-keyword)
|
||||
t))
|
||||
;; Full insertion with recursive inclusion.
|
||||
(should
|
||||
(equal
|
||||
|
@ -1262,15 +1267,15 @@ Footnotes[fn:2], foot[fn:test] and [fn:inline:inline footnote]
|
|||
;; Adjacent INCLUDE-keywords should have the same :minlevel if unspecified.
|
||||
(should
|
||||
(cl-every (lambda (level) (zerop (1- level)))
|
||||
(org-test-with-temp-text
|
||||
(concat
|
||||
(format "#+INCLUDE: \"%s/examples/include.org::#ah\"\n"
|
||||
org-test-dir)
|
||||
(format "#+INCLUDE: \"%s/examples/include.org::*Heading\""
|
||||
org-test-dir))
|
||||
(org-export-expand-include-keyword)
|
||||
(org-element-map (org-element-parse-buffer) 'headline
|
||||
(lambda (head) (org-element-property :level head))))))
|
||||
(org-test-with-temp-text
|
||||
(concat
|
||||
(format "#+INCLUDE: \"%s/examples/include.org::#ah\"\n"
|
||||
org-test-dir)
|
||||
(format "#+INCLUDE: \"%s/examples/include.org::*Heading\""
|
||||
org-test-dir))
|
||||
(org-export-expand-include-keyword)
|
||||
(org-element-map (org-element-parse-buffer) 'headline
|
||||
(lambda (head) (org-element-property :level head))))))
|
||||
;; INCLUDE does not insert induced :minlevel for src-blocks.
|
||||
(should-not
|
||||
(equal
|
||||
|
|
Loading…
Reference in New Issue