emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* ox-md.el: Export TOC and Footnotes as Markdown rather than HTML
@ 2016-08-08  1:31 Jake Romer
  2016-08-08 13:35 ` Nicolas Goaziou
  0 siblings, 1 reply; 10+ messages in thread
From: Jake Romer @ 2016-08-08  1:31 UTC (permalink / raw)
  To: emacs-orgmode


[-- Attachment #1.1: Type: text/plain, Size: 478 bytes --]

Hi all,

I notice that in Org 8.3, `org-md-export-as-markdown` and
`org-md-export-to-markdown` render a document's Table of Contents and
Footnotes sections as HTML rather than Markdown.

I have a couple of patches that change this behavior so that both are
rendered as Markdown. I'd love to hear any thoughts or suggestions for
improvement if you think this would be useful to include in ox-md.el.

A description of the changes is attached in the accompanying Org doc.

Thanks!

[-- Attachment #1.2: Type: text/html, Size: 604 bytes --]

[-- Attachment #2: render-toc-and-fn-as-md.org --]
[-- Type: application/octet-stream, Size: 7772 bytes --]

* Summary of Changes
#+TITLE: Render TOC and Footnotes as Markdown
#+AUTHOR: Jake Romer
#+EMAIL: jkrmr@github.com
#+DATE: 2016-08-07


The included patch updates ox-md.el so that it generates Table of Contents and
Footnotes sections in Markdown. Currently it generates these in HTML.

* Render footnotes section as Markdown
An example footnotes section, in the Org source:

#+BEGIN_SRC org
 * Footnotes

 [fn:1] for example, when an issue has received no activity in over three months.
 (We can perhaps make that threshold configurable.)

 [fn:2] Another footnote
#+END_SRC

The exported Markdown before and after the patch is applied follows:

** Before
#+BEGIN_SRC html
<div id="footnotes">
<h2 class="footnotes">Footnotes: </h2>
<div id="text-footnotes">

<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1">1</a></sup> <div class="footpara">for example, when an issue has received no activity in over three months.
(We can perhaps make that threshold configurable.)</div></div>

<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2">2</a></sup> <div class="footpara">Another footnote</div></div>


</div>
</div>
#+END_SRC

** Patch
Previously, ~org-md-inner-template~ delegated to ~org-html-inner-template~ and
just modified how ~contents~ was parsed. Since my ultimate aim was to render
the entire document as Markdown and not just the main body, I departed from that
approach and transcoded each section (TOC, body, Footnotes) to Markdown strings,
using the approach taken by Lars Tveito in his [[https://github.com/larstvei/ox-gfm][ox-gfm]] exporter.

#+BEGIN_SRC diff
 (defun org-md-inner-template (contents info)
@@ -474,7 +536,15 @@ CONTENTS is the transcoded contents string.  INFO is a plist
 holding export options."
   ;; Make sure CONTENTS is separated from table of contents and
   ;; footnotes with at least a blank line.
-  (org-trim (org-html-inner-template (concat "\n" contents "\n") info)))
+  (let* ((depth (plist-get info :with-toc))
+         (headlines (and depth (org-export-collect-headlines info depth)))
+         (toc-string (org-html-toc depth info))
+         (toc-tail (if headlines "\n\n" ""))
+         (footnotes (org-md-footnote-section info)))
+    (org-trim (concat toc-string
+                      toc-tail
+                      contents
+                      footnotes))))
#+END_SRC

The key addition here is ~org-md-footnote-section~, which modifies
~org-html-footnote-section~ from [[http://orgmode.org/w/?p=org-mode.git;a=blob_plain;f=lisp/ox-html.el;hb=HEAD][ox-html.el]], removing unnecessary HTML tags and
invoking ~org-md-footnote-section-header~ (in the listing below) to generate the
section heading.

#+BEGIN_SRC emacs-lisp
;;;; Footnotes Section

(defun org-md-footnote-section (info)
  "Format the footnote section as Markdown.
INFO is a plist used as a communication channel."
  (let* ((fn-alist (org-export-collect-footnote-definitions info))
         (fn-alist
          (loop for (n type raw) in fn-alist collect
                (cons n (org-trim (org-export-data raw info))))))
    (when fn-alist
      (format
       (org-md-footnote-section-header info)
       (format
        "\n%s\n"
        (mapconcat
         (lambda (fn)
           (let ((n (car fn)) (def (cdr fn)))
             (format
              "%s %s\n"
              (format
               (plist-get info :md-footnote-format)
               (org-html--anchor
                (format "fn.%d" n)
                n
                (format " href=\"#fnr.%d\"" n)
                info))
              def)))
         fn-alist
         "\n"))))))
#+END_SRC

#+BEGIN_SRC emacs-lisp
;;;; Footnotes Section Header

(defun org-md-footnote-section-header (info)
  "Renders a template for the footnotes section header in the preferred style.
INFO is used as a communication channel."
  (let ((style (plist-get info :md-headline-style))
        (section-title (plist-get info :md-footnote-section-title)))
    (cond
     ((equal style 'atx) (format "\n%s %s\n%%s\n" "##" section-title))
     ((equal style 'setext) (format "\n%s\n%s\n%%s\n"
                                    section-title
                                    (make-string (length section-title) ?-))))))
#+END_SRC

Note that the footnote section title and the footnote format are both
user-customizable, and the footnote section header is rendered in the preferred
style (atx or setext).

#+BEGIN_SRC emacs-lisp
(defcustom org-md-footnote-format "<sup>%s</sup>"
  "The format for the footnote reference.
%s will be replaced by the footnote reference itself."
  :group 'org-export-md
  :type 'string)

(defcustom org-md-footnote-section-title "Footnotes"
  "The title for the Footnotes section.
Example: `Footnotes', `References', `Sources', etc."
  :group 'org-export-md
  :type 'string)
#+END_SRC

** After
#+BEGIN_SRC markdown
## Footnotes

<sup><a id="fn.1" href="#fnr.1">1</a></sup> for example, when an issue has received no activity in over three months.
(We can perhaps make that threshold configurable.)

<sup><a id="fn.2" href="#fnr.2">2</a></sup> Another footnote
#+END_SRC

* Render table of contents as Markdown
An example table of contents, in the Org source:

#+BEGIN_SRC org
 * Feature: Issue Reaping
 * Motivation
 * Scenarios
 * Tasks
 * Prior art
 ** Org settings panel left nav
 ** Viewing an organization's Member privileges
 ** Updating an organization's Repository creation setting
 * TODO Implementation notes
#+END_SRC

The exported Markdown before and after the patch is applied follows:

** Before
#+BEGIN_SRC html
<div id="table-of-contents">
<h2>Table of Contents</h2>
<div id="text-table-of-contents">
<ul>
<li><a href="#orgheadline1">1. Feature: Issue Reaping</a></li>
<li><a href="#orgheadline2">2. Motivation</a></li>
<li><a href="#orgheadline3">3. Scenarios</a></li>
<li><a href="#orgheadline4">4. Tasks</a></li>
<li><a href="#orgheadline8">5. Prior art</a>
<ul>
<li><a href="#orgheadline5">5.1. Org settings panel left nav</a></li>
<li><a href="#orgheadline6">5.2. Viewing an organization's Member privileges</a></li>
<li><a href="#orgheadline7">5.3. Updating an organization's Repository creation setting</a></li>
</ul>
</li>
<li><a href="#orgheadline9">6. <span class="todo TODO">TODO</span> Implementation notes</a></li>
</ul>
</div>
</div>
#+END_SRC

** Patch
In ~org-md-inner-template~, we replace ~org-html-toc~ with a string generated by
applying ~org-md-format-toc~ (credit to [[https://github.com/larstvei/ox-gfm/blob/master/ox-gfm.el#L233-L242][Lars Tveito]] again) across the set of
~headlines~.

#+BEGIN_SRC diff
-         (toc-string (org-html-toc depth info))
+         (toc-string (or (mapconcat 'org-md-format-toc headlines "\n") ""))
          (toc-tail (if headlines "\n\n" ""))
#+END_SRC

#+BEGIN_SRC emacs-lisp
;;;; Table of contents

(defun org-md-format-toc (headline)
  "Return an appropriate table of contents entry for HEADLINE.
INFO is a plist used as a communication channel."
  (let* ((title (org-export-data (org-export-get-alt-title headline info) info))
         (level (1- (org-element-property :level headline)))
         (indent (concat (make-string (* level 2) ? )))
         (anchor (or (org-element-property :CUSTOM_ID headline)
                     (org-export-get-reference headline info))))
    (concat indent "- [" title "]" "(#" anchor ")")))
#+END_SRC

** After
#+BEGIN_SRC markdown
- [Feature: Issue Reaping](#orgheadline1)
- [Motivation](#orgheadline2)
- [Scenarios](#orgheadline3)
- [Tasks](#orgheadline4)
- [Prior art](#orgheadline8)
  - [Org settings panel left nav](#orgheadline5)
  - [Viewing an organization's Member privileges](#orgheadline6)
  - [Updating an organization's Repository creation setting](#orgheadline7)
- [Implementation notes](#orgheadline9)
#+END_SRC

[-- Attachment #3: render-toc-and-fn-as-md.patch --]
[-- Type: application/octet-stream, Size: 5562 bytes --]

From b64d21e6b5bb35b6446abf37233463e40df040c3 Mon Sep 17 00:00:00 2001
From: Jake Romer <jkrmr@github.com>
Date: Sun, 7 Aug 2016 16:04:39 -0400
Subject: [PATCH 1/2] Export Footnotes section as Markdown

---
 ox-md.el | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 72 insertions(+), 2 deletions(-)

diff --git a/ox-md.el b/ox-md.el
index 0aaade6..865e123 100644
--- a/ox-md.el
+++ b/ox-md.el
@@ -50,6 +50,20 @@ This variable can be set to either `atx' or `setext'."
 	  (const :tag "Use \"atx\" style" atx)
 	  (const :tag "Use \"Setext\" style" setext)))

+;;;; Footnotes
+
+(defcustom org-md-footnote-format "<sup>%s</sup>"
+  "The format for the footnote reference.
+%s will be replaced by the footnote reference itself."
+  :group 'org-export-md
+  :type 'string)
+
+(defcustom org-md-footnote-section-title "Footnotes"
+  "The title for the Footnotes section.
+Example: `Footnotes', `References', `Sources', etc."
+  :group 'org-export-md
+  :type 'string)
+

 \f
 ;;; Define Back-End
@@ -90,7 +104,10 @@ This variable can be set to either `atx' or `setext'."
 		     (src-block . org-md-example-block)
 		     (template . org-md-template)
 		     (verbatim . org-md-verbatim))
-  :options-alist '((:md-headline-style nil nil org-md-headline-style)))
+  :options-alist
+  '((:md-headline-style nil nil org-md-headline-style)
+    (:md-footnote-format nil nil org-md-footnote-format)
+    (:md-footnote-section-title nil nil org-md-footnote-section-title)))

 \f
 ;;; Filters
@@ -466,6 +483,51 @@ a communication channel."
   contents)


+;;;; Footnotes Section Header
+
+(defun org-md-footnote-section-header (info)
+  "Renders a template for the footnotes section header in the preferred style.
+INFO is used as a communication channel."
+  (let ((style (plist-get info :md-headline-style))
+        (section-title (plist-get info :md-footnote-section-title)))
+    (cond
+     ((equal style 'atx) (format "\n%s %s\n%%s\n" "##" section-title))
+     ((equal style 'setext) (format "\n%s\n%s\n%%s\n"
+                                    section-title
+                                    (make-string (length section-title) ?-))))))
+
+
+;;;; Footnotes Section
+
+(defun org-md-footnote-section (info)
+  "Format the footnote section as Markdown.
+INFO is a plist used as a communication channel."
+  (let* ((fn-alist (org-export-collect-footnote-definitions info))
+         (fn-alist
+          (loop for (n type raw) in fn-alist collect
+                (cons n (org-trim (org-export-data raw info))))))
+    (when fn-alist
+      (format
+       (org-md-footnote-section-header info)
+       (format
+        "\n%s\n"
+        (mapconcat
+         (lambda (fn)
+           (let ((n (car fn)) (def (cdr fn)))
+             (format
+              "%s %s\n"
+              (format
+               (plist-get info :md-footnote-format)
+               (org-html--anchor
+                (format "fn.%d" n)
+                n
+                (format " href=\"#fnr.%d\"" n)
+                info))
+              def)))
+         fn-alist
+         "\n"))))))
+
+
 ;;;; Template

 (defun org-md-inner-template (contents info)
@@ -474,7 +536,15 @@ CONTENTS is the transcoded contents string.  INFO is a plist
 holding export options."
   ;; Make sure CONTENTS is separated from table of contents and
   ;; footnotes with at least a blank line.
-  (org-trim (org-html-inner-template (concat "\n" contents "\n") info)))
+  (let* ((depth (plist-get info :with-toc))
+         (headlines (and depth (org-export-collect-headlines info depth)))
+         (toc-string (org-html-toc depth info))
+         (toc-tail (if headlines "\n\n" ""))
+         (footnotes (org-md-footnote-section info)))
+    (org-trim (concat toc-string
+                      toc-tail
+                      contents
+                      footnotes))))

 (defun org-md-template (contents info)
   "Return complete document string after Markdown conversion.
--
2.9.2


From 31091e4bd4b48d1394482a1542e6d90abf04b32d Mon Sep 17 00:00:00 2001
From: Jake Romer <jkrmr@github.com>
Date: Sun, 7 Aug 2016 16:15:50 -0400
Subject: [PATCH 2/2] Export Table of Contents as Markdown

---
 ox-md.el | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/ox-md.el b/ox-md.el
index 865e123..0e2a499 100644
--- a/ox-md.el
+++ b/ox-md.el
@@ -528,6 +528,19 @@ INFO is a plist used as a communication channel."
          "\n"))))))


+;;;; Table of contents
+
+(defun org-md-format-toc (headline)
+  "Return an appropriate table of contents entry for HEADLINE.
+INFO is a plist used as a communication channel."
+  (let* ((title (org-export-data (org-export-get-alt-title headline info) info))
+         (level (1- (org-element-property :level headline)))
+         (indent (concat (make-string (* level 2) ? )))
+         (anchor (or (org-element-property :CUSTOM_ID headline)
+                     (org-export-get-reference headline info))))
+    (concat indent "- [" title "]" "(#" anchor ")")))
+
+
 ;;;; Template

 (defun org-md-inner-template (contents info)
@@ -538,7 +551,7 @@ holding export options."
   ;; footnotes with at least a blank line.
   (let* ((depth (plist-get info :with-toc))
          (headlines (and depth (org-export-collect-headlines info depth)))
-         (toc-string (org-html-toc depth info))
+         (toc-string (or (mapconcat 'org-md-format-toc headlines "\n") ""))
          (toc-tail (if headlines "\n\n" ""))
          (footnotes (org-md-footnote-section info)))
     (org-trim (concat toc-string
--
2.9.2

^ permalink raw reply related	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2016-09-01 16:45 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-08-08  1:31 ox-md.el: Export TOC and Footnotes as Markdown rather than HTML Jake Romer
2016-08-08 13:35 ` Nicolas Goaziou
2016-08-13 17:52   ` Jake Romer
2016-08-15 15:34     ` Nicolas Goaziou
2016-08-22  5:31       ` Jake Romer
2016-08-22  8:47         ` Nicolas Goaziou
2016-08-25 17:35           ` Jake Romer
2016-08-26 14:32             ` Nicolas Goaziou
2016-08-31  1:25               ` Jake Romer
2016-09-01 16:45                 ` Nicolas Goaziou

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).