Changes
[emacs.git] / .emacs.d / elisp / org / ox-md.el
index f1c4db9..181ecb1 100644 (file)
@@ -1,6 +1,6 @@
 ;;; ox-md.el --- Markdown Back-End for Org Export Engine
 
-;; Copyright (C) 2012-2014 Free Software Foundation, Inc.
+;; Copyright (C) 2012-2015 Free Software Foundation, Inc.
 
 ;; Author: Nicolas Goaziou <n.goaziou@gmail.com>
 ;; Keywords: org, wp, markdown
@@ -30,7 +30,7 @@
 
 (eval-when-compile (require 'cl))
 (require 'ox-html)
-
+(require 'ox-publish)
 
 \f
 ;;; User-Configurable Variables
@@ -68,30 +68,29 @@ This variable can be set to either `atx' or `setext'."
                (org-open-file (org-md-export-to-markdown nil s v)))))))
   :translate-alist '((bold . org-md-bold)
                     (code . org-md-verbatim)
-                    (comment . (lambda (&rest args) ""))
-                    (comment-block . (lambda (&rest args) ""))
                     (example-block . org-md-example-block)
+                    (export-block . org-md-export-block)
                     (fixed-width . org-md-example-block)
-                    (footnote-definition . ignore)
-                    (footnote-reference . ignore)
                     (headline . org-md-headline)
                     (horizontal-rule . org-md-horizontal-rule)
                     (inline-src-block . org-md-verbatim)
                     (inner-template . org-md-inner-template)
                     (italic . org-md-italic)
                     (item . org-md-item)
+                    (keyword . org-md-keyword)
                     (line-break . org-md-line-break)
                     (link . org-md-link)
+                    (node-property . org-md-node-property)
                     (paragraph . org-md-paragraph)
                     (plain-list . org-md-plain-list)
                     (plain-text . org-md-plain-text)
+                    (property-drawer . org-md-property-drawer)
                     (quote-block . org-md-quote-block)
-                    (quote-section . org-md-example-block)
                     (section . org-md-section)
                     (src-block . org-md-example-block)
                     (template . org-md-template)
-                    (verbatim . org-md-verbatim)))
-
+                    (verbatim . org-md-verbatim))
+  :options-alist '((:md-headline-style nil nil org-md-headline-style)))
 
 \f
 ;;; Filters
@@ -102,19 +101,24 @@ This variable can be set to either `atx' or `setext'."
 TREE is the parse tree being exported.  BACKEND is the export
 back-end used.  INFO is a plist used as a communication channel.
 
-Make sure there's no blank line before a plain list, unless it is
-located right after a paragraph.  Otherwise, add a blank line
-between elements.  Blank lines between items are preserved.
+Enforce a blank line between elements.  There are two exceptions
+to this rule:
+
+  1. Preserve blank lines between sibling items in a plain list,
+
+  2. In an item, remove any blank line before the very first
+     paragraph and the next sub-list.
 
 Assume BACKEND is `md'."
   (org-element-map tree (remq 'item org-element-all-elements)
-    (lambda (elem)
+    (lambda (e)
       (org-element-put-property
-       elem :post-blank
-       (if (and (eq (org-element-type (org-export-get-next-element elem info))
+       e :post-blank
+       (if (and (eq (org-element-type e) 'paragraph)
+               (eq (org-element-type (org-element-property :parent e)) 'item)
+               (eq (org-element-type (org-export-get-next-element e info))
                    'plain-list)
-               (not (and (eq (org-element-type elem) 'paragraph)
-                         (org-export-get-previous-element elem info))))
+               (not (org-export-get-previous-element e info)))
           0
         1))))
   ;; Return updated tree.
@@ -148,7 +152,7 @@ channel."
            value)))
 
 
-;;;; Example Block and Src Block
+;;;; Example Block, Src Block and export Block
 
 (defun org-md-example-block (example-block contents info)
   "Transcode EXAMPLE-BLOCK element into Markdown format.
@@ -159,6 +163,14 @@ channel."
    (org-remove-indentation
     (org-export-format-code-default example-block info))))
 
+(defun org-md-export-block (export-block contents info)
+  "Transcode a EXPORT-BLOCK element from Org to Markdown.
+CONTENTS is nil.  INFO is a plist holding contextual information."
+  (if (member (org-element-property :type export-block) '("MARKDOWN" "MD"))
+      (org-remove-indentation (org-element-property :value export-block))
+    ;; Also include HTML export blocks.
+    (org-export-with-backend 'html export-block contents info)))
+
 
 ;;;; Headline
 
@@ -186,18 +198,17 @@ a communication channel."
            (when (plist-get info :with-toc)
              (org-html--anchor
               (or (org-element-property :CUSTOM_ID headline)
-                  (concat "sec-"
-                          (mapconcat 'number-to-string
-                                     (org-export-get-headline-number
-                                      headline info) "-"))))))
+                  (org-export-get-headline-id headline info))
+              nil nil info)))
           ;; Headline text without tags.
-          (heading (concat todo priority title)))
+          (heading (concat todo priority title))
+          (style (plist-get info :md-headline-style)))
       (cond
        ;; Cannot create a headline.  Fall-back to a list.
        ((or (org-export-low-level-p headline info)
-           (not (memq org-md-headline-style '(atx setext)))
-           (and (eq org-md-headline-style 'atx) (> level 6))
-           (and (eq org-md-headline-style 'setext) (> level 2)))
+           (not (memq style '(atx setext)))
+           (and (eq style 'atx) (> level 6))
+           (and (eq style 'setext) (> level 2)))
        (let ((bullet
               (if (not (org-export-numbered-headline-p headline info)) "-"
                 (concat (number-to-string
@@ -209,7 +220,7 @@ a communication channel."
                  (and contents
                       (replace-regexp-in-string "^" "    " contents)))))
        ;; Use "Setext" style.
-       ((eq org-md-headline-style 'setext)
+       ((eq style 'setext)
        (concat heading tags anchor "\n"
                (make-string (length heading) (if (= level 1) ?= ?-))
                "\n\n"
@@ -264,6 +275,18 @@ a communication channel."
                 (org-trim (replace-regexp-in-string "^" "    " contents))))))
 
 
+
+;;;; Keyword
+
+(defun org-md-keyword (keyword contents info)
+  "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)))
+
+
 ;;;; Line Break
 
 (defun org-md-line-break (line-break contents info)
@@ -287,57 +310,80 @@ a communication channel."
                (concat (file-name-sans-extension raw-path) ".md")
              raw-path))))
        (type (org-element-property :type link)))
-    (cond ((member type '("custom-id" "id"))
-          (let ((destination (org-export-resolve-id-link link info)))
-            (if (stringp destination)  ; External file.
-                (let ((path (funcall link-org-files-as-md destination)))
-                  (if (not contents) (format "<%s>" path)
-                    (format "[%s](%s)" contents path)))
-              (concat
-               (and contents (concat contents " "))
-               (format "(%s)"
-                       (format (org-export-translate "See section %s" :html info)
-                               (mapconcat 'number-to-string
-                                          (org-export-get-headline-number
-                                           destination info)
-                                          ".")))))))
-         ((org-export-inline-image-p link org-html-inline-image-rules)
-          (let ((path (let ((raw-path (org-element-property :path link)))
-                        (if (not (file-name-absolute-p raw-path)) raw-path
-                          (expand-file-name raw-path))))
-                (caption (org-export-data
-                          (org-export-get-caption
-                           (org-export-get-parent-element link)) info)))
-            (format "![img](%s)"
-                    (if (not (org-string-nw-p caption)) path
-                      (format "%s \"%s\"" path caption)))))
-         ((string= type "coderef")
-          (let ((ref (org-element-property :path link)))
-            (format (org-export-get-coderef-format ref contents)
-                    (org-export-resolve-coderef ref info))))
-         ((equal type "radio") contents)
-         ((equal type "fuzzy")
-          (let ((destination (org-export-resolve-fuzzy-link link info)))
-            (if (org-string-nw-p contents) contents
-              (when destination
-                (let ((number (org-export-get-ordinal destination info)))
-                  (when number
-                    (if (atom number) (number-to-string number)
-                      (mapconcat 'number-to-string number "."))))))))
-         (t (let* ((raw-path (org-element-property :path link))
-                   (path
-                    (cond
-                     ((member type '("http" "https" "ftp"))
-                      (concat type ":" raw-path))
-                     ((string= type "file")
-                      (let ((path (funcall link-org-files-as-md raw-path)))
-                        (if (not (file-name-absolute-p path)) path
-                          ;; If file path is absolute, prepend it
-                          ;; with "file:" component.
-                          (concat "file:" path))))
-                     (t raw-path))))
-              (if (not contents) (format "<%s>" path)
-                (format "[%s](%s)" contents path)))))))
+    (cond
+     ;; Link type is handled by a special function.
+     ((org-export-custom-protocol-maybe link contents 'md))
+     ((member type '("custom-id" "id"))
+      (let ((destination (org-export-resolve-id-link link info)))
+       (if (stringp destination)       ; External file.
+           (let ((path (funcall link-org-files-as-md destination)))
+             (if (not contents) (format "<%s>" path)
+               (format "[%s](%s)" contents path)))
+         (concat
+          (and contents (concat contents " "))
+          (format "(%s)"
+                  (format (org-export-translate "See section %s" :html info)
+                          (if (org-export-numbered-headline-p destination info)
+                              (mapconcat #'number-to-string
+                                         (org-export-get-headline-number
+                                          destination info)
+                                         ".")
+                            (org-export-data
+                             (org-element-property :title destination) info))))))))
+     ((org-export-inline-image-p link org-html-inline-image-rules)
+      (let ((path (let ((raw-path (org-element-property :path link)))
+                   (if (not (file-name-absolute-p raw-path)) raw-path
+                     (expand-file-name raw-path))))
+           (caption (org-export-data
+                     (org-export-get-caption
+                      (org-export-get-parent-element link)) info)))
+       (format "![img](%s)"
+               (if (not (org-string-nw-p caption)) path
+                 (format "%s \"%s\"" path caption)))))
+     ((string= type "coderef")
+      (let ((ref (org-element-property :path link)))
+       (format (org-export-get-coderef-format ref contents)
+               (org-export-resolve-coderef ref info))))
+     ((equal type "radio") contents)
+     ((equal type "fuzzy")
+      (let ((destination (org-export-resolve-fuzzy-link link info)))
+       (if (org-string-nw-p contents) contents
+         (when destination
+           (let ((number (org-export-get-ordinal destination info)))
+             (if number
+                 (if (atom number) (number-to-string number)
+                   (mapconcat #'number-to-string number "."))
+               ;; Unnumbered headline.
+               (and (eq 'headline (org-element-type destination))
+                    ;; BUG: shouldn't headlines have a form like [ref](name) in md?
+                    (org-export-data
+                     (org-element-property :title destination) info))))))))
+     (t (let* ((raw-path (org-element-property :path link))
+              (path
+               (cond
+                ((member type '("http" "https" "ftp"))
+                 (concat type ":" raw-path))
+                ((string= type "file")
+                 (let ((path (funcall link-org-files-as-md raw-path)))
+                   (if (not (file-name-absolute-p path)) path
+                     ;; If file path is absolute, prepend it
+                     ;; with "file:" component.
+                     (concat "file:" path))))
+                (t raw-path))))
+         (if (not contents) (format "<%s>" path)
+           (format "[%s](%s)" contents path)))))))
+
+
+;;;; Node Property
+
+(defun org-md-node-property (node-property contents info)
+  "Transcode a NODE-PROPERTY element into Markdown syntax.
+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
@@ -388,6 +434,16 @@ contextual information."
   text)
 
 
+;;;; Property Drawer
+
+(defun org-md-property-drawer (property-drawer contents info)
+  "Transcode a PROPERTY-DRAWER element into Markdown format.
+CONTENTS holds the contents of the drawer.  INFO is a plist
+holding contextual information."
+  (and (org-string-nw-p contents)
+       (replace-regexp-in-string "^" "    " contents)))
+
+
 ;;;; Quote Block
 
 (defun org-md-quote-block (quote-block contents info)
@@ -490,6 +546,16 @@ Return output file's name."
   (let ((outfile (org-export-output-file-name ".md" subtreep)))
     (org-export-to-file 'md outfile async subtreep visible-only)))
 
+;;;###autoload
+(defun org-md-publish-to-md (plist filename pub-dir)
+  "Publish an org file to Markdown.
+
+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 'md filename ".md" plist pub-dir))
 
 (provide 'ox-md)