diff options
author | Kyle Meyer <kyle@kyleam.com> | 2022-11-29 23:05:53 -0500 |
---|---|---|
committer | Kyle Meyer <kyle@kyleam.com> | 2022-11-29 23:05:53 -0500 |
commit | 0625651e8a61c9effc31ff771f15885a3a37c6e6 (patch) | |
tree | db4c09e8ef119ad4a9a4028c5e615fd58d2dee69 /lisp/org/ox.el | |
parent | edd64e64a389e0f0e6ce670846d4fae79a9d8b35 (diff) | |
download | emacs-0625651e8a61c9effc31ff771f15885a3a37c6e6.tar.gz emacs-0625651e8a61c9effc31ff771f15885a3a37c6e6.tar.bz2 emacs-0625651e8a61c9effc31ff771f15885a3a37c6e6.zip |
Update to Org 9.6-3-ga4d38e
Diffstat (limited to 'lisp/org/ox.el')
-rw-r--r-- | lisp/org/ox.el | 721 |
1 files changed, 456 insertions, 265 deletions
diff --git a/lisp/org/ox.el b/lisp/org/ox.el index ca6b3f22080..5c0a8f2424b 100644 --- a/lisp/org/ox.el +++ b/lisp/org/ox.el @@ -2,8 +2,8 @@ ;; Copyright (C) 2012-2022 Free Software Foundation, Inc. -;; Author: Nicolas Goaziou <n.goaziou at gmail dot com> -;; Maintainer: Nicolas Goaziou <n.goaziou at gmail dot com> +;; Author: Nicolas Goaziou <mail@nicolasgoaziou.fr> +;; Maintainer: Nicolas Goaziou <mail@nicolasgoaziou.fr> ;; Keywords: outlines, hypermedia, calendar, wp ;; This file is part of GNU Emacs. @@ -72,15 +72,16 @@ ;;; Code: +(require 'org-macs) +(org-assert-version) + (require 'cl-lib) (require 'ob-exp) (require 'oc) -(require 'oc-basic) ;default value for `org-cite-export-processors' (require 'ol) (require 'org-element) (require 'org-macro) (require 'tabulated-list) -(require 'subr-x) (declare-function org-src-coderef-format "org-src" (&optional element)) (declare-function org-src-coderef-regexp "org-src" (fmt &optional label)) @@ -88,6 +89,9 @@ (declare-function org-publish-all "ox-publish" (&optional force async)) (declare-function org-publish-current-file "ox-publish" (&optional force async)) (declare-function org-publish-current-project "ox-publish" (&optional force async)) +(declare-function org-at-heading-p "org" (&optional _)) +(declare-function org-back-to-heading "org" (&optional invisible-ok)) +(declare-function org-next-visible-heading "org" (arg)) (defvar org-publish-project-alist) (defvar org-table-number-fraction) @@ -260,9 +264,13 @@ See `org-export-inline-image-p' for more information about rules.") (defconst org-export-ignored-local-variables - '(org-font-lock-keywords - org-element--cache org-element--cache-objects org-element--cache-sync-keys - org-element--cache-sync-requests org-element--cache-sync-timer) + '( org-font-lock-keywords org-element--cache-change-tic + org-element--cache-change-tic org-element--cache-size + org-element--headline-cache-size + org-element--cache-sync-keys-value + org-element--cache-change-warning org-element--headline-cache + org-element--cache org-element--cache-sync-keys + org-element--cache-sync-requests org-element--cache-sync-timer) "List of variables not copied through upon buffer duplication. Export process takes place on a copy of the original buffer. When this copy is created, all Org related local variables not in @@ -869,8 +877,12 @@ This option can also be set with the OPTIONS keyword, e.g., This variable allows providing shortcuts for export snippets. -For example, with a value of \\='((\"h\" . \"html\")), the -HTML back-end will recognize the contents of \"@@h:<b>@@\" as +For example, with: + + (setq org-export-snippet-translation-alist + \\='((\"h\" . \"html\"))) + +the HTML back-end will recognize the contents of \"@@h:<b>@@\" as HTML code while every other back-end will ignore it." :group 'org-export-general :version "24.4" @@ -1179,7 +1191,7 @@ keywords are understood: Menu entry for the export dispatcher. It should be a list like: - \\='(KEY DESCRIPTION-OR-ORDINAL ACTION-OR-MENU) + (KEY DESCRIPTION-OR-ORDINAL ACTION-OR-MENU) where : @@ -1203,17 +1215,17 @@ keywords are understood: If it is an alist, associations should follow the pattern: - \\='(KEY DESCRIPTION ACTION) + (KEY DESCRIPTION ACTION) where KEY, DESCRIPTION and ACTION are described above. Valid values include: - \\='(?m \"My Special Back-end\" my-special-export-function) + (?m \"My Special Back-end\" my-special-export-function) or - \\='(?l \"Export to LaTeX\" + (?l \"Export to LaTeX\" ((?p \"As PDF file\" org-latex-export-to-pdf) (?o \"As PDF file and open\" (lambda (a s v b) @@ -1224,7 +1236,7 @@ keywords are understood: or the following, which will be added to the previous sub-menu, - \\='(?l 1 + (?l 1 ((?B \"As TEX buffer (Beamer)\" org-beamer-export-as-latex) (?P \"As PDF file (Beamer)\" org-beamer-export-to-pdf))) @@ -1392,14 +1404,15 @@ Optional argument BACKEND is an export back-end, as returned by, e.g., `org-export-create-backend'. It specifies which back-end specific items to read, if any." (let ((line - (let ((s 0) alist) - (while (string-match "\\(.+?\\):\\((.*?)\\|\\S-+\\)?[ \t]*" options s) - (setq s (match-end 0)) - (let ((value (match-string 2 options))) - (when value - (push (cons (match-string 1 options) - (read value)) - alist)))) + (let (alist) + (with-temp-buffer + (insert options) + (goto-char (point-min)) + (while (re-search-forward "\\s-*\\(.+?\\):" nil t) + (when (looking-at-p "\\S-") + (push (cons (match-string 1) + (read (current-buffer))) ; moves point + alist)))) alist)) ;; Priority is given to back-end specific options. (all (append (org-export-get-all-options backend) @@ -1422,7 +1435,7 @@ for export. Return options as a plist." ;; property is the keyword with "EXPORT_" appended to it. (org-with-wide-buffer ;; Make sure point is at a heading. - (if (org-at-heading-p) (org-up-heading-safe) (org-back-to-heading t)) + (org-back-to-heading t) (let ((plist ;; EXPORT_OPTIONS are parsed in a non-standard way. Take ;; care of them right from the start. @@ -1926,34 +1939,28 @@ Return a string." (and (not greaterp) (memq type org-element-recursive-objects))) (contents - (let ((export-buffer (current-buffer))) - (with-temp-buffer - (dolist (element (org-element-contents - (if (or greaterp objectp) data - ;; Elements directly containing - ;; objects must have their indentation - ;; normalized first. - (org-element-normalize-contents - data - ;; When normalizing first paragraph - ;; of an item or - ;; a footnote-definition, ignore - ;; first line's indentation. - (and - (eq type 'paragraph) - (memq (org-element-type parent) - '(footnote-definition item)) - (eq (car (org-element-contents parent)) - data) - (eq (org-element-property :pre-blank parent) - 0)))))) - (insert - ;; Use right local variable - ;; environment if there are, for - ;; example, #+BIND variables. - (with-current-buffer export-buffer - (org-export-data element info)))) - (buffer-string))))) + (mapconcat + (lambda (element) (org-export-data element info)) + (org-element-contents + (if (or greaterp objectp) data + ;; Elements directly containing + ;; objects must have their indentation + ;; normalized first. + (org-element-normalize-contents + data + ;; When normalizing first paragraph + ;; of an item or + ;; a footnote-definition, ignore + ;; first line's indentation. + (and + (eq type 'paragraph) + (memq (org-element-type parent) + '(footnote-definition item)) + (eq (car (org-element-contents parent)) + data) + (eq (org-element-property :pre-blank parent) + 0))))) + ""))) (broken-link-handler (funcall transcoder data (if (not greaterp) contents @@ -2084,8 +2091,8 @@ keywords before output." ;;;; Hooks -(defvar org-export-before-processing-hook nil - "Hook run at the beginning of the export process. +(defvar org-export-before-processing-functions nil + "Abnormal hook run at the beginning of the export process. This is run before include keywords and macros are expanded and Babel code blocks executed, on a copy of the original buffer @@ -2095,8 +2102,8 @@ is at the beginning of the buffer. Every function in this hook will be called with one argument: the back-end currently used, as a symbol.") -(defvar org-export-before-parsing-hook nil - "Hook run before parsing an export buffer. +(defvar org-export-before-parsing-functions nil + "Abnormal hook run before parsing an export buffer. This is run after include keywords and macros have been expanded and Babel code blocks executed, on a copy of the original buffer @@ -2538,100 +2545,176 @@ Return the updated communication channel." ;; a default template (or a back-end specific template) at point or in ;; current subtree. -(defun org-export-copy-buffer () +(cl-defun org-export-copy-buffer (&key to-buffer drop-visibility + drop-narrowing drop-contents + drop-locals) "Return a copy of the current buffer. The copy preserves Org buffer-local variables, visibility and -narrowing." - (let ((copy-buffer-fun (org-export--generate-copy-script (current-buffer))) - (new-buf (generate-new-buffer (buffer-name)))) +narrowing. + +IMPORTANT: The buffer copy may also have `buffer-file-name' copied. +To prevent Emacs overwriting the original buffer file, +`write-contents-functions' is set to (always). Do not alter this +variable and do not do anything that might alter it (like calling a +major mode) to prevent data corruption. Also, do note that Emacs may +jump into the created buffer if the original file buffer is closed and +then re-opened. Making edits in the buffer copy may also trigger +Emacs save dialogue. Prefer using `org-export-with-buffer-copy' macro +when possible. + +When optional key `:to-buffer' is non-nil, copy into BUFFER. + +Optional keys `:drop-visibility', `:drop-narrowing', `:drop-contents', +and `:drop-locals' are passed to `org-export--generate-copy-script'." + (let ((copy-buffer-fun (org-export--generate-copy-script + (current-buffer) + :copy-unreadable 'do-not-check + :drop-visibility drop-visibility + :drop-narrowing drop-narrowing + :drop-contents drop-contents + :drop-locals drop-locals)) + (new-buf (or to-buffer (generate-new-buffer (buffer-name))))) (with-current-buffer new-buf (funcall copy-buffer-fun) (set-buffer-modified-p nil)) new-buf)) -(defmacro org-export-with-buffer-copy (&rest body) +(cl-defmacro org-export-with-buffer-copy ( &rest body + &key to-buffer drop-visibility + drop-narrowing drop-contents + drop-locals + &allow-other-keys) "Apply BODY in a copy of the current buffer. The copy preserves local variables, visibility and contents of the original buffer. Point is at the beginning of the buffer -when BODY is applied." +when BODY is applied. + +Optional keys can modify what is being copied and the generated buffer +copy. `:to-buffer', `:drop-visibility', `:drop-narrowing', +`:drop-contents', and `:drop-locals' are passed as arguments to +`org-export-copy-buffer'." (declare (debug t)) (org-with-gensyms (buf-copy) - `(let ((,buf-copy (org-export-copy-buffer))) + `(let ((,buf-copy (org-export-copy-buffer + :to-buffer ,to-buffer + :drop-visibility ,drop-visibility + :drop-narrowing ,drop-narrowing + :drop-contents ,drop-contents + :drop-locals ,drop-locals))) (unwind-protect (with-current-buffer ,buf-copy (goto-char (point-min)) - (progn ,@body)) + (prog1 + (progn ,@body) + ;; `org-export-copy-buffer' carried the value of + ;; `buffer-file-name' from the original buffer. When not + ;; killed, the new buffer copy may become a target of + ;; `find-file'. Prevent this. + (setq buffer-file-name nil))) (and (buffer-live-p ,buf-copy) ;; Kill copy without confirmation. (progn (with-current-buffer ,buf-copy (restore-buffer-modified-p nil)) - (kill-buffer ,buf-copy))))))) - -(defun org-export--generate-copy-script (buffer) + (unless ,to-buffer + (kill-buffer ,buf-copy)))))))) + +(cl-defun org-export--generate-copy-script (buffer + &key + copy-unreadable + drop-visibility + drop-narrowing + drop-contents + drop-locals) "Generate a function duplicating BUFFER. The copy will preserve local variables, visibility, contents and narrowing of the original buffer. If a region was active in BUFFER, contents will be narrowed to that region instead. +When optional key `:copy-unreadable' is non-nil, do not ensure that all +the copied local variables will be readable in another Emacs session. + +When optional keys `:drop-visibility', `:drop-narrowing', +`:drop-contents', or `:drop-locals' are non-nil, do not preserve +visibility, narrowing, contents, or local variables correspondingly. + The resulting function can be evaluated at a later time, from another buffer, effectively cloning the original buffer there. The function assumes BUFFER's major mode is `org-mode'." (with-current-buffer buffer - (let ((str (org-with-wide-buffer (buffer-string))) + (let ((str (unless drop-contents (org-with-wide-buffer (buffer-string)))) (narrowing - (if (org-region-active-p) - (list (region-beginning) (region-end)) - (list (point-min) (point-max)))) + (unless drop-narrowing + (if (org-region-active-p) + (list (region-beginning) (region-end)) + (list (point-min) (point-max))))) (pos (point)) (varvals - (let ((bound-variables (org-export--list-bound-variables)) - (varvals nil)) - (dolist (entry (buffer-local-variables (buffer-base-buffer))) - (when (consp entry) - (let ((var (car entry)) - (val (cdr entry))) - (and (not (memq var org-export-ignored-local-variables)) - (or (memq var - '(default-directory - buffer-file-name - buffer-file-coding-system)) - (assq var bound-variables) - (string-match "^\\(org-\\|orgtbl-\\)" - (symbol-name var))) - ;; Skip unreadable values, as they cannot be - ;; sent to external process. - (or (not val) (ignore-errors (read (format "%S" val)))) - (push (cons var val) varvals))))) - varvals)) + (unless drop-locals + (let ((bound-variables (org-export--list-bound-variables)) + (varvals nil)) + (dolist (entry (buffer-local-variables (buffer-base-buffer))) + (when (consp entry) + (let ((var (car entry)) + (val (cdr entry))) + (and (not (memq var org-export-ignored-local-variables)) + (or (memq var + '(default-directory + ;; Required to convert file + ;; links in the #+INCLUDEd + ;; files. See + ;; `org-export--prepare-file-contents'. + buffer-file-name + buffer-file-coding-system + ;; Needed to preserve folding state + char-property-alias-alist)) + (assq var bound-variables) + (string-match "^\\(org-\\|orgtbl-\\)" + (symbol-name var))) + ;; Skip unreadable values, as they cannot be + ;; sent to external process. + (or copy-unreadable (not val) + (ignore-errors (read (format "%S" val)))) + (push (cons var val) varvals))))) + varvals))) (ols - (let (ov-set) - (dolist (ov (overlays-in (point-min) (point-max))) - (let ((invis-prop (overlay-get ov 'invisible))) - (when invis-prop - (push (list (overlay-start ov) (overlay-end ov) - invis-prop) - ov-set)))) - ov-set))) + (unless drop-visibility + (let (ov-set) + (dolist (ov (overlays-in (point-min) (point-max))) + (let ((invis-prop (overlay-get ov 'invisible))) + (when invis-prop + (push (list (overlay-start ov) (overlay-end ov) + invis-prop) + ov-set)))) + ov-set)))) (lambda () (let ((inhibit-modification-hooks t)) - ;; Set major mode. Ignore `org-mode-hook' as it has been run - ;; already in BUFFER. - (let ((org-mode-hook nil) (org-inhibit-startup t)) (org-mode)) + ;; Set major mode. Ignore `org-mode-hook' and other hooks as + ;; they have been run already in BUFFER. + (unless (eq major-mode 'org-mode) + (delay-mode-hooks + (let ((org-inhibit-startup t)) (org-mode)))) ;; Copy specific buffer local variables and variables set ;; through BIND keywords. (pcase-dolist (`(,var . ,val) varvals) (set (make-local-variable var) val)) - ;; Whole buffer contents. - (insert str) + ;; Whole buffer contents when requested. + (when str (erase-buffer) (insert str)) + ;; Make org-element-cache not complain about changed buffer + ;; state. + (org-element-cache-reset nil 'no-persistence) ;; Narrowing. - (apply #'narrow-to-region narrowing) + (when narrowing + (apply #'narrow-to-region narrowing)) ;; Current position of point. (goto-char pos) ;; Overlays with invisible property. (pcase-dolist (`(,start ,end ,invis) ols) - (overlay-put (make-overlay start end) 'invisible invis))))))) + (overlay-put (make-overlay start end) 'invisible invis)) + ;; Never write the buffer copy to disk, despite + ;; `buffer-file-name' not being nil. + (setq write-contents-functions (list (lambda (&rest _) t)))))))) (defun org-export--delete-comment-trees () "Delete commented trees and commented inlinetasks in the buffer. @@ -2941,141 +3024,142 @@ still inferior to file-local settings. Return code as a string." (when (symbolp backend) (setq backend (org-export-get-backend backend))) (org-export-barf-if-invalid-backend backend) - (save-excursion - (save-restriction - ;; Narrow buffer to an appropriate region or subtree for - ;; parsing. If parsing subtree, be sure to remove main - ;; headline, planning data and property drawer. - (cond ((org-region-active-p) - (narrow-to-region (region-beginning) (region-end))) - (subtreep - (org-narrow-to-subtree) - (goto-char (point-min)) - (org-end-of-meta-data) - (narrow-to-region (point) (point-max)))) - ;; Initialize communication channel with original buffer - ;; attributes, unavailable in its copy. - (let* ((org-export-current-backend (org-export-backend-name backend)) - (info (org-combine-plists - (org-export--get-export-attributes - backend subtreep visible-only body-only) - (org-export--get-buffer-attributes))) - (parsed-keywords - (delq nil - (mapcar (lambda (o) (and (eq (nth 4 o) 'parse) (nth 1 o))) - (append (org-export-get-all-options backend) - org-export-options-alist)))) - tree modified-tick) - ;; Update communication channel and get parse tree. Buffer - ;; isn't parsed directly. Instead, all buffer modifications - ;; and consequent parsing are undertaken in a temporary copy. - (org-export-with-buffer-copy - (font-lock-mode -1) - ;; Run first hook with current back-end's name as argument. - (run-hook-with-args 'org-export-before-processing-hook - (org-export-backend-name backend)) - (org-export-expand-include-keyword) - (org-export--delete-comment-trees) - (org-macro-initialize-templates org-export-global-macros) - (org-macro-replace-all org-macro-templates parsed-keywords) - ;; Refresh buffer properties and radio targets after previous - ;; potentially invasive changes. - (org-set-regexps-and-options) - (org-update-radio-target-regexp) - (setq modified-tick (buffer-chars-modified-tick)) - ;; Possibly execute Babel code. Re-run a macro expansion - ;; specifically for {{{results}}} since inline source blocks - ;; may have generated some more. Refresh buffer properties - ;; and radio targets another time. - (when org-export-use-babel - (org-babel-exp-process-buffer) - (org-macro-replace-all '(("results" . "$1")) parsed-keywords) + (org-fold-core-ignore-modifications + (save-excursion + (save-restriction + ;; Narrow buffer to an appropriate region or subtree for + ;; parsing. If parsing subtree, be sure to remove main + ;; headline, planning data and property drawer. + (cond ((org-region-active-p) + (narrow-to-region (region-beginning) (region-end))) + (subtreep + (org-narrow-to-subtree) + (goto-char (point-min)) + (org-end-of-meta-data) + (narrow-to-region (point) (point-max)))) + ;; Initialize communication channel with original buffer + ;; attributes, unavailable in its copy. + (let* ((org-export-current-backend (org-export-backend-name backend)) + (info (org-combine-plists + (org-export--get-export-attributes + backend subtreep visible-only body-only) + (org-export--get-buffer-attributes))) + (parsed-keywords + (delq nil + (mapcar (lambda (o) (and (eq (nth 4 o) 'parse) (nth 1 o))) + (append (org-export-get-all-options backend) + org-export-options-alist)))) + tree modified-tick) + ;; Update communication channel and get parse tree. Buffer + ;; isn't parsed directly. Instead, all buffer modifications + ;; and consequent parsing are undertaken in a temporary copy. + (org-export-with-buffer-copy + (font-lock-mode -1) + ;; Run first hook with current back-end's name as argument. + (run-hook-with-args 'org-export-before-processing-hook + (org-export-backend-name backend)) + (org-export-expand-include-keyword) + (org-export--delete-comment-trees) + (org-macro-initialize-templates org-export-global-macros) + (org-macro-replace-all org-macro-templates parsed-keywords) + ;; Refresh buffer properties and radio targets after previous + ;; potentially invasive changes. + (org-set-regexps-and-options) + (org-update-radio-target-regexp) + (setq modified-tick (buffer-chars-modified-tick)) + ;; Possibly execute Babel code. Re-run a macro expansion + ;; specifically for {{{results}}} since inline source blocks + ;; may have generated some more. Refresh buffer properties + ;; and radio targets another time. + (when org-export-use-babel + (org-babel-exp-process-buffer) + (org-macro-replace-all '(("results" . "$1")) parsed-keywords) + (unless (eq modified-tick (buffer-chars-modified-tick)) + (org-set-regexps-and-options) + (org-update-radio-target-regexp)) + (setq modified-tick (buffer-chars-modified-tick))) + ;; Run last hook with current back-end's name as argument. + ;; Update buffer properties and radio targets one last time + ;; before parsing. + (goto-char (point-min)) + (save-excursion + (run-hook-with-args 'org-export-before-parsing-hook + (org-export-backend-name backend))) (unless (eq modified-tick (buffer-chars-modified-tick)) (org-set-regexps-and-options) (org-update-radio-target-regexp)) - (setq modified-tick (buffer-chars-modified-tick))) - ;; Run last hook with current back-end's name as argument. - ;; Update buffer properties and radio targets one last time - ;; before parsing. - (goto-char (point-min)) - (save-excursion - (run-hook-with-args 'org-export-before-parsing-hook - (org-export-backend-name backend))) - (unless (eq modified-tick (buffer-chars-modified-tick)) - (org-set-regexps-and-options) - (org-update-radio-target-regexp)) - (setq modified-tick (buffer-chars-modified-tick)) - ;; Update communication channel with environment. - (setq info - (org-combine-plists - info (org-export-get-environment backend subtreep ext-plist))) - ;; Pre-process citations environment, i.e. install - ;; bibliography list, and citation processor in INFO. - (org-cite-store-bibliography info) - (org-cite-store-export-processor info) - ;; De-activate uninterpreted data from parsed keywords. - (dolist (entry (append (org-export-get-all-options backend) - org-export-options-alist)) - (pcase entry - (`(,p ,_ ,_ ,_ parse) - (let ((value (plist-get info p))) - (plist-put info - p - (org-export--remove-uninterpreted-data value info)))) - (_ nil))) - ;; Install user's and developer's filters. - (setq info (org-export-install-filters info)) - ;; Call options filters and update export options. We do not - ;; use `org-export-filter-apply-functions' here since the - ;; arity of such filters is different. - (let ((backend-name (org-export-backend-name backend))) - (dolist (filter (plist-get info :filter-options)) - (let ((result (funcall filter info backend-name))) - (when result (setq info result))))) - ;; Parse buffer. - (setq tree (org-element-parse-buffer nil visible-only)) - ;; Prune tree from non-exported elements and transform - ;; uninterpreted elements or objects in both parse tree and - ;; communication channel. - (org-export--prune-tree tree info) - (org-export--remove-uninterpreted-data tree info) - ;; Call parse tree filters. - (setq tree - (org-export-filter-apply-functions - (plist-get info :filter-parse-tree) tree info)) - ;; Now tree is complete, compute its properties and add them - ;; to communication channel. - (setq info (org-export--collect-tree-properties tree info)) - ;; Process citations and bibliography. Replace each citation - ;; and "print_bibliography" keyword in the parse tree with - ;; the output of the selected citation export processor. - (org-cite-process-citations info) - (org-cite-process-bibliography info) - ;; Eventually transcode TREE. Wrap the resulting string into - ;; a template. - (let* ((body (org-element-normalize-string - (or (org-export-data tree info) ""))) - (inner-template (cdr (assq 'inner-template - (plist-get info :translate-alist)))) - (full-body (org-export-filter-apply-functions - (plist-get info :filter-body) - (if (not (functionp inner-template)) body - (funcall inner-template body info)) - info)) - (template (cdr (assq 'template - (plist-get info :translate-alist)))) - (output - (if (or (not (functionp template)) body-only) full-body - (funcall template full-body info)))) - ;; Call citation export finalizer. - (setq output (org-cite-finalize-export output info)) - ;; Remove all text properties since they cannot be - ;; retrieved from an external process. Finally call - ;; final-output filter and return result. - (org-no-properties - (org-export-filter-apply-functions - (plist-get info :filter-final-output) - output info)))))))) + (setq modified-tick (buffer-chars-modified-tick)) + ;; Update communication channel with environment. + (setq info + (org-combine-plists + info (org-export-get-environment backend subtreep ext-plist))) + ;; Pre-process citations environment, i.e. install + ;; bibliography list, and citation processor in INFO. + (org-cite-store-bibliography info) + (org-cite-store-export-processor info) + ;; De-activate uninterpreted data from parsed keywords. + (dolist (entry (append (org-export-get-all-options backend) + org-export-options-alist)) + (pcase entry + (`(,p ,_ ,_ ,_ parse) + (let ((value (plist-get info p))) + (plist-put info + p + (org-export--remove-uninterpreted-data value info)))) + (_ nil))) + ;; Install user's and developer's filters. + (setq info (org-export-install-filters info)) + ;; Call options filters and update export options. We do not + ;; use `org-export-filter-apply-functions' here since the + ;; arity of such filters is different. + (let ((backend-name (org-export-backend-name backend))) + (dolist (filter (plist-get info :filter-options)) + (let ((result (funcall filter info backend-name))) + (when result (setq info result))))) + ;; Parse buffer. + (setq tree (org-element-parse-buffer nil visible-only)) + ;; Prune tree from non-exported elements and transform + ;; uninterpreted elements or objects in both parse tree and + ;; communication channel. + (org-export--prune-tree tree info) + (org-export--remove-uninterpreted-data tree info) + ;; Call parse tree filters. + (setq tree + (org-export-filter-apply-functions + (plist-get info :filter-parse-tree) tree info)) + ;; Now tree is complete, compute its properties and add them + ;; to communication channel. + (setq info (org-export--collect-tree-properties tree info)) + ;; Process citations and bibliography. Replace each citation + ;; and "print_bibliography" keyword in the parse tree with + ;; the output of the selected citation export processor. + (org-cite-process-citations info) + (org-cite-process-bibliography info) + ;; Eventually transcode TREE. Wrap the resulting string into + ;; a template. + (let* ((body (org-element-normalize-string + (or (org-export-data tree info) ""))) + (inner-template (cdr (assq 'inner-template + (plist-get info :translate-alist)))) + (full-body (org-export-filter-apply-functions + (plist-get info :filter-body) + (if (not (functionp inner-template)) body + (funcall inner-template body info)) + info)) + (template (cdr (assq 'template + (plist-get info :translate-alist)))) + (output + (if (or (not (functionp template)) body-only) full-body + (funcall template full-body info)))) + ;; Call citation export finalizer. + (setq output (org-cite-finalize-export output info)) + ;; Remove all text properties since they cannot be + ;; retrieved from an external process. Finally call + ;; final-output filter and return result. + (org-no-properties + (org-export-filter-apply-functions + (plist-get info :filter-final-output) + output info))))))))) ;;;###autoload (defun org-export-string-as (string backend &optional body-only ext-plist) @@ -3226,7 +3310,7 @@ storing and resolving footnotes. It is created automatically." (beginning-of-line) ;; Extract arguments from keyword's value. (let* ((value (org-element-property :value element)) - (ind (current-indentation)) + (ind (org-current-text-indentation)) location (coding-system-for-read (or (and (string-match ":coding +\\(\\S-+\\)>" value) @@ -3238,15 +3322,18 @@ storing and resolving footnotes. It is created automatically." value) (prog1 (save-match-data - (let ((matched (match-string 1 value))) + (let ((matched (match-string 1 value)) + stripped) (when (string-match "\\(::\\(.*?\\)\\)\"?\\'" matched) (setq location (match-string 2 matched)) (setq matched (replace-match "" nil nil matched 1))) - (expand-file-name (org-strip-quotes matched) - dir))) - (setq value (replace-match "" nil nil value))))) + (setq stripped (org-strip-quotes matched)) + (if (org-url-p stripped) + stripped + (expand-file-name stripped dir)))) + (setq value (replace-match "" nil nil value))))) (only-contents (and (string-match ":only-contents *\\([^: \r\t\n]\\S-*\\)?" value) @@ -3282,7 +3369,7 @@ storing and resolving footnotes. It is created automatically." (delete-region (point) (line-beginning-position 2)) (cond ((not file) nil) - ((not (file-readable-p file)) + ((and (not (org-url-p file)) (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 @@ -3328,8 +3415,9 @@ storing and resolving footnotes. It is created automatically." includer-file))) (org-export-expand-include-keyword (cons (list file lines) included) - (file-name-directory file) - footnotes) + (unless (org-url-p file) + (file-name-directory file)) + footnotes) (buffer-string))))) ;; Expand footnotes after all files have been ;; included. Footnotes are stored at end of buffer. @@ -3352,7 +3440,7 @@ Org-Element. If LINES is non-nil only those lines are included. Return a string of lines to be included in the format expected by `org-export--prepare-file-contents'." (with-temp-buffer - (insert-file-contents file) + (insert (org-file-contents file)) (unless (eq major-mode 'org-mode) (let ((org-inhibit-startup t)) (org-mode))) (condition-case err @@ -3457,7 +3545,7 @@ the included document. Optional argument INCLUDER is the file name where the inclusion is to happen." (with-temp-buffer - (insert-file-contents file) + (insert (org-file-contents file)) (when lines (let* ((lines (split-string lines "-")) (lbeg (string-to-number (car lines))) @@ -4109,7 +4197,7 @@ meant to be translated with `org-export-data' or alike." ((and fmt (not (cdr date)) (eq (org-element-type (car date)) 'timestamp)) - (org-timestamp-format (car date) fmt)) + (org-format-timestamp (car date) fmt)) (t date)))) @@ -4146,7 +4234,7 @@ meant to be translated with `org-export-data' or alike." ;; `org-export-data' for further processing, depending on ;; `org-export-with-broken-links' value. -(org-define-error 'org-link-broken "Unable to resolve link; aborting") +(define-error 'org-link-broken "Unable to resolve link; aborting") (defun org-export-custom-protocol-maybe (link desc backend &optional info) "Try exporting LINK object with a dedicated function. @@ -4294,7 +4382,7 @@ A search cell follows the pattern (TYPE . SEARCH) where - target's or radio-target's name as a list of strings if TYPE is `target'. - - NAME affiliated keyword if TYPE is `other'. + - NAME or RESULTS affiliated keyword if TYPE is `other'. A search cell is the internal representation of a fuzzy link. It ignores white spaces and statistics cookies, if applicable." @@ -4312,7 +4400,8 @@ ignores white spaces and statistics cookies, if applicable." (and custom-id (cons 'custom-id custom-id))))))) (`target (list (cons 'target (split-string (org-element-property :value datum))))) - ((and (let name (org-element-property :name datum)) + ((and (let name (or (org-element-property :name datum) + (car (org-element-property :results datum)))) (guard name)) (list (cons 'other (split-string name)))) (_ nil))) @@ -4344,8 +4433,9 @@ Return value can be an object or an element: - If LINK path matches a target object (i.e. <<path>>) return it. -- If LINK path exactly matches the name affiliated keyword - (i.e. #+NAME: path) of an element, return that element. +- If LINK path exactly matches the name or results affiliated keyword + (i.e. #+NAME: path or #+RESULTS: name) of an element, return that + element. - If LINK path exactly matches any headline name, return that element. @@ -4437,11 +4527,11 @@ INFO is a plist used as a communication channel. Return value can be a radio-target object or nil. Assume LINK has type \"radio\"." - (let ((path (string-clean-whitespace (org-element-property :path link)))) + (let ((path (org-string-clean-whitespace (org-element-property :path link)))) (org-element-map (plist-get info :parse-tree) 'radio-target (lambda (radio) - (and (string-equal-ignore-case - (string-clean-whitespace (org-element-property :value radio)) + (and (org-string-equal-ignore-case + (org-string-clean-whitespace (org-element-property :value radio)) path) radio)) info 'first-match))) @@ -4481,6 +4571,50 @@ Return value can be an object or an element: (concat (if (string-prefix-p "/" fullname) "file://" "file:///") fullname))))) +(defun org-export-link-remote-p (link) + "Returns non-nil if the link refers to a remote resource." + (or (member (org-element-property :type link) '("http" "https" "ftp")) + (and (string= (org-element-property :type link) "file") + (file-remote-p (org-element-property :path link))))) + +(defun org-export-link--remote-local-copy (link) + "Download the remote resource specified by LINK, and return its local path." + ;; TODO work this into ol.el as a link parameter, say :download. + (let* ((location-type + (pcase (org-element-property :type link) + ((or "http" "https" "ftp") 'url) + ((and "file" (guard (file-remote-p + (org-element-property :path link)))) + 'file) + (_ (error "Cannot copy %s:%s to a local file" + (org-element-property :type link) + (org-element-property :path link))))) + (path + (pcase location-type + ('url + (concat (org-element-property :type link) + ":" (org-element-property :path link))) + ('file + (org-element-property :path link))))) + (or (org-persist-read location-type path) + (org-persist-register location-type path + :write-immediately t)))) + +(require 'subr-x) ;; FIXME: For `thread-first' in Emacs 26. +(defun org-export-link-localise (link) + "Convert remote LINK to local link. +If LINK refers to a remote resource, modify it to point to a local +downloaded copy. Otherwise, return unchanged LINK." + (when (org-export-link-remote-p link) + (let* ((local-path (org-export-link--remote-local-copy link))) + (setcdr link + (thread-first (cadr link) + (plist-put :type "file") + (plist-put :path local-path) + (plist-put :raw-link (concat "file:" local-path)) + list)))) + link) + ;;;; For References ;; ;; `org-export-get-reference' associate a unique reference for any @@ -4616,10 +4750,22 @@ objects of the same type." (org-element-map (plist-get info :parse-tree) (or types (org-element-type element)) (lambda (el) - (cond - ((eq element el) (1+ counter)) - ((not predicate) (cl-incf counter) nil) - ((funcall predicate el info) (cl-incf counter) nil))) + (let ((cached (org-element-property :org-export--counter el))) + (cond + ((eq element el) (1+ counter)) + ;; Use cached result. + ((and cached (equal predicate (car cached))) + (cdr cached)) + ((not predicate) + (cl-incf counter) + (org-element-put-property + el :org-export--counter (cons predicate counter)) + nil) + ((funcall predicate el info) + (cl-incf counter) + (org-element-put-property + el :org-export--counter (cons predicate counter)) + nil)))) info 'first-match))))) ;;;; For Raw objects @@ -5505,6 +5651,18 @@ transcoding it." (secondary-opening :utf-8 "“" :html "“" :latex "``" :texinfo "``") (secondary-closing :utf-8 "”" :html "”" :latex "''" :texinfo "''") (apostrophe :utf-8 "’" :html "’")) + ("fa" + (primary-opening + :utf-8 "«" :html "«" :latex "\\guillemotleft{}" + :texinfo "@guillemetleft{}") + (primary-closing + :utf-8 "»" :html "»" :latex "\\guillemotright{}" + :texinfo "@guillemetright{}") + (secondary-opening :utf-8 "‹" :html "‹" :latex "\\guilsinglleft{}" + :texinfo "@guilsinglleft{}") + (secondary-closing :utf-8 "›" :html "›" :latex "\\guilsinglright{}" + :texinfo "@guilsinglright{}") + (apostrophe :utf-8 "’" :html "’")) ("fr" (primary-opening :utf-8 "« " :html "« " :latex "\\og " @@ -5826,6 +5984,7 @@ them." ("eo" :html "Aŭtoro") ("es" :default "Autor") ("et" :default "Autor") + ("fa" :default "نویسنده") ("fi" :html "Tekijä") ("fr" :default "Auteur") ("hu" :default "Szerzõ") @@ -5851,6 +6010,7 @@ them." ("cs" :default "Pokračování z předchozí strany") ("de" :default "Fortsetzung von vorheriger Seite") ("es" :html "Continúa de la página anterior" :ascii "Continua de la pagina anterior" :default "Continúa de la página anterior") + ("fa" :default "ادامه از صفحهٔ قبل") ("fr" :default "Suite de la page précédente") ("it" :default "Continua da pagina precedente") ("ja" :default "前ページからの続き") @@ -5868,6 +6028,7 @@ them." ("cs" :default "Pokračuje na další stránce") ("de" :default "Fortsetzung nächste Seite") ("es" :html "Continúa en la siguiente página" :ascii "Continua en la siguiente pagina" :default "Continúa en la siguiente página") + ("fa" :default "ادامه در صفحهٔ بعد") ("fr" :default "Suite page suivante") ("it" :default "Continua alla pagina successiva") ("ja" :default "次ページに続く") @@ -5882,6 +6043,7 @@ them." ("tr" :default "Devamı sonraki sayfada")) ("Created" ("cs" :default "Vytvořeno") + ("fa" :default "ساخته شده") ("nl" :default "Gemaakt op") ;; must be followed by a date or date+time ("pt_BR" :default "Criado em") ("ro" :default "Creat") @@ -5896,6 +6058,7 @@ them." ("eo" :default "Dato") ("es" :default "Fecha") ("et" :html "Kuupäev" :utf-8 "Kuupäev") + ("fa" :default "تاریخ") ("fi" :html "Päivämäärä") ("hu" :html "Dátum") ("is" :default "Dagsetning") @@ -5922,6 +6085,7 @@ them." ("de" :default "Gleichung") ("es" :ascii "Ecuacion" :html "Ecuación" :default "Ecuación") ("et" :html "Võrrand" :utf-8 "Võrrand") + ("fa" :default "معادله") ("fr" :ascii "Equation" :default "Équation") ("is" :default "Jafna") ("ja" :default "方程式") @@ -5944,6 +6108,7 @@ them." ("de" :default "Abbildung") ("es" :default "Figura") ("et" :default "Joonis") + ("fa" :default "شکل") ("is" :default "Mynd") ("it" :default "Figura") ("ja" :default "図" :html "図") @@ -5964,6 +6129,7 @@ them." ("de" :default "Abbildung %d:") ("es" :default "Figura %d:") ("et" :default "Joonis %d:") + ("fa" :default "شکل %d:") ("fr" :default "Figure %d :" :html "Figure %d :") ("is" :default "Mynd %d") ("it" :default "Figura %d:") @@ -5988,6 +6154,7 @@ them." ("eo" :default "Piednotoj") ("es" :ascii "Notas al pie de pagina" :html "Notas al pie de página" :default "Notas al pie de página") ("et" :html "Allmärkused" :utf-8 "Allmärkused") + ("fa" :default "پانوشتها") ("fi" :default "Alaviitteet") ("fr" :default "Notes de bas de page") ("hu" :html "Lábjegyzet") @@ -6016,6 +6183,7 @@ them." ("de" :default "Programmauflistungsverzeichnis") ("es" :ascii "Indice de Listados de programas" :html "Índice de Listados de programas" :default "Índice de Listados de programas") ("et" :default "Loendite nimekiri") + ("fa" :default "فهرست برنامهریزیها") ("fr" :default "Liste des programmes") ("ja" :default "ソースコード目次") ("nl" :default "Lijst van programma's") @@ -6034,6 +6202,7 @@ them." ("de" :default "Tabellenverzeichnis") ("es" :ascii "Indice de tablas" :html "Índice de tablas" :default "Índice de tablas") ("et" :default "Tabelite nimekiri") + ("fa" :default "فهرست جدولها") ("fr" :default "Liste des tableaux") ("is" :default "Töfluskrá" :html "Töfluskrá") ("it" :default "Indice delle tabelle") @@ -6057,6 +6226,7 @@ them." ("de" :default "Programmlisting") ("es" :default "Listado de programa") ("et" :default "Loend") + ("fa" :default "برنامهریزی") ("fr" :default "Programme" :html "Programme") ("it" :default "Listato") ("ja" :default "ソースコード") @@ -6077,6 +6247,7 @@ them." ("de" :default "Programmlisting %d") ("es" :default "Listado de programa %d") ("et" :default "Loend %d") + ("fa" :default "برنامهریزی %d:") ("fr" :default "Programme %d :" :html "Programme %d :") ("it" :default "Listato %d :") ("ja" :default "ソースコード%d:") @@ -6095,6 +6266,7 @@ them." ("cs" :default "Reference") ("de" :default "Quellen") ("es" :default "Referencias") + ("fa" :default "منابع") ("fr" :ascii "References" :default "Références") ("it" :default "Riferimenti") ("nl" :default "Bronverwijzingen") @@ -6104,6 +6276,7 @@ them." ("tr" :default "Referanslar")) ("See figure %s" ("cs" :default "Viz obrázek %s") + ("fa" :default "نمایش شکل %s") ("fr" :default "cf. figure %s" :html "cf. figure %s" :latex "cf.~figure~%s") ("it" :default "Vedi figura %s") @@ -6115,6 +6288,7 @@ them." ("tr" :default "bkz. şekil %s")) ("See listing %s" ("cs" :default "Viz program %s") + ("fa" :default "نمایش برنامهریزی %s") ("fr" :default "cf. programme %s" :html "cf. programme %s" :latex "cf.~programme~%s") ("nl" :default "Zie programma %s" @@ -6130,6 +6304,7 @@ them." ("de" :default "siehe Abschnitt %s") ("es" :ascii "Vea seccion %s" :html "Vea sección %s" :default "Vea sección %s") ("et" :html "Vaata peatükki %s" :utf-8 "Vaata peatükki %s") + ("fa" :default "نمایش بخش %s") ("fr" :default "cf. section %s") ("it" :default "Vedi sezione %s") ("ja" :default "セクション %s を参照") @@ -6145,6 +6320,7 @@ them." ("zh-CN" :html "参见第%s节" :utf-8 "参见第%s节")) ("See table %s" ("cs" :default "Viz tabulka %s") + ("fa" :default "نمایش جدول %s") ("fr" :default "cf. tableau %s" :html "cf. tableau %s" :latex "cf.~tableau~%s") ("it" :default "Vedi tabella %s") @@ -6160,6 +6336,7 @@ them." ("de" :default "Tabelle") ("es" :default "Tabla") ("et" :default "Tabel") + ("fa" :default "جدول") ("fr" :default "Tableau") ("is" :default "Tafla") ("it" :default "Tabella") @@ -6178,6 +6355,7 @@ them." ("de" :default "Tabelle %d") ("es" :default "Tabla %d") ("et" :default "Tabel %d") + ("fa" :default "جدول %d") ("fr" :default "Tableau %d :") ("is" :default "Tafla %d") ("it" :default "Tabella %d:") @@ -6203,6 +6381,7 @@ them." ("eo" :default "Enhavo") ("es" :ascii "Indice" :html "Índice" :default "Índice") ("et" :default "Sisukord") + ("fa" :default "فهرست") ("fi" :html "Sisällysluettelo") ("fr" :ascii "Sommaire" :default "Table des matières") ("hu" :html "Tartalomjegyzék") @@ -6230,6 +6409,7 @@ them." ("de" :default "Unbekannter Verweis") ("es" :default "Referencia desconocida") ("et" :default "Tundmatu viide") + ("fa" :default "منبع ناشناس") ("fr" :ascii "Destination inconnue" :default "Référence inconnue") ("it" :default "Riferimento sconosciuto") ("ja" :default "不明な参照先") @@ -6489,8 +6669,7 @@ or FILE." (declare (indent 2)) (if (not (file-writable-p file)) (error "Output file not writable") (let ((ext-plist (org-combine-plists `(:output-file ,file) ext-plist)) - (encoding (or org-export-coding-system buffer-file-coding-system)) - auto-mode-alist) + (encoding (or org-export-coding-system buffer-file-coding-system))) (if async (org-export-async-start (lambda (file) @@ -6502,14 +6681,14 @@ or FILE." (with-temp-buffer (insert output) (let ((coding-system-for-write ',encoding)) - (write-file ,file))) + (write-region (point-min) (point-max) ,file))) (or (ignore-errors (funcall ',post-process ,file)) ,file))) (let ((output (org-export-as backend subtreep visible-only body-only ext-plist))) (with-temp-buffer (insert output) (let ((coding-system-for-write encoding)) - (write-file file))) + (write-region (point-min) (point-max) file))) (when (and (org-export--copy-to-kill-ring-p) (org-string-nw-p output)) (org-kill-new output)) ;; Get proper return value. @@ -6965,8 +7144,20 @@ back to standard interface." (delete-other-windows) (org-switch-to-buffer-other-window (get-buffer-create "*Org Export Dispatcher*")) - (setq cursor-type nil - header-line-format "Use SPC, DEL, C-n or C-p to navigate.") + (setq cursor-type nil) + (setq header-line-format + (let ((propertize-help-key + (lambda (key) + ;; Add `face' *and* `font-lock-face' to "work + ;; reliably in any buffer", per a comment in + ;; `help--key-description-fontified'. + (propertize key + 'font-lock-face 'help-key-binding + 'face 'help-key-binding)))) + (apply 'format + (cons "Use %s, %s, %s, or %s to navigate." + (mapcar propertize-help-key + (list "SPC" "DEL" "C-n" "C-p")))))) ;; Make sure that invisible cursor will not highlight square ;; brackets. (set-syntax-table (copy-syntax-table)) |