summaryrefslogtreecommitdiff
path: root/lisp/ox.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/ox.el')
-rw-r--r--lisp/ox.el2135
1 files changed, 1140 insertions, 995 deletions
diff --git a/lisp/ox.el b/lisp/ox.el
index b9c99eb..d3d1a0e 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -1,4 +1,4 @@
-;;; ox.el --- Generic Export Engine for Org Mode
+;;; ox.el --- Export Framework for Org Mode -*- lexical-binding: t; -*-
;; Copyright (C) 2012-2016 Free Software Foundation, Inc.
@@ -71,17 +71,18 @@
;;; Code:
-(eval-when-compile (require 'cl))
+(require 'cl-lib)
+(require 'ob-exp)
(require 'org-element)
(require 'org-macro)
-(require 'ob-exp)
+(require 'tabulated-list)
(declare-function org-publish "ox-publish" (project &optional force async))
(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-publish-current-file "ox-publish" (&optional force async))
+(declare-function org-publish-current-project "ox-publish" (&optional force async))
+(declare-function org-src-coderef-format "org-src" (&optional element))
+(declare-function org-src-coderef-regexp "org-src" (fmt &optional label))
(defvar org-publish-project-alist)
(defvar org-table-number-fraction)
@@ -112,6 +113,7 @@
(:time-stamp-file nil "timestamp" org-export-time-stamp-file)
(:with-archived-trees nil "arch" org-export-with-archived-trees)
(:with-author nil "author" org-export-with-author)
+ (:with-broken-links nil "broken-links" org-export-with-broken-links)
(:with-clocks nil "c" org-export-with-clocks)
(:with-creator nil "creator" org-export-with-creator)
(:with-date nil "date" org-export-with-date)
@@ -345,21 +347,24 @@ e.g. \"arch:nil\"."
:type '(choice
(const :tag "Not at all" nil)
(const :tag "Headline only" headline)
- (const :tag "Entirely" t)))
+ (const :tag "Entirely" t))
+ :safe (lambda (x) (memq x '(t nil headline))))
(defcustom org-export-with-author t
"Non-nil means insert author name into the exported file.
This option can also be set with the OPTIONS keyword,
e.g. \"author:nil\"."
:group 'org-export-general
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-with-clocks nil
"Non-nil means export CLOCK keywords.
This option can also be set with the OPTIONS keyword,
e.g. \"c:t\"."
:group 'org-export-general
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-with-creator nil
"Non-nil means the postamble should contain a creator sentence.
@@ -370,16 +375,18 @@ see.
This option can also be set with the OPTIONS keyword, e.g.,
\"creator:t\"."
:group 'org-export-general
- :version "25.1"
+ :version "25.2"
:package-version '(Org . "8.3")
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-with-date t
"Non-nil means insert date in the exported document.
This option can also be set with the OPTIONS keyword,
e.g. \"date:nil\"."
:group 'org-export-general
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-date-timestamp-format nil
"Time-stamp format string to use for DATE keyword.
@@ -392,7 +399,8 @@ string."
:group 'org-export-general
:type '(choice
(string :tag "Time-stamp format string")
- (const :tag "No format string" nil)))
+ (const :tag "No format string" nil))
+ :safe (lambda (x) (or (null x) (stringp x))))
(defcustom org-export-creator-string
(format "Emacs %s (Org mode %s)"
@@ -401,7 +409,8 @@ string."
"Information about the creator of the document.
This option can also be set on with the CREATOR keyword."
:group 'org-export-general
- :type '(string :tag "Creator string"))
+ :type '(string :tag "Creator string")
+ :safe #'stringp)
(defcustom org-export-with-drawers '(not "LOGBOOK")
"Non-nil means export contents of standard drawers.
@@ -427,14 +436,20 @@ e.g. \"d:nil\"."
(const :format "" not)
(repeat :tag "Specify names of drawers to ignore during export"
:inline t
- (string :tag "Drawer name")))))
+ (string :tag "Drawer name"))))
+ :safe (lambda (x) (or (booleanp x)
+ (and (listp x)
+ (or (cl-every #'stringp x)
+ (and (eq (nth 0 x) 'not)
+ (cl-every #'stringp (cdr x))))))))
(defcustom org-export-with-email nil
"Non-nil means insert author email into the exported file.
This option can also be set with the OPTIONS keyword,
e.g. \"email:t\"."
:group 'org-export-general
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-with-emphasize t
"Non-nil means interpret *word*, /word/, _word_ and +word+.
@@ -446,7 +461,8 @@ respectively.
This option can also be set with the OPTIONS keyword,
e.g. \"*:nil\"."
:group 'org-export-general
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-exclude-tags '("noexport")
"Tags that exclude a tree from export.
@@ -457,7 +473,8 @@ carry one of the `org-export-select-tags' will be removed.
This option can also be set with the EXCLUDE_TAGS keyword."
:group 'org-export-general
- :type '(repeat (string :tag "Tag")))
+ :type '(repeat (string :tag "Tag"))
+ :safe (lambda (x) (and (listp x) (cl-every #'stringp x))))
(defcustom org-export-with-fixed-width t
"Non-nil means export lines starting with \":\".
@@ -466,14 +483,16 @@ e.g. \"::nil\"."
:group 'org-export-general
:version "24.4"
:package-version '(Org . "8.0")
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-with-footnotes t
"Non-nil means Org footnotes should be exported.
This option can also be set with the OPTIONS keyword,
e.g. \"f:nil\"."
:group 'org-export-general
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-with-latex t
"Non-nil means process LaTeX environments and fragments.
@@ -490,7 +509,8 @@ t Allow export of math snippets."
:type '(choice
(const :tag "Do not process math in any way" nil)
(const :tag "Interpret math snippets" t)
- (const :tag "Leave math verbatim" verbatim)))
+ (const :tag "Leave math verbatim" verbatim))
+ :safe (lambda (x) (memq x '(t nil verbatim))))
(defcustom org-export-headline-levels 3
"The last level which is still exported as a headline.
@@ -501,7 +521,8 @@ when exported, but back-end behavior may differ.
This option can also be set with the OPTIONS keyword,
e.g. \"H:2\"."
:group 'org-export-general
- :type 'integer)
+ :type 'integer
+ :safe #'integerp)
(defcustom org-export-default-language "en"
"The default language for export and clocktable translations, as a string.
@@ -510,14 +531,16 @@ This may have an association in
`org-export-smart-quotes-alist' and `org-export-dictionary'.
This option can also be set with the LANGUAGE keyword."
:group 'org-export-general
- :type '(string :tag "Language"))
+ :type '(string :tag "Language")
+ :safe #'stringp)
(defcustom org-export-preserve-breaks nil
"Non-nil means preserve all line breaks when exporting.
This option can also be set with the OPTIONS keyword,
e.g. \"\\n:t\"."
:group 'org-export-general
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-with-entities t
"Non-nil means interpret entities when exporting.
@@ -531,7 +554,8 @@ and the user option `org-entities-user'.
This option can also be set with the OPTIONS keyword,
e.g. \"e:nil\"."
:group 'org-export-general
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-with-inlinetasks t
"Non-nil means inlinetasks should be exported.
@@ -540,7 +564,8 @@ e.g. \"inline:nil\"."
:group 'org-export-general
:version "24.4"
:package-version '(Org . "8.0")
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-with-planning nil
"Non-nil means include planning info in export.
@@ -553,14 +578,16 @@ e.g. \"p:t\"."
:group 'org-export-general
:version "24.4"
:package-version '(Org . "8.0")
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-with-priority nil
"Non-nil means include priority cookies in export.
This option can also be set with the OPTIONS keyword,
e.g. \"pri:t\"."
:group 'org-export-general
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-with-properties nil
"Non-nil means export contents of properties drawers.
@@ -577,7 +604,9 @@ e.g. \"prop:t\"."
(const :tag "All properties" t)
(const :tag "None" nil)
(repeat :tag "Selected properties"
- (string :tag "Property name"))))
+ (string :tag "Property name")))
+ :safe (lambda (x) (or (booleanp x)
+ (and (listp x) (cl-every #'stringp x)))))
(defcustom org-export-with-section-numbers t
"Non-nil means add section numbers to headlines when exporting.
@@ -588,7 +617,8 @@ headlines whose relative level is higher or equal to n.
This option can also be set with the OPTIONS keyword,
e.g. \"num:t\"."
:group 'org-export-general
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-select-tags '("export")
"Tags that select a tree for export.
@@ -600,7 +630,8 @@ tagging it with one of the `org-export-exclude-tags'.
This option can also be set with the SELECT_TAGS keyword."
:group 'org-export-general
- :type '(repeat (string :tag "Tag")))
+ :type '(repeat (string :tag "Tag"))
+ :safe (lambda (x) (and (listp x) (cl-every #'stringp x))))
(defcustom org-export-with-smart-quotes nil
"Non-nil means activate smart quotes during export.
@@ -615,7 +646,8 @@ E.g., you can load Babel for french like this:
:group 'org-export-general
:version "24.4"
:package-version '(Org . "8.0")
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-with-special-strings t
"Non-nil means interpret \"\\-\", \"--\" and \"---\" for export.
@@ -632,7 +664,8 @@ When this option is turned on, these strings will be exported as:
This option can also be set with the OPTIONS keyword,
e.g. \"-:nil\"."
:group 'org-export-general
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-with-statistics-cookies t
"Non-nil means include statistics cookies in export.
@@ -641,7 +674,8 @@ e.g. \"stat:nil\""
:group 'org-export-general
:version "24.4"
:package-version '(Org . "8.0")
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-with-sub-superscripts t
"Non-nil means interpret \"_\" and \"^\" for export.
@@ -678,7 +712,8 @@ frequently in plain text."
:type '(choice
(const :tag "Interpret them" t)
(const :tag "Curly brackets only" {})
- (const :tag "Do not interpret them" nil)))
+ (const :tag "Do not interpret them" nil))
+ :safe (lambda (x) (memq x '(t nil {}))))
(defcustom org-export-with-toc t
"Non-nil means create a table of contents in exported files.
@@ -696,7 +731,9 @@ e.g. \"toc:nil\" or \"toc:3\"."
:type '(choice
(const :tag "No Table of Contents" nil)
(const :tag "Full Table of Contents" t)
- (integer :tag "TOC to level")))
+ (integer :tag "TOC to level"))
+ :safe (lambda (x) (or (booleanp x)
+ (integerp x))))
(defcustom org-export-with-tables t
"Non-nil means export tables.
@@ -705,7 +742,8 @@ e.g. \"|:nil\"."
:group 'org-export-general
:version "24.4"
:package-version '(Org . "8.0")
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-with-tags t
"If nil, do not export tags, just remove them from headlines.
@@ -720,7 +758,8 @@ e.g. \"tags:nil\"."
:type '(choice
(const :tag "Off" nil)
(const :tag "Not in TOC" not-in-toc)
- (const :tag "On" t)))
+ (const :tag "On" t))
+ :safe (lambda (x) (memq x '(t nil not-in-toc))))
(defcustom org-export-with-tasks t
"Non-nil means include TODO items for export.
@@ -741,23 +780,28 @@ e.g. \"tasks:nil\"."
(const :tag "Not-done tasks" todo)
(const :tag "Only done tasks" done)
(repeat :tag "Specific TODO keywords"
- (string :tag "Keyword"))))
+ (string :tag "Keyword")))
+ :safe (lambda (x) (or (memq x '(nil t todo done))
+ (and (listp x)
+ (cl-every #'stringp x)))))
(defcustom org-export-with-title t
"Non-nil means print title into the exported file.
This option can also be set with the OPTIONS keyword,
e.g. \"title:nil\"."
:group 'org-export-general
- :version "25.1"
+ :version "25.2"
:package-version '(Org . "8.3")
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-time-stamp-file t
"Non-nil means insert a time stamp into the exported file.
The time stamp shows when the file was created. This option can
also be set with the OPTIONS keyword, e.g. \"timestamp:nil\"."
:group 'org-export-general
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom org-export-with-timestamps t
"Non nil means allow timestamps in export.
@@ -779,7 +823,8 @@ This option can also be set with the OPTIONS keyword, e.g.
(const :tag "All timestamps" t)
(const :tag "Only active timestamps" active)
(const :tag "Only inactive timestamps" inactive)
- (const :tag "No timestamp" nil)))
+ (const :tag "No timestamp" nil))
+ :safe (lambda (x) (memq x '(t nil active inactive))))
(defcustom org-export-with-todo-keywords t
"Non-nil means include TODO keywords in export.
@@ -797,6 +842,27 @@ is nil. You can also allow them through local buffer variables."
:package-version '(Org . "8.0")
:type 'boolean)
+(defcustom org-export-with-broken-links nil
+ "Non-nil means do not raise an error on broken links.
+
+When this variable is non-nil, broken links are ignored, without
+stopping the export process. If it is set to `mark', broken
+links are marked as such in the output, with a string like
+
+ [BROKEN LINK: path]
+
+where PATH is the un-resolvable reference.
+
+This option can also be set with the OPTIONS keyword, e.g.,
+\"broken-links:mark\"."
+ :group 'org-export-general
+ :version "25.2"
+ :package-version '(Org . "9.0")
+ :type '(choice
+ (const :tag "Ignore broken links" t)
+ (const :tag "Mark broken links in output" mark)
+ (const :tag "Raise an error" nil)))
+
(defcustom org-export-snippet-translation-alist nil
"Alist between export snippets back-ends and exporter back-ends.
@@ -810,7 +876,12 @@ HTML code while every other back-end will ignore it."
:package-version '(Org . "8.0")
:type '(repeat
(cons (string :tag "Shortcut")
- (string :tag "Back-end"))))
+ (string :tag "Back-end")))
+ :safe (lambda (x)
+ (and (listp x)
+ (cl-every #'consp x)
+ (cl-every #'stringp (mapcar #'car x))
+ (cl-every #'stringp (mapcar #'cdr x)))))
(defcustom org-export-coding-system nil
"Coding system for the exported file."
@@ -823,7 +894,7 @@ HTML code while every other back-end will ignore it."
"Non-nil means pushing export output to the kill ring.
This variable is ignored during asynchronous export."
:group 'org-export-general
- :version "25.1"
+ :version "25.2"
:package-version '(Org . "8.3")
:type '(choice
(const :tag "Always" t)
@@ -851,7 +922,7 @@ these cases."
(defcustom org-export-in-background nil
"Non-nil means export and publishing commands will run in background.
Results from an asynchronous export are never displayed
-automatically. But you can retrieve them with \\[org-export-stack]."
+automatically. But you can retrieve them with `\\[org-export-stack]'."
:group 'org-export-general
:version "24.4"
:package-version '(Org . "8.0")
@@ -921,8 +992,8 @@ mode."
;; Eventually `org-export-barf-if-invalid-backend' returns an error
;; when a given back-end hasn't been registered yet.
-(defstruct (org-export-backend (:constructor org-export-create-backend)
- (:copier nil))
+(cl-defstruct (org-export-backend (:constructor org-export-create-backend)
+ (:copier nil))
name parent transcoders options filters blocks menu)
(defun org-export-get-backend (name)
@@ -943,10 +1014,6 @@ BACKEND is a structure with `org-export-backend' type."
(let ((parent (org-export-backend-parent backend)))
(when (and parent (not (org-export-get-backend parent)))
(error "Cannot use unknown \"%s\" back-end as a parent" parent)))
- ;; Register dedicated export blocks in the parser.
- (dolist (name (org-export-backend-blocks backend))
- (add-to-list 'org-element-block-name-alist
- (cons name 'org-element-export-block-parser)))
;; If a back-end with the same name as BACKEND is already
;; registered, replace it with BACKEND. Otherwise, simply add
;; BACKEND to the list of registered back-ends.
@@ -1073,14 +1140,6 @@ back-end.
BODY can start with pre-defined keyword arguments. The following
keywords are understood:
- :export-block
-
- String, or list of strings, representing block names that
- will not be parsed. This is used to specify blocks that will
- contain raw code specific to the back-end. These blocks
- still have to be handled by the relative `export-block' type
- translator.
-
:filters-alist
Alist between filters and function, or list of functions,
@@ -1150,23 +1209,19 @@ keywords are understood:
`org-export-options-alist' for more information about
structure of the values."
(declare (indent 1))
- (let (blocks filters menu-entry options contents)
+ (let (filters menu-entry options)
(while (keywordp (car body))
(let ((keyword (pop body)))
- (case keyword
- (:export-block (let ((names (pop body)))
- (setq blocks (if (consp names) (mapcar 'upcase names)
- (list (upcase names))))))
+ (pcase keyword
(:filters-alist (setq filters (pop body)))
(:menu-entry (setq menu-entry (pop body)))
(:options-alist (setq options (pop body)))
- (t (error "Unknown keyword: %s" keyword)))))
+ (_ (error "Unknown keyword: %s" keyword)))))
(org-export-register-backend
(org-export-create-backend :name backend
:transcoders transcoders
:options options
:filters filters
- :blocks blocks
:menu menu-entry))))
(defun org-export-define-derived-backend (child parent &rest body)
@@ -1178,14 +1233,6 @@ the parent back-end.
BODY can start with pre-defined keyword arguments. The following
keywords are understood:
- :export-block
-
- String, or list of strings, representing block names that
- will not be parsed. This is used to specify blocks that will
- contain raw code specific to the back-end. These blocks
- still have to be handled by the relative `export-block' type
- translator.
-
:filters-alist
Alist of filters that will overwrite or complete filters
@@ -1222,25 +1269,21 @@ The back-end could then be called with, for example:
(org-export-to-buffer \\='my-latex \"*Test my-latex*\")"
(declare (indent 2))
- (let (blocks filters menu-entry options transcoders contents)
+ (let (filters menu-entry options transcoders)
(while (keywordp (car body))
(let ((keyword (pop body)))
- (case keyword
- (:export-block (let ((names (pop body)))
- (setq blocks (if (consp names) (mapcar 'upcase names)
- (list (upcase names))))))
+ (pcase keyword
(:filters-alist (setq filters (pop body)))
(:menu-entry (setq menu-entry (pop body)))
(:options-alist (setq options (pop body)))
(:translate-alist (setq transcoders (pop body)))
- (t (error "Unknown keyword: %s" keyword)))))
+ (_ (error "Unknown keyword: %s" keyword)))))
(org-export-register-backend
(org-export-create-backend :name child
:parent parent
:transcoders transcoders
:options options
:filters filters
- :blocks blocks
:menu menu-entry))))
@@ -1259,7 +1302,7 @@ The back-end could then be called with, for example:
;; `org-export-options-alist' variable.
;;
;; 2. Tree properties are extracted directly from the parsed tree,
-;; just before export, by `org-export-collect-tree-properties'.
+;; just before export, by `org-export--collect-tree-properties'.
;;;; Environment Options
;;
@@ -1304,7 +1347,7 @@ inferior to file-local settings."
;; First install #+BIND variables since these must be set before
;; global options are read.
(dolist (pair (org-export--list-bound-variables))
- (org-set-local (car pair) (nth 1 pair)))
+ (set (make-local-variable (car pair)) (nth 1 pair)))
;; Get and prioritize export options...
(org-combine-plists
;; ... from global variables...
@@ -1314,25 +1357,7 @@ inferior to file-local settings."
;; ... from in-buffer settings...
(org-export--get-inbuffer-options backend)
;; ... and from subtree, when appropriate.
- (and subtreep (org-export--get-subtree-options backend))
- ;; Eventually add misc. properties.
- (list
- :back-end
- backend
- :translate-alist (org-export-get-all-transcoders backend)
- :id-alist
- ;; Collect id references.
- (let (alist)
- (org-with-wide-buffer
- (goto-char (point-min))
- (while (re-search-forward "\\[\\[id:\\S-+?\\]" nil t)
- (let ((link (org-element-context)))
- (when (eq (org-element-type link) 'link)
- (let* ((id (org-element-property :path link))
- (file (car (org-id-find id))))
- (when file
- (push (cons id (file-relative-name file)) alist)))))))
- alist))))
+ (and subtreep (org-export--get-subtree-options backend))))
(defun org-export--parse-option-keyword (options &optional backend)
"Parse an OPTIONS line and return values as a plist.
@@ -1380,8 +1405,9 @@ for export. Return options as a plist."
(cache (list
(cons "TITLE"
(or (org-entry-get (point) "EXPORT_TITLE" 'selective)
- (progn (looking-at org-complex-heading-regexp)
- (org-match-string-no-properties 4))))))
+ (let ((case-fold-search nil))
+ (looking-at org-complex-heading-regexp)
+ (match-string-no-properties 4))))))
;; Look for both general keywords and back-end specific
;; options, with priority given to the latter.
(options (append (and backend (org-export-get-all-options backend))
@@ -1401,7 +1427,7 @@ for export. Return options as a plist."
(setq plist
(plist-put plist
property
- (case (nth 4 option)
+ (cl-case (nth 4 option)
(parse
(org-element-parse-secondary-string
value (org-element-restriction 'keyword)))
@@ -1417,9 +1443,7 @@ which back-end specific options should also be read in the
process.
Assume buffer is in Org mode. Narrowing, if any, is ignored."
- (let* (plist
- get-options ; For byte-compiler.
- (case-fold-search t)
+ (let* ((case-fold-search t)
(options (append
;; Priority is given to back-end specific options.
(and backend (org-export-get-all-options backend))
@@ -1427,110 +1451,124 @@ Assume buffer is in Org mode. Narrowing, if any, is ignored."
(regexp (format "^[ \t]*#\\+%s:"
(regexp-opt (nconc (delq nil (mapcar #'cadr options))
org-export-special-keywords))))
- (find-properties
- (lambda (keyword)
- ;; Return all properties associated to KEYWORD.
- (let (properties)
- (dolist (option options properties)
- (when (equal (nth 1 option) keyword)
- (pushnew (car option) properties))))))
- to-parse
- (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)))))
- ;; Avoid circular dependencies.
- (unless (member file files)
- (with-temp-buffer
- (setq default-directory
+ plist to-parse)
+ (letrec ((find-properties
+ (lambda (keyword)
+ ;; Return all properties associated to KEYWORD.
+ (let (properties)
+ (dolist (option options properties)
+ (when (equal (nth 1 option) keyword)
+ (cl-pushnew (car option) properties))))))
+ (get-options
+ (lambda (&optional files)
+ ;; 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-unbracket-string "\"" "\"" (org-trim val)))))
+ ;; Avoid circular dependencies.
+ (unless (member file files)
+ (with-temp-buffer
+ (setq default-directory
(file-name-directory file))
- (insert (org-file-contents file 'noerror))
- (let ((org-inhibit-startup t)) (org-mode))
- (setq plist (funcall get-options
- (cons file files) plist))))))
- ((equal key "OPTIONS")
- (setq plist
- (org-combine-plists
- plist
- (org-export--parse-option-keyword val backend))))
- ((equal key "FILETAGS")
- (setq plist
- (org-combine-plists
+ (insert (org-file-contents file 'noerror))
+ (let ((org-inhibit-startup t)) (org-mode))
+ (funcall get-options (cons file files))))))
+ ((equal key "OPTIONS")
+ (setq plist
+ (org-combine-plists
+ plist
+ (org-export--parse-option-keyword
+ val backend))))
+ ((equal key "FILETAGS")
+ (setq 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'.
+ (dolist (property (funcall find-properties key))
+ (setq
plist
- (list :filetags
- (org-uniquify
- (append (org-split-string val ":")
- (plist-get plist :filetags)))))))
- (t
- ;; Options in `org-export-options-alist'.
- (dolist (property (funcall find-properties key))
- (setq
- plist
- (plist-put
- plist property
- ;; Handle value depending on specified
- ;; BEHAVIOR.
- (case (nth 4 (assq property options))
- (parse
- (unless (memq property to-parse)
- (push property to-parse))
- ;; Even if `parse' implies `space'
- ;; behavior, we separate line with "\n"
- ;; so as to preserve line-breaks.
- ;; However, empty lines are forbidden
- ;; since `parse' doesn't allow more than
- ;; one paragraph.
- (let ((old (plist-get plist property)))
- (cond ((not (org-string-nw-p val)) old)
- (old (concat old "\n" val))
- (t val))))
- (space
- (if (not (plist-get plist property))
- (org-trim val)
- (concat (plist-get plist property)
- " "
- (org-trim val))))
- (newline
- (org-trim
- (concat (plist-get plist property)
- "\n"
- (org-trim val))))
- (split `(,@(plist-get plist property)
- ,@(org-split-string val)))
- ((t) val)
- (otherwise
- (if (not (plist-member plist property)) val
- (plist-get plist property)))))))))))))
- plist))))
- ;; Read options in the current buffer and return value.
- (let ((options (funcall get-options
- (and buffer-file-name (list buffer-file-name))
- nil)))
+ (plist-put
+ plist property
+ ;; Handle value depending on specified
+ ;; BEHAVIOR.
+ (cl-case (nth 4 (assq property options))
+ (parse
+ (unless (memq property to-parse)
+ (push property to-parse))
+ ;; Even if `parse' implies `space'
+ ;; behavior, we separate line with
+ ;; "\n" so as to preserve
+ ;; line-breaks. However, empty
+ ;; lines are forbidden since `parse'
+ ;; doesn't allow more than one
+ ;; paragraph.
+ (let ((old (plist-get plist property)))
+ (cond ((not (org-string-nw-p val)) old)
+ (old (concat old "\n" val))
+ (t val))))
+ (space
+ (if (not (plist-get plist property))
+ (org-trim val)
+ (concat (plist-get plist property)
+ " "
+ (org-trim val))))
+ (newline
+ (org-trim
+ (concat (plist-get plist property)
+ "\n"
+ (org-trim val))))
+ (split `(,@(plist-get plist property)
+ ,@(org-split-string val)))
+ ((t) val)
+ (otherwise
+ (if (not (plist-member plist property)) val
+ (plist-get plist property)))))))))))))))))
+ ;; Read options in the current buffer and return value.
+ (funcall get-options (and buffer-file-name (list buffer-file-name)))
;; Parse properties in TO-PARSE. Remove newline characters not
;; involved in line breaks to simulate `space' behavior.
;; Finally return options.
- (dolist (p to-parse options)
+ (dolist (p to-parse plist)
(let ((value (org-element-parse-secondary-string
- (plist-get options p)
+ (plist-get plist p)
(org-element-restriction 'keyword))))
(org-element-map value 'plain-text
(lambda (s)
(org-element-set-element
s (replace-regexp-in-string "\n" " " s))))
- (setq options (plist-put options p value)))))))
+ (setq plist (plist-put plist p value)))))))
+
+(defun org-export--get-export-attributes
+ (&optional backend subtreep visible-only body-only)
+ "Return properties related to export process, as a plist.
+Optional arguments BACKEND, SUBTREEP, VISIBLE-ONLY and BODY-ONLY
+are like the arguments with the same names of function
+`org-export-as'."
+ (list :export-options (delq nil
+ (list (and subtreep 'subtree)
+ (and visible-only 'visible-only)
+ (and body-only 'body-only)))
+ :back-end backend
+ :translate-alist (org-export-get-all-transcoders backend)
+ :exported-data (make-hash-table :test #'eq :size 4001)))
(defun org-export--get-buffer-attributes ()
"Return properties related to buffer attributes, as a plist."
@@ -1566,37 +1604,37 @@ process."
Also look for BIND keywords in setup files. The return value is
an alist where associations are (VARIABLE-NAME VALUE)."
(when org-export-allow-bind-keywords
- (let* (collect-bind ; For byte-compiler.
- (collect-bind
- (lambda (files alist)
- ;; Return an alist between variable names and their
- ;; value. FILES is a list of setup files names read so
- ;; far, used to avoid circular dependencies. ALIST is
- ;; the alist collected so far.
- (let ((case-fold-search t))
- (org-with-wide-buffer
- (goto-char (point-min))
- (while (re-search-forward
- "^[ \t]*#\\+\\(BIND\\|SETUPFILE\\):" nil t)
- (let ((element (org-element-at-point)))
- (when (eq (org-element-type element) 'keyword)
- (let ((val (org-element-property :value element)))
- (if (equal (org-element-property :key element) "BIND")
- (push (read (format "(%s)" val)) alist)
- ;; Enter setup file.
- (let ((file (expand-file-name
- (org-remove-double-quotes val))))
- (unless (member file files)
- (with-temp-buffer
- (setq default-directory
- (file-name-directory file))
- (let ((org-inhibit-startup t)) (org-mode))
- (insert (org-file-contents file 'noerror))
- (setq alist
- (funcall collect-bind
- (cons file files)
- alist))))))))))
- alist)))))
+ (letrec ((collect-bind
+ (lambda (files alist)
+ ;; Return an alist between variable names and their
+ ;; value. FILES is a list of setup files names read
+ ;; so far, used to avoid circular dependencies. ALIST
+ ;; is the alist collected so far.
+ (let ((case-fold-search t))
+ (org-with-wide-buffer
+ (goto-char (point-min))
+ (while (re-search-forward
+ "^[ \t]*#\\+\\(BIND\\|SETUPFILE\\):" nil t)
+ (let ((element (org-element-at-point)))
+ (when (eq (org-element-type element) 'keyword)
+ (let ((val (org-element-property :value element)))
+ (if (equal (org-element-property :key element)
+ "BIND")
+ (push (read (format "(%s)" val)) alist)
+ ;; Enter setup file.
+ (let ((file (expand-file-name
+ (org-unbracket-string "\"" "\"" val))))
+ (unless (member file files)
+ (with-temp-buffer
+ (setq default-directory
+ (file-name-directory file))
+ (let ((org-inhibit-startup t)) (org-mode))
+ (insert (org-file-contents file 'noerror))
+ (setq alist
+ (funcall collect-bind
+ (cons file files)
+ alist))))))))))
+ alist)))))
;; Return value in appropriate order of appearance.
(nreverse (funcall collect-bind nil nil)))))
@@ -1612,7 +1650,7 @@ BLOB is the element or object considered."
;;
;; Tree properties are information extracted from parse tree. They
;; are initialized at the beginning of the transcoding process by
-;; `org-export-collect-tree-properties'.
+;; `org-export--collect-tree-properties'.
;;
;; Dedicated functions focus on computing the value of specific tree
;; properties during initialization. Thus,
@@ -1623,7 +1661,7 @@ BLOB is the element or object considered."
;; `org-export--collect-headline-numbering' builds an alist between
;; headlines and their numbering.
-(defun org-export-collect-tree-properties (data info)
+(defun org-export--collect-tree-properties (data info)
"Extract tree properties from parse tree.
DATA is the parse tree from which information is retrieved. INFO
@@ -1631,17 +1669,17 @@ is a list holding export options.
Following tree properties are set or updated:
-`:exported-data' Hash table used to memoize results from
- `org-export-data'.
-
`:headline-offset' Offset between true level of headlines and
local level. An offset of -1 means a headline
of level 2 should be considered as a level
1 headline in the context.
-`:headline-numbering' Alist of all headlines as key an the
+`:headline-numbering' Alist of all headlines as key and the
associated numbering as value.
+`:id-alist' Alist of all ID references as key and associated file
+ as value.
+
Return updated plist."
;; Install the parse tree in the communication channel.
(setq info (plist-put info :parse-tree data))
@@ -1651,12 +1689,18 @@ Return updated plist."
(plist-put info
:headline-offset
(- 1 (org-export--get-min-level data info))))
- ;; Properties order doesn't matter: get the rest of the tree
- ;; properties.
- (nconc
- `(:headline-numbering ,(org-export--collect-headline-numbering data info)
- :exported-data ,(make-hash-table :test 'eq :size 4001))
- info))
+ ;; From now on, properties order doesn't matter: get the rest of the
+ ;; tree properties.
+ (org-combine-plists
+ info
+ (list :headline-numbering (org-export--collect-headline-numbering data info)
+ :id-alist
+ (org-element-map data 'link
+ (lambda (l)
+ (and (string= (org-element-property :type l) "id")
+ (let* ((id (org-element-property :path l))
+ (file (car (org-id-find id))))
+ (and file (cons id (file-relative-name file))))))))))
(defun org-export--get-min-level (data options)
"Return minimum exportable headline's level in DATA.
@@ -1664,14 +1708,12 @@ DATA is parsed tree as returned by `org-element-parse-buffer'.
OPTIONS is a plist holding export options."
(catch 'exit
(let ((min-level 10000))
- (mapc
- (lambda (blob)
- (when (and (eq (org-element-type blob) 'headline)
- (not (org-element-property :footnote-section-p blob))
- (not (memq blob (plist-get options :ignore-list))))
- (setq min-level (min (org-element-property :level blob) min-level)))
- (when (= min-level 1) (throw 'exit 1)))
- (org-element-contents data))
+ (dolist (datum (org-element-contents data))
+ (when (and (eq (org-element-type datum) 'headline)
+ (not (org-element-property :footnote-section-p datum))
+ (not (memq datum (plist-get options :ignore-list))))
+ (setq min-level (min (org-element-property :level datum) min-level))
+ (when (= min-level 1) (throw 'exit 1))))
;; If no headline was found, for the sake of consistency, set
;; minimum level to 1 nonetheless.
(if (= min-level 10000) 1 min-level))))
@@ -1694,55 +1736,58 @@ for a footnotes section."
(1- (org-export-get-relative-level headline options))))
(cons
headline
- (loop for n across numbering
- for idx from 0 to org-export-max-depth
- when (< idx relative-level) collect n
- when (= idx relative-level) collect (aset numbering idx (1+ n))
- when (> idx relative-level) do (aset numbering idx 0))))))
+ (cl-loop
+ for n across numbering
+ for idx from 0 to org-export-max-depth
+ when (< idx relative-level) collect n
+ when (= idx relative-level) collect (aset numbering idx (1+ n))
+ when (> idx relative-level) do (aset numbering idx 0))))))
options)))
(defun org-export--selected-trees (data info)
"List headlines and inlinetasks with a select tag in their tree.
DATA is parsed data as returned by `org-element-parse-buffer'.
INFO is a plist holding export options."
- (let* (selected-trees
- walk-data ; For byte-compiler.
- (walk-data
- (function
- (lambda (data genealogy)
- (let ((type (org-element-type data)))
- (cond
- ((memq type '(headline inlinetask))
- (let ((tags (org-element-property :tags data)))
- (if (loop for tag in (plist-get info :select-tags)
- thereis (member tag tags))
- ;; When a select tag is found, mark full
- ;; genealogy and every headline within the tree
- ;; as acceptable.
- (setq selected-trees
- (append
- genealogy
- (org-element-map data '(headline inlinetask)
- #'identity)
- selected-trees))
- ;; If at a headline, continue searching in tree,
- ;; recursively.
- (when (eq type 'headline)
- (dolist (el (org-element-contents data))
- (funcall walk-data el (cons data genealogy)))))))
- ((or (eq type 'org-data)
- (memq type org-element-greater-elements))
- (dolist (el (org-element-contents data))
- (funcall walk-data el genealogy)))))))))
- (funcall walk-data data nil)
- selected-trees))
+ (let ((select (plist-get info :select-tags)))
+ (if (cl-some (lambda (tag) (member tag select)) (plist-get info :filetags))
+ ;; If FILETAGS contains a select tag, every headline or
+ ;; inlinetask is returned.
+ (org-element-map data '(headline inlinetask) #'identity)
+ (letrec ((selected-trees nil)
+ (walk-data
+ (lambda (data genealogy)
+ (let ((type (org-element-type data)))
+ (cond
+ ((memq type '(headline inlinetask))
+ (let ((tags (org-element-property :tags data)))
+ (if (cl-some (lambda (tag) (member tag select)) tags)
+ ;; When a select tag is found, mark full
+ ;; genealogy and every headline within the
+ ;; tree as acceptable.
+ (setq selected-trees
+ (append
+ genealogy
+ (org-element-map data '(headline inlinetask)
+ #'identity)
+ selected-trees))
+ ;; If at a headline, continue searching in
+ ;; tree, recursively.
+ (when (eq type 'headline)
+ (dolist (el (org-element-contents data))
+ (funcall walk-data el (cons data genealogy)))))))
+ ((or (eq type 'org-data)
+ (memq type org-element-greater-elements))
+ (dolist (el (org-element-contents data))
+ (funcall walk-data el genealogy))))))))
+ (funcall walk-data data nil)
+ selected-trees))))
(defun org-export--skip-p (blob options selected)
"Non-nil when element or object BLOB should be skipped during export.
OPTIONS is the plist holding export options. SELECTED, when
non-nil, is a list of headlines or inlinetasks belonging to
a tree with a select tag."
- (case (org-element-type blob)
+ (cl-case (org-element-type blob)
(clock (not (plist-get options :with-clocks)))
(drawer
(let ((with-drawers-p (plist-get options :with-drawers)))
@@ -1764,13 +1809,13 @@ a tree with a select tag."
(todo (org-element-property :todo-keyword blob))
(todo-type (org-element-property :todo-type blob))
(archived (plist-get options :with-archived-trees))
- (tags (org-element-property :tags blob)))
+ (tags (org-export-get-tags blob options nil t)))
(or
(and (eq (org-element-type blob) 'inlinetask)
(not (plist-get options :with-inlinetasks)))
;; Ignore subtrees with an exclude tag.
- (loop for k in (plist-get options :exclude-tags)
- thereis (member k tags))
+ (cl-loop for k in (plist-get options :exclude-tags)
+ thereis (member k tags))
;; When a select tag is present in the buffer, ignore any tree
;; without it.
(and selected (not (memq blob selected)))
@@ -1812,7 +1857,7 @@ a tree with a select tag."
(lambda (obj)
(or (not (stringp obj)) (org-string-nw-p obj)))
options t))))
- (case (plist-get options :with-timestamps)
+ (cl-case (plist-get options :with-timestamps)
((nil) t)
(active
(not (memq (org-element-property :type blob) '(active active-range))))
@@ -1842,7 +1887,7 @@ a tree with a select tag."
INFO is a plist containing export directives."
(let ((type (org-element-type blob)))
;; Return contents only for complete parse trees.
- (if (eq type 'org-data) (lambda (blob contents info) contents)
+ (if (eq type 'org-data) (lambda (_datum contents _info) contents)
(let ((transcoder (cdr (assq type (plist-get info :translate-alist)))))
(and (functionp transcoder) transcoder)))))
@@ -1854,91 +1899,103 @@ string. INFO is a plist holding export options.
Return a string."
(or (gethash data (plist-get info :exported-data))
- (let* ((type (org-element-type data))
- (results
- (cond
- ;; Ignored element/object.
- ((memq data (plist-get info :ignore-list)) nil)
- ;; Plain text.
- ((eq type 'plain-text)
- (org-export-filter-apply-functions
- (plist-get info :filter-plain-text)
- (let ((transcoder (org-export-transcoder data info)))
- (if transcoder (funcall transcoder data info) data))
- info))
- ;; Secondary string.
- ((not type)
- (mapconcat (lambda (obj) (org-export-data obj info)) data ""))
- ;; Element/Object without contents or, as a special
- ;; case, headline with archive tag and archived trees
- ;; restricted to title only.
- ((or (not (org-element-contents data))
- (and (eq type 'headline)
- (eq (plist-get info :with-archived-trees) 'headline)
- (org-element-property :archivedp data)))
- (let ((transcoder (org-export-transcoder data info)))
- (or (and (functionp transcoder)
- (funcall transcoder data nil info))
- ;; Export snippets never return a nil value so
- ;; that white spaces following them are never
- ;; ignored.
- (and (eq type 'export-snippet) ""))))
- ;; Element/Object with contents.
- (t
- (let ((transcoder (org-export-transcoder data info)))
- (when transcoder
- (let* ((greaterp (memq type org-element-greater-elements))
- (objectp
- (and (not greaterp)
- (memq type org-element-recursive-objects)))
- (contents
- (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 contents of the
- ;; first paragraph in an item or
- ;; a footnote definition, ignore
- ;; first line's indentation: there is
- ;; none and it might be misleading.
- (when (eq type 'paragraph)
- (let ((parent (org-export-get-parent data)))
+ ;; Handle broken links according to
+ ;; `org-export-with-broken-links'.
+ (cl-macrolet
+ ((broken-link-handler
+ (&rest body)
+ `(condition-case err
+ (progn ,@body)
+ (org-link-broken
+ (pcase (plist-get info :with-broken-links)
+ (`nil (user-error "Unable to resolve link: %S" (nth 1 err)))
+ (`mark (org-export-data
+ (format "[BROKEN LINK: %s]" (nth 1 err)) info))
+ (_ nil))))))
+ (let* ((type (org-element-type data))
+ (parent (org-export-get-parent data))
+ (results
+ (cond
+ ;; Ignored element/object.
+ ((memq data (plist-get info :ignore-list)) nil)
+ ;; Plain text.
+ ((eq type 'plain-text)
+ (org-export-filter-apply-functions
+ (plist-get info :filter-plain-text)
+ (let ((transcoder (org-export-transcoder data info)))
+ (if transcoder (funcall transcoder data info) data))
+ info))
+ ;; Secondary string.
+ ((not type)
+ (mapconcat (lambda (obj) (org-export-data obj info)) data ""))
+ ;; Element/Object without contents or, as a special
+ ;; case, headline with archive tag and archived trees
+ ;; restricted to title only.
+ ((or (not (org-element-contents data))
+ (and (eq type 'headline)
+ (eq (plist-get info :with-archived-trees) 'headline)
+ (org-element-property :archivedp data)))
+ (let ((transcoder (org-export-transcoder data info)))
+ (or (and (functionp transcoder)
+ (broken-link-handler
+ (funcall transcoder data nil info)))
+ ;; Export snippets never return a nil value so
+ ;; that white spaces following them are never
+ ;; ignored.
+ (and (eq type 'export-snippet) ""))))
+ ;; Element/Object with contents.
+ (t
+ (let ((transcoder (org-export-transcoder data info)))
+ (when transcoder
+ (let* ((greaterp (memq type org-element-greater-elements))
+ (objectp
+ (and (not greaterp)
+ (memq type org-element-recursive-objects)))
+ (contents
+ (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 contents of the
+ ;; first paragraph in an item or
+ ;; a footnote definition, ignore
+ ;; first line's indentation: there is
+ ;; none and it might be misleading.
+ (when (eq type 'paragraph)
(and
(eq (car (org-element-contents parent))
data)
(memq (org-element-type parent)
- '(footnote-definition item))))))))
- "")))
- (funcall transcoder data
- (if (not greaterp) contents
- (org-element-normalize-string contents))
- info))))))))
- ;; Final result will be memoized before being returned.
- (puthash
- data
- (cond
- ((not results) "")
- ((memq type '(org-data plain-text nil)) results)
- ;; Append the same white space between elements or objects
- ;; as in the original buffer, and call appropriate filters.
- (t
- (let ((results
- (org-export-filter-apply-functions
- (plist-get info (intern (format ":filter-%s" type)))
- (let ((post-blank (or (org-element-property :post-blank data)
- 0)))
- (if (memq type org-element-all-elements)
- (concat (org-element-normalize-string results)
- (make-string post-blank ?\n))
- (concat results (make-string post-blank ?\s))))
- info)))
- results)))
- (plist-get info :exported-data)))))
+ '(footnote-definition item)))))))
+ "")))
+ (broken-link-handler
+ (funcall transcoder data
+ (if (not greaterp) contents
+ (org-element-normalize-string contents))
+ info)))))))))
+ ;; Final result will be memoized before being returned.
+ (puthash
+ data
+ (cond
+ ((not results) "")
+ ((memq type '(org-data plain-text nil)) results)
+ ;; Append the same white space between elements or objects
+ ;; as in the original buffer, and call appropriate filters.
+ (t
+ (org-export-filter-apply-functions
+ (plist-get info (intern (format ":filter-%s" type)))
+ (let ((blank (or (org-element-property :post-blank data) 0)))
+ (if (eq (org-element-class data parent) 'object)
+ (concat results (make-string blank ?\s))
+ (concat (org-element-normalize-string results)
+ (make-string blank ?\n))))
+ info)))
+ (plist-get info :exported-data))))))
(defun org-export-data-with-backend (data backend info)
"Convert DATA into BACKEND format.
@@ -1972,7 +2029,8 @@ contents, as a string or nil.
When optional argument WITH-AFFILIATED is non-nil, add affiliated
keywords before output."
(let ((type (org-element-type blob)))
- (concat (and with-affiliated (memq type org-element-all-elements)
+ (concat (and with-affiliated
+ (eq (org-element-class blob) 'element)
(org-element--interpret-affiliated-keywords blob))
(funcall (intern (format "org-element-%s-interpreter" type))
blob contents))))
@@ -2437,29 +2495,27 @@ Return the updated communication channel."
(let (plist)
;; Install user-defined filters with `org-export-filters-alist'
;; and filters already in INFO (through ext-plist mechanism).
- (mapc (lambda (p)
- (let* ((prop (car p))
- (info-value (plist-get info prop))
- (default-value (symbol-value (cdr p))))
- (setq plist
- (plist-put plist prop
- ;; Filters in INFO will be called
- ;; before those user provided.
- (append (if (listp info-value) info-value
- (list info-value))
- default-value)))))
- org-export-filters-alist)
+ (dolist (p org-export-filters-alist)
+ (let* ((prop (car p))
+ (info-value (plist-get info prop))
+ (default-value (symbol-value (cdr p))))
+ (setq plist
+ (plist-put plist prop
+ ;; Filters in INFO will be called
+ ;; before those user provided.
+ (append (if (listp info-value) info-value
+ (list info-value))
+ default-value)))))
;; Prepend back-end specific filters to that list.
- (mapc (lambda (p)
- ;; Single values get consed, lists are appended.
- (let ((key (car p)) (value (cdr p)))
- (when value
- (setq plist
- (plist-put
- plist key
- (if (atom value) (cons value (plist-get plist key))
- (append value (plist-get plist key))))))))
- (org-export-get-all-filters (plist-get info :back-end)))
+ (dolist (p (org-export-get-all-filters (plist-get info :back-end)))
+ ;; Single values get consed, lists are appended.
+ (let ((key (car p)) (value (cdr p)))
+ (when value
+ (setq plist
+ (plist-put
+ plist key
+ (if (atom value) (cons value (plist-get plist key))
+ (append value (plist-get plist key))))))))
;; Return new communication channel.
(org-combine-plists info plist)))
@@ -2572,17 +2628,14 @@ The function assumes BUFFER's major mode is `org-mode'."
(goto-char ,(point))
;; Overlays with invisible property.
,@(let (ov-set)
- (mapc
- (lambda (ov)
- (let ((invis-prop (overlay-get ov 'invisible)))
- (when invis-prop
- (push `(overlay-put
- (make-overlay ,(overlay-start ov)
- ,(overlay-end ov))
- 'invisible (quote ,invis-prop))
- ov-set))))
- (overlays-in (point-min) (point-max)))
- ov-set)))))
+ (dolist (ov (overlays-in (point-min) (point-max)) ov-set)
+ (let ((invis-prop (overlay-get ov 'invisible)))
+ (when invis-prop
+ (push `(overlay-put
+ (make-overlay ,(overlay-start ov)
+ ,(overlay-end ov))
+ 'invisible (quote ,invis-prop))
+ ov-set)))))))))
(defun org-export--delete-comments ()
"Delete commented areas in the buffer.
@@ -2598,12 +2651,12 @@ the document. Narrowing, if any, is ignored."
comment-re)))
(while (re-search-forward regexp nil t)
(let ((element (org-element-at-point)))
- (case (org-element-type element)
- ((headline inlinetask)
+ (pcase (org-element-type element)
+ ((or `headline `inlinetask)
(when (org-element-property :commentedp element)
(delete-region (org-element-property :begin element)
(org-element-property :end element))))
- ((comment comment-block)
+ ((or `comment `comment-block)
(let* ((parent (org-element-property :parent element))
(start (org-element-property :begin element))
(end (org-element-property :end element))
@@ -2622,7 +2675,7 @@ the document. Narrowing, if any, is ignored."
end)
(progn
(forward-line -1)
- (or (org-looking-at-p "^[ \t]*$")
+ (or (looking-at-p "^[ \t]*$")
(org-with-limited-levels
(org-at-heading-p)))))))))
(delete-region start end)
@@ -2634,33 +2687,47 @@ DATA is the parse tree to traverse. INFO is the plist holding
export info. Also set `:ignore-list' in INFO to a list of
objects which should be ignored during export, but not removed
from tree."
- (let* (walk-data
- ignore
- ;; First find trees containing a select tag, if any.
- (selected (org-export--selected-trees data info))
- (walk-data
- (lambda (data)
- ;; Prune non-exportable elements and objects from tree.
- ;; As a special case, special rows and cells from tables
- ;; are stored in IGNORE, as they still need to be accessed
- ;; during export.
- (when data
- (let ((type (org-element-type data)))
- (if (org-export--skip-p data info selected)
- (if (memq type '(table-cell table-row)) (push data ignore)
- (org-element-extract-element data))
- (if (and (eq type 'headline)
- (eq (plist-get info :with-archived-trees) 'headline)
- (org-element-property :archivedp data))
- ;; If headline is archived but tree below has to
- ;; be skipped, remove contents.
- (org-element-set-contents data)
- ;; Move into recursive objects/elements.
- (mapc walk-data (org-element-contents data)))
- ;; Move into secondary string, if any.
- (dolist (p (cdr (assq type
- org-element-secondary-value-alist)))
- (mapc walk-data (org-element-property p data)))))))))
+ (letrec ((ignore nil)
+ ;; First find trees containing a select tag, if any.
+ (selected (org-export--selected-trees data info))
+ (walk-data
+ (lambda (data)
+ ;; Prune non-exportable elements and objects from tree.
+ ;; As a special case, special rows and cells from tables
+ ;; are stored in IGNORE, as they still need to be
+ ;; accessed during export.
+ (when data
+ (let ((type (org-element-type data)))
+ (if (org-export--skip-p data info selected)
+ (if (memq type '(table-cell table-row)) (push data ignore)
+ (org-element-extract-element data))
+ (if (and (eq type 'headline)
+ (eq (plist-get info :with-archived-trees)
+ 'headline)
+ (org-element-property :archivedp data))
+ ;; If headline is archived but tree below has
+ ;; to be skipped, remove contents.
+ (org-element-set-contents data)
+ ;; Move into recursive objects/elements.
+ (mapc walk-data (org-element-contents data)))
+ ;; Move into secondary string, if any.
+ (dolist (p (cdr (assq type
+ org-element-secondary-value-alist)))
+ (mapc walk-data (org-element-property p data))))))))
+ (definitions
+ ;; Collect definitions before possibly pruning them so as
+ ;; to avoid parsing them again if they are required.
+ (org-element-map data '(footnote-definition footnote-reference)
+ (lambda (f)
+ (cond
+ ((eq (org-element-type f) 'footnote-definition) f)
+ ((eq (org-element-property :type f) 'standard) nil)
+ (t (let ((label (org-element-property :label f)))
+ (when label ;Skip anonymous references.
+ (apply
+ #'org-element-create
+ 'footnote-definition `(:label ,label :post-blank 1)
+ (org-element-contents f))))))))))
;; If a select tag is active, also ignore the section before the
;; first headline, if any.
(when selected
@@ -2669,16 +2736,156 @@ from tree."
(org-element-extract-element first-element))))
;; Prune tree and communication channel.
(funcall walk-data data)
- (dolist (entry
- (append
- ;; Priority is given to back-end specific options.
- (org-export-get-all-options (plist-get info :back-end))
- org-export-options-alist))
+ (dolist (entry (append
+ ;; Priority is given to back-end specific options.
+ (org-export-get-all-options (plist-get info :back-end))
+ org-export-options-alist))
(when (eq (nth 4 entry) 'parse)
(funcall walk-data (plist-get info (car entry)))))
+ (let ((missing (org-export--missing-definitions data definitions)))
+ (funcall walk-data missing)
+ (org-export--install-footnote-definitions missing data))
;; Eventually set `:ignore-list'.
(plist-put info :ignore-list ignore)))
+(defun org-export--missing-definitions (tree definitions)
+ "List footnote definitions missing from TREE.
+Missing definitions are searched within DEFINITIONS, which is
+a list of footnote definitions or in the widened buffer."
+ (let* ((list-labels
+ (lambda (data)
+ ;; List all footnote labels encountered in DATA. Inline
+ ;; footnote references are ignored.
+ (org-element-map data 'footnote-reference
+ (lambda (reference)
+ (and (eq (org-element-property :type reference) 'standard)
+ (org-element-property :label reference))))))
+ defined undefined missing-definitions)
+ ;; Partition DIRECT-REFERENCES between DEFINED and UNDEFINED
+ ;; references.
+ (let ((known-definitions
+ (org-element-map tree '(footnote-reference footnote-definition)
+ (lambda (f)
+ (and (or (eq (org-element-type f) 'footnote-definition)
+ (eq (org-element-property :type f) 'inline))
+ (org-element-property :label f)))))
+ seen)
+ (dolist (l (funcall list-labels tree))
+ (cond ((member l seen))
+ ((member l known-definitions) (push l defined))
+ (t (push l undefined)))))
+ ;; Complete MISSING-DEFINITIONS by finding the definition of every
+ ;; undefined label, first by looking into DEFINITIONS, then by
+ ;; searching the widened buffer. This is a recursive process
+ ;; since definitions found can themselves contain an undefined
+ ;; reference.
+ (while undefined
+ (let* ((label (pop undefined))
+ (definition
+ (cond
+ ((cl-some
+ (lambda (d) (and (equal (org-element-property :label d) label)
+ d))
+ definitions))
+ ((pcase (org-footnote-get-definition label)
+ (`(,_ ,beg . ,_)
+ (org-with-wide-buffer
+ (goto-char beg)
+ (let ((datum (org-element-context)))
+ (if (eq (org-element-type datum) 'footnote-reference)
+ datum
+ ;; Parse definition with contents.
+ (save-restriction
+ (narrow-to-region
+ (org-element-property :begin datum)
+ (org-element-property :end datum))
+ (org-element-map (org-element-parse-buffer)
+ 'footnote-definition #'identity nil t))))))
+ (_ nil)))
+ (t (user-error "Definition not found for footnote %s" label)))))
+ (push label defined)
+ (push definition missing-definitions)
+ ;; Look for footnote references within DEFINITION, since
+ ;; we may need to also find their definition.
+ (dolist (l (funcall list-labels definition))
+ (unless (or (member l defined) ;Known label
+ (member l undefined)) ;Processed later
+ (push l undefined)))))
+ ;; MISSING-DEFINITIONS may contain footnote references with inline
+ ;; definitions. Make sure those are changed into real footnote
+ ;; definitions.
+ (mapcar (lambda (d)
+ (if (eq (org-element-type d) 'footnote-definition) d
+ (let ((label (org-element-property :label d)))
+ (apply #'org-element-create
+ 'footnote-definition `(:label ,label :post-blank 1)
+ (org-element-contents d)))))
+ missing-definitions)))
+
+(defun org-export--install-footnote-definitions (definitions tree)
+ "Install footnote definitions in tree.
+
+DEFINITIONS is the list of footnote definitions to install. TREE
+is the parse tree.
+
+If there is a footnote section in TREE, definitions found are
+appended to it. If `org-footnote-section' is non-nil, a new
+footnote section containing all definitions is inserted in TREE.
+Otherwise, definitions are appended at the end of the section
+containing their first reference."
+ (cond
+ ((null definitions))
+ ;; If there is a footnote section, insert definitions there.
+ ((let ((footnote-section
+ (org-element-map tree 'headline
+ (lambda (h) (and (org-element-property :footnote-section-p h) h))
+ nil t)))
+ (and footnote-section
+ (apply #'org-element-adopt-elements
+ footnote-section
+ (nreverse definitions)))))
+ ;; If there should be a footnote section, create one containing all
+ ;; the definitions at the end of the tree.
+ (org-footnote-section
+ (org-element-adopt-elements
+ tree
+ (org-element-create 'headline
+ (list :footnote-section-p t
+ :level 1
+ :title org-footnote-section)
+ (apply #'org-element-create
+ 'section
+ nil
+ (nreverse definitions)))))
+ ;; Otherwise add each definition at the end of the section where it
+ ;; is first referenced.
+ (t
+ (letrec ((seen nil)
+ (insert-definitions
+ (lambda (data)
+ ;; Insert footnote definitions in the same section as
+ ;; their first reference in DATA.
+ (org-element-map data 'footnote-reference
+ (lambda (reference)
+ (when (eq (org-element-property :type reference) 'standard)
+ (let ((label (org-element-property :label reference)))
+ (unless (member label seen)
+ (push label seen)
+ (let ((definition
+ (cl-some
+ (lambda (d)
+ (and (equal (org-element-property :label d)
+ label)
+ d))
+ definitions)))
+ (org-element-adopt-elements
+ (org-element-lineage reference '(section))
+ definition)
+ ;; Also insert definitions for nested
+ ;; references, if any.
+ (funcall insert-definitions definition))))))))))
+ (funcall insert-definitions tree)))))
+
(defun org-export--remove-uninterpreted-data (data info)
"Change uninterpreted elements back into Org syntax.
DATA is the parse tree. INFO is a plist containing export
@@ -2705,7 +2912,7 @@ returned by the function."
subscript superscript underline)
(lambda (blob)
(let ((new
- (case (org-element-type blob)
+ (cl-case (org-element-type blob)
;; ... entities...
(entity
(and (not (plist-get info :with-entities))
@@ -2717,7 +2924,7 @@ returned by the function."
;; ... emphasis...
((bold italic strike-through underline)
(and (not (plist-get info :with-emphasize))
- (let ((marker (case (org-element-type blob)
+ (let ((marker (cl-case (org-element-type blob)
(bold "*")
(italic "/")
(strike-through "+")
@@ -2762,131 +2969,6 @@ returned by the function."
;; Return modified parse tree.
data)
-(defun org-export--merge-external-footnote-definitions (tree)
- "Insert footnote definitions outside parsing scope in TREE.
-
-If there is a footnote section in TREE, definitions found are
-appended to it. If `org-footnote-section' is non-nil, a new
-footnote section containing all definitions is inserted in TREE.
-Otherwise, definitions are appended at the end of the section
-containing their first reference.
-
-Only definitions actually referred to within TREE, directly or
-not, are considered."
- (let* ((collect-labels
- (lambda (data)
- (org-element-map data 'footnote-reference
- (lambda (f)
- (and (eq (org-element-property :type f) 'standard)
- (org-element-property :label f))))))
- (referenced-labels (funcall collect-labels tree)))
- (when referenced-labels
- (let* ((definitions)
- (push-definition
- (lambda (datum)
- (case (org-element-type datum)
- (footnote-definition
- (push (save-restriction
- (narrow-to-region (org-element-property :begin datum)
- (org-element-property :end datum))
- (org-element-map (org-element-parse-buffer)
- 'footnote-definition #'identity nil t))
- definitions))
- (footnote-reference
- (let ((label (org-element-property :label datum))
- (cbeg (org-element-property :contents-begin datum)))
- (when (and label cbeg
- (eq (org-element-property :type datum) 'inline))
- (push
- (apply #'org-element-create
- 'footnote-definition
- (list :label label :post-blank 1)
- (org-element-parse-secondary-string
- (buffer-substring
- cbeg (org-element-property :contents-end datum))
- (org-element-restriction 'footnote-reference)))
- definitions))))))))
- ;; Collect all out of scope definitions.
- (save-excursion
- (goto-char (point-min))
- (org-with-wide-buffer
- (while (re-search-backward org-footnote-re nil t)
- (funcall push-definition (org-element-context))))
- (goto-char (point-max))
- (org-with-wide-buffer
- (while (re-search-forward org-footnote-re nil t)
- (funcall push-definition (org-element-context)))))
- ;; Filter out definitions referenced neither in the original
- ;; tree nor in the external definitions.
- (let* ((directly-referenced
- (org-remove-if-not
- (lambda (d)
- (member (org-element-property :label d) referenced-labels))
- definitions))
- (all-labels
- (append (funcall collect-labels directly-referenced)
- referenced-labels)))
- (setq definitions
- (org-remove-if-not
- (lambda (d)
- (member (org-element-property :label d) all-labels))
- definitions)))
- ;; Install definitions in subtree.
- (cond
- ((null definitions))
- ;; If there is a footnote section, insert them here.
- ((let ((footnote-section
- (org-element-map tree 'headline
- (lambda (h)
- (and (org-element-property :footnote-section-p h) h))
- nil t)))
- (and footnote-section
- (apply #'org-element-adopt-elements (nreverse definitions)))))
- ;; If there should be a footnote section, create one containing
- ;; all the definitions at the end of the tree.
- (org-footnote-section
- (org-element-adopt-elements
- tree
- (org-element-create 'headline
- (list :footnote-section-p t
- :level 1
- :title org-footnote-section)
- (apply #'org-element-create
- 'section
- nil
- (nreverse definitions)))))
- ;; Otherwise add each definition at the end of the section where
- ;; it is first referenced.
- (t
- (let* ((seen)
- (insert-definitions) ; For byte-compiler.
- (insert-definitions
- (lambda (data)
- ;; Insert definitions in the same section as their
- ;; first reference in DATA.
- (org-element-map tree 'footnote-reference
- (lambda (f)
- (when (eq (org-element-property :type f) 'standard)
- (let ((label (org-element-property :label f)))
- (unless (member label seen)
- (push label seen)
- (let ((definition
- (catch 'found
- (dolist (d definitions)
- (when (equal
- (org-element-property :label d)
- label)
- (setq definitions
- (delete d definitions))
- (throw 'found d))))))
- (when definition
- (org-element-adopt-elements
- (org-element-lineage f '(section))
- definition)
- (funcall insert-definitions
- definition)))))))))))
- (funcall insert-definitions tree))))))))
-
;;;###autoload
(defun org-export-as
(backend &optional subtreep visible-only body-only ext-plist)
@@ -2934,11 +3016,8 @@ Return code as a string."
;; attributes, unavailable in its copy.
(let* ((org-export-current-backend (org-export-backend-name backend))
(info (org-combine-plists
- (list :export-options
- (delq nil
- (list (and subtreep 'subtree)
- (and visible-only 'visible-only)
- (and body-only 'body-only))))
+ (org-export--get-export-attributes
+ backend subtreep visible-only body-only)
(org-export--get-buffer-attributes)))
(parsed-keywords
(delq nil
@@ -2963,9 +3042,10 @@ Return code as a string."
;; again after executing Babel code.
(org-set-regexps-and-options)
(org-update-radio-target-regexp)
- (org-export-execute-babel-code)
- (org-set-regexps-and-options)
- (org-update-radio-target-regexp)
+ (when org-export-babel-evaluate
+ (org-babel-exp-process-buffer)
+ (org-set-regexps-and-options)
+ (org-update-radio-target-regexp))
;; Run last hook with current back-end's name as argument.
;; Update buffer properties and radio targets one last time
;; before parsing.
@@ -3013,8 +3093,6 @@ Return code as a string."
parsed-keywords)
;; Parse buffer.
(setq tree (org-element-parse-buffer nil visible-only))
- ;; Merge footnote definitions outside scope into parse tree.
- (org-export--merge-external-footnote-definitions tree)
;; Prune tree from non-exported elements and transform
;; uninterpreted elements or objects in both parse tree and
;; communication channel.
@@ -3026,9 +3104,7 @@ Return code as a string."
(plist-get info :filter-parse-tree) tree info))
;; Now tree is complete, compute its properties and add them
;; to communication channel.
- (setq info
- (org-combine-plists
- info (org-export-collect-tree-properties tree info)))
+ (setq info (org-export--collect-tree-properties tree info))
;; Eventually transcode TREE. Wrap the resulting string into
;; a template.
(let* ((body (org-element-normalize-string
@@ -3146,7 +3222,7 @@ locally for the subtree through node properties."
(< (+ width (length (car items)) 1) fill-column))
(let ((item (pop items)))
(insert " " item)
- (incf width (1+ (length item))))))
+ (cl-incf width (1+ (length item))))))
(insert "\n")))))
;; Then the rest of keywords, in the order specified in either
;; `org-export-options-alist' or respective export back-ends.
@@ -3213,8 +3289,7 @@ storing and resolving footnotes. It is created automatically."
(setq matched
(replace-match "" nil nil matched 1)))
(expand-file-name
- (org-remove-double-quotes
- matched)
+ (org-unbracket-string "\"" "\"" matched)
dir)))
(setq value (replace-match "" nil nil value)))))
(only-contents
@@ -3228,10 +3303,12 @@ storing and resolving footnotes. It is created automatically."
value)
(prog1 (match-string 1 value)
(setq value (replace-match "" nil nil value)))))
- (env (cond ((string-match "\\<example\\>" value)
- 'literal)
- ((string-match "\\<src\\(?: +\\(.*\\)\\)?" value)
- 'literal)))
+ (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
@@ -3243,12 +3320,11 @@ storing and resolving footnotes. It is created automatically."
(setq value (replace-match "" nil nil value)))
(get-text-property (point)
:org-include-induced-level))))
- (src-args (and (eq env 'literal)
- (match-string 1 value)))
+ (args (and (eq env 'literal) (match-string 1 value)))
(block (and (string-match "\\<\\(\\S-+\\)\\>" value)
(match-string 1 value))))
;; Remove keyword.
- (delete-region (point) (progn (forward-line) (point)))
+ (delete-region (point) (line-beginning-position 2))
(cond
((not file) nil)
((not (file-readable-p file))
@@ -3262,10 +3338,8 @@ storing and resolving footnotes. It is created automatically."
(cond
((eq env 'literal)
(insert
- (let ((ind-str (make-string ind ? ))
- (arg-str (if (stringp src-args)
- (format " %s" src-args)
- ""))
+ (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))))
@@ -3273,7 +3347,7 @@ storing and resolving footnotes. It is created automatically."
ind-str block arg-str contents ind-str block))))
((stringp block)
(insert
- (let ((ind-str (make-string ind ? ))
+ (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"
@@ -3292,7 +3366,7 @@ storing and resolving footnotes. It is created automatically."
(org-export--prepare-file-contents
file lines ind minlevel
(or (gethash file file-prefix)
- (puthash file (incf current-prefix) file-prefix))
+ (puthash file (cl-incf current-prefix) file-prefix))
footnotes)))
(org-export-expand-include-keyword
(cons (list file lines) included)
@@ -3304,7 +3378,7 @@ storing and resolving footnotes. It is created automatically."
(unless included
(org-with-wide-buffer
(goto-char (point-max))
- (maphash (lambda (k v) (insert (format "\n[%s] %s\n" k v)))
+ (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)
@@ -3338,7 +3412,7 @@ Return a string of lines to be included in the format expected by
(memq (org-element-type element) '(headline inlinetask)))
;; Skip planning line and property-drawer.
(goto-char (point-min))
- (when (org-looking-at-p org-planning-line-re) (forward-line))
+ (when (looking-at-p org-planning-line-re) (forward-line))
(when (looking-at org-property-drawer-re) (goto-char (match-end 0)))
(unless (bolp) (forward-line))
(narrow-to-region (point) (point-max))))
@@ -3366,7 +3440,7 @@ Return a string of lines to be included in the format expected by
(save-excursion
(+ start-line
(let ((counter 0))
- (while (< (point) end) (incf counter) (forward-line))
+ (while (< (point) end) (cl-incf counter) (forward-line))
counter))))))))
(defun org-export--prepare-file-contents
@@ -3425,7 +3499,7 @@ the included document."
(unless (eq major-mode 'org-mode)
(let ((org-inhibit-startup t)) (org-mode)))
(goto-char (point-min))
- (let ((ind-str (make-string ind ? )))
+ (let ((ind-str (make-string ind ?\s)))
(while (not (or (eobp) (looking-at org-outline-regexp-bol)))
;; Do not move footnote definitions out of column 0.
(unless (and (looking-at org-footnote-definition-re)
@@ -3461,17 +3535,14 @@ the included document."
(marker-max (point-max-marker))
(get-new-label
(lambda (label)
- ;; Generate new label from LABEL. If LABEL is akin to
- ;; [1] convert it to [fn:--ID-1]. Otherwise add "-ID-"
- ;; after "fn:".
- (if (org-string-match-p "\\`[0-9]+\\'" label)
- (format "fn:--%d-%s" id label)
- (format "fn:-%d-%s" id (substring label 3)))))
+ ;; Generate new label from LABEL by prefixing it with
+ ;; "-ID-".
+ (format "-%d-%s" id label)))
(set-new-label
(lambda (f old new)
;; Replace OLD label with NEW in footnote F.
(save-excursion
- (goto-char (1+ (org-element-property :begin f)))
+ (goto-char (+ (org-element-property :begin f) 4))
(looking-at (regexp-quote old))
(replace-match new))))
(seen-alist))
@@ -3507,14 +3578,6 @@ the included document."
(set-marker marker-max nil)))
(org-element-normalize-string (buffer-string))))
-(defun org-export-execute-babel-code ()
- "Execute every Babel code in the visible part of current buffer."
- ;; Get a pristine copy of current buffer so Babel references can be
- ;; properly resolved.
- (let ((reference (org-export-copy-buffer)))
- (unwind-protect (org-babel-exp-process-buffer reference)
- (kill-buffer reference))))
-
(defun org-export--copy-to-kill-ring-p ()
"Return a non-nil value when output should be added to the kill ring.
See also `org-export-copy-to-kill-ring'."
@@ -3666,16 +3729,28 @@ definition can be found, raise an error."
(let ((hash (make-hash-table :test #'equal)))
(plist-put info :footnote-definition-cache hash)
hash))))
- (or (gethash label cache)
- (puthash label
- (org-element-map (plist-get info :parse-tree)
- '(footnote-definition footnote-reference)
- (lambda (f)
- (and (equal (org-element-property :label f) label)
- (org-element-contents f)))
- info t)
- cache)
- (error "Definition not found for footnote %s" label))))))
+ (or
+ (gethash label cache)
+ (puthash label
+ (org-element-map (plist-get info :parse-tree)
+ '(footnote-definition footnote-reference)
+ (lambda (f)
+ (cond
+ ;; Skip any footnote with a different label.
+ ;; Also skip any standard footnote reference
+ ;; with the same label since those cannot
+ ;; contain a definition.
+ ((not (equal (org-element-property :label f) label)) nil)
+ ((eq (org-element-property :type f) 'standard) nil)
+ ((org-element-contents f))
+ ;; Even if the contents are empty, we can not
+ ;; return nil since that would eventually raise
+ ;; the error. Instead, return the equivalent
+ ;; empty string.
+ (t "")))
+ info t)
+ cache)
+ (error "Definition not found for footnote %s" label))))))
(defun org-export--footnote-reference-map
(function data info &optional body-first)
@@ -3684,41 +3759,41 @@ INFO is a plist containing export state. By default, as soon as
a new footnote reference is encountered, FUNCTION is called onto
its definition. However, if BODY-FIRST is non-nil, this step is
delayed until the end of the process."
- (let* ((definitions)
- (seen-refs)
- (search-ref) ; For byte-compiler.
- (search-ref
- (lambda (data delayp)
- ;; Search footnote references through DATA, filling
- ;; SEEN-REFS along the way. When DELAYP is non-nil, store
- ;; footnote definitions so they can be entered later.
- (org-element-map data 'footnote-reference
- (lambda (f)
- (funcall function f)
- (let ((--label (org-element-property :label f)))
- (unless (and --label (member --label seen-refs))
- (when --label (push --label seen-refs))
- ;; Search for subsequent references in footnote
- ;; definition so numbering follows reading logic,
- ;; unless DELAYP in non-nil.
- (cond
- (delayp
- (push (org-export-get-footnote-definition f info)
- definitions))
- ;; Do not force entering inline definitions,
- ;; since `org-element-map' already traverses them
- ;; at the right time.
- ((eq (org-element-property :type f) 'inline))
- (t (funcall search-ref
- (org-export-get-footnote-definition f info)
- nil))))))
- info nil
- ;; Don't enter footnote definitions since it will happen
- ;; when their first reference is found. Moreover, if
- ;; DELAYP is non-nil, make sure we postpone entering
- ;; definitions of inline references.
- (if delayp '(footnote-definition footnote-reference)
- 'footnote-definition)))))
+ (letrec ((definitions nil)
+ (seen-refs nil)
+ (search-ref
+ (lambda (data delayp)
+ ;; Search footnote references through DATA, filling
+ ;; SEEN-REFS along the way. When DELAYP is non-nil,
+ ;; store footnote definitions so they can be entered
+ ;; later.
+ (org-element-map data 'footnote-reference
+ (lambda (f)
+ (funcall function f)
+ (let ((--label (org-element-property :label f)))
+ (unless (and --label (member --label seen-refs))
+ (when --label (push --label seen-refs))
+ ;; Search for subsequent references in footnote
+ ;; definition so numbering follows reading
+ ;; logic, unless DELAYP in non-nil.
+ (cond
+ (delayp
+ (push (org-export-get-footnote-definition f info)
+ definitions))
+ ;; Do not force entering inline definitions,
+ ;; since `org-element-map' already traverses
+ ;; them at the right time.
+ ((eq (org-element-property :type f) 'inline))
+ (t (funcall search-ref
+ (org-export-get-footnote-definition f info)
+ nil))))))
+ info nil
+ ;; Don't enter footnote definitions since it will
+ ;; happen when their first reference is found.
+ ;; Moreover, if DELAYP is non-nil, make sure we
+ ;; postpone entering definitions of inline references.
+ (if delayp '(footnote-definition footnote-reference)
+ 'footnote-definition)))))
(funcall search-ref data body-first)
(funcall search-ref (nreverse definitions) nil)))
@@ -3744,7 +3819,7 @@ for inlined footnotes. Unreferenced definitions are ignored."
;; Collect footnote number, label and definition.
(let ((l (org-element-property :label f)))
(unless (and l (member l labels))
- (incf n)
+ (cl-incf n)
(push (list n l (org-export-get-footnote-definition f info)) alist))
(when l (push l labels))))
(or data (plist-get info :parse-tree)) info body-first)
@@ -3803,8 +3878,8 @@ process, leading to a different order when footnotes are nested."
((and label l (string= label l)) (throw 'exit (1+ count)))
;; Otherwise store label and increase counter if label
;; wasn't encountered yet.
- ((not l) (incf count))
- ((not (member l seen)) (push l seen) (incf count)))))
+ ((not l) (cl-incf count))
+ ((not (member l seen)) (push l seen) (cl-incf count)))))
(or data (plist-get info :parse-tree)) info body-first))))
@@ -3861,7 +3936,7 @@ INFO is a plist holding contextual information."
(defun org-export-numbered-headline-p (headline info)
"Return a non-nil value if HEADLINE element should be numbered.
INFO is a plist used as a communication channel."
- (unless (org-some
+ (unless (cl-some
(lambda (head) (org-not-nil (org-element-property :UNNUMBERED head)))
(org-element-lineage headline nil t))
(let ((sec-num (plist-get info :section-numbers))
@@ -3890,18 +3965,13 @@ INFO is a plist used as a communication channel."
ELEMENT has either an `headline' or an `inlinetask' type. INFO
is a plist used as a communication channel.
-Select tags (see `org-export-select-tags') and exclude tags (see
-`org-export-exclude-tags') are removed from the list.
-
When non-nil, optional argument TAGS should be a list of strings.
Any tag belonging to this list will also be removed.
When optional argument INHERITED is non-nil, tags can also be
inherited from parent headlines and FILETAGS keywords."
- (org-remove-if
- (lambda (tag) (or (member tag (plist-get info :select-tags))
- (member tag (plist-get info :exclude-tags))
- (member tag tags)))
+ (cl-remove-if
+ (lambda (tag) (member tag tags))
(if (not inherited) (org-element-property :tags element)
;; Build complete list of inherited tags.
(let ((current-tag-list (org-element-property :tags element)))
@@ -3926,7 +3996,7 @@ Return value is a string or nil."
(let ((headline (if (eq (org-element-type blob) 'headline) blob
(org-export-get-parent-headline blob))))
(if (not inherited) (org-element-property property blob)
- (let ((parent headline) value)
+ (let ((parent headline))
(catch 'found
(while parent
(when (plist-member (nth 1 parent) property)
@@ -3951,10 +4021,9 @@ fail, the fall-back value is \"???\"."
(and file (file-name-sans-extension (file-name-nondirectory file))))
"???"))
-(defun org-export-get-alt-title (headline info)
+(defun org-export-get-alt-title (headline _)
"Return alternative title for HEADLINE, as a secondary string.
-INFO is a plist used as a communication channel. If no optional
-title is defined, fall-back to the regular title."
+If no optional title is defined, fall-back to the regular title."
(let ((alt (org-element-property :ALT_TITLE headline)))
(if alt (org-element-parse-secondary-string
alt (org-element-restriction 'headline) headline)
@@ -4004,7 +4073,7 @@ meant to be translated with `org-export-data' or alike."
;;;; For Links
;;
;; `org-export-custom-protocol-maybe' handles custom protocol defined
-;; with `org-add-link-type', which see.
+;; in `org-link-parameters'.
;;
;; `org-export-get-coderef-format' returns an appropriate format
;; string for coderefs.
@@ -4014,11 +4083,11 @@ meant to be translated with `org-export-data' or alike."
;;
;; `org-export-resolve-fuzzy-link' searches destination of fuzzy links
;; (i.e. links with "fuzzy" as type) within the parsed tree, and
-;; returns an appropriate unique identifier when found, or nil.
+;; returns an appropriate unique identifier.
;;
;; `org-export-resolve-id-link' returns the first headline with
;; specified id or custom-id in parse tree, the path to the external
-;; file with the id or nil when neither was found.
+;; file with the id.
;;
;; `org-export-resolve-coderef' associates a reference to a line
;; number in the element it belongs, or returns the reference itself
@@ -4026,6 +4095,12 @@ meant to be translated with `org-export-data' or alike."
;;
;; `org-export-file-uri' expands a filename as stored in :path value
;; of a "file" link into a file URI.
+;;
+;; Broken links raise a `org-link-broken' error, which is caught by
+;; `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")
(defun org-export-custom-protocol-maybe (link desc backend)
"Try exporting LINK with a dedicated function.
@@ -4041,7 +4116,7 @@ The function ignores links with an implicit type (e.g.,
(let ((type (org-element-property :type link)))
(unless (or (member type '("coderef" "custom-id" "fuzzy" "radio"))
(not backend))
- (let ((protocol (nth 2 (assoc type org-link-protocols))))
+ (let ((protocol (org-link-get-parameter type :export)))
(and (functionp protocol)
(funcall protocol
(org-link-unescape (org-element-property :path link))
@@ -4076,8 +4151,8 @@ This only applies to links without a description."
(catch 'exit
(dolist (rule (or rules org-export-default-inline-image-rule))
(and (string= (org-element-property :type link) (car rule))
- (org-string-match-p (cdr rule)
- (org-element-property :path link))
+ (string-match-p (cdr rule)
+ (org-element-property :path link))
(throw 'exit t)))))))
(defun org-export-resolve-coderef (ref info)
@@ -4103,11 +4178,69 @@ error if no block contains REF."
(when (re-search-backward ref-re nil t)
(cond
((org-element-property :use-labels el) ref)
- ((eq (org-element-property :number-lines el) 'continued)
- (+ (org-export-get-loc el info) (line-number-at-pos)))
- (t (line-number-at-pos)))))))
+ (t (+ (or (org-export-get-loc el info) 0) (line-number-at-pos))))))))
info 'first-match)
- (user-error "Unable to resolve code reference: %s" ref)))
+ (signal 'org-link-broken (list ref))))
+
+(defun org-export-search-cells (datum)
+ "List search cells for element or object DATUM.
+
+A search cell follows the pattern (TYPE . SEARCH) where
+
+ TYPE is a symbol among `headline', `custom-id', `target' and
+ `other'.
+
+ SEARCH is the string a link is expected to match. More
+ accurately, it is
+
+ - headline's title, as a list of strings, if TYPE is
+ `headline'.
+
+ - CUSTOM_ID value, as a string, if TYPE is `custom-id'.
+
+ - target's or radio-target's name as a list of strings if
+ TYPE is `target'.
+
+ - NAME affiliated keyword is TYPE is `other'.
+
+A search cell is the internal representation of a fuzzy link. It
+ignores white spaces and statistics cookies, if applicable."
+ (pcase (org-element-type datum)
+ (`headline
+ (let ((title (split-string
+ (replace-regexp-in-string
+ "\\[[0-9]*\\(?:%\\|/[0-9]*\\)\\]" ""
+ (org-element-property :raw-value datum)))))
+ (delq nil
+ (list
+ (cons 'headline title)
+ (cons 'other title)
+ (let ((custom-id (org-element-property :custom-id datum)))
+ (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))
+ (guard name))
+ (list (cons 'other (split-string name))))
+ (_ nil)))
+
+(defun org-export-string-to-search-cell (s)
+ "Return search cells associated to string S.
+S is either the path of a fuzzy link or a search option, i.e., it
+tries to match either a headline (through custom ID or title),
+a target or a named element."
+ (pcase (string-to-char s)
+ (?* (list (cons 'headline (split-string (substring s 1)))))
+ (?# (list (cons 'custom-id (substring s 1))))
+ ((let search (split-string s))
+ (list (cons 'target search) (cons 'other search)))))
+
+(defun org-export-match-search-cell-p (datum cells)
+ "Non-nil when DATUM matches search cells CELLS.
+DATUM is an element or object. CELLS is a list of search cells,
+as returned by `org-export-search-cells'."
+ (let ((targets (org-export-search-cells datum)))
+ (and targets (cl-some (lambda (cell) (member cell targets)) cells))))
(defun org-export-resolve-fuzzy-link (link info)
"Return LINK destination.
@@ -4128,54 +4261,37 @@ Return value can be an object or an element:
Assume LINK type is \"fuzzy\". White spaces are not
significant."
- (let* ((raw-path (org-link-unescape (org-element-property :path link)))
- (headline-only (eq (string-to-char raw-path) ?*))
- ;; Split PATH at white spaces so matches are space
- ;; insensitive.
- (path (org-split-string
- (if headline-only (substring raw-path 1) raw-path)))
+ (let* ((search-cells (org-export-string-to-search-cell
+ (org-link-unescape (org-element-property :path link))))
(link-cache
(or (plist-get info :resolve-fuzzy-link-cache)
(plist-get (plist-put info
:resolve-fuzzy-link-cache
(make-hash-table :test #'equal))
:resolve-fuzzy-link-cache)))
- (cached (gethash path link-cache 'not-found)))
+ (cached (gethash search-cells link-cache 'not-found)))
(if (not (eq cached 'not-found)) cached
- (let ((ast (plist-get info :parse-tree)))
+ (let ((matches
+ (org-element-map (plist-get info :parse-tree)
+ (cons 'target org-element-all-elements)
+ (lambda (datum)
+ (and (org-export-match-search-cell-p datum search-cells)
+ datum)))))
+ (unless matches
+ (signal 'org-link-broken
+ (list (org-element-property :raw-path link))))
(puthash
- path
- (cond
- ;; First try to find a matching "<<path>>" unless user
- ;; specified he was looking for a headline (path starts with
- ;; a "*" character).
- ((and (not headline-only)
- (org-element-map ast 'target
- (lambda (datum)
- (and (equal (org-split-string
- (org-element-property :value datum))
- path)
- datum))
- info 'first-match)))
- ;; Then try to find an element with a matching "#+NAME: path"
- ;; affiliated keyword.
- ((and (not headline-only)
- (org-element-map ast org-element-all-elements
- (lambda (datum)
- (let ((name (org-element-property :name datum)))
- (and name (equal (org-split-string name) path) datum)))
- info 'first-match)))
- ;; Try to find a matching headline.
- ((org-element-map ast 'headline
- (lambda (h)
- (and (equal (org-split-string
- (replace-regexp-in-string
- "\\[[0-9]+%\\]\\|\\[[0-9]+/[0-9]+\\]" ""
- (org-element-property :raw-value h)))
- path)
- h))
- info 'first-match))
- (t (user-error "Unable to resolve link \"%s\"" raw-path)))
+ search-cells
+ ;; There can be multiple matches for un-typed searches, i.e.,
+ ;; for searches not starting with # or *. In this case,
+ ;; prioritize targets and names over headline titles.
+ ;; Matching both a name and a target is not valid, and
+ ;; therefore undefined.
+ (or (cl-some (lambda (datum)
+ (and (not (eq (org-element-type datum) 'headline))
+ datum))
+ matches)
+ (car matches))
link-cache)))))
(defun org-export-resolve-id-link (link info)
@@ -4196,7 +4312,7 @@ tree or a file name. Assume LINK type is either \"id\" or
info 'first-match)
;; Otherwise, look for external files.
(cdr (assoc id (plist-get info :id-alist)))
- (user-error "Unable to resolve ID \"%s\"" id))))
+ (signal 'org-link-broken (list id)))))
(defun org-export-resolve-radio-link (link info)
"Return radio-target object referenced as LINK destination.
@@ -4219,7 +4335,7 @@ has type \"radio\"."
(defun org-export-file-uri (filename)
"Return file URI associated to FILENAME."
- (cond ((org-string-match-p "\\`//" filename) (concat "file:" filename))
+ (cond ((string-match-p "\\`//" filename) (concat "file:" filename))
((not (file-name-absolute-p filename)) filename)
((org-file-remote-p filename) (concat "file:/" filename))
(t (concat "file://" (expand-file-name filename)))))
@@ -4228,29 +4344,63 @@ has type \"radio\"."
;;;; For References
;;
;; `org-export-get-reference' associate a unique reference for any
-;; object or element.
+;; object or element. It uses `org-export-new-reference' and
+;; `org-export-format-reference' to, respectively, generate new
+;; internal references and turn them into a string suitable for
+;; output.
;;
;; `org-export-get-ordinal' associates a sequence number to any object
;; or element.
+(defun org-export-new-reference (references)
+ "Return a unique reference, among REFERENCES.
+REFERENCES is an alist whose values are in-use references, as
+numbers. Returns a number, which is the internal representation
+of a reference. See also `org-export-format-reference'."
+ ;; Generate random 7 digits hexadecimal numbers. Collisions
+ ;; increase exponentially with the numbers of references. However,
+ ;; the odds for encountering at least one collision with 1000 active
+ ;; references in the same document are roughly 0.2%, so this
+ ;; shouldn't be the bottleneck.
+ (let ((new (random #x10000000)))
+ (while (rassq new references) (setq new (random #x10000000)))
+ new))
+
+(defun org-export-format-reference (reference)
+ "Format REFERENCE into a string.
+REFERENCE is a number representing a reference, as returned by
+`org-export-new-reference', which see."
+ (format "org%07x" reference))
+
(defun org-export-get-reference (datum info)
"Return a unique reference for DATUM, as a string.
+
DATUM is either an element or an object. INFO is the current
-export state, as a plist. Returned reference consists of
-alphanumeric characters only."
- (let ((type (org-element-type datum))
- (cache (or (plist-get info :internal-references)
- (let ((h (make-hash-table :test #'eq)))
- (plist-put info :internal-references h)
- h))))
- (or (gethash datum cache)
- (puthash datum
- (format "org%s%d"
- (if type
- (replace-regexp-in-string "-" "" (symbol-name type))
- "secondarystring")
- (incf (gethash type cache 0)))
- cache))))
+export state, as a plist.
+
+This function checks `:crossrefs' property in INFO for search
+cells matching DATUM before creating a new reference. Returned
+reference consists of alphanumeric characters only."
+ (let ((cache (plist-get info :internal-references)))
+ (or (car (rassq datum cache))
+ (let* ((crossrefs (plist-get info :crossrefs))
+ (cells (org-export-search-cells datum))
+ ;; If any other published document relies on an
+ ;; association between a search cell and a reference,
+ ;; make sure to preserve it. See
+ ;; `org-publish-resolve-external-link' for details.
+ (new (or (cdr (cl-some (lambda (c) (assoc c crossrefs)) cells))
+ (org-export-new-reference cache)))
+ (reference-string (org-export-format-reference new)))
+ ;; Cache contains both data already associated to
+ ;; a reference and in-use internal references, so as to make
+ ;; unique references.
+ (dolist (cell cells) (push (cons cell new) cache))
+ ;; Keep an associated related to DATUM as not every object
+ ;; and element can be associated to a search cell.
+ (push (cons reference-string datum) cache)
+ (plist-put info :internal-references cache)
+ reference-string))))
(defun org-export-get-ordinal (element info &optional types predicate)
"Return ordinal number of an element or object.
@@ -4282,7 +4432,7 @@ objects of the same type."
(org-element-lineage
element
'(footnote-definition footnote-reference headline item table))))
- (case (org-element-type element)
+ (cl-case (org-element-type element)
;; Special case 1: A headline returns its number as a list.
(headline (org-export-get-headline-number element info))
;; Special case 2: An item returns its number as a list.
@@ -4302,8 +4452,8 @@ objects of the same type."
(lambda (el)
(cond
((eq element el) (1+ counter))
- ((not predicate) (incf counter) nil)
- ((funcall predicate el info) (incf counter) nil)))
+ ((not predicate) (cl-incf counter) nil)
+ ((funcall predicate el info) (cl-incf counter) nil)))
info 'first-match)))))
@@ -4330,32 +4480,34 @@ objects of the same type."
;; code in a format suitable for plain text or verbatim output.
(defun org-export-get-loc (element info)
- "Return accumulated lines of code up to ELEMENT.
-
-INFO is the plist used as a communication channel.
-
-ELEMENT is excluded from count."
- (let ((loc 0))
- (org-element-map (plist-get info :parse-tree)
- `(src-block example-block ,(org-element-type element))
- (lambda (el)
- (cond
- ;; ELEMENT is reached: Quit the loop.
- ((eq el element))
- ;; Only count lines from src-block and example-block elements
- ;; with a "+n" or "-n" switch. A "-n" switch resets counter.
- ((not (memq (org-element-type el) '(src-block example-block))) nil)
- ((let ((linums (org-element-property :number-lines el)))
- (when linums
- ;; Accumulate locs or reset them.
- (let ((lines (org-count-lines
- (org-trim (org-element-property :value el)))))
- (setq loc (if (eq linums 'new) lines (+ loc lines))))))
- ;; Return nil to stay in the loop.
- nil)))
- info 'first-match)
- ;; Return value.
- loc))
+ "Return count of lines of code before ELEMENT.
+
+ELEMENT is an example-block or src-block element. INFO is the
+plist used as a communication channel.
+
+Count includes every line of code in example-block or src-block
+with a \"+n\" or \"-n\" switch before block. Return nil if
+ELEMENT doesn't allow line numbering."
+ (pcase (org-element-property :number-lines element)
+ (`(new . ,n) n)
+ (`(continued . ,n)
+ (let ((loc 0))
+ (org-element-map (plist-get info :parse-tree) '(src-block example-block)
+ (lambda (el)
+ ;; ELEMENT is reached: Quit loop and return locs.
+ (if (eq el element) (+ loc n)
+ ;; Only count lines from src-block and example-block
+ ;; elements with a "+n" or "-n" switch.
+ (let ((linum (org-element-property :number-lines el)))
+ (when linum
+ (let ((lines (org-count-lines
+ (org-trim (org-element-property :value el)))))
+ ;; Accumulate locs or reset them.
+ (pcase linum
+ (`(new . ,n) (setq loc (+ n lines)))
+ (`(continued . ,n) (cl-incf loc (+ n lines)))))))
+ nil)) ;Return nil to stay in the loop.
+ info 'first-match)))))
(defun org-export-unravel-code (element)
"Clean source code and extract references out of it.
@@ -4377,24 +4529,17 @@ reference on that line (string)."
(if (or org-src-preserve-indentation
(org-element-property :preserve-indent element))
value
- (org-element-remove-indentation value)))))
- ;; Get format used for references.
- (label-fmt (regexp-quote
- (or (org-element-property :label-fmt element)
- org-coderef-label-format)))
+ (org-remove-indentation value)))))
;; Build a regexp matching a loc with a reference.
- (with-ref-re
- (format "^.*?\\S-.*?\\([ \t]*\\(%s\\)[ \t]*\\)$"
- (replace-regexp-in-string
- "%s" "\\([-a-zA-Z0-9_ ]+\\)" label-fmt nil t))))
+ (ref-re (org-src-coderef-regexp (org-src-coderef-format element))))
;; Return value.
(cons
;; Code with references removed.
(org-element-normalize-string
(mapconcat
(lambda (loc)
- (incf line)
- (if (not (string-match with-ref-re loc)) loc
+ (cl-incf line)
+ (if (not (string-match ref-re loc)) loc
;; Ref line: remove ref, and signal its position in REFS.
(push (cons line (match-string 3 loc)) refs)
(replace-match "" nil nil loc 1)))
@@ -4426,7 +4571,7 @@ be nil. It can be obtained through the use of
(org-element-normalize-string
(mapconcat
(lambda (--loc)
- (incf --line)
+ (cl-incf --line)
(let ((--ref (cdr (assq --line ref-alist))))
(funcall fun --loc (and num-lines (+ num-lines --line)) --ref)))
--locs "\n"))))
@@ -4451,9 +4596,7 @@ code."
(let* ((refs (and (org-element-property :retain-labels element)
(cdr code-info)))
;; Handle line numbering.
- (num-start (case (org-element-property :number-lines element)
- (continued (org-export-get-loc element info))
- (new 0)))
+ (num-start (org-export-get-loc element info))
(num-fmt
(and num-start
(format "%%%ds "
@@ -4517,16 +4660,14 @@ All special columns will be ignored during export."
;; only empty cells as special.
(let ((special-column-p 'empty))
(catch 'exit
- (mapc
- (lambda (row)
- (when (eq (org-element-property :type row) 'standard)
- (let ((value (org-element-contents
- (car (org-element-contents row)))))
- (cond ((member value '(("/") ("#") ("!") ("$") ("*") ("_") ("^")))
- (setq special-column-p 'special))
- ((not value))
- (t (throw 'exit nil))))))
- (org-element-contents table))
+ (dolist (row (org-element-contents table))
+ (when (eq (org-element-property :type row) 'standard)
+ (let ((value (org-element-contents
+ (car (org-element-contents row)))))
+ (cond ((member value '(("/") ("#") ("!") ("$") ("*") ("_") ("^")))
+ (setq special-column-p 'special))
+ ((not value))
+ (t (throw 'exit nil))))))
(eq special-column-p 'special))))
(defun org-export-table-has-header-p (table info)
@@ -4549,18 +4690,15 @@ A table has a header when it contains at least two row groups."
(cond
((> rowgroup 1) t)
((and row-flag (eq (org-element-property :type row) 'rule))
- (incf rowgroup) (setq row-flag nil))
+ (cl-incf rowgroup) (setq row-flag nil))
((and (not row-flag) (eq (org-element-property :type row)
'standard))
(setq row-flag t) nil)))
info 'first-match)
cache)))))
-(defun org-export-table-row-is-special-p (table-row info)
+(defun org-export-table-row-is-special-p (table-row _)
"Non-nil if TABLE-ROW is considered special.
-
-INFO is a plist used as the communication channel.
-
All special rows will be ignored during export."
(when (eq (org-element-property :type table-row) 'standard)
(let ((first-cell (org-element-contents
@@ -4577,19 +4715,17 @@ All special rows will be ignored during export."
;; ... it contains only alignment cookies and empty cells.
(let ((special-row-p 'empty))
(catch 'exit
- (mapc
- (lambda (cell)
- (let ((value (org-element-contents cell)))
- ;; Since VALUE is a secondary string, the following
- ;; checks avoid expanding it with `org-export-data'.
- (cond ((not value))
- ((and (not (cdr value))
- (stringp (car value))
- (string-match "\\`<[lrc]?\\([0-9]+\\)?>\\'"
- (car value)))
- (setq special-row-p 'cookie))
- (t (throw 'exit nil)))))
- (org-element-contents table-row))
+ (dolist (cell (org-element-contents table-row))
+ (let ((value (org-element-contents cell)))
+ ;; Since VALUE is a secondary string, the following
+ ;; checks avoid expanding it with `org-export-data'.
+ (cond ((not value))
+ ((and (not (cdr value))
+ (stringp (car value))
+ (string-match "\\`<[lrc]?\\([0-9]+\\)?>\\'"
+ (car value)))
+ (setq special-row-p 'cookie))
+ (t (throw 'exit nil)))))
(eq special-row-p 'cookie)))))))
(defun org-export-table-row-group (table-row info)
@@ -4612,7 +4748,7 @@ header."
(lambda (row)
(if (eq (org-element-property :type row) 'rule)
(setq row-flag nil)
- (unless row-flag (incf group) (setq row-flag t)))
+ (unless row-flag (cl-incf group) (setq row-flag t)))
(when (eq table-row row) (puthash table-row group cache)))
info 'first-match))))))
@@ -4712,14 +4848,14 @@ Possible values are `left', `right' and `center'."
(org-element-contents
(elt (org-element-contents row) column))
info)))
- (incf total-cells)
+ (cl-incf total-cells)
;; Treat an empty cell as a number if it follows
;; a number.
(if (not (or (string-match org-table-number-regexp value)
(and (string= value "") previous-cell-number-p)))
(setq previous-cell-number-p nil)
(setq previous-cell-number-p t)
- (incf number-cells))))))
+ (cl-incf number-cells))))))
;; Return value. Alignment specified by cookies has
;; precedence over alignment deduced from cell's contents.
(aset align-vector
@@ -4752,14 +4888,13 @@ Returned borders ignore special rows."
;; another regular row has to be found above that rule.
(let (rule-flag)
(catch 'exit
- (mapc (lambda (row)
- (cond ((eq (org-element-property :type row) 'rule)
- (setq rule-flag t))
- ((not (org-export-table-row-is-special-p row info))
- (if rule-flag (throw 'exit (push 'above borders))
- (throw 'exit nil)))))
- ;; Look at every row before the current one.
- (cdr (memq row (reverse (org-element-contents table)))))
+ ;; Look at every row before the current one.
+ (dolist (row (cdr (memq row (reverse (org-element-contents table)))))
+ (cond ((eq (org-element-property :type row) 'rule)
+ (setq rule-flag t))
+ ((not (org-export-table-row-is-special-p row info))
+ (if rule-flag (throw 'exit (push 'above borders))
+ (throw 'exit nil)))))
;; No rule above, or rule found starts the table (ignoring any
;; special row): TABLE-CELL is at the top of the table.
(when rule-flag (push 'above borders))
@@ -4768,14 +4903,13 @@ Returned borders ignore special rows."
;; non-regular row below is a rule.
(let (rule-flag)
(catch 'exit
- (mapc (lambda (row)
- (cond ((eq (org-element-property :type row) 'rule)
- (setq rule-flag t))
- ((not (org-export-table-row-is-special-p row info))
- (if rule-flag (throw 'exit (push 'below borders))
- (throw 'exit nil)))))
- ;; Look at every row after the current one.
- (cdr (memq row (org-element-contents table))))
+ ;; Look at every row after the current one.
+ (dolist (row (cdr (memq row (org-element-contents table))))
+ (cond ((eq (org-element-property :type row) 'rule)
+ (setq rule-flag t))
+ ((not (org-export-table-row-is-special-p row info))
+ (if rule-flag (throw 'exit (push 'below borders))
+ (throw 'exit nil)))))
;; No rule below, or rule found ends the table (modulo some
;; special row): TABLE-CELL is at the bottom of the table.
(when rule-flag (push 'below borders))
@@ -4787,37 +4921,35 @@ Returned borders ignore special rows."
(catch 'exit
(let ((column (let ((cells (org-element-contents row)))
(- (length cells) (length (memq table-cell cells))))))
- (mapc
- (lambda (row)
- (unless (eq (org-element-property :type row) 'rule)
- (when (equal (org-element-contents
- (car (org-element-contents row)))
- '("/"))
- (let ((column-groups
- (mapcar
- (lambda (cell)
- (let ((value (org-element-contents cell)))
- (when (member value '(("<") ("<>") (">") nil))
- (car value))))
- (org-element-contents row))))
- ;; There's a left border when previous cell, if
- ;; any, ends a group, or current one starts one.
- (when (or (and (not (zerop column))
- (member (elt column-groups (1- column))
- '(">" "<>")))
- (member (elt column-groups column) '("<" "<>")))
- (push 'left borders))
- ;; There's a right border when next cell, if any,
- ;; starts a group, or current one ends one.
- (when (or (and (/= (1+ column) (length column-groups))
- (member (elt column-groups (1+ column))
- '("<" "<>")))
- (member (elt column-groups column) '(">" "<>")))
- (push 'right borders))
- (throw 'exit nil)))))
- ;; Table rows are read in reverse order so last column groups
- ;; row has precedence over any previous one.
- (reverse (org-element-contents table)))))
+ ;; Table rows are read in reverse order so last column groups
+ ;; row has precedence over any previous one.
+ (dolist (row (reverse (org-element-contents table)))
+ (unless (eq (org-element-property :type row) 'rule)
+ (when (equal (org-element-contents
+ (car (org-element-contents row)))
+ '("/"))
+ (let ((column-groups
+ (mapcar
+ (lambda (cell)
+ (let ((value (org-element-contents cell)))
+ (when (member value '(("<") ("<>") (">") nil))
+ (car value))))
+ (org-element-contents row))))
+ ;; There's a left border when previous cell, if
+ ;; any, ends a group, or current one starts one.
+ (when (or (and (not (zerop column))
+ (member (elt column-groups (1- column))
+ '(">" "<>")))
+ (member (elt column-groups column) '("<" "<>")))
+ (push 'left borders))
+ ;; There's a right border when next cell, if any,
+ ;; starts a group, or current one ends one.
+ (when (or (and (/= (1+ column) (length column-groups))
+ (member (elt column-groups (1+ column))
+ '("<" "<>")))
+ (member (elt column-groups column) '(">" "<>")))
+ (push 'right borders))
+ (throw 'exit nil)))))))
;; Return value.
borders))
@@ -4892,7 +5024,7 @@ special columns and separators."
(lambda (row)
(cond ((eq row table-row) number)
((eq (org-element-property :type row) 'standard)
- (incf number) nil)))
+ (cl-incf number) nil)))
info 'first-match))))
(defun org-export-table-dimensions (table info)
@@ -4908,10 +5040,10 @@ rows (resp. columns)."
(org-element-map table 'table-row
(lambda (row)
(when (eq (org-element-property :type row) 'standard)
- (incf rows)
+ (cl-incf rows)
(unless first-row (setq first-row row)))) info)
;; Set number of columns.
- (org-element-map first-row 'table-cell (lambda (cell) (incf columns)) info)
+ (org-element-map first-row 'table-cell (lambda (_) (cl-incf columns)) info)
;; Return value.
(cons rows columns)))
@@ -4931,7 +5063,7 @@ function returns nil for other cells."
(let ((col-count 0))
(org-element-map table-row 'table-cell
(lambda (cell)
- (if (eq cell table-cell) col-count (incf col-count) nil))
+ (if (eq cell table-cell) col-count (cl-incf col-count) nil))
info 'first-match))))))
(defun org-export-get-table-cell-at (address table info)
@@ -4951,12 +5083,12 @@ return nil."
(lambda (row)
(cond ((eq (org-element-property :type row) 'rule) nil)
((= row-count row-pos) row)
- (t (incf row-count) nil)))
+ (t (cl-incf row-count) nil)))
info 'first-match))
'table-cell
(lambda (cell)
(if (= column-count column-pos) cell
- (incf column-count) nil))
+ (cl-incf column-count) nil))
info 'first-match)))
@@ -5064,10 +5196,6 @@ Return a list of src-block elements with a caption."
;;
;; Dictionary for smart quotes is stored in
;; `org-export-smart-quotes-alist'.
-;;
-;; Internally, regexps matching potential smart quotes (checks at
-;; string boundaries are also necessary) are defined in
-;; `org-export-smart-quotes-regexps'.
(defconst org-export-smart-quotes-alist
'(("da"
@@ -5122,6 +5250,16 @@ Return a list of src-block elements with a caption."
(secondary-closing :utf-8 " »" :html "&nbsp;&raquo;" :latex "\\fg{}"
:texinfo "@tie{}@guillemetright{}")
(apostrophe :utf-8 "’" :html "&rsquo;"))
+ ("is"
+ (primary-opening
+ :utf-8 "„" :html "&bdquo;" :latex "\"`" :texinfo "@quotedblbase{}")
+ (primary-closing
+ :utf-8 "“" :html "&ldquo;" :latex "\"'" :texinfo "@quotedblleft{}")
+ (secondary-opening
+ :utf-8 "‚" :html "&sbquo;" :latex "\\glq{}" :texinfo "@quotesinglbase{}")
+ (secondary-closing
+ :utf-8 "‘" :html "&lsquo;" :latex "\\grq{}" :texinfo "@quoteleft{}")
+ (apostrophe :utf-8 "’" :html "&rsquo;"))
("no"
;; https://nn.wikipedia.org/wiki/Sitatteikn
(primary-opening
@@ -5200,7 +5338,11 @@ INFO is the current export state, as a plist."
(value (gethash parent cache 'missing-data)))
(if (not (eq value 'missing-data)) (cdr (assq s value))
(let (level1-open full-status)
- (org-element-map parent 'plain-text
+ (org-element-map
+ (let ((secondary (org-element-secondary-p s)))
+ (if secondary (org-element-property secondary parent)
+ (org-element-contents parent)))
+ 'plain-text
(lambda (text)
(let ((start 0) current-status)
(while (setq start (string-match "['\"]" text start))
@@ -5223,7 +5365,7 @@ INFO is the current export state, as a plist."
(let ((p (org-export-get-previous-element
text info)))
(cond ((not p) nil)
- ((stringp p) (substring p (1- (length p))))
+ ((stringp p) (substring p -1))
((memq (org-element-property :post-blank p)
'(0 nil))
'no-blank)
@@ -5257,7 +5399,7 @@ INFO is the current export state, as a plist."
(allow-close 'secondary-closing)
(t 'apostrophe)))))
current-status)
- (setq start (1+ start)))
+ (cl-incf start))
(when current-status
(push (cons text (nreverse current-status)) full-status))))
info nil org-element-recursive-objects)
@@ -5303,9 +5445,6 @@ Return the new string."
;; defsubst org-export-get-parent must be defined before first use
-(define-obsolete-function-alias
- 'org-export-get-genealogy 'org-element-lineage "25.1")
-
(defun org-export-get-parent-headline (blob)
"Return BLOB parent headline or nil.
BLOB is the element or object being considered."
@@ -5344,7 +5483,7 @@ all of them."
((null n) (throw 'exit obj))
((not (wholenump n)) (push obj prev))
((zerop n) (throw 'exit prev))
- (t (decf n) (push obj prev)))))))
+ (t (cl-decf n) (push obj prev)))))))
(defun org-export-get-next-element (blob info &optional n)
"Return next element or object.
@@ -5370,7 +5509,7 @@ them."
((null n) (throw 'exit obj))
((not (wholenump n)) (push obj next))
((zerop n) (throw 'exit (nreverse next)))
- (t (decf n) (push obj next)))))))
+ (t (cl-decf n) (push obj next)))))))
;;;; Translation
@@ -5457,6 +5596,7 @@ them."
("es" :ascii "Ecuacion" :html "Ecuaci&oacute;n" :default "Ecuación")
("et" :html "V&#245;rrand" :utf-8 "Võrrand")
("fr" :ascii "Equation" :default "Équation")
+ ("is" :default "Jafna")
("ja" :default "方程式")
("no" :default "Ligning")
("nb" :default "Ligning")
@@ -5471,6 +5611,7 @@ them."
("de" :default "Abbildung")
("es" :default "Figura")
("et" :default "Joonis")
+ ("is" :default "Mynd")
("ja" :default "図" :html "&#22259;")
("no" :default "Illustrasjon")
("nb" :default "Illustrasjon")
@@ -5485,6 +5626,7 @@ them."
("es" :default "Figura %d:")
("et" :default "Joonis %d:")
("fr" :default "Figure %d :" :html "Figure&nbsp;%d&nbsp;:")
+ ("is" :default "Mynd %d")
("ja" :default "図%d: " :html "&#22259;%d: ")
("no" :default "Illustrasjon %d")
("nb" :default "Illustrasjon %d")
@@ -5537,6 +5679,7 @@ them."
("es" :ascii "Indice de tablas" :html "&Iacute;ndice de tablas" :default "Índice de tablas")
("et" :default "Tabelite nimekiri")
("fr" :default "Liste des tableaux")
+ ("is" :default "Töfluskrá" :html "T&ouml;fluskr&aacute;")
("ja" :default "表目次")
("no" :default "Tabeller")
("nb" :default "Tabeller")
@@ -5593,6 +5736,7 @@ them."
("es" :default "Tabla")
("et" :default "Tabel")
("fr" :default "Tableau")
+ ("is" :default "Tafla")
("ja" :default "表" :html "&#34920;")
("pt_BR" :default "Tabela")
("ru" :html "&#1058;&#1072;&#1073;&#1083;&#1080;&#1094;&#1072;"
@@ -5604,6 +5748,7 @@ them."
("es" :default "Tabla %d")
("et" :default "Tabel %d")
("fr" :default "Tableau %d :")
+ ("is" :default "Tafla %d")
("ja" :default "表%d:" :html "&#34920;%d:")
("no" :default "Tabell %d")
("nb" :default "Tabell %d")
@@ -5938,24 +6083,17 @@ Return file name as a string."
(let* ((visited-file (buffer-file-name (buffer-base-buffer)))
(base-name
;; File name may come from EXPORT_FILE_NAME subtree
- ;; property, assuming point is at beginning of said
- ;; sub-tree.
+ ;; property.
(file-name-sans-extension
- (or (and subtreep
- (org-entry-get
- (save-excursion
- (ignore-errors (org-back-to-heading) (point)))
- "EXPORT_FILE_NAME" 'selective))
+ (or (and subtreep (org-entry-get nil "EXPORT_FILE_NAME" 'selective))
;; File name may be extracted from buffer's associated
;; file, if any.
(and visited-file (file-name-nondirectory visited-file))
;; Can't determine file name on our own: Ask user.
- (let ((read-file-name-function
- (and org-completion-use-ido 'ido-read-file-name)))
- (read-file-name
- "Output file: " pub-dir nil nil nil
- (lambda (name)
- (string= (file-name-extension name t) extension)))))))
+ (read-file-name
+ "Output file: " pub-dir nil nil nil
+ (lambda (name)
+ (string= (file-name-extension name t) extension))))))
(output-file
;; Build file name. Enforce EXTENSION over whatever user
;; may have come up with. PUB-DIR, if defined, always has
@@ -5969,7 +6107,7 @@ Return file name as a string."
(t (concat (file-name-as-directory ".") base-name extension)))))
;; If writing to OUTPUT-FILE would overwrite original file, append
;; EXTENSION another time to final name.
- (if (and visited-file (org-file-equal-p visited-file output-file))
+ (if (and visited-file (file-equal-p visited-file output-file))
(concat output-file extension)
output-file)))
@@ -5990,68 +6128,21 @@ removed beforehand. Return the new stack."
"Menu for asynchronous export results and running processes."
(interactive)
(let ((buffer (get-buffer-create "*Org Export Stack*")))
- (set-buffer buffer)
- (when (zerop (buffer-size)) (org-export-stack-mode))
- (org-export-stack-refresh)
+ (with-current-buffer buffer
+ (org-export-stack-mode)
+ (tabulated-list-print t))
(pop-to-buffer buffer))
(message "Type \"q\" to quit, \"?\" for help"))
-(defun org-export--stack-source-at-point ()
- "Return source from export results at point in stack."
- (let ((source (car (nth (1- (org-current-line)) org-export-stack-contents))))
- (if (not source) (error "Source unavailable, please refresh buffer")
- (let ((source-name (if (stringp source) source (buffer-name source))))
- (if (save-excursion
- (beginning-of-line)
- (looking-at (concat ".* +" (regexp-quote source-name) "$")))
- source
- ;; SOURCE is not consistent with current line. The stack
- ;; view is outdated.
- (error "Source unavailable; type `g' to update buffer"))))))
-
(defun org-export-stack-clear ()
"Remove all entries from export stack."
(interactive)
(setq org-export-stack-contents nil))
-(defun org-export-stack-refresh (&rest dummy)
- "Refresh the asynchronous export stack.
-DUMMY is ignored. Unavailable sources are removed from the list.
-Return the new stack."
- (let ((inhibit-read-only t))
- (org-preserve-lc
- (erase-buffer)
- (insert (concat
- (let ((counter 0))
- (mapconcat
- (lambda (entry)
- (let ((proc-p (processp (nth 2 entry))))
- (concat
- ;; Back-end.
- (format " %-12s " (or (nth 1 entry) ""))
- ;; Age.
- (let ((data (nth 2 entry)))
- (if proc-p (format " %6s " (process-status data))
- ;; Compute age of the results.
- (org-format-seconds
- "%4h:%.2m "
- (float-time (time-since data)))))
- ;; Source.
- (format " %s"
- (let ((source (car entry)))
- (if (stringp source) source
- (buffer-name source)))))))
- ;; Clear stack from exited processes, dead buffers or
- ;; non-existent files.
- (setq org-export-stack-contents
- (org-remove-if-not
- (lambda (el)
- (if (processp (nth 2 el))
- (buffer-live-p (process-buffer (nth 2 el)))
- (let ((source (car el)))
- (if (bufferp source) (buffer-live-p source)
- (file-exists-p source)))))
- org-export-stack-contents)) "\n")))))))
+(defun org-export-stack-refresh ()
+ "Refresh the export stack."
+ (interactive)
+ (tabulated-list-print t))
(defun org-export-stack-remove (&optional source)
"Remove export results at point from stack.
@@ -6059,7 +6150,7 @@ If optional argument SOURCE is non-nil, remove it instead."
(interactive)
(let ((source (or source (org-export--stack-source-at-point))))
(setq org-export-stack-contents
- (org-remove-if (lambda (el) (equal (car el) source))
+ (cl-remove-if (lambda (el) (equal (car el) source))
org-export-stack-contents))))
(defun org-export-stack-view (&optional in-emacs)
@@ -6075,11 +6166,10 @@ within Emacs."
(defvar org-export-stack-mode-map
(let ((km (make-sparse-keymap)))
+ (set-keymap-parent km tabulated-list-mode-map)
(define-key km " " 'next-line)
- (define-key km "n" 'next-line)
(define-key km "\C-n" 'next-line)
(define-key km [down] 'next-line)
- (define-key km "p" 'previous-line)
(define-key km "\C-p" 'previous-line)
(define-key km "\C-?" 'previous-line)
(define-key km [up] 'previous-line)
@@ -6090,31 +6180,85 @@ within Emacs."
km)
"Keymap for Org Export Stack.")
-(define-derived-mode org-export-stack-mode special-mode "Org-Stack"
+(define-derived-mode org-export-stack-mode tabulated-list-mode "Org-Stack"
"Mode for displaying asynchronous export stack.
-Type \\[org-export-stack] to visualize the asynchronous export
+Type `\\[org-export-stack]' to visualize the asynchronous export
stack.
-In an Org Export Stack buffer, use \\<org-export-stack-mode-map>\\[org-export-stack-view] to view export output
-on current line, \\[org-export-stack-remove] to remove it from the stack and \\[org-export-stack-clear] to clear
+In an Org Export Stack buffer, use \
+\\<org-export-stack-mode-map>`\\[org-export-stack-view]' to view export output
+on current line, `\\[org-export-stack-remove]' to remove it from the stack and \
+`\\[org-export-stack-clear]' to clear
stack completely.
-Removing entries in an Org Export Stack buffer doesn't affect
-files or buffers, only the display.
+Removing entries in a stack buffer does not affect files
+or buffers, only display.
\\{org-export-stack-mode-map}"
- (abbrev-mode 0)
- (auto-fill-mode 0)
- (setq buffer-read-only t
- buffer-undo-list t
- truncate-lines t
- header-line-format
- '(:eval
- (format " %-12s | %6s | %s" "Back-End" "Age" "Source")))
- (org-add-hook 'post-command-hook 'org-export-stack-refresh nil t)
- (set (make-local-variable 'revert-buffer-function)
- 'org-export-stack-refresh))
+ (setq tabulated-list-format
+ (vector (list "#" 4 #'org-export--stack-num-predicate)
+ (list "Back-End" 12 t)
+ (list "Age" 6 nil)
+ (list "Source" 0 nil)))
+ (setq tabulated-list-sort-key (cons "#" nil))
+ (setq tabulated-list-entries #'org-export--stack-generate)
+ (add-hook 'tabulated-list-revert-hook #'org-export--stack-generate nil t)
+ (add-hook 'post-command-hook #'org-export-stack-refresh nil t)
+ (tabulated-list-init-header))
+
+(defun org-export--stack-generate ()
+ "Generate the asynchronous export stack for display.
+Unavailable sources are removed from the list. Return a list
+appropriate for `tabulated-list-print'."
+ ;; Clear stack from exited processes, dead buffers or non-existent
+ ;; files.
+ (setq org-export-stack-contents
+ (cl-remove-if-not
+ (lambda (el)
+ (if (processp (nth 2 el))
+ (buffer-live-p (process-buffer (nth 2 el)))
+ (let ((source (car el)))
+ (if (bufferp source) (buffer-live-p source)
+ (file-exists-p source)))))
+ org-export-stack-contents))
+ ;; Update `tabulated-list-entries'.
+ (let ((counter 0))
+ (mapcar
+ (lambda (entry)
+ (let ((source (car entry)))
+ (list source
+ (vector
+ ;; Counter.
+ (number-to-string (cl-incf counter))
+ ;; Back-End.
+ (if (nth 1 entry) (symbol-name (nth 1 entry)) "")
+ ;; Age.
+ (let ((info (nth 2 entry)))
+ (if (processp info) (symbol-name (process-status info))
+ (format-seconds "%h:%.2m" (float-time (time-since info)))))
+ ;; Source.
+ (if (stringp source) source (buffer-name source))))))
+ org-export-stack-contents)))
+
+(defun org-export--stack-num-predicate (a b)
+ (< (string-to-number (aref (nth 1 a) 0))
+ (string-to-number (aref (nth 1 b) 0))))
+
+(defun org-export--stack-source-at-point ()
+ "Return source from export results at point in stack."
+ (let ((source (car (nth (1- (org-current-line)) org-export-stack-contents))))
+ (if (not source) (error "Source unavailable, please refresh buffer")
+ (let ((source-name (if (stringp source) source (buffer-name source))))
+ (if (save-excursion
+ (beginning-of-line)
+ (looking-at-p (concat ".* +" (regexp-quote source-name) "$")))
+ source
+ ;; SOURCE is not consistent with current line. The stack
+ ;; view is outdated.
+ (error (substitute-command-keys
+ "Source unavailable; type `\\[org-export-stack-refresh]' \
+to refresh buffer")))))))
@@ -6140,10 +6284,12 @@ SPC and DEL (resp. C-n and C-p) keys.
Set variable `org-export-dispatch-use-expert-ui' to switch to one
flavor or the other.
-When ARG is \\[universal-argument], repeat the last export action, with the same set
-of options used back then, on the current buffer.
+When ARG is `\\[universal-argument]', repeat the last export action, with the\
+ same
+set of options used back then, on the current buffer.
-When ARG is \\[universal-argument] \\[universal-argument], display the asynchronous export stack."
+When ARG is `\\[universal-argument] \\[universal-argument]', display the \
+asynchronous export stack."
(interactive "P")
(let* ((input
(cond ((equal arg '(16)) '(stack))
@@ -6168,7 +6314,7 @@ When ARG is \\[universal-argument] \\[universal-argument], display the asynchron
(optns (cdr input)))
(unless (memq 'subtree optns)
(move-marker org-export-dispatch-last-position nil))
- (case action
+ (cl-case action
;; First handle special hard-coded actions.
(template (org-export-insert-default-template nil optns))
(stack (org-export-stack))
@@ -6177,7 +6323,7 @@ When ARG is \\[universal-argument] \\[universal-argument], display the asynchron
(publish-current-project
(org-publish-current-project (memq 'force optns) (memq 'async optns)))
(publish-choose-project
- (org-publish (assoc (org-icompleting-read
+ (org-publish (assoc (completing-read
"Publish project: "
org-publish-project-alist nil t)
org-publish-project-alist)
@@ -6228,12 +6374,12 @@ back to standard interface."
;; on the first key, if any. A nil value means KEY will
;; only be activated at first level.
(if (or (eq access-key t) (eq access-key first-key))
- (org-propertize key 'face 'org-warning)
+ (propertize key 'face 'org-warning)
key)))
(fontify-value
(lambda (value)
;; Fontify VALUE string.
- (org-propertize value 'face 'font-lock-variable-name-face)))
+ (propertize value 'face 'font-lock-variable-name-face)))
;; Prepare menu entries by extracting them from registered
;; back-ends and sorting them by access key and by ordinal,
;; if any.
@@ -6307,7 +6453,7 @@ back to standard interface."
(concat
(mapconcat
(lambda (sub-entry)
- (incf index)
+ (cl-incf index)
(format
(if (zerop (mod index 2)) " [%s] %-26s"
"[%s] %s\n")
@@ -6378,7 +6524,7 @@ back to standard interface."
standard-prompt allowed-keys entries options first-key expertp))))
(defun org-export--dispatch-action
- (prompt allowed-keys entries options first-key expertp)
+ (prompt allowed-keys entries options first-key expertp)
"Read a character from command input and act accordingly.
PROMPT is the displayed prompt, as a string. ALLOWED-KEYS is
@@ -6396,7 +6542,7 @@ options as CDR."
(while (and (setq key (read-char-exclusive prompt))
(not expertp)
(memq key '(14 16 ?\s ?\d)))
- (case key
+ (cl-case key
(14 (if (not (pos-visible-in-window-p (point-max)))
(ignore-errors (scroll-up 1))
(message "End of buffer")
@@ -6433,8 +6579,8 @@ options as CDR."
;; Toggle options: C-b (2) C-v (22) C-s (19) C-f (6) C-a (1).
((memq key '(2 22 19 6 1))
(org-export--dispatch-ui
- (let ((option (case key (2 'body) (22 'visible) (19 'subtree)
- (6 'force) (1 'async))))
+ (let ((option (cl-case key (2 'body) (22 'visible) (19 'subtree)
+ (6 'force) (1 'async))))
(if (memq option options) (remq option options)
(cons option options)))
first-key expertp))
@@ -6446,7 +6592,7 @@ options as CDR."
;; Publishing actions are hard-coded. Send a special
;; signal to `org-export-dispatch'.
((eq first-key ?P)
- (case key
+ (cl-case key
(?f 'publish-current-file)
(?p 'publish-current-project)
(?x 'publish-choose-project)
@@ -6455,10 +6601,9 @@ options as CDR."
;; path. Indeed, derived backends can share the same
;; FIRST-KEY.
(t (catch 'found
- (mapc (lambda (entry)
- (let ((match (assq key (nth 2 entry))))
- (when match (throw 'found (nth 2 match)))))
- (member (assq first-key entries) entries)))))
+ (dolist (entry (member (assq first-key entries) entries))
+ (let ((match (assq key (nth 2 entry))))
+ (when match (throw 'found (nth 2 match))))))))
options))
;; Otherwise, enter sub-menu.
(t (org-export--dispatch-ui options key expertp)))))