From 69860cad2c22d7d931fa36212f004cf9f0e186fc Mon Sep 17 00:00:00 2001 From: Daniel Weschke Date: Thu, 25 Dec 2025 14:07:38 +0100 Subject: [PATCH] update packages --- lisp/ox-rst/ox-rst-autoloads.el | 90 ++ lisp/ox-rst/ox-rst-pkg.el | 11 + lisp/ox-rst/ox-rst.el | 1733 +++++++++++++++++++++++++++++++ lisp/update-autoloads.el | 1 + 4 files changed, 1835 insertions(+) create mode 100644 lisp/ox-rst/ox-rst-autoloads.el create mode 100644 lisp/ox-rst/ox-rst-pkg.el create mode 100644 lisp/ox-rst/ox-rst.el diff --git a/lisp/ox-rst/ox-rst-autoloads.el b/lisp/ox-rst/ox-rst-autoloads.el new file mode 100644 index 00000000..535496bc --- /dev/null +++ b/lisp/ox-rst/ox-rst-autoloads.el @@ -0,0 +1,90 @@ +;;; ox-rst-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- +;; Generated by the `loaddefs-generate' function. + +;; This file is part of GNU Emacs. + +;;; Code: + +(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) + + + +;;; Generated autoloads from ox-rst.el + +(autoload 'org-rst-export-as-rst "ox-rst" "\ +Export current buffer to a reStructuredText buffer. + +If narrowing is active in the current buffer, only export its +narrowed part. + +If a region is active, export that region. + +A non-nil optional argument ASYNC means the process should happen +asynchronously. The resulting buffer should be accessible +through the `org-export-stack' interface. + +When optional argument SUBTREEP is non-nil, export the sub-tree +at point, extracting information from the headline properties +first. + +When optional argument VISIBLE-ONLY is non-nil, don't export +contents of hidden elements. + +Export is done in a buffer named \"*Org RST Export*\", which will +be displayed when `org-export-show-temporary-export-buffer' is +non-nil. + +(fn &optional ASYNC SUBTREEP VISIBLE-ONLY BODY-ONLY EXT-PLIST)" t) +(autoload 'org-rst-convert-region-to-rst "ox-rst" "\ +Assume the current region has Org syntax, and convert it to +reStructuredText. +This can be used in any buffer. For example, you can write an +itemized list in Org syntax in a Markdown buffer and use this command +to convert it." t) +(autoload 'org-rst-export-to-rst "ox-rst" "\ +Export current buffer to a reStructuredText file. + +If narrowing is active in the current buffer, only export its +narrowed part. + +If a region is active, export that region. + +A non-nil optional argument ASYNC means the process should happen +asynchronously. The resulting file should be accessible through +the `org-export-stack' interface. + +When optional argument SUBTREEP is non-nil, export the sub-tree +at point, extracting information from the headline properties +first. + +When optional argument VISIBLE-ONLY is non-nil, don't export +contents of hidden elements. + +Return output file's name. + +(fn &optional ASYNC SUBTREEP VISIBLE-ONLY BODY-ONLY EXT-PLIST)" t) +(autoload 'org-rst-publish-to-rst "ox-rst" "\ +Publish an org file to reStructuredText. + +FILENAME is the filename of the Org file to be published. PLIST +is the property list for the given project. PUB-DIR is the +publishing directory. + +Return output file name. + +(fn PLIST FILENAME PUB-DIR)") +(register-definition-prefixes "ox-rst" '("my-org-export-inline-image-p" "org-")) + +;;; End of scraped data + +(provide 'ox-rst-autoloads) + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; no-native-compile: t +;; coding: utf-8-emacs-unix +;; End: + +;;; ox-rst-autoloads.el ends here diff --git a/lisp/ox-rst/ox-rst-pkg.el b/lisp/ox-rst/ox-rst-pkg.el new file mode 100644 index 00000000..de537eaf --- /dev/null +++ b/lisp/ox-rst/ox-rst-pkg.el @@ -0,0 +1,11 @@ +;; -*- no-byte-compile: t; lexical-binding: nil -*- +(define-package "ox-rst" "20250428.534" + "Export reStructuredText using org-mode." + '((emacs "25.1") + (org "8.3")) + :url "https://github.com/msnoigrs/ox-rst" + :commit "b73eff187eebac24b457688bfd27f09eff434860" + :revdesc "b73eff187eeb" + :keywords '("org" "rst" "rest" "restructuredtext") + :authors '(("Masanao Igarashi" . "syoux2@gmail.com")) + :maintainers '(("Masanao Igarashi" . "syoux2@gmail.com"))) diff --git a/lisp/ox-rst/ox-rst.el b/lisp/ox-rst/ox-rst.el new file mode 100644 index 00000000..1d66f88d --- /dev/null +++ b/lisp/ox-rst/ox-rst.el @@ -0,0 +1,1733 @@ +;;; ox-rst.el --- Export reStructuredText using org-mode. -*- lexical-binding: t; -*- + +;; Copyright (C) 2015-2025 Masanao Igarashi + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;; Author: Masanao Igarashi +;; Keywords: org, rst, reST, reStructuredText +;; Package-Version: 20250428.534 +;; Package-Revision: b73eff187eeb +;; URL: https://github.com/msnoigrs/ox-rst +;; Package-Requires: ((emacs "25.1") (org "8.3")) + +;;; Commentary: +;; This library implements an reStructuredText back-end for +;; Org generic exporter. + +;;; Code: + +(require 'org-macs) +(org-assert-version) + +(require 'cl-lib) +(require 'ox) +(require 'ox-publish) + + +;;; Define Back-End +(org-export-define-backend 'rst + '((bold . org-rst-bold) + (center-block . org-rst-center-block) + (clock . org-rst-clock) + (code . org-rst-code) + (drawer . org-rst-drawer) + (dynamic-block . org-rst-dynamic-block) + (entity . org-rst-entity) + (example-block . org-rst-example-block) + (export-block . org-rst-export-block) + (export-snippet . org-rst-export-snippet) + (fixed-width . org-rst-example-block) + (footnote-reference . org-rst-footnote-reference) + (headline . org-rst-headline) + (horizontal-rule . org-rst-horizontal-rule) + (inline-src-block . org-rst-inline-src-block) + (inlinetask . org-rst-inlinetask) + (inner-template . org-rst-inner-template) + (italic . org-rst-italic) + (item . org-rst-item) + (keyword . org-rst-keyword) + (latex-environment . org-rst-latex-environment) + (latex-fragment . org-rst-latex-fragment) + (line-break . org-rst-line-break) + (link . org-rst-link) + (node-property . org-rst-node-property) + (paragraph . org-rst-paragraph) + (plain-list . org-rst-plain-list) + (plain-text . org-rst-plain-text) + (planning . org-rst-planning) + (property-drawer . org-rst-property-drawer) + (quote-block . org-rst-quote-block) + (radio-target . org-rst-radio-target) + (section . org-rst-section) + (special-block . org-rst-special-block) + (src-block . org-rst-src-block) + (statistics-cookie . org-rst-statistics-cookie) + (strike-through . org-rst-strike-through) + (subscript . org-rst-subscript) + (superscript . org-rst-superscript) + (table . org-rst-table) + (table-cell . org-rst-table-cell) + (table-row . org-rst-table-row) + (target . org-rst-target) + (template . org-rst-template) + (timestamp . org-rst-timestamp) + (underline . org-rst-underline) + (verbatim . org-rst-verbatim) + (verse-block . org-rst-verse-block) + ;; Pseudo objects and elements. + (latex-math-block . org-rst-math-block)) + :menu-entry + '(?r "Export to reStructuredText" + ((?R "As reStructuredText buffer" org-rst-export-as-rst) + (?r "As reStructuredText file" org-rst-export-to-rst))) + :options-alist + '((:subtitle "SUBTITLE" nil nil parse) + (:rst-link-use-abs-url nil "rst-link-use-abs-url" org-rst-link-use-abs-url) + (:rst-inline-images nil nil org-rst-inline-images) + (: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-home "RST_LINK_HOME" nil org-rst-link-home) + (:rst-link-use-ref-role nil nil org-rst-link-use-ref-role) + (:rst-extension nil nil org-rst-extension) + (: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-quote-margin nil nil org-rst-quote-margin) + (:rst-headline-underline-characters nil nil org-rst-headline-underline-characters) + (:rst-headline-spacing nil nil org-rst-headline-spacing) + (:rst-paragraph-spacing nil nil org-rst-paragraph-spacing) + (:rst-format-drawer-function nil nil org-rst-format-drawer-function) + (:rst-format-inlinetask-function nil nil org-rst-format-inlinetask-function) + (:rst-code-block nil nil org-rst-code-block) + (:rst-pygments-langs nil nil org-rst-pygments-langs)) + :filters-alist '((:filter-options . org-rst-math-block-options-filter) + (:filter-headline . org-rst-filter-headline-blank-lines) + (:filter-parse-tree . (org-rst-math-block-tree-filter + org-rst-separate-elements + org-rst-filter-paragraph-spacing)))) + + +;;; Internal Variables + + + +;;; User Configurable Variables + +(defgroup org-export-rst nil + "Options for exporting Org mode files to reStructuredText." + :tag "Org RST" + :group 'org-export) + + +(defcustom org-rst-extension "rst" + "The extension for exported reStructuredText files." + :group 'org-export-rst + :type 'string) + + +(defcustom org-rst-link-org-files-as-rst t + "Non-nil means make file links to `file.org' point to `file.rst'. +When `org-mode' is exporting an `org-mode' file to reStructuredText, +links to non-rst files are directly put into a href tag in +reStructuredText. +However, links to other Org mode files (recognized by the extension +`.org.) should become links to the corresponding reStructuredText +file, assuming that the linked `org-mode' file will also be +converted to reStructuredText. +When nil, the links still point to the plain \".org\" file." + :group 'org-export-rst + :type 'boolean) + + +(defcustom org-rst-link-home "" + "Where should the \"HOME\" link of exported rst files lead?" + :group 'org-export-rst + :type '(string :tag "File or URL")) + + +(defcustom org-rst-link-use-abs-url nil + "Should we prepend relative links with RST_LINK_HOME?" + :group 'org-export-rst + :type 'boolean) + +;;;; Links :: Inline images + +(defcustom org-rst-inline-images t + "Non-nil means inline images into exported reStructuredText. +This is done using an image directive or an figure directive. +When nil, an anchor with reference is used to link to the image." + :group 'org-export-rst + :type 'boolean) + +(defcustom org-rst-inline-image-rules + `(("file" . ,(regexp-opt '(".jpeg" ".jpg" ".png" ".gif" ".svg" ".svgz" ".webp" ".avif"))) + ("http" . ,(regexp-opt '(".jpeg" ".jpg" ".png" ".gif" ".svg" ".svgz" ".webp" ".avif"))) + ("https" . ,(regexp-opt '(".jpeg" ".jpg" ".png" ".gif" ".svg" ".svgz" ".webp" ".avif")))) + "Rules characterizing image files that can be inlined into reStructuredText. +A rule consists in an association whose key is the type of link +to consider, and value is a regexp that will be matched against +link's path." + :group 'org-export-rst + :type '(alist :key-type (string :tag "Type") + :value-type (regexp :tag "Path"))) + +(defcustom org-rst-link-use-ref-role nil + "Non-nil means export internal links using :ref: role." + :group 'org-export-rst + :type 'boolean) + +(defcustom org-rst-file-link-use-ref-role nil + "Non-nil means export internal file links using :ref: role." + :group 'org-export-rst + :type 'boolean) + +(defcustom org-rst-text-markup-alist '((bold . "**%s**") + (code . verb) + (italic . "*%s*") + (verbatim . verb)) + "Alist of reStructredText expressions to convert text markup. + +The key must be a symbol among `bold', `code', `italic', +`verbatim'. The value is a formatting string to +wrap fontified text with. + +Value can also be set to the following symbols: `verb'. + +If no association can be found for a given markup, text will be +returned as-is." + :group 'org-export-rst + :type 'alist + :options '(bold code italic verbatim)) + + +(defcustom org-rst-quote-margin 4 + "Width of margin used for quoting text, in characters. +This margin is applied on left side of the text." + :group 'org-export-rst + :type 'integer) + + +(defcustom org-rst-headline-spacing '(1 . 1) + "Number of blank lines inserted around headlines. + +This variable can be set to a cons cell. In that case, its car +represents the number of blank lines present before headline +contents whereas its cdr reflects the number of blank lines after +contents. + +A nil value replicates the number of blank lines found in the +original Org buffer at the same place." + :group 'org-export-rst + :type '(choice + (const :tag "Replicate original spacing" nil) + (cons :tag "Set a uniform spacing" + (integer :tag "Number of blank lines before contents") + (integer :tag "Number of blank lines after contents")))) + + +(defcustom org-rst-paragraph-spacing 'auto + "Number of white lines between paragraphs. +If the value is an integer, add this number of blank lines +between contiguous paragraphs. If is it the symbol `auto', keep +the same number of blank lines as in the original document." + :group 'org-export-rst + :type '(choice + (integer :tag "Number of blank lines") + (const :tag "Preserve original spacing" auto))) + + +(defcustom org-rst-headline-underline-characters '(?- ?~ ?^ ?: ?' ?\ ?_) + "List of underline characters for each headline level." + :group 'org-export-rst + :type '(repeat character)) + +;;;; Drawers + +(defcustom org-rst-format-drawer-function (lambda (_name contents) contents) + "Function called to format a drawer in reStructuredText code. + +The function must accept two parameters: + NAME the drawer name, like \"LOGBOOK\" + CONTENTS the contents of the drawer. + +The function should return the string to be exported. + +For example, the variable could be set to the following function +in order to mimic default behaviour: + +\(defun org-rst-format-drawer-default \(name contents\) + \"Format a drawer element for reStructuredText export.\" + contents\)" + :group 'org-export-rst + :type 'function) + + +;;;; Inlinetasks + +(defcustom org-rst-format-inlinetask-function nil + "Function called to format an inlinetask in reStructuredText code. + +The function must accept six parameters: + TODO the todo keyword, as a string + TODO-TYPE the todo type, a symbol among `todo', `done' and nil. + PRIORITY the inlinetask priority, as a string + NAME the inlinetask name, as a string. + TAGS the inlinetask tags, as a list of strings. + CONTENTS the contents of the inlinetask, as a string. + +The function should return the string to be exported." + :group 'org-export-rst + :type 'function) + + +;;;; Src blocks + +(defcustom org-rst-code-block 'code + "The directive used to export SRC-BLOCKs." + :group 'org-export-rst + :type '(choice + (const :tag "Use a code-block directive" code-block) + (const :tag "Use a code directive" code) + (const :tag "Use a literal block" nil)) + :safe (lambda (s) (memq s '(code-block code nil))) + ) + +(defcustom org-rst-pygments-langs + '((emacs-lisp "common-lisp") (lisp "common-lisp") + (cc "c++") + (cperl "perl") + (latex "tex") + (shell-script "bash") + (caml "ocaml") + (sqlite3 "sqlite")) + "Alist mapping languages to their listing language counterpart. +The key is a symbol, the major mode symbol without the \"-mode\". +The value is the string that should be inserted as the language +parameter for the listings package. If the mode name and the +listings name are the same, the language does not need an entry +in this list - but it does not hurt if it is present." + :group 'org-export-rst + :type '(repeat + (list + (symbol :tag "Major mode ") + (string :tag "Pygments language")))) + + + +;;; Internal Functions + +(defun org-rst--justify-lines (s text-width how) + "Justify all lines in string S. +TEXT-WIDTH is an integer specifying maximum length of a line. +HOW determines the type of justification: it can be `left', +`right', `full' or `center'." + (with-temp-buffer + (insert s) + (goto-char (point-min)) + (let ((fill-column text-width) + ;; Disable `adaptive-fill-mode' so it doesn't prevent + ;; filling lines matching `adaptive-fill-regexp'. + (adaptive-fill-mode nil)) + (while (< (point) (point-max)) + (justify-current-line how) + (forward-line))) + (buffer-string))) + + +(defun org-rst--indent-string (s width) + "Indent string S by WIDTH white spaces. +Empty lines are not indented." + (when (stringp s) + (replace-regexp-in-string + "\\(^\\)\\(?:.*\\S-\\)" (make-string width ? ) s nil nil 1))) + + +(defun org-rst--make-attribute-string (attributes) + "Return a list of attributes, as a string. +ATTRIBUTES is a plist where values are either strings or nil. An +attributes with a nil value will be omitted from the result." + (let (output) + (dolist (item attributes (mapconcat 'identity (nreverse output) "\n")) + (cond ((null item) (pop output)) + ((symbolp item) (push (substring (symbol-name item) 1) output)) + (t (let ((key (org-trim (car output))) + (value (replace-regexp-in-string "\"" "\\\"" + (replace-regexp-in-string + "\\\\" "\\\\" (org-trim item))))) + (setcar output (format " :%s: %s" key value)))))))) + + +(defun org-rst--build-title + (element info &optional underline notags toc) + "Format ELEMENT title and return it. + +ELEMENT is either an `headline' or `inlinetask' element. INFO is +a plist used as a communication channel. + +When optional argument UNDERLINE is non-nil, underline title, +without the tags, according to `org-rst-underline' +specifications. + +If optional argument NOTAGS is non-nil, no tags will be added to +the title. + +When optional argument TOC is non-nil, use optional title if +possible. It doesn't apply to `inlinetask' elements." + (let* ((headlinep (org-element-type-p element 'headline)) + (numbers + ;; Numbering is specific to headlines. + (and headlinep (org-export-numbered-headline-p element info) + ;; All tests passed: build numbering string. + (concat + (mapconcat + 'number-to-string + (org-export-get-headline-number element info) ".") + " "))) + (text + (org-trim + (org-export-data + (if (and toc headlinep) (org-export-get-alt-title element info) + (org-element-property :title element)) + info))) + (todo + (and (plist-get info :with-todo-keywords) + (let ((todo (org-element-property :todo-keyword element))) + (and todo (concat (org-export-data todo info) " "))))) + (tags (and (not notags) + (plist-get info :with-tags) + (let ((tag-list (org-export-get-tags element info))) + (and tag-list + (org-make-tag-string tag-list))))) + (priority + (and (plist-get info :with-priority) + (let ((char (org-element-property :priority element))) + (and char (format "(#%c) " char))))) + (first-part (concat numbers todo priority text))) + (concat + first-part + ;; Align tags, if any. + (when tags + (format + (format " %%%ds" (string-width tags)) + tags)) + ;; Maybe underline text, if ELEMENT type is `headline' and an + ;; underline character has been defined. + (when (and underline headlinep) + (let ((under-char + (nth (1- (org-export-get-relative-level element info)) + org-rst-headline-underline-characters))) + (and under-char + (concat "\n" + (make-string (string-width first-part) under-char)))))))) + + +(defun org-rst--text-markup (text markup info) + "Format TEXT depending on MARKUP text markup. +INFO is a plist used as a communication channel. +See `org-rst-text-markup-alist' for details." + (let ((fmt (cdr (assq markup (plist-get info :rst-text-markup-alist)))) + (text (replace-regexp-in-string "[ \t\n]+" " " text))) + (cond + ;; No format string: Return raw text. + ((not fmt) text) + ;; Handle the `verb' special case: Protect some + ;; special chars and use "\\" escape. + ((eq 'verb fmt) + (let ((rtn "") + char) + (while (string-match "\\`*" text) + (setq char (match-string 0 text)) + (when (> (match-beginning 0) 0) + (setq rtn (concat rtn (substring text 0 (match-beginning 0))))) + (setq text (substring text (1+ (match-beginning 0)))) + (setq char (concat "\\" char) + rtn (concat rtn char))) + (setq text (concat rtn text) + fmt "``%s``") + (format fmt text))) + ;; Else use format string. + (t (format fmt text))))) + + +(defun org-rst--checkbox (item _info) + "Return checkbox string for ITEM or nil. +INFO is a plist used as a communication channel." + ;(let ((utf8p (eq (plist-get info :ascii-charset) 'utf-8))) + ; (case (org-element-property :checkbox item) + ; (on (if utf8p "☑ " "[X] ")) + ; (off (if utf8p "☐ " "[ ] ")) + ; (trans (if utf8p "☒ " "[-] "))))) + (cl-case (org-element-property :checkbox item) + (on "☑ ") + (off "☐ ") + (trans "☒ "))) + + + +;;; Template + +(defun org-rst-template--document-title (info) + "Return document title, as a string. +INFO is a plist used as a communication channel." + (let* (;; Links in the title will not be resolved later, so we make + ;; sure their path is located right after them. + (with-title (plist-get info :with-title)) + (title (if with-title + (org-export-data (plist-get info :title) info) + "")) + (subtitle (if with-title + (org-export-data (plist-get info :subtitle) info) + "")) + (author (and (plist-get info :with-author) + (let ((auth (plist-get info :author))) + (and auth (org-export-data auth info))))) + (email (and (plist-get info :with-email) + (org-export-data (plist-get info :email) info))) + (date (and (plist-get info :with-date) + (org-export-data (org-export-get-date info) info))) + (titleline (make-string (string-width title) ?=)) + (subtitleline (make-string (string-width subtitle) ?-)) + (title (if (not (string= title "")) + (concat titleline "\n" + title "\n" + titleline "\n") "")) + (subtitle (if (not (string= subtitle "")) + (concat subtitleline "\n" + subtitle "\n" + subtitleline "\n") ""))) + (cond + ((string= title "") + (concat + (when (org-string-nw-p author) (concat " :Author: " author "\n")) + (when (org-string-nw-p email) (concat " :Contact: " email "\n")) + (when (org-string-nw-p date) (concat " :Date: " date "\n")))) + (t + (concat + title + subtitle + (when (org-string-nw-p author) (concat "\n :Author: " author)) + (when (org-string-nw-p email) (concat "\n :Contact: " email)) + (when (org-string-nw-p date) (concat "\n :Date: " date)) + "\n"))))) + + +(defun org-rst-template (contents info) + "Return complete document string after reStructuredText conversion. +CONTENTS is the transcoded contents string. INFO is a plist +holding export options." + (concat + ;; Build title block. + (concat (org-rst-template--document-title info) + "\n" + ;; 2. Table of contents. + (let ((depth (plist-get info :with-toc))) + (when depth ".. contents::\n\n"))) + ;; Document's body. + contents + ;; Creator. Justify it to the bottom right. + (and (plist-get info :with-creater) + (concat + "\n :Creator: " + (plist-get info :creator) "\n")))) + + + +;;; Transcode Functions + +;;;; Bold + +(defun org-rst-bold (_bold contents info) + "Transcode BOLD from Org to reStructuredText. +CONTENTS is the text with bold markup. INFO is a plist holding +contextual information." + (org-rst--text-markup contents 'bold info)) + + +;;;; Center Block + +(defun org-rst-center-block (_center-block contents _info) + "Transcode a CENTER-BLOCK element from Org to reStructuredText. +CONTENTS holds the contents of the center block. INFO is a plist +holding contextual information." + contents) + + +;;;; Clock + +(defun org-rst-clock (clock _contents _info) + "Transcode a CLOCK object from Org to reStructuredText. +CONTENTS is nil. INFO is a plist holding contextual +information." + (concat org-clock-string " " + (org-timestamp-translate (org-element-property :value clock)) + (let ((time (org-element-property :duration clock))) + (and time + (concat " => " + (apply 'format + "%2s:%02s" + (org-split-string time ":"))))))) + + +;;;; Code + +(defun org-rst-code (code _contents info) + "Transcode a CODE object from Org to reStructuredText. +CONTENTS is nil. INFO is a plist used as a communication +channel." + (org-rst--text-markup (org-element-property :value code) 'code info)) + + +;;;; Drawer + +(defun org-rst-drawer (drawer contents info) + "Transcode a DRAWER element from Org to reStructuredText. +CONTENTS holds the contents of the block. INFO is a plist +holding contextual information." + (funcall (plist-get info :rst-format-drawer-function) + (org-element-property :drawer-name drawer) + contents)) + + +;;;; Dynamic Block + +(defun org-rst-dynamic-block (_dynamic-block contents _info) + "Transcode a DYNAMIC-BLOCK element from Org to reStructuredText. +CONTENTS holds the contents of the block. INFO is a plist +holding contextual information." + contents) + + +;;;; Entity + +(defun org-rst-entity (entity _contents _info) + "Transcode an ENTITY object from Org to reStructuredText. +CONTENTS are the definition itself. INFO is a plist holding +contextual information." + (let ((ent (org-element-property :latex entity))) + (if (org-element-property :latex-math-p entity) + (format ":math:`%s`" ent) + (org-element-property :utf-8 entity)))) + + +;;;; Example Block + +(defun org-rst-example-block (example-block _contents _info) + "Transcode an EXAMPLE-BLOCK element from Org to reStructuredText. +CONTENTS is nil. INFO is a plist holding contextual +information." + (let* ((example (org-remove-indentation + (org-element-property :value example-block))) + (label (org-element-property :name example-block)) + (attributes + (org-export-read-attribute :attr_rst example-block)) + (class (plist-get attributes :class))) + (when example + (concat + "::\n" + (when class (format " :class: %s\n" class)) + (when label (format " :name: %s\n" label)) + "\n" + (org-rst--indent-string example org-rst-quote-margin))))) + + +;;;; Export Block + +(defun org-rst-export-block (export-block _contents _info) + "Transcode a EXPORT-BLOCK element from Org to reStructuredText. +CONTENTS is nil. INFO is a plist holding contextual information." + (when (member (org-element-property :type export-block) '("RST" "REST" "RESTRUCTUREDTEXT")) + (org-element-property :value export-block))) + + +;;;; Export Snippet + +(defun org-rst-export-snippet (export-snippet _contents _info) + "Transcode a EXPORT-SNIPPET object from Org to reStructuredText. +CONTENTS is nil. INFO is a plist holding contextual information." + (when (eq (org-export-snippet-backend export-snippet) 'rst) + (org-element-property :value export-snippet))) + + +;;;; Footnote Definition + +;(defun org-rst-footnote-definition (footnote-definition contents info) +; "Transcode a FOOTNOTE-DEFINITION element from Org to reStructuredText. +;CONTENTS is nil. INFO is a plist holding contextual information." +; (replace-regexp-in-string +; "^" ".. " +; (org-remove-indentation +; (org-element-property :value footnote-definition)))) + + +;;;; Footnote Reference + +(defun org-rst-footnote-reference (footnote-reference _contents info) + "Transcode a FOOTNOTE-REFERENCE element from Org to reStructuredText. +CONTENTS is nil. INFO is a plist holding contextual information." + (format " [%s]_ " (org-export-get-footnote-number footnote-reference info))) + + +;;;; Headline + +(defun org-rst-headline (headline contents info) + "Transcode a HEADLINE element from Org to reStructuredText. +CONTENTS holds the contents of the headline. INFO is a plist +holding contextual information." + ;; Don't export footnote section, which will be handled at the end + ;; of the template. + (unless (org-element-property :footnote-section-p headline) + (let* (;; Blank lines between headline and its contents. + ;; `org-rst-headline-spacing', when set, overwrites + ;; original buffer's spacing. + (pre-blanks + (make-string + (if org-rst-headline-spacing (car org-rst-headline-spacing) + (org-element-property :pre-blank headline)) ?\n)) + (id (org-element-property :ID headline)) + (customid (org-element-property :CUSTOM_ID headline))) + (concat + (if customid + (format ".. _%s:\n\n" customid) + (if id (format ".. _%s:\n\n" id) "")) + (org-rst--build-title headline info 'underline) + "\n" pre-blanks + contents)))) + + +;;;; Horizontal Rule + +(defun org-rst-horizontal-rule (_horizontal-rule _contents _info) + "Transcode an HORIZONTAL-RULE object from Org to reStructuredText. +CONTENTS is nil. INFO is a plist holding contextual information." + "\n------------\n") + +;;;; Inline Src Block + +(defun org-rst-inline-src-block (inline-src-block _contents info) + "Transcode an INLINE-SRC-BLOCK element from Org to reStructuredText. +CONTENTS holds the contents of the item. INFO is a plist holding +contextual information." + (org-rst--text-markup + (org-element-property :value inline-src-block) 'verbatim info)) + + +;;;; Inlinetask + +(defun org-rst-inlinetask (inlinetask contents info) + "Transcode an INLINETASK element from Org to reStructuredText. +CONTENTS holds the contents of the block. INFO is a plist +holding contextual information." + (let ((title (org-export-data (org-element-property :title inlinetask) info)) + (todo (and (plist-get info :with-todo-keywords) + (let ((todo (org-element-property :todo-keyword inlinetask))) + (and todo (org-export-data todo info))))) + (todo-type (org-element-property :todo-type inlinetask)) + (tags (and (plist-get info :with-tags) + (org-export-get-tags inlinetask info))) + (priority (and (plist-get info :with-priority) + (org-element-property :priority inlinetask)))) + ;; If `org-rst-format-inlinetask-function' is provided, call it + ;; with appropriate arguments. + (if (functionp org-rst-format-inlinetask-function) + (funcall org-rst-format-inlinetask-function + todo todo-type priority title tags contents) + ;; Otherwise, use a default template. + (let ((full-title + (concat + (when todo (format "%s" todo)) + (when priority (format "\#%c " priority)) + title + (when tags (format ":%s:" + (mapconcat 'identity tags ":")))))) + (format (concat "%s\n\n" + "%s\n") + full-title contents))))) + + +;;;; Inner template + +(defun org-rst-inner-template (contents info) + "Return complete document string after reStructuredText conversion. +CONTENTS is the transcoded contents string. INFO is a plist +holding export options." + (org-element-normalize-string + (concat + ;; 1. Document's body. + contents + ;; 2. Footnote definitions. + (let ((definitions (org-export-collect-footnote-definitions info))) + (when definitions + (concat + "\n\n" + (mapconcat + (lambda (ref) + (let* ((id (format ".. [%s] " (car ref))) + (def (nth 2 ref)) + (lines (split-string (org-export-data def info) "\n+[ \t\n]*")) + (fntext (concat (car lines) "\n" + (apply 'concat (mapcar + #'(lambda (x) (if (> (length x) 0) + (concat (org-rst--indent-string x org-rst-quote-margin) "\n"))) + (cdr lines))))) + ) + (concat id fntext))) + definitions "\n"))))))) + + +;;;; Italic + +(defun org-rst-italic (_italic contents info) + "Transcode ITALIC from Org to reStructuredText. +CONTENTS is the text with italic markup. INFO is a plist holding +contextual information." + (org-rst--text-markup contents 'italic info)) + + +;;;; Item + +(defun org-rst-item (item contents info) + "Transcode ITEM element into reStructuredText format. +CONTENTS is the item contents. INFO is a plist used as +a communication channel." + (let* ((checkbox (org-rst--checkbox item info)) + (list-type (org-element-property :type (org-element-parent item))) + (tag (let + ((tag (org-element-property :tag item))) + (and tag (concat (org-export-data tag info) checkbox)))) + (bullet + ;; First parent of ITEM is always the plain-list. Get + ;; `:type' property from it. + (org-list-bullet-string + (cond + ((eq list-type 'ordered) + ;; Return correct number for ITEM, paying attention to + ;; counters. + (let* ((struct (org-element-property :structure item)) + (bul (org-element-property :bullet item)) + (num (number-to-string + (car (last (org-list-get-item-number + (org-element-begin item) + struct + (org-list-prevs-alist struct) + (org-list-parents-alist struct))))))) + (replace-regexp-in-string "[0-9]+" num bul))) + (tag "") + (t "-")))) + (width (if tag 4 (string-width bullet))) + ) + (concat + (if tag tag (concat bullet checkbox)) + (let ((contents (org-rst--indent-string contents width))) + (if (and (not tag) + (org-element-type-p (car (org-element-contents item)) 'paragraph)) + (org-trim contents) + (concat "\n" contents)))))) + + +;;;; Keyword + +(defun org-rst-keyword (keyword _contents _info) + "Transcode a KEYWORD element from Org to reStructuredText. +CONTENTS is nil. INFO is a plist holding contextual information." + (let ((key (org-element-property :key keyword)) + (value (org-element-property :value keyword))) + (cond + ((string= key "RST") value)))) + + +;;;; Latex Environment + +(defun org-rst-latex-environment (latex-environment _contents info) + "Transcode a LATEX-ENVIRONMENT element from Org to reStructuredText. +CONTENTS is nil. INFO is a plist holding contextual +information." + (when (plist-get info :with-latex) + (org-remove-indentation (org-element-property :value latex-environment)))) + + +;;;; Latex Fragment + +(defun org-rst-latex-fragment (latex-fragment _contents _info) + "Transcode a LATEX-FRAGMENT object from Org to reStructuredText. +CONTENTS is nil. INFO is a plist holding contextual +information." + (let ((value (org-element-property :value latex-fragment))) + (cond + ((string-match "\\`\\(\\$\\{2\\}\\)\\([^\000]*\\)\\1\\'" value) + (format ".. math::\n\n%s" + (org-rst--indent-string + (org-trim (match-string 2 value)) org-rst-quote-margin))) + ((string-match "\\`\\(\\$\\{1\\}\\)\\([^\000]*\\)\\1\\'" value) + (format ":math:`%s`" (org-trim (match-string 2 value)))) + ((string-match "\\`\\\\(\\([^\000]*\\)\\\\)\\'" value) + (format ":math:`%s`" (org-trim (match-string 1 value)))) + ((string-match "\\`\\\\\\[\\([^\000]*\\)\\\\\\]\\'" value) + (format "\.. math::\n\n%s" + (org-rst--indent-string + (org-trim (match-string 1 value)) org-rst-quote-margin))) + (t value)))) + + +;;;; Line Break + +(defun org-rst-line-break (_line-break _contents _info) + "Transcode a LINE-BREAK object from Org to reStructuredText. +CONTENTS is nil. INFO is a plist holding contextual + information." + hard-newline) + + +;;;; Link + +(defun org-rst-inline-image-p (link info) + "Non-nil when LINK is meant to appear as an image. +INFO is a plist used as a communication channel. LINK is an +inline image when it has no description and targets an image +file (see `org-rst-inline-image-rules' for more information), or +if its description is a single link targeting an image file." + (if (not (org-element-contents link)) + (org-export-inline-image-p + link (plist-get info :rst-inline-image-rules)) + (not + (let ((link-count 0)) + (org-element-map (org-element-contents link) + (cons 'plain-text org-element-all-objects) + (lambda (obj) + (cl-case (org-element-type obj) + (plain-text (org-string-nw-p obj)) + (link (if (= link-count 1) t + (cl-incf link-count) + (not (org-export-inline-image-p + obj (plist-get info :rst-inline-image-rules))))) + (otherwise t))) + info t))))) + + +(defun my-org-export-inline-image-p (link &optional rules) + (let ((case-fold-search t) + (rules (or rules org-export-default-inline-image-rule))) + (catch 'exit + (mapc + (lambda (rule) + (if (string-match (cdr rule) link) + (throw 'exit t))) + rules) + ;; Return nil if no rule matched. + nil))) + + +(defun org-rst-link (link desc info) + "Transcode a LINK object from Org to reStructuredText. + +DESC is the description part of the link, or the empty string. +INFO is a plist holding contextual information." + (let* ((home (when (plist-get info :rst-link-home) + (org-trim (plist-get info :rst-link-home)))) + (use-abs-url (plist-get info :rst-link-use-abs-url)) + (link-org-files-as-rst-maybe + (function + (lambda (raw-path info) + "Treat links to `file.org' as links to `file.rst', if needed. + See `org-rst-link-org-files-as-rst'." + (cond + ((and (plist-get info :rst-link-org-files-as-rst) + (string= ".org" + (downcase (file-name-extension raw-path ".")))) + (concat (file-name-sans-extension raw-path) "." + (plist-get info :rst-extension))) + (t raw-path))))) + (type (org-element-property :type link)) + (search-option (org-element-property :search-option link)) + (raw-path (org-element-property :path link)) + ;; Ensure DESC really exists, or set it to nil. + (desc (and (not (string= desc "")) desc)) + (path (cond + ((string= type "file") + ;; Treat links to ".org" files as ".rst", if needed. + (setq raw-path + (funcall link-org-files-as-rst-maybe raw-path info)) + (cond ((and home use-abs-url) + (setq raw-path + (concat (file-name-as-directory home) raw-path))) + (t raw-path))) + (t (concat type ":" raw-path)))) + (attributes-plist + (org-combine-plists + ;; Extract attributes from parent's paragraph. HACK: Only + ;; do this for the first link in parent (inner image link + ;; for inline images). This is needed as long as + ;; attributes cannot be set on a per link basis. + (let* ((parent (org-element-parent-element link)) + (link (let ((container (org-element-parent link))) + (if (and (org-element-type-p container 'link) + (org-rst-inline-image-p link info)) + container + link)))) + (and (eq link (org-element-map parent 'link #'identity info t)) + (org-export-read-attribute :attr_rst parent))) + ;; Also add attributes from link itself. Currently, those + ;; need to be added programmatically before `org-rst-link' + ;; is invoked, for example, by backends building upon HTML + ;; export. + (org-export-read-attribute :attr_rst link))) + (attributes + (let ((attr (org-rst--make-attribute-string attributes-plist))) + (if (org-string-nw-p attr) (concat "\n" attr "\n") "")))) + (cond + ;; Link type is handled by a special function. + ((org-export-custom-protocol-maybe link desc 'rst info)) + ;; Image file. + ((and (plist-get info :rst-inline-images) + (org-export-inline-image-p + link (plist-get info :rst-inline-image-rules))) + (let* ((ipath (if (not (file-name-absolute-p raw-path)) raw-path + (expand-file-name raw-path))) + (caption (org-export-get-caption + (org-element-parent-element link))) + (linkname + (org-element-property :name (org-element-parent-element link))) + (label (if linkname (format ".. _%s:\n\n" linkname) ""))) + (if caption (format "%s.. figure:: %s%s\n\n %s\n" + label ipath attributes + (org-export-data caption info)) + (format "%s.. image:: %s%s\n" label ipath attributes)))) + ((and (plist-get info :rst-inline-images) + desc + (my-org-export-inline-image-p + desc (plist-get info :rst-inline-image-rules))) + (format ".. image:: %s\n :target: %s%s" desc path attributes)) + ;; Radio link: Transcode target's contents and use them as link's + ;; description. + ((string= type "radio") + (let ((destination (org-export-resolve-radio-link link info))) + (when destination + (format "`%s <%s>`_" + path + (org-export-data (org-element-contents destination) info))))) + ;; Links pointing to a headline: Find destination and build + ;; appropriate referencing command. + ((member type '("custom-id" "fuzzy" "id")) + (let ((destination (if (string= type "fuzzy") + (org-export-resolve-fuzzy-link link info) + (org-export-resolve-id-link link info)))) + (cl-case (org-element-type destination) + ;; Id link points to an external file. + (plain-text + (if desc (format "`%s <%s>`_" desc destination) + (format "`%s`_" destination))) + ;; Fuzzy link points nowhere. + ;; ('nil + ;; (let ((rawlink + ;; (org-export-data (org-element-property :raw-link link) info))) + ;; (if desc (format "`%s <%s>`_" desc rawlink) + ;; (format "`%s`_" rawlink)))) + ;; LINK points to a headline. + (headline + (if (member type '("custom-id" "id")) + (if (plist-get info :rst-link-use-ref-role) + (if desc (format " :ref:`%s <%s>`" desc raw-path) + (format " :ref:`%s`" raw-path)) + (format "`%s`_" raw-path)) + (format "`%s`_" (org-rst--build-title destination info nil)))) + ;; Fuzzy link points to a target. + (otherwise + (if (not desc) (format "`%s`_" raw-path) + (format "`%s <%s>`_" desc raw-path)))))) + ;; Coderef: replace link with the reference name or the + ;; equivalent line number. It is not supported in ReST. + ((string= type "coderef") + (format (org-export-get-coderef-format path desc) + (org-export-resolve-coderef path info))) + ((and (plist-get info :rst-file-link-use-ref-role) + (string= type "file") + search-option) + (let ((ref (replace-regexp-in-string "^#" "" search-option))) + (if desc + (format ":ref:`%s <%s>`" desc ref) + (format ":ref:`%s`" ref)))) + ;; Link type is handled by a special function. + ;((functionp (setq protocol (nth 2 (assoc type org-link-protocols)))) + ; (funcall protocol (org-link-unescape path) desc 'latex)) + ;; External link with a description part. + ((and path desc) (format "`%s <%s>`_" desc path)) + ;; External link without a description part. + (path (format "`%s <%s>`_" + (replace-regexp-in-string "^//" "" path) path)) + ;; No path, only description. Try to do something useful. + (t (format "`%s <%s>`_" desc desc))))) + + +;;;; Node Property + +(defun org-rst-node-property (node-property _contents _info) + "Transcode a NODE-PROPERTY element from Org to reStructuredText. +CONTENTS is nil. INFO is a plist holding contextual +information." + (format "%s:%s" + (org-element-property :key node-property) + (let ((value (org-element-property :value node-property))) + (if value (concat " " value) "")))) + + +;;;; Paragraph + +(defun org-rst-paragraph (_paragraph contents info) + "Transcode a PARAGRAPH element from Org to reStructuredText. +CONTENTS is the contents of the paragraph, as a string. INFO is +the plist used as a communication channel." + (when (plist-get info :preserve-breaks) + (let ((lines (split-string contents "\n+[ \t\n]*"))) + (cond ((> (length lines) 2) + (setq contents (apply 'concat (mapcar + #'(lambda (x) (if (> (length x) 0) (concat "| " x "\n") x)) + lines))))))) + contents) + + +;;;; Plain List + +(defun org-rst-plain-list (_plain-list contents _info) + "Transcode a PLAIN-LIST element from Org to reStructuredText. +CONTENTS is the contents of the list. INFO is a plist holding +contextual information." + contents) + + +;;;; Plain Text + +(defun org-rst-plain-text (text info) + "Transcode a TEXT string from Org to reStructuredText. +TEXT is the string to transcode. INFO is a plist holding +contextual information." + (when (plist-get info :with-smart-quotes) + (setq text (org-export-activate-smart-quotes text :utf-8 info))) + ;; Protect `, *, _ and \ + (setq text (replace-regexp-in-string "[`*_\\]" "\\\\\\&" text)) + ;; Protect .. + (setq text (replace-regexp-in-string "^[\s-]*\\.\\. [^\\[]" "\\\\\\&" text)) + ;; Protect :: + (setq text (replace-regexp-in-string "::" "\\\\:\\\\:" text)) + ;; Return value. + text) + + +;;;; Planning + +(defun org-rst-planning (planning _contents _info) + "Transcode a PLANNING element from Org to reStructuredText. +CONTENTS is nil. INFO is a plist used as a communication +channel." + (mapconcat + 'identity + (delq nil + (list (let ((closed (org-element-property :closed planning))) + (when closed + (concat org-closed-string " " + (org-timestamp-translate closed)))) + (let ((deadline (org-element-property :deadline planning))) + (when deadline + (concat org-deadline-string " " + (org-timestamp-translate deadline)))) + (let ((scheduled (org-element-property :scheduled planning))) + (when scheduled + (concat org-scheduled-string " " + (org-timestamp-translate scheduled)))))) + " ")) + + +;;;; Property Drawer + +(defun org-rst-property-drawer (_property-drawer contents _info) + "Transcode a PROPERTY-DRAWER element from Org to reStructuredText. +CONTENTS holds the contents of the drawer. INFO is a plist +holding contextual information." + (when (org-string-nw-p contents) + (concat + "::\n\n" + (org-rst--indent-string contents org-rst-quote-margin)))) + + +;;;; Pseudo Object: LaTeX Math Block + +;; `latex-math-block' objects have the following property: +;; `:post-blank'. + +(defun org-rst--wrap-latex-math-block (data info) + "Merge contiguous math objects in a pseudo-object container. +DATA is a parse tree or a secondary string. INFO is a plist +containing export options. Modify DATA by side-effect and return it." + (let ((valid-object-p + (function + ;; Non-nil when OBJ can be added to the latex math block. + (lambda (obj) + (cl-case (org-element-type obj) + (entity (org-element-property :latex-math-p obj)) + (latex-fragment + (let ((value (org-element-property :value obj))) + (or (string-match-p "\\`\\\\([^\000]*\\\\)\\'" value) + (string-match-p "\\`\\$[^\000]*\\$\\'" value) + (string-match-p "\\`\\\\\\[[^\000]*\\\\\\]\\'" value)))) + ((subscript superscript) t)))))) + (org-element-map data '(entity latex-fragment subscript superscript) + (lambda (object) + ;; Skip objects already wrapped. + (when (and (not (org-element-type-p + (org-element-property :parent object) + 'latex-math-block)) + (funcall valid-object-p object)) + (let ((math-block (list 'latex-math-block nil)) + (next-elements (org-export-get-next-element object info t)) + (last object)) + ;; Wrap MATH-BLOCK around OBJECT in DATA. + (org-element-insert-before math-block object) + (org-element-extract object) + (org-element-adopt-elements math-block object) + (when (zerop (or (org-element-property :post-blank object) 0)) + ;; MATH-BLOCK swallows consecutive math objects. + (catch 'exit + (dolist (next next-elements) + (if (not (funcall valid-object-p next)) (throw 'exit nil) + (org-element-extract next) + (org-element-adopt-elements math-block next) + ;; Eschew the case: \beta$x$ -> \(\betax\). + (unless (memq (org-element-type next) + '(subscript superscript)) + (org-element-put-property last :post-blank 1)) + (setq last next) + (when (> (or (org-element-property :post-blank next) 0) 0) + (throw 'exit nil)))))) + (org-element-put-property + math-block :post-blank (org-element-property :post-blank last))))) + info nil '(subscript superscript latex-math-block) t) + ;; Return updated DATA. + data)) + +(defun org-rst-math-block-tree-filter (tree _backend info) + (org-rst--wrap-latex-math-block tree info)) + +(defun org-rst-math-block-options-filter (info _backend) + (dolist (prop '(:author :date :title) info) + (plist-put info prop + (org-rst--wrap-latex-math-block (plist-get info prop) info)))) + +(defun org-rst-math-block (_math-block contents _info) + "Transcode a MATH-BLOCK object from Org to reStructuredText. +CONTENTS is a string. INFO is a plist used as a communication +channel." + (let* ((value (org-trim contents)) + (value + (if (> (string-width value) 2) + (if (string= ".." (substring value 0 2)) + (format "\n\n%s\n\n" value) + value)))) + (format "%s" value))) + + +;;;; Quote Block + +(defun org-rst-quote-block (quote-block contents _info) + "Transcode a QUOTE-BLOCK element from Org to reStructuredText. +CONTENTS holds the contents of the block. INFO is a plist +holding contextual information." + (let* ((attributes + (org-export-read-attribute :attr_rst quote-block)) + (directive (plist-get attributes :directive)) + (title (plist-get attributes :title)) + (subtitle (plist-get attributes :subtitle)) + (margin (plist-get attributes :margin)) + (class (plist-get attributes :class)) + (label (org-element-property :name quote-block))) + (cond ((and margin contents) + (org-rst--indent-string contents (string-to-number margin))) + (directive + (concat + (format ".. %s::" directive) + (when title (format " %s" title)) + "\n" + (when (and subtitle (string= "sidebar" directive)) + (format " :subtitle: %s\n" subtitle)) + (when (and class (not (string= "container" directive))) + (format " :class: %s\n" class)) + (when label (format " :name: %s\n" label)) + "\n" + (when contents + (org-rst--indent-string contents org-rst-quote-margin)))) + (t + (concat + "::\n" + (when class (format " :class: %s\n" class)) + (when label (format " :name: %s\n" label)) + "\n" + (when contents + (org-rst--indent-string contents org-rst-quote-margin))))))) + + +;;;; Radio Target + +(defun org-rst-radio-target (_radio-target contents _info) + "Transcode a RADIO-TARGET object from Org to reStructuredText. +CONTENTS is the contents of the target. INFO is a plist holding +contextual information." + contents) + + +;;;; Section + +(defun org-rst-section (_section contents _info) + "Transcode a SECTION element from Org to reStructuredText. +CONTENTS holds the contents of the section. INFO is a plist +holding contextual information." + contents) + + +;;;; Special Block + +(defun org-rst-special-block (special-block contents _info) + "Transcode a SPECIAL-BLOCK element from Org to reStructuredText. +CONTENTS holds the contents of the block. INFO is a plist +holding contextual information." + (let* ((attributes + (org-export-read-attribute :attr_rst special-block)) + (title (plist-get attributes :title)) + (class (plist-get attributes :class)) + (label (org-element-property :name special-block)) + (type (org-element-property :type special-block))) + (concat + (format ".. %s::" type) + (when title (format " %s" title)) + "\n" + (when class (format " :class: %s\n" class)) + (when label (format " :name: %s\n" label)) + "\n" + (when contents + (org-rst--indent-string contents org-rst-quote-margin))))) + +;;;; Src Block + +(defun org-rst-src-block (src-block _contents info) + "Transcode a SRC-BLOCK element from Org to reStructuredText. +CONTENTS holds the contents of the item. INFO is a plist holding +contextual information." + (when (org-string-nw-p (org-element-property :value src-block)) + (let* ((lang (org-element-property :language src-block)) + (label (org-element-property :name src-block)) + (value (org-remove-indentation + (org-element-property :value src-block))) + (num-start (org-export-get-loc src-block info)) + (codeblockd (plist-get info :rst-code-block)) + (attributes + (org-export-read-attribute :attr_rst src-block)) + (class (plist-get attributes :class))) + (cond + ;; Case 1. + ((eq codeblockd 'code-block) + (let ((lst-lang + (or (cadr (assq (intern lang) org-rst-pygments-langs)) lang))) + (concat + (format ".. code-block:: %s\n" lst-lang) + (when num-start (format " :lineno-start: %s\n" (1+ num-start))) + (when class (format " :class: %s\n" class)) + (when label (format " :name: %s\n" label)) + "\n" + (org-rst--indent-string value org-rst-quote-margin)))) + ;; Case 2. code. + ((eq codeblockd 'code) + (let ((lst-lang + (or (cadr (assq (intern lang) org-rst-pygments-langs)) lang))) + (concat + (format ".. code:: %s\n" lst-lang) + (when num-start (format " :number-lines: %s\n" (1+ num-start))) + (when class (format " :class: %s\n" class)) + (when label (format " :name: %s\n" label)) + "\n" + (org-rst--indent-string value org-rst-quote-margin)))) + (t + (concat + "::\n" + (when class (format " :class: %s\n" class)) + (when label (format " :name: %s\n" label)) + "\n" + (org-rst--indent-string value org-rst-quote-margin))))))) + + +;;;; Statistics Cookie + +(defun org-rst-statistics-cookie (statistics-cookie _contents _info) + "Transcode a STATISTICS-COOKIE object from Org to reStructuredText. +CONTENTS is nil. INFO is a plist holding contextual information." + (org-element-property :value statistics-cookie)) + + +;;;; Strike-Through + +(defun org-rst-strike-through (_strike-through contents _info) + "Transcode STRIKE-THROUGH from Org to reStructuredText. +CONTENTS is the text with strike-through markup. INFO is a plist +holding contextual information." + contents) + + +;;;; Subscript + +(defun org-rst-subscript (_subscript contents _info) + "Transcode a SUBSCRIPT object from Org to reStructuredText. +CONTENTS is the contents of the object. INFO is a plist holding +contextual information." + (format "\\ :sub:`%s`\\ " contents)) + + +;;;; Superscript + +(defun org-rst-superscript (_superscript contents _info) + "Transcode a SUPERSCRIPT object from Org to reStructuredText. +CONTENTS is the contents of the object. INFO is a plist holding +contextual information." + (format "\\ :sup:`%s`\\ " contents)) + + +;;;; Table + +(defun org-rst-table-first-row-data-cells (table info) + "Transcode the first row of TABLE. +INFO is a plist used as a communication channel." + (let ((table-row + (org-element-map table 'table-row + (lambda (row) + (unless (eq (org-element-property :type row) 'rule) row)) + info 'first-match)) + (special-column-p (org-export-table-has-special-column-p table))) + (if (not special-column-p) (org-element-contents table-row) + (cdr (org-element-contents table-row))))) + +(defun org-rst-table (table contents info) + "Transcode a TABLE element from Org to reStructuredText. +CONTENTS is the contents of the table. INFO is a plist holding +contextual information." + (let* ((caption (org-export-get-caption table)) + (attributes + (org-export-read-attribute :attr_rst table)) + (class (plist-get attributes :class)) + (label (org-element-property :name table))) + (concat + (if caption (format ".. table:: %s\n" (org-export-data caption info)) + ".. table::\n") + (when class (format " :class: %s\n" class)) + (when label (format " :name: %s\n" label)) + "\n" + (org-rst--indent-string contents org-rst-quote-margin)))) + + +;;;; Table Cell + +(defun org-rst--table-cell-width (table-cell info) + "Return width of TABLE-CELL. + +INFO is a plist used as a communication channel. + +Width of a cell is determined either by a width cookie in the +same column as the cell, or by the maximum cell's length in that +column." + (let* ((row (org-element-parent table-cell)) + (table (org-element-parent row)) + (col (let ((cells (org-element-contents row))) + (- (length cells) (length (memq table-cell cells))))) + (cache + (or (plist-get info :rst-table-cell-width-cache) + (plist-get (setq info + (plist-put info :rst-table-cell-width-cache + (make-hash-table :test 'equal))) + :rst-table-cell-width-cache))) + (key (cons table col))) + (or (gethash key cache) + (puthash + key + (let ((cookie-width (org-export-table-cell-width table-cell info))) + (or cookie-width + (let ((contents-width + (let ((max-width 0)) + (org-element-map table 'table-row + (lambda (row) + (setq max-width + (max (string-width + (org-export-data + (org-element-contents + (elt (org-element-contents row) col)) + info)) + max-width))) + info) + max-width))) + (cond ((not cookie-width) contents-width) + (t cookie-width))))) + cache)))) + + +(defun org-rst-table-cell (table-cell contents info) + "Transcode a TABLE-CELL object from Org to reStructuredText. +CONTENTS is the cell contents. INFO is a plist used as +a communication channel." + (let ((width (org-rst--table-cell-width table-cell info))) + ;; Align contents correctly within the cell. + (let* ((indent-tabs-mode nil) + (data + (if contents + (org-rst--justify-lines + contents width + (org-export-table-cell-alignment table-cell info)) "\\"))) + (setq contents + (concat data + (make-string (max 0 (- width (string-width data))) ? )))) + ;; Return cell. + (concat (format " %s " contents) + (when (org-export-get-next-element table-cell info) "|")))) + + +;;;; Table Row + +(defun org-rst-table-row (table-row contents info) + "Transcode a TABLE-ROW element from Org to reStructuredText. +CONTENTS is the row contents. INFO is a plist used as +a communication channel." + (let ((borders (org-export-table-cell-borders + (org-element-map table-row 'table-cell 'identity info t) + info))) + (if (not (and (memq 'bottom borders) (memq 'top borders))) + (let* ((rowgroup-number (org-export-table-row-group table-row info)) + (row-number (org-export-table-row-number table-row info)) + (line-bit + (cond + ((not (= 1 rowgroup-number)) + ?-) + ((org-export-table-has-header-p + (org-element-lineage table-row 'table) info) + ?=) + (t ?-))) + (makeline + (function + (lambda (_rowcontents linebit) + (format "+%s+" + (mapconcat + 'identity + (mapcar + (lambda (table-cell) + (make-string (string-width table-cell) + linebit)) + (split-string contents "|")) + "+"))))) + (hline (format "+%s+" + (mapconcat + 'identity + (mapcar + (lambda (table-cell) + (make-string (string-width table-cell) + line-bit)) + (split-string contents "|")) + "+")))) + (concat + (when (= 0 row-number) + (concat (funcall makeline contents ?-) "\n")) + "|" contents "|\n" hline)) + nil + ))) + + +;;;; Target + +(defun org-rst-target (target _contents _info) + "Transcode a TARGET object from Org to reStructuredText. +CONTENTS is nil. INFO is a plist holding contextual +information." + (format " _`%s` " (org-element-property :value target))) + + +;;;; Timestamp + +(defun org-rst-timestamp (timestamp _contents info) + "Transcode a TIMESTAMP object from Org to reStructuredText. +CONTENTS is nil. INFO is a plist holding contextual +information." + (org-rst-plain-text (org-timestamp-translate timestamp) info)) + + +;;;; Underline + +(defun org-rst-underline (_underline contents _info) + "Transcode UNDERLINE from Org to reStructuredText. +CONTENTS is the text with underline markup. INFO is a plist +holding contextual information." + contents) + + +;;;; Verbatim + +(defun org-rst-verbatim (verbatim _contents info) + "Transcode a VERBATIM object from Org to reStructredText. +CONTENTS is nil. INFO is a plist used as a communication +channel." + (org-rst--text-markup (org-element-property :value verbatim) 'verbatim info)) + + +;;;; Verse Block + +(defun org-rst-verse-block (_verse-block contents _info) + "Transcode a VERSE-BLOCK element from Org to reStructuredText. +CONTENTS is verse block contents. INFO is a plist holding +contextual information." + (let ((lines (split-string contents "\n"))) + (cond ((> (length lines) 0) + (mapconcat + (function (lambda (x) (if (> (string-width x) 0) + (concat "| " x "\n") ""))) lines ""))))) + + +;;; Filters + +(defun org-rst-separate-elements (tree _backend _info) + "Make sure elements are separated by at least one blank line. + +TREE is the parse tree being exported. BACKEND is the export +back-end used. INFO is a plist used as a communication channel. + +Assume BACKEND is `rst'." + (org-element-map tree org-element-all-elements + (lambda (elem) + (unless (or (org-element-type-p elem 'org-data) + (org-element-type-p elem 'table-row)) + (org-element-put-property + elem :post-blank + (let ((post-blank (org-element-property :post-blank elem))) + (if (not post-blank) 1 (max 1 post-blank))))))) + ;; Return updated tree. + tree) + +(defun org-rst-filter-headline-blank-lines (headline _back-end _info) + "Filter controlling number of blank lines after a headline. + +HEADLINE is a string representing a transcoded headline. +BACK-END is symbol specifying back-end used for export. INFO is +plist containing the communication channel. + +This function only applies to `rst' back-end. See +`org-rst-headline-spacing' for information." + (if (not org-rst-headline-spacing) headline + (let ((blanks (make-string (1+ (cdr org-rst-headline-spacing)) ?\n))) + (replace-regexp-in-string "\n\\(?:\n[ \t]*\\)*\\'" blanks headline)))) + +(defun org-rst-filter-paragraph-spacing (tree _back-end info) + "Filter controlling number of blank lines between paragraphs. + +TREE is the parse tree. BACK-END is the symbol specifying +back-end used for export. INFO is a plist used as +a communication channel. + +See `org-rst-paragraph-spacing' for information." + (when (wholenump org-rst-paragraph-spacing) + (org-element-map tree 'paragraph + (lambda (p) + (when (org-element-type-p (org-export-get-next-element p info) + 'paragraph) + (org-element-put-property + p :post-blank org-rst-paragraph-spacing))))) + tree) + + + +;;; End-user functions + +;;;###autoload +(defun org-rst-export-as-rst + (&optional async subtreep visible-only body-only ext-plist) + "Export current buffer to a reStructuredText buffer. + +If narrowing is active in the current buffer, only export its +narrowed part. + +If a region is active, export that region. + +A non-nil optional argument ASYNC means the process should happen +asynchronously. The resulting buffer should be accessible +through the `org-export-stack' interface. + +When optional argument SUBTREEP is non-nil, export the sub-tree +at point, extracting information from the headline properties +first. + +When optional argument VISIBLE-ONLY is non-nil, don't export +contents of hidden elements. + +Export is done in a buffer named \"*Org RST Export*\", which will +be displayed when `org-export-show-temporary-export-buffer' is +non-nil." + (interactive) + (org-export-to-buffer 'rst "*Org RST Export*" + async subtreep visible-only body-only ext-plist (lambda () (rst-mode)))) + +;;;###autoload +(defun org-rst-convert-region-to-rst () + "Assume the current region has Org syntax, and convert it to +reStructuredText. +This can be used in any buffer. For example, you can write an +itemized list in Org syntax in a Markdown buffer and use this command +to convert it." + (interactive) + (org-export-replace-region-by 'rst)) + +(defalias 'org-export-region-to-rst #'org-rst-convert-region-to-rst) + +;;;###autoload +(defun org-rst-export-to-rst + (&optional async subtreep visible-only body-only ext-plist) + "Export current buffer to a reStructuredText file. + +If narrowing is active in the current buffer, only export its +narrowed part. + +If a region is active, export that region. + +A non-nil optional argument ASYNC means the process should happen +asynchronously. The resulting file should be accessible through +the `org-export-stack' interface. + +When optional argument SUBTREEP is non-nil, export the sub-tree +at point, extracting information from the headline properties +first. + +When optional argument VISIBLE-ONLY is non-nil, don't export +contents of hidden elements. + +Return output file's name." + (interactive) + (let* ((extension (concat "." (or (plist-get ext-plist :rst-extension) + org-rst-extension + "rst"))) + (file (org-export-output-file-name extension subtreep))) + (org-export-to-file 'rst file + async subtreep visible-only body-only ext-plist))) + +;;;###autoload +(defun org-rst-publish-to-rst (plist filename pub-dir) + "Publish an org file to reStructuredText. + +FILENAME is the filename of the Org file to be published. PLIST +is the property list for the given project. PUB-DIR is the +publishing directory. + +Return output file name." + (org-publish-org-to 'rst filename ".rst" plist pub-dir)) + + +;;; provide + +(provide 'ox-rst) + +;;; ox-rst.el ends here diff --git a/lisp/update-autoloads.el b/lisp/update-autoloads.el index 2e305ad2..ae841f3e 100644 --- a/lisp/update-autoloads.el +++ b/lisp/update-autoloads.el @@ -63,6 +63,7 @@ (package-generate-autoloads "org-appear" (concat config-dir "lisp/org-appear")) (package-generate-autoloads "org-contrib" (concat config-dir "lisp/org-contrib")) (package-generate-autoloads "ox-pandoc" (concat config-dir "lisp/ox-pandoc")) + (package-generate-autoloads "ox-rst" (concat config-dir "lisp/ox-rst")) (package-generate-autoloads "rainbow-mode" (concat config-dir "lisp/rainbow-mode")) (package-generate-autoloads "spacemacs-theme" (concat config-dir "lisp/spacemacs-theme")) (package-generate-autoloads "srefactor" (concat config-dir "lisp/srefactor"))