Compare commits

...

10 Commits

389
ox-rst.el
View File

@@ -93,12 +93,18 @@
(?r "As reStructuredText file" org-rst-export-to-rst))) (?r "As reStructuredText file" org-rst-export-to-rst)))
:options-alist :options-alist
'((:subtitle "SUBTITLE" nil nil parse) '((:subtitle "SUBTITLE" nil nil parse)
(:rst-head "RST_HEAD" nil org-rst-head newline)
(:rst-link-use-abs-url nil "rst-link-use-abs-url" org-rst-link-use-abs-url) (:rst-link-use-abs-url nil "rst-link-use-abs-url" org-rst-link-use-abs-url)
(:rst-metadata nil "rst-metadata" org-rst-metadata)
(:rst-metadata-format nil nil org-rst-metadata-format)
(:rst-metadata-timestamp-format nil nil org-rst-metadata-timestamp-format)
(:rst-equation-reference-format "RST_EQUATION_REFERENCE_FORMAT" nil org-rst-equation-reference-format t)
(:rst-inline-images nil nil org-rst-inline-images) (:rst-inline-images nil nil org-rst-inline-images)
(:rst-inline-image-rules nil nil org-rst-inline-image-rules) (:rst-inline-image-rules nil nil org-rst-inline-image-rules)
(:rst-link-org-files-as-rst nil nil org-rst-link-org-files-as-rst) (:rst-link-org-files-as-rst nil nil org-rst-link-org-files-as-rst)
(:rst-link-home "RST_LINK_HOME" nil org-rst-link-home) (:rst-link-home "RST_LINK_HOME" nil org-rst-link-home)
(:rst-link-use-ref-role nil nil org-rst-link-use-ref-role) (:rst-link-use-ref-role nil nil org-rst-link-use-ref-role)
(:rst-prefer-user-labels nil nil org-rst-prefer-user-labels)
(:rst-extension nil nil org-rst-extension) (:rst-extension nil nil org-rst-extension)
(:rst-file-link-use-ref-role nil nil org-rst-file-link-use-ref-role) (:rst-file-link-use-ref-role nil nil org-rst-file-link-use-ref-role)
(:rst-text-markup-alist nil nil org-rst-text-markup-alist) (:rst-text-markup-alist nil nil org-rst-text-markup-alist)
@@ -119,6 +125,10 @@
;;; Internal Variables ;;; Internal Variables
(defvar org-rst--id-attr-prefix "ID-"
"Prefix to use in ID attributes.
This affects IDs that are determined from the ID property.")
;;; User Configurable Variables ;;; User Configurable Variables
@@ -135,6 +145,25 @@
:type 'string) :type 'string)
(defcustom org-rst-head ""
"Org-wide head definitions for exported RST files.
This variable can contain the file-wide metadata.
As the value of this option simply gets inserted at the top of the RST
file, you can use it to add any arbitrary text to the header.
You can set this on a per-file basis using #+RST_HEAD:,
or for publication projects using the :rst-head property.
Example:
#+RST_HEAD: :orphan:"
:group 'org-export-rst
:type 'string)
;;;###autoload
(put 'org-rst-head 'safe-local-variable 'stringp)
(defcustom org-rst-link-org-files-as-rst t (defcustom org-rst-link-org-files-as-rst t
"Non-nil means make file links to `file.org' point to `file.rst'. "Non-nil means make file links to `file.org' point to `file.rst'.
When `org-mode' is exporting an `org-mode' file to reStructuredText, When `org-mode' is exporting an `org-mode' file to reStructuredText,
@@ -160,6 +189,91 @@ When nil, the links still point to the plain \".org\" file."
:group 'org-export-rst :group 'org-export-rst
:type 'boolean) :type 'boolean)
(defcustom org-rst-metadata 'auto
"Non-nil means insert metadata in RST export.
When set to `auto', check against the
`org-export-with-author/email/creator/date' variables to set the
content of the metadata. When t, insert a string as defined by the
formatting string in `org-rst-metadata-format'. When set to a
string, use this formatting string instead (see
`org-rst-metadata-format' for an example of such a formatting
string).
When set to a function, apply this function and insert the
returned string. The function takes the property list of export
options as its only argument.
Setting :rst-metadata in publishing projects will take
precedence over this variable.
Usage:
#+options: rst-metadata:nil
#+options: rst-metadata:auto
#+options: rst-metadata:t
#+options: rst-metadata:\"\\t:Author: %a\\n\\t:Date: %d\"
#+options: rst-metadata:\"::\\n\\n\\t:Author: %a\\n\\t:Date: %d\"
#+options: rst-metadata:\".. meta::\\n\\t:author: %a\\n\\t:date: %d\"
#+options: rst-metadata:\".. article-info::\\n\\t:Author: %a\\n\\t:Date: %d\"
#+options: rst-metadata:my-org-rst-metadata-format-function
(defun my-org-rst-metadata-format-function (info)
\"Custom format function for `org-rst-matadata'.\"
(let ((author (org-export-data (plist-get info :author) info)))
(concat (format \" :Author: %s\\n\" author))))
"
:group 'org-export-rst
:type '(choice (const :tag "No postamble" nil)
(const :tag "Auto postamble" auto)
(const :tag "Default formatting string" t)
(string :tag "Custom formatting string")
(function :tag "Function (must return a string)")))
(defcustom org-rst-metadata-format "
.. meta::
:Author: %a
:Contact: %e
:Date: %d
"
"A format string to format the metadata itself. This format string can
contain these elements:
%t stands for the title.
%s stands for the subtitle.
%a stands for the author's name.
%e stands for the author's email.
%d stands for the date.
%c will be replaced by `org-rst-creator-string'.
%T will be replaced by the export time.
%C will be replaced by the last modification time.
If you need to use a \"%\" character, you need to escape it
like that: \"%%\".
Examples:
Put information in a block-quote:
:Author: %a
:Contact: %e
:Date: %d
Or, to put information to a meta directive:
.. meta::
:author: %a
:date: %d
Another example with a sphinx-design directive:
:: article-info
:Author: %a
:Date: %d
"
:group 'org-export-rst
:type '(string :tag "Format string"))
;;;; Links :: Inline images ;;;; Links :: Inline images
(defcustom org-rst-inline-images t (defcustom org-rst-inline-images t
@@ -271,6 +385,25 @@ in order to mimic default behaviour:
:group 'org-export-rst :group 'org-export-rst
:type 'function) :type 'function)
;;;; RST-specific
(defcustom org-rst-prefer-user-labels nil
"When non-nil use user-defined names and ID over internal ones.
By default, Org generates its own internal ID values during RST
export. This process ensures that these values are unique and
valid, but the keys are not available in advance of the export
process, and not so readable.
When this variable is non-nil, Org will use NAME keyword, or the
real name of the target to create the ID attribute.
Independently of this variable, however, CUSTOM_ID are always
used as a reference."
:group 'org-export-rst
:type 'boolean
:safe #'booleanp)
;;;; Inlinetasks ;;;; Inlinetasks
@@ -290,6 +423,26 @@ The function should return the string to be exported."
:type 'function) :type 'function)
;;;; LaTeX
(defcustom org-rst-equation-reference-format "\\eqref{%s}"
"The MathJax command to use when referencing equations.
This is a format control string that expects a single string argument
specifying the label that is being referenced. The argument is
generated automatically on export.
The default is to wrap equations in parentheses (using \"\\eqref{%s}\)\".
Most common values are:
\\eqref{%s} Wrap the equation in parentheses
\\ref{%s} Do not wrap the equation in parentheses"
:group 'org-export-rst
:type 'string
:safe #'stringp)
;;;; Src blocks ;;;; Src blocks
(defcustom org-rst-code-block 'code (defcustom org-rst-code-block 'code
@@ -322,6 +475,12 @@ in this list - but it does not hurt if it is present."
(symbol :tag "Major mode ") (symbol :tag "Major mode ")
(string :tag "Pygments language")))) (string :tag "Pygments language"))))
(defcustom org-rst-metadata-timestamp-format "%Y-%m-%d %a %H:%M"
"Format used for timestamps in metadata.
See `format-time-string' for more information on its components."
:group 'org-export-rst
:type 'string)
;;; Internal Functions ;;; Internal Functions
@@ -462,11 +621,11 @@ See `org-rst-text-markup-alist' for details."
(defun org-rst--checkbox (item _info) (defun org-rst--checkbox (item _info)
"Return checkbox string for ITEM or nil. "Return checkbox string for ITEM or nil.
INFO is a plist used as a communication channel." INFO is a plist used as a communication channel."
;(let ((utf8p (eq (plist-get info :ascii-charset) 'utf-8))) ;; (let ((utf8p (eq (plist-get info :ascii-charset) 'utf-8)))
; (case (org-element-property :checkbox item) ;; (case (org-element-property :checkbox item)
; (on (if utf8p "☑ " "[X] ")) ;; (on (if utf8p "☑ " "[X] "))
; (off (if utf8p "☐ " "[ ] ")) ;; (off (if utf8p "☐ " "[ ] "))
; (trans (if utf8p "☒ " "[-] "))))) ;; (trans (if utf8p "☒ " "[-] ")))))
(cl-case (org-element-property :checkbox item) (cl-case (org-element-property :checkbox item)
(on "") (on "")
(off "") (off "")
@@ -476,6 +635,27 @@ INFO is a plist used as a communication channel."
;;; Template ;;; Template
(defun org-rst-format-spec (info)
"Return format specification for metadata.
INFO is a plist used as a communication channel."
;; org-export-date-timestamp-format
(let ((timestamp-format (plist-get info :rst-metadata-timestamp-format)))
`((?t . ,(org-export-data (plist-get info :title) info))
(?s . ,(org-export-data (plist-get info :subtitle) info))
(?d . ,(org-export-data (org-export-get-date info timestamp-format)
info))
(?T . ,(format-time-string timestamp-format))
(?a . ,(org-export-data (plist-get info :author) info))
(?e . ,(mapconcat
(lambda (e) (format "%s" e e))
(split-string (plist-get info :email) ",+ *")
", "))
(?c . ,(plist-get info :creator))
(?C . ,(let ((file (plist-get info :input-file)))
(format-time-string timestamp-format
(and file (file-attribute-modification-time
(file-attributes file)))))))))
(defun org-rst-template--document-title (info) (defun org-rst-template--document-title (info)
"Return document title, as a string. "Return document title, as a string.
INFO is a plist used as a communication channel." INFO is a plist used as a communication channel."
@@ -488,6 +668,9 @@ INFO is a plist used as a communication channel."
(subtitle (if with-title (subtitle (if with-title
(org-export-data (plist-get info :subtitle) info) (org-export-data (plist-get info :subtitle) info)
"")) ""))
(head (org-element-normalize-string (plist-get info :rst-head)))
(spec (org-rst-format-spec info))
(metadata (plist-get info :rst-metadata))
(author (and (plist-get info :with-author) (author (and (plist-get info :with-author)
(let ((auth (plist-get info :author))) (let ((auth (plist-get info :author)))
(and auth (org-export-data auth info))))) (and auth (org-export-data auth info)))))
@@ -505,20 +688,33 @@ INFO is a plist used as a communication channel."
(concat subtitleline "\n" (concat subtitleline "\n"
subtitle "\n" subtitle "\n"
subtitleline "\n") ""))) subtitleline "\n") "")))
(cond
((string= title "")
(concat (concat
(when (org-string-nw-p author) (concat " :Author: " author "\n")) (when head (concat head "\n"))
(when (org-string-nw-p email) (concat " :Contact: " email "\n")) (unless (string= title "")
(when (org-string-nw-p date) (concat " :Date: " date "\n"))))
(t
(concat (concat
title title
subtitle subtitle
(when (org-string-nw-p author) (concat "\n :Author: " author)) "\n"))
(when (org-string-nw-p email) (concat "\n :Contact: " email)) (cond
(when (org-string-nw-p date) (concat "\n :Date: " date)) ((eq metadata 'auto)
"\n"))))) (org-element-normalize-string
(org-rst--make-attribute-string
(list :Author author :Contact email :Date date))))
((eq metadata t)
(concat
(format-spec
(org-element-normalize-string org-rst-metadata-format)
spec)))
((functionp metadata)
(concat
(format-spec
(org-element-normalize-string (funcall metadata info))
spec)))
((stringp metadata)
(concat
(format-spec
(org-element-normalize-string metadata)
spec)))))))
(defun org-rst-template (contents info) (defun org-rst-template (contents info)
@@ -645,7 +841,8 @@ information."
(defun org-rst-export-block (export-block _contents _info) (defun org-rst-export-block (export-block _contents _info)
"Transcode a EXPORT-BLOCK element from Org to reStructuredText. "Transcode a EXPORT-BLOCK element from Org to reStructuredText.
CONTENTS is nil. INFO is a plist holding contextual information." CONTENTS is nil. INFO is a plist holding contextual information."
(when (member (org-element-property :type export-block) '("RST" "REST" "RESTRUCTUREDTEXT")) (when (member (org-element-property :type export-block)
'("RST" "REST" "RESTRUCTUREDTEXT"))
(org-element-property :value export-block))) (org-element-property :value export-block)))
@@ -660,13 +857,13 @@ CONTENTS is nil. INFO is a plist holding contextual information."
;;;; Footnote Definition ;;;; Footnote Definition
;(defun org-rst-footnote-definition (footnote-definition contents info) ;; (defun org-rst-footnote-definition (footnote-definition contents info)
; "Transcode a FOOTNOTE-DEFINITION element from Org to reStructuredText. ;; "Transcode a FOOTNOTE-DEFINITION element from Org to reStructuredText.
;CONTENTS is nil. INFO is a plist holding contextual information." ;; CONTENTS is nil. INFO is a plist holding contextual information."
; (replace-regexp-in-string ;; (replace-regexp-in-string
; "^" ".. " ;; "^" ".. "
; (org-remove-indentation ;; (org-remove-indentation
; (org-element-property :value footnote-definition)))) ;; (org-element-property :value footnote-definition))))
;;;; Footnote Reference ;;;; Footnote Reference
@@ -847,12 +1044,88 @@ CONTENTS is nil. INFO is a plist holding contextual information."
;;;; Latex Environment ;;;; Latex Environment
(defun org-rst--reference (datum info &optional named-only)
"Return an appropriate reference for DATUM.
DATUM is an element or a `target' type object. INFO is the
current export state, as a plist.
When NAMED-ONLY is non-nil and DATUM has no NAME keyword, return
nil. This doesn't apply to headlines, inline tasks, radio
targets and targets."
(let* ((type (org-element-type datum))
(custom-id (and (memq type '(headline inlinetask))
(org-element-property :CUSTOM_ID datum)))
(user-label
(or
custom-id
(and (memq type '(radio-target target))
(org-element-property :value datum))
(org-element-property :name datum)
(when-let* ((id (org-element-property :ID datum)))
(concat org-rst--id-attr-prefix id)))))
(cond
((and user-label
(or (plist-get info :rst-prefer-user-labels)
;; Used CUSTOM_ID property unconditionally.
custom-id))
user-label)
((and named-only
(not (memq type '(headline inlinetask radio-target target)))
(not user-label))
nil)
(t
(org-export-get-reference datum info)))))
(defun org-rst--wrap-latex-environment (contents _ &optional caption label)
"Wrap CONTENTS string within appropriate environment for equations.
When optional arguments CAPTION and LABEL are given, use them for
caption and \"id\" attribute."
(format "\n.. math::\n%s%s%s\n"
;; label.
(if (org-string-nw-p label) (format " :label: %s\n" label) "")
;; Caption.
(if (not (org-string-nw-p caption)) ""
(format " :label: %s\n :nowrap:\n" caption)) ;; todo: flag for nowrap?
;; Contents.
(format "\n%s\n" (org-rst--indent-string
(org-trim contents) org-rst-quote-margin))))
(defun org-rst--math-environment-p (element &optional _)
"Non-nil when ELEMENT is a LaTeX math environment.
Math environments match the regular expression defined in
`org-latex-math-environments-re'. This function is meant to be
used as a predicate for `org-export-get-ordinal' or a value to
`org-rst-standalone-image-predicate'."
(string-match-p org-latex-math-environments-re
(org-element-property :value element)))
(defun org-rst--latex-environment-numbered-p (element)
"Non-nil when ELEMENT is a numbered LaTeX math environment.
Starred and \"displaymath\" environments are not numbered."
(not (string-match-p "\\`[ \t]*\\\\begin{\\(.*\\*\\|displaymath\\)}"
(org-element-property :value element))))
(defun org-rst-latex-environment (latex-environment _contents info) (defun org-rst-latex-environment (latex-environment _contents info)
"Transcode a LATEX-ENVIRONMENT element from Org to reStructuredText. "Transcode a LATEX-ENVIRONMENT element from Org to reStructuredText.
CONTENTS is nil. INFO is a plist holding contextual CONTENTS is nil. INFO is a plist holding contextual
information." information."
(when (plist-get info :with-latex) (let ((processing-type t) ;; (plist-get info :with-latex)
(org-remove-indentation (org-element-property :value latex-environment)))) (latex-frag (org-remove-indentation
(org-element-property :value latex-environment)))
(label (org-rst--reference latex-environment info t))
(caption (and (org-rst--latex-environment-numbered-p latex-environment)
(org-rst--math-environment-p latex-environment)
(number-to-string
(org-export-get-ordinal
latex-environment info nil
(lambda (l _)
(and (org-rst--math-environment-p l)
(org-rst--latex-environment-numbered-p l))))))))
(when processing-type
;; latex-frag
(org-rst--wrap-latex-environment latex-frag info caption label))))
;;;; Latex Fragment ;;;; Latex Fragment
@@ -937,8 +1210,8 @@ INFO is a plist holding contextual information."
(link-org-files-as-rst-maybe (link-org-files-as-rst-maybe
(function (function
(lambda (raw-path info) (lambda (raw-path info)
"Treat links to `file.org' as links to `file.rst', if needed. ;; Treat links to `file.org' as links to `file.rst', if
See `org-rst-link-org-files-as-rst'." ;; needed. See `org-rst-link-org-files-as-rst'.
(cond (cond
((and (plist-get info :rst-link-org-files-as-rst) ((and (plist-get info :rst-link-org-files-as-rst)
(string= ".org" (string= ".org"
@@ -1037,12 +1310,21 @@ INFO is a plist holding contextual information."
(if (plist-get info :rst-link-use-ref-role) (if (plist-get info :rst-link-use-ref-role)
(if desc (format " :ref:`%s <%s>`" desc raw-path) (if desc (format " :ref:`%s <%s>`" desc raw-path)
(format " :ref:`%s`" raw-path)) (format " :ref:`%s`" raw-path))
(format "`%s`_" raw-path)) (if desc (format "`<%s>`_" raw-path) ;; desc cannot be used w/o a :role:
(format "`%s`_" raw-path)))
(format "`%s`_" (org-rst--build-title destination info nil)))) (format "`%s`_" (org-rst--build-title destination info nil))))
;; Fuzzy link points to a target. ;; Fuzzy link points to a target.
(otherwise (otherwise
(if (and destination
(org-element-type-p destination 'latex-environment)
(eq 'math (org-latex--environment-type destination)))
;; Caption and labels are introduced within LaTeX
;; environment. Use "ref" or "eqref" macro, depending on user
;; preference to refer to those in the document.
(format (plist-get info :rst-equation-reference-format)
(org-rst--reference destination info))
(if (not desc) (format "`%s`_" raw-path) (if (not desc) (format "`%s`_" raw-path)
(format "`%s <%s>`_" desc raw-path)))))) (format "`%s <%s>`_" desc raw-path)))))))
;; Coderef: replace link with the reference name or the ;; Coderef: replace link with the reference name or the
;; equivalent line number. It is not supported in ReST. ;; equivalent line number. It is not supported in ReST.
((string= type "coderef") ((string= type "coderef")
@@ -1056,8 +1338,8 @@ INFO is a plist holding contextual information."
(format ":ref:`%s <%s>`" desc ref) (format ":ref:`%s <%s>`" desc ref)
(format ":ref:`%s`" ref)))) (format ":ref:`%s`" ref))))
;; Link type is handled by a special function. ;; Link type is handled by a special function.
;((functionp (setq protocol (nth 2 (assoc type org-link-protocols)))) ;; ((functionp (setq protocol (nth 2 (assoc type org-link-protocols))))
; (funcall protocol (org-link-unescape path) desc 'latex)) ;; (funcall protocol (org-link-unescape path) desc 'latex))
;; External link with a description part. ;; External link with a description part.
((and path desc) (format "`%s <%s>`_" desc path)) ((and path desc) (format "`%s <%s>`_" desc path))
;; External link without a description part. ;; External link without a description part.
@@ -1301,13 +1583,23 @@ holding contextual information."
(title (plist-get attributes :title)) (title (plist-get attributes :title))
(class (plist-get attributes :class)) (class (plist-get attributes :class))
(label (org-element-property :name special-block)) (label (org-element-property :name special-block))
(type (org-element-property :type special-block))) (type (org-element-property :type special-block))
(attr-str-ignore '(:title))
(attr-str ""))
;; combine with `org-rst--make-attribute-string'?
(cl-loop for (key value) on attributes by 'cddr
do (unless (member key attr-str-ignore)
(setq attr-str
(concat attr-str
(format " %s:" key)
(when value (format " %s" value))
"\n"))))
(concat (concat
(format ".. %s::" type) (format ".. %s::" type)
(when title (format " %s" title)) (when title (format " %s" title))
"\n" "\n"
(when class (format " :class: %s\n" class))
(when label (format " :name: %s\n" label)) (when label (format " :name: %s\n" label))
(when attr-str attr-str)
"\n" "\n"
(when contents (when contents
(org-rst--indent-string contents org-rst-quote-margin))))) (org-rst--indent-string contents org-rst-quote-margin)))))
@@ -1512,8 +1804,13 @@ a communication channel."
(cond (cond
((not (= 1 rowgroup-number)) ((not (= 1 rowgroup-number))
?-) ?-)
((org-export-table-has-header-p ;; ((org-export-table-has-header-p
;; (org-element-lineage table-row 'table) info)
;; ?=)
;; double line only at the end of the header
((and (org-export-table-has-header-p
(org-element-lineage table-row 'table) info) (org-element-lineage table-row 'table) info)
(org-export-table-row-ends-header-p table-row info))
?=) ?=)
(t ?-))) (t ?-)))
(makeline (makeline
@@ -1541,7 +1838,29 @@ a communication channel."
(when (= 0 row-number) (when (= 0 row-number)
(concat (funcall makeline contents ?-) "\n")) (concat (funcall makeline contents ?-) "\n"))
"|" contents "|\n" hline)) "|" contents "|\n" hline))
nil ;; nil
;; the else section is hit whenever there is a new group, i.e. the row
;; after a hline, OR if there is only one row in the table!!!
;; (org-export-table-row-number table-row info) returns non-nil if there
;; is only ONE row in the table (nil otherwise)
;; same for `contents'; it is nil if not first row
(when contents
(let* ((makeline
(function
(lambda (_rowcontents linebit)
(format "+%s+"
(mapconcat
'identity
(mapcar
(lambda (table-cell)
(make-string (string-width table-cell)
linebit))
(split-string contents "|"))
"+"))))))
(concat
(concat (funcall makeline contents ?-) "\n")
(concat "|" contents "|\n")
(concat (funcall makeline contents ?-) "\n"))))
))) )))