summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Goaziou <mail@nicolasgoaziou.fr>2017-03-11 15:21:27 +0100
committerNicolas Goaziou <mail@nicolasgoaziou.fr>2017-03-11 15:21:27 +0100
commit2370a885e70b5fc658375c0951d36e7fc3342b56 (patch)
tree2664af448bd33407bdbc31f7654d4f4ea83804ed
parent2d1f73ebf8baeff5495651078d5d10dfc6bc1315 (diff)
downloadorg-mode-2370a885e70b5fc658375c0951d36e7fc3342b56.tar.gz
ox-md: Implement native table of contents
* lisp/ox-md.el (org-md--headline-referred-p): Try hard to limit anchors for headlines really referred to, either globally, or locally, in a table of contents. (org-md-keyword): Add support for "TOC" keyword. (org-md--build-toc): New function. (org-md-inner-template): Use new function.
-rw-r--r--etc/ORG-NEWS4
-rw-r--r--lisp/ox-md.el119
2 files changed, 110 insertions, 13 deletions
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index aa0c7dc..df99191 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -165,6 +165,10 @@ Where clue > 0
Added a nullary function that returns a list of files as a possible
argument for the scope of the clock table.
*** Export
+**** Implement vernacular table of contents in Markdown exporter
+Global table of contents are generated using vanilla Markdown syntax
+instead of HTML. Also #+TOC keyword, including local table of
+contents, are now supported.
**** Add Slovanian translations
**** Implement ~org-export-insert-image-links~
This new function is meant to be used in back-ends supporting images
diff --git a/lisp/ox-md.el b/lisp/ox-md.el
index c8ea1fa..e2b3d29 100644
--- a/lisp/ox-md.el
+++ b/lisp/ox-md.el
@@ -248,15 +248,42 @@ a communication channel."
"Non-nil when HEADLINE is being referred to.
INFO is a plist used as a communication channel. Links and table
of contents can refer to headlines."
- (or (plist-get info :with-toc)
- (org-element-map (plist-get info :parse-tree) 'link
- (lambda (link)
- (eq headline
- (pcase (org-element-property :type link)
- ((or "custom-id" "id") (org-export-resolve-id-link link info))
- ("fuzzy" (org-export-resolve-fuzzy-link link info))
- (_ nil))))
- info t)))
+ (unless (org-element-property :footnote-section-p headline)
+ (or
+ ;; Global table of contents includes HEADLINE.
+ (and (plist-get info :with-toc)
+ (memq headline
+ (org-export-collect-headlines info (plist-get info :with-toc))))
+ ;; A local table of contents includes HEADLINE.
+ (cl-some
+ (lambda (h)
+ (let ((section (car (org-element-contents h))))
+ (and
+ (eq 'section (org-element-type section))
+ (org-element-map section 'keyword
+ (lambda (keyword)
+ (when (equal "TOC" (org-element-property :key keyword))
+ (let ((case-fold-search t)
+ (value (org-element-property :value keyword)))
+ (and (string-match-p "\\<headlines\\>" value)
+ (let ((n (and
+ (string-match "\\<[0-9]+\\>" value)
+ (string-to-number (match-string 0 value))))
+ (local? (string-match-p "\\<local\\>" value)))
+ (memq headline
+ (org-export-collect-headlines
+ info n (and local? keyword))))))))
+ info t))))
+ (org-element-lineage headline))
+ ;; A link refers internally to HEADLINE.
+ (org-element-map (plist-get info :parse-tree) 'link
+ (lambda (link)
+ (eq headline
+ (pcase (org-element-property :type link)
+ ((or "custom-id" "id") (org-export-resolve-id-link link info))
+ ("fuzzy" (org-export-resolve-fuzzy-link link info))
+ (_ nil))))
+ info t))))
(defun org-md--headline-title (style level title &optional anchor tags)
"Generate a headline title in the preferred Markdown headline style.
@@ -328,9 +355,19 @@ a communication channel."
"Transcode a KEYWORD element into Markdown format.
CONTENTS is nil. INFO is a plist used as a communication
channel."
- (if (member (org-element-property :key keyword) '("MARKDOWN" "MD"))
- (org-element-property :value keyword)
- (org-export-with-backend 'html keyword contents info)))
+ (pcase (org-element-property :key keyword)
+ ((or "MARKDOWN" "MD") (org-element-property :value keyword))
+ ("TOC"
+ (let ((case-fold-search t)
+ (value (org-element-property :value keyword)))
+ (cond
+ ((string-match-p "\\<headlines\\>" value)
+ (let ((depth (and (string-match "\\<[0-9]+\\>" value)
+ (string-to-number (match-string 0 value))))
+ (local? (string-match-p "\\<local\\>" value)))
+ (org-remove-indentation
+ (org-md--build-toc info depth keyword local?)))))))
+ (_ (org-export-with-backend 'html keyword contents info))))
;;;; Line Break
@@ -512,6 +549,61 @@ a communication channel."
;;;; Template
+(defun org-md--build-toc (info &optional n keyword local)
+ "Return a table of contents.
+
+INFO is a plist used as a communication channel.
+
+Optional argument N, when non-nil, is an integer specifying the
+depth of the table.
+
+Optional argument KEYWORD specifies the TOC keyword, if any, from
+which the table of contents generation has been initiated.
+
+When optional argument LOCAL is non-nil, build a table of
+contents according to the current headline."
+ (concat
+ (unless local
+ (let ((style (plist-get info :md-headline-style))
+ (title (org-html--translate "Table of Contents" info)))
+ (org-md--headline-title style 1 title nil)))
+ (mapconcat
+ (lambda (headline)
+ (let* ((indentation
+ (make-string
+ (* 4 (1- (org-export-get-relative-level headline info)))
+ ?\s))
+ (number (format "%d."
+ (org-last
+ (org-export-get-headline-number headline info))))
+ (bullet (concat number (make-string (- 4 (length number)) ?\s)))
+ (title
+ (format "[%s](#%s)"
+ (org-export-data-with-backend
+ (org-export-get-alt-title headline info)
+ ;; Create an anonymous back-end that will
+ ;; ignore any footnote-reference, link,
+ ;; radio-target and target in table of
+ ;; contents.
+ (org-export-create-backend
+ :parent 'md
+ :transcoders '((footnote-reference . ignore)
+ (link . (lambda (object c i) c))
+ (radio-target . (lambda (object c i) c))
+ (target . ignore)))
+ info)
+ (or (org-element-property :CUSTOM_ID headline)
+ (org-export-get-reference headline info))))
+ (tags (and (plist-get info :with-tags)
+ (not (eq 'not-in-toc (plist-get info :with-tags)))
+ (let ((tags (org-export-get-tags headline info)))
+ (and tags
+ (format ":%s:"
+ (mapconcat #'identity tags ":")))))))
+ (concat indentation bullet title tags)))
+ (org-export-collect-headlines info n (and local keyword)) "\n")
+ "\n"))
+
(defun org-md--footnote-formatted (footnote info)
"Formats a single footnote entry FOOTNOTE.
FOOTNOTE is a cons cell of the form (number . definition).
@@ -548,7 +640,8 @@ holding export options."
(concat
;; Table of contents.
(let ((depth (plist-get info :with-toc)))
- (when depth (org-html-toc depth info)))
+ (when depth
+ (concat (org-md--build-toc info (and (wholenump depth) depth)) "\n")))
;; Document contents.
contents
"\n"