dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

ox-md.el (28406B)


      1 ;;; ox-md.el --- Markdown Back-End for Org Export Engine -*- lexical-binding: t; -*-
      2 
      3 ;; Copyright (C) 2012-2023 Free Software Foundation, Inc.
      4 
      5 ;; Author: Nicolas Goaziou <n.goaziou@gmail.com>
      6 ;; Maintainer: Nicolas Goaziou <mail@nicolasgoaziou.fr>
      7 ;; Keywords: org, wp, markdown
      8 
      9 ;; This file is part of GNU Emacs.
     10 
     11 ;; GNU Emacs is free software: you can redistribute it and/or modify
     12 ;; it under the terms of the GNU General Public License as published by
     13 ;; the Free Software Foundation, either version 3 of the License, or
     14 ;; (at your option) any later version.
     15 
     16 ;; GNU Emacs is distributed in the hope that it will be useful,
     17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     19 ;; GNU General Public License for more details.
     20 
     21 ;; You should have received a copy of the GNU General Public License
     22 ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
     23 
     24 ;;; Commentary:
     25 
     26 ;; This library implements a Markdown back-end (vanilla flavor) for
     27 ;; Org exporter, based on `html' back-end.  See Org manual for more
     28 ;; information.
     29 
     30 ;;; Code:
     31 
     32 (require 'org-macs)
     33 (org-assert-version)
     34 
     35 (require 'cl-lib)
     36 (require 'ox-html)
     37 (require 'ox-publish)
     38 
     39 
     40 ;;; User-Configurable Variables
     41 
     42 (defgroup org-export-md nil
     43   "Options specific to Markdown export back-end."
     44   :tag "Org Markdown"
     45   :group 'org-export
     46   :version "24.4"
     47   :package-version '(Org . "8.0"))
     48 
     49 (defcustom org-md-headline-style 'atx
     50   "Style used to format headlines.
     51 This variable can be set to either `atx' or `setext'."
     52   :group 'org-export-md
     53   :type '(choice
     54 	  (const :tag "Use \"atx\" style" atx)
     55 	  (const :tag "Use \"Setext\" style" setext)))
     56 
     57 
     58 ;;;; Footnotes
     59 
     60 (defcustom org-md-footnotes-section "%s%s"
     61   "Format string for the footnotes section.
     62 The first %s placeholder will be replaced with the localized Footnotes section
     63 heading, the second with the contents of the Footnotes section."
     64   :group 'org-export-md
     65   :type 'string
     66   :version "26.1"
     67   :package-version '(Org . "9.0"))
     68 
     69 (defcustom org-md-footnote-format "<sup>%s</sup>"
     70   "Format string for the footnote reference.
     71 The %s will be replaced by the footnote reference itself."
     72   :group 'org-export-md
     73   :type 'string
     74   :version "26.1"
     75   :package-version '(Org . "9.0"))
     76 
     77 (defcustom org-md-toplevel-hlevel 1
     78   "Heading level to use for level 1 Org headings in markdown export.
     79 
     80 If this is 1, headline levels will be preserved on export.  If this is
     81 2, top level Org headings will be exported to level 2 markdown
     82 headings, level 2 Org headings will be exported to level 3 markdown
     83 headings, and so on.
     84 
     85 Incrementing this value may be helpful when creating markdown to be
     86 included into another document or application that reserves top-level
     87 headings for its own use."
     88   :group 'org-export-md
     89   :package-version '(Org . "9.6")
     90   ;; Avoid `natnum' because that's not available until Emacs 28.1.
     91   :type 'integer)
     92 
     93 
     94 
     95 ;;; Define Back-End
     96 
     97 (org-export-define-derived-backend 'md 'html
     98   :filters-alist '((:filter-parse-tree . org-md-separate-elements))
     99   :menu-entry
    100   '(?m "Export to Markdown"
    101        ((?M "To temporary buffer"
    102 	    (lambda (a s v b) (org-md-export-as-markdown a s v)))
    103 	(?m "To file" (lambda (a s v b) (org-md-export-to-markdown a s v)))
    104 	(?o "To file and open"
    105 	    (lambda (a s v b)
    106 	      (if a (org-md-export-to-markdown t s v)
    107 		(org-open-file (org-md-export-to-markdown nil s v)))))))
    108   :translate-alist '((bold . org-md-bold)
    109 		     (center-block . org-md--convert-to-html)
    110 		     (code . org-md-verbatim)
    111 		     (drawer . org-md--identity)
    112 		     (dynamic-block . org-md--identity)
    113 		     (example-block . org-md-example-block)
    114 		     (export-block . org-md-export-block)
    115 		     (fixed-width . org-md-example-block)
    116 		     (headline . org-md-headline)
    117 		     (horizontal-rule . org-md-horizontal-rule)
    118 		     (inline-src-block . org-md-verbatim)
    119 		     (inlinetask . org-md--convert-to-html)
    120 		     (inner-template . org-md-inner-template)
    121 		     (italic . org-md-italic)
    122 		     (item . org-md-item)
    123 		     (keyword . org-md-keyword)
    124                      (latex-environment . org-md-latex-environment)
    125                      (latex-fragment . org-md-latex-fragment)
    126 		     (line-break . org-md-line-break)
    127 		     (link . org-md-link)
    128 		     (node-property . org-md-node-property)
    129 		     (paragraph . org-md-paragraph)
    130 		     (plain-list . org-md-plain-list)
    131 		     (plain-text . org-md-plain-text)
    132 		     (property-drawer . org-md-property-drawer)
    133 		     (quote-block . org-md-quote-block)
    134 		     (section . org-md-section)
    135 		     (special-block . org-md--convert-to-html)
    136 		     (src-block . org-md-example-block)
    137 		     (table . org-md--convert-to-html)
    138 		     (template . org-md-template)
    139 		     (verbatim . org-md-verbatim))
    140   :options-alist
    141   '((:md-footnote-format nil nil org-md-footnote-format)
    142     (:md-footnotes-section nil nil org-md-footnotes-section)
    143     (:md-headline-style nil nil org-md-headline-style)
    144     (:md-toplevel-hlevel nil nil org-md-toplevel-hlevel)))
    145 
    146 
    147 ;;; Filters
    148 
    149 (defun org-md-separate-elements (tree _backend info)
    150   "Fix blank lines between elements.
    151 
    152 TREE is the parse tree being exported.  BACKEND is the export
    153 back-end used.  INFO is a plist used as a communication channel.
    154 
    155 Enforce a blank line between elements.  There are two exceptions
    156 to this rule:
    157 
    158   1. Preserve blank lines between sibling items in a plain list,
    159 
    160   2. In an item, remove any blank line before the very first
    161      paragraph and the next sub-list when the latter ends the
    162      current item.
    163 
    164 Assume BACKEND is `md'."
    165   (org-element-map tree (remq 'item org-element-all-elements)
    166     (lambda (e)
    167       (org-element-put-property
    168        e :post-blank
    169        (if (and (eq (org-element-type e) 'paragraph)
    170 		(eq (org-element-type (org-element-property :parent e)) 'item)
    171 		(org-export-first-sibling-p e info)
    172 		(let ((next (org-export-get-next-element e info)))
    173 		  (and (eq (org-element-type next) 'plain-list)
    174 		       (not (org-export-get-next-element next info)))))
    175 	   0
    176 	 1))))
    177   ;; Return updated tree.
    178   tree)
    179 
    180 
    181 ;;; Internal functions
    182 
    183 (defun org-md--headline-referred-p (headline info)
    184   "Non-nil when HEADLINE is being referred to.
    185 INFO is a plist used as a communication channel.  Links and table
    186 of contents can refer to headlines."
    187   (unless (org-element-property :footnote-section-p headline)
    188     (or
    189      ;; Global table of contents includes HEADLINE.
    190      (and (plist-get info :with-toc)
    191 	  (memq headline
    192 		(org-export-collect-headlines info (plist-get info :with-toc))))
    193      ;; A local table of contents includes HEADLINE.
    194      (cl-some
    195       (lambda (h)
    196 	(let ((section (car (org-element-contents h))))
    197 	  (and
    198 	   (eq 'section (org-element-type section))
    199 	   (org-element-map section 'keyword
    200 	     (lambda (keyword)
    201 	       (when (equal "TOC" (org-element-property :key keyword))
    202 		 (let ((case-fold-search t)
    203 		       (value (org-element-property :value keyword)))
    204 		   (and (string-match-p "\\<headlines\\>" value)
    205 			(let ((n (and
    206 				  (string-match "\\<[0-9]+\\>" value)
    207 				  (string-to-number (match-string 0 value))))
    208 			      (local? (string-match-p "\\<local\\>" value)))
    209 			  (memq headline
    210 				(org-export-collect-headlines
    211 				 info n (and local? keyword))))))))
    212 	     info t))))
    213       (org-element-lineage headline))
    214      ;; A link refers internally to HEADLINE.
    215      (org-element-map (plist-get info :parse-tree) 'link
    216        (lambda (link)
    217 	 (equal headline
    218                 ;; Ignore broken links.
    219                 (condition-case nil
    220                     (org-export-resolve-id-link link info)
    221                   (org-link-broken nil))))
    222        info t))))
    223 
    224 (defun org-md--headline-title (style level title &optional anchor tags)
    225   "Generate a headline title in the preferred Markdown headline style.
    226 STYLE is the preferred style (`atx' or `setext').  LEVEL is the
    227 header level.  TITLE is the headline title.  ANCHOR is the HTML
    228 anchor tag for the section as a string.  TAGS are the tags set on
    229 the section."
    230   (let ((anchor-lines (and anchor (concat anchor "\n\n"))))
    231     ;; Use "Setext" style
    232     (if (and (eq style 'setext) (< level 3))
    233         (let* ((underline-char (if (= level 1) ?= ?-))
    234                (underline (concat (make-string (length title) underline-char)
    235 				  "\n")))
    236           (concat "\n" anchor-lines title tags "\n" underline "\n"))
    237       ;; Use "Atx" style
    238       (let ((level-mark (make-string level ?#)))
    239         (concat "\n" anchor-lines level-mark " " title tags "\n\n")))))
    240 
    241 (defun org-md--build-toc (info &optional n _keyword scope)
    242   "Return a table of contents.
    243 
    244 INFO is a plist used as a communication channel.
    245 
    246 Optional argument N, when non-nil, is an integer specifying the
    247 depth of the table.
    248 
    249 When optional argument SCOPE is non-nil, build a table of
    250 contents according to the specified element."
    251   (concat
    252    (unless scope
    253      (let ((level (plist-get info :md-toplevel-hlevel))
    254            (style (plist-get info :md-headline-style))
    255 	   (title (org-html--translate "Table of Contents" info)))
    256        (org-md--headline-title style level title nil)))
    257    (mapconcat
    258     (lambda (headline)
    259       (let* ((indentation
    260 	      (make-string
    261 	       (* 4 (1- (org-export-get-relative-level headline info)))
    262 	       ?\s))
    263 	     (bullet
    264 	      (if (not (org-export-numbered-headline-p headline info)) "-   "
    265 		(let ((prefix
    266 		       (format "%d." (org-last (org-export-get-headline-number
    267 						headline info)))))
    268 		  (concat prefix (make-string (max 1 (- 4 (length prefix)))
    269 					      ?\s)))))
    270 	     (title
    271 	      (format "[%s](#%s)"
    272 		      (org-export-data-with-backend
    273 		       (org-export-get-alt-title headline info)
    274 		       (org-export-toc-entry-backend 'md)
    275 		       info)
    276 		      (or (org-element-property :CUSTOM_ID headline)
    277 			  (org-export-get-reference headline info))))
    278 	     (tags (and (plist-get info :with-tags)
    279 			(not (eq 'not-in-toc (plist-get info :with-tags)))
    280 			(org-make-tag-string
    281 			 (org-export-get-tags headline info)))))
    282 	(concat indentation bullet title tags)))
    283     (org-export-collect-headlines info n scope) "\n")
    284    "\n"))
    285 
    286 (defun org-md--footnote-formatted (footnote info)
    287   "Formats a single footnote entry FOOTNOTE.
    288 FOOTNOTE is a cons cell of the form (number . definition).
    289 INFO is a plist with contextual information."
    290   (let* ((fn-num (car footnote))
    291          (fn-text (cdr footnote))
    292          (fn-format (plist-get info :md-footnote-format))
    293          (fn-anchor (format "fn.%d" fn-num))
    294          (fn-href (format " href=\"#fnr.%d\"" fn-num))
    295          (fn-link-to-ref (org-html--anchor fn-anchor fn-num fn-href info)))
    296     (concat (format fn-format fn-link-to-ref) " " fn-text "\n")))
    297 
    298 (defun org-md--footnote-section (info)
    299   "Format the footnote section.
    300 INFO is a plist used as a communication channel."
    301   (let* ((fn-alist (org-export-collect-footnote-definitions info))
    302          (fn-alist (cl-loop for (n _type raw) in fn-alist collect
    303                             (cons n (org-trim (org-export-data raw info)))))
    304          (headline-style (plist-get info :md-headline-style))
    305          (section-title (org-html--translate "Footnotes" info)))
    306     (when fn-alist
    307       (format (plist-get info :md-footnotes-section)
    308               (org-md--headline-title headline-style 1 section-title)
    309               (mapconcat (lambda (fn) (org-md--footnote-formatted fn info))
    310                          fn-alist
    311                          "\n")))))
    312 
    313 (defun org-md--convert-to-html (datum _contents info)
    314   "Convert DATUM into raw HTML, including contents."
    315   (org-export-data-with-backend datum 'html info))
    316 
    317 (defun org-md--identity (_datum contents _info)
    318   "Return CONTENTS only."
    319   contents)
    320 
    321 
    322 ;;; Transcode Functions
    323 
    324 ;;;; Bold
    325 
    326 (defun org-md-bold (_bold contents _info)
    327   "Transcode BOLD object into Markdown format.
    328 CONTENTS is the text within bold markup.  INFO is a plist used as
    329 a communication channel."
    330   (format "**%s**" contents))
    331 
    332 
    333 ;;;; Code and Verbatim
    334 
    335 (defun org-md-verbatim (verbatim _contents _info)
    336   "Transcode VERBATIM object into Markdown format.
    337 CONTENTS is nil.  INFO is a plist used as a communication
    338 channel."
    339   (let ((value (org-element-property :value verbatim)))
    340     (format (cond ((not (string-match "`" value)) "`%s`")
    341 		  ((or (string-prefix-p "`" value)
    342 		       (string-suffix-p "`" value))
    343 		   "`` %s ``")
    344 		  (t "``%s``"))
    345 	    value)))
    346 
    347 
    348 ;;;; Example Block, Src Block and Export Block
    349 
    350 (defun org-md-example-block (example-block _contents info)
    351   "Transcode EXAMPLE-BLOCK element into Markdown format.
    352 CONTENTS is nil.  INFO is a plist used as a communication
    353 channel."
    354   (replace-regexp-in-string
    355    "^" "    "
    356    (org-remove-indentation
    357     (org-export-format-code-default example-block info))))
    358 
    359 (defun org-md-export-block (export-block contents info)
    360   "Transcode a EXPORT-BLOCK element from Org to Markdown.
    361 CONTENTS is nil.  INFO is a plist holding contextual information."
    362   (if (member (org-element-property :type export-block) '("MARKDOWN" "MD"))
    363       (org-remove-indentation (org-element-property :value export-block))
    364     ;; Also include HTML export blocks.
    365     (org-export-with-backend 'html export-block contents info)))
    366 
    367 
    368 ;;;; Headline
    369 
    370 (defun org-md-headline (headline contents info)
    371   "Transcode HEADLINE element into Markdown format.
    372 CONTENTS is the headline contents.  INFO is a plist used as
    373 a communication channel."
    374   (unless (org-element-property :footnote-section-p headline)
    375     (let* ((level (+ (org-export-get-relative-level headline info)
    376                      (1- (plist-get info :md-toplevel-hlevel))))
    377 	   (title (org-export-data (org-element-property :title headline) info))
    378 	   (todo (and (plist-get info :with-todo-keywords)
    379 		      (let ((todo (org-element-property :todo-keyword
    380 							headline)))
    381 			(and todo (concat (org-export-data todo info) " ")))))
    382 	   (tags (and (plist-get info :with-tags)
    383 		      (let ((tag-list (org-export-get-tags headline info)))
    384 			(and tag-list
    385 			     (concat "     " (org-make-tag-string tag-list))))))
    386 	   (priority
    387 	    (and (plist-get info :with-priority)
    388 		 (let ((char (org-element-property :priority headline)))
    389 		   (and char (format "[#%c] " char)))))
    390 	   ;; Headline text without tags.
    391 	   (heading (concat todo priority title))
    392 	   (style (plist-get info :md-headline-style)))
    393       (cond
    394        ;; Cannot create a headline.  Fall-back to a list.
    395        ((or (org-export-low-level-p headline info)
    396 	    (not (memq style '(atx setext)))
    397 	    (and (eq style 'atx) (> level 6))
    398 	    (and (eq style 'setext) (> level 2)))
    399 	(let ((bullet
    400 	       (if (not (org-export-numbered-headline-p headline info)) "-"
    401 		 (concat (number-to-string
    402 			  (car (last (org-export-get-headline-number
    403 				      headline info))))
    404 			 "."))))
    405 	  (concat bullet (make-string (- 4 (length bullet)) ?\s) heading tags "\n\n"
    406 		  (and contents (replace-regexp-in-string "^" "    " contents)))))
    407        (t
    408 	(let ((anchor
    409 	       (and (org-md--headline-referred-p headline info)
    410 		    (format "<a id=\"%s\"></a>"
    411 			    (or (org-element-property :CUSTOM_ID headline)
    412 				(org-export-get-reference headline info))))))
    413 	  (concat (org-md--headline-title style level heading anchor tags)
    414 		  contents)))))))
    415 
    416 ;;;; Horizontal Rule
    417 
    418 (defun org-md-horizontal-rule (_horizontal-rule _contents _info)
    419   "Transcode HORIZONTAL-RULE element into Markdown format.
    420 CONTENTS is the horizontal rule contents.  INFO is a plist used
    421 as a communication channel."
    422   "---")
    423 
    424 
    425 ;;;; Italic
    426 
    427 (defun org-md-italic (_italic contents _info)
    428   "Transcode ITALIC object into Markdown format.
    429 CONTENTS is the text within italic markup.  INFO is a plist used
    430 as a communication channel."
    431   (format "*%s*" contents))
    432 
    433 
    434 ;;;; Item
    435 
    436 (defun org-md-item (item contents info)
    437   "Transcode ITEM element into Markdown format.
    438 CONTENTS is the item contents.  INFO is a plist used as
    439 a communication channel."
    440   (let* ((type (org-element-property :type (org-export-get-parent item)))
    441 	 (struct (org-element-property :structure item))
    442 	 (bullet (if (not (eq type 'ordered)) "-"
    443 		   (concat (number-to-string
    444 			    (car (last (org-list-get-item-number
    445 					(org-element-property :begin item)
    446 					struct
    447 					(org-list-prevs-alist struct)
    448 					(org-list-parents-alist struct)))))
    449 			   "."))))
    450     (concat bullet
    451 	    (make-string (- 4 (length bullet)) ? )
    452 	    (pcase (org-element-property :checkbox item)
    453 	      (`on "[X] ")
    454 	      (`trans "[-] ")
    455 	      (`off "[ ] "))
    456 	    (let ((tag (org-element-property :tag item)))
    457 	      (and tag (format "**%s:** "(org-export-data tag info))))
    458 	    (and contents
    459 		 (org-trim (replace-regexp-in-string "^" "    " contents))))))
    460 
    461 
    462 
    463 ;;;; Keyword
    464 
    465 (defun org-md-keyword (keyword contents info)
    466   "Transcode a KEYWORD element into Markdown format.
    467 CONTENTS is nil.  INFO is a plist used as a communication
    468 channel."
    469   (pcase (org-element-property :key keyword)
    470     ((or "MARKDOWN" "MD") (org-element-property :value keyword))
    471     ("TOC"
    472      (let ((case-fold-search t)
    473 	   (value (org-element-property :value keyword)))
    474        (cond
    475 	((string-match-p "\\<headlines\\>" value)
    476 	 (let ((depth (and (string-match "\\<[0-9]+\\>" value)
    477 			   (string-to-number (match-string 0 value))))
    478 	       (scope
    479 		(cond
    480 		 ((string-match ":target +\\(\".+?\"\\|\\S-+\\)" value) ;link
    481 		  (org-export-resolve-link
    482 		   (org-strip-quotes (match-string 1 value)) info))
    483 		 ((string-match-p "\\<local\\>" value) keyword)))) ;local
    484 	   (org-remove-indentation
    485 	    (org-md--build-toc info depth keyword scope)))))))
    486     (_ (org-export-with-backend 'html keyword contents info))))
    487 
    488 
    489 ;;;; Latex Environment
    490 
    491 (defun org-md-latex-environment (latex-environment _contents info)
    492   "Transcode a LATEX-ENVIRONMENT object from Org to Markdown.
    493 CONTENTS is nil.  INFO is a plist holding contextual information."
    494   (when (plist-get info :with-latex)
    495     (let ((latex-frag (org-remove-indentation
    496                        (org-element-property :value latex-environment)))
    497           (label (org-html--reference latex-environment info t)))
    498       (if (org-string-nw-p label)
    499           (replace-regexp-in-string "\\`.*"
    500                                     (format "\\&\n\\\\label{%s}" label)
    501                                     latex-frag)
    502         latex-frag))))
    503 
    504 ;;;; Latex Fragment
    505 
    506 (defun org-md-latex-fragment (latex-fragment _contents info)
    507   "Transcode a LATEX-FRAGMENT object from Org to Markdown.
    508 CONTENTS is nil.  INFO is a plist holding contextual information."
    509   (when (plist-get info :with-latex)
    510     (let ((frag (org-element-property :value latex-fragment)))
    511       (cond
    512        ((string-match-p "^\\\\(" frag)
    513         (concat "$" (substring frag 2 -2) "$"))
    514        ((string-match-p "^\\\\\\[" frag)
    515         (concat "$$" (substring frag 2 -2) "$$"))
    516        (t frag))))) ; either already $-deliminated or a macro
    517 
    518 ;;;; Line Break
    519 
    520 (defun org-md-line-break (_line-break _contents _info)
    521   "Transcode LINE-BREAK object into Markdown format.
    522 CONTENTS is nil.  INFO is a plist used as a communication
    523 channel."
    524   "  \n")
    525 
    526 
    527 ;;;; Link
    528 
    529 (defun org-md-link (link desc info)
    530   "Transcode LINK object into Markdown format.
    531 DESC is the description part of the link, or the empty string.
    532 INFO is a plist holding contextual information.  See
    533 `org-export-data'."
    534   (let* ((link-org-files-as-md
    535 	  (lambda (raw-path)
    536 	    ;; Treat links to `file.org' as links to `file.md'.
    537 	    (if (string= ".org" (downcase (file-name-extension raw-path ".")))
    538 		(concat (file-name-sans-extension raw-path) ".md")
    539 	      raw-path)))
    540 	 (type (org-element-property :type link))
    541 	 (raw-path (org-element-property :path link))
    542 	 (path (cond
    543 		((member type '("http" "https" "ftp" "mailto"))
    544 		 (concat type ":" raw-path))
    545 		((string-equal  type "file")
    546 		 (org-export-file-uri (funcall link-org-files-as-md raw-path)))
    547 		(t raw-path))))
    548     (cond
    549      ;; Link type is handled by a special function.
    550      ((org-export-custom-protocol-maybe link desc 'md info))
    551      ((member type '("custom-id" "id" "fuzzy"))
    552       (let ((destination (if (string= type "fuzzy")
    553 			     (org-export-resolve-fuzzy-link link info)
    554 			   (org-export-resolve-id-link link info))))
    555 	(pcase (org-element-type destination)
    556 	  (`plain-text			; External file.
    557 	   (let ((path (funcall link-org-files-as-md destination)))
    558 	     (if (not desc) (format "<%s>" path)
    559 	       (format "[%s](%s)" desc path))))
    560 	  (`headline
    561 	   (format
    562 	    "[%s](#%s)"
    563 	    ;; Description.
    564 	    (cond ((org-string-nw-p desc))
    565 		  ((org-export-numbered-headline-p destination info)
    566 		   (mapconcat #'number-to-string
    567 			      (org-export-get-headline-number destination info)
    568 			      "."))
    569 		  (t (org-export-data (org-element-property :title destination)
    570 				      info)))
    571 	    ;; Reference.
    572 	    (or (org-element-property :CUSTOM_ID destination)
    573 		(org-export-get-reference destination info))))
    574 	  (_
    575 	   (let ((description
    576 		  (or (org-string-nw-p desc)
    577 		      (let ((number (org-export-get-ordinal destination info)))
    578 			(cond
    579 			 ((not number) nil)
    580 			 ((atom number) (number-to-string number))
    581 			 (t (mapconcat #'number-to-string number ".")))))))
    582 	     (when description
    583 	       (format "[%s](#%s)"
    584 		       description
    585 		       (org-export-get-reference destination info))))))))
    586      ((org-export-inline-image-p link org-html-inline-image-rules)
    587       (let ((path (cond ((not (string-equal type "file"))
    588 			 (concat type ":" raw-path))
    589 			((not (file-name-absolute-p raw-path)) raw-path)
    590 			(t (expand-file-name raw-path))))
    591 	    (caption (org-export-data
    592 		      (org-export-get-caption
    593 		       (org-export-get-parent-element link))
    594 		      info)))
    595 	(format "![img](%s)"
    596 		(if (not (org-string-nw-p caption)) path
    597 		  (format "%s \"%s\"" path caption)))))
    598      ((string= type "coderef")
    599       (format (org-export-get-coderef-format path desc)
    600 	      (org-export-resolve-coderef path info)))
    601      ((string= type "radio")
    602       (let ((destination (org-export-resolve-radio-link link info)))
    603 	(if (not destination) desc
    604 	  (format "<a href=\"#%s\">%s</a>"
    605 		  (org-export-get-reference destination info)
    606 		  desc))))
    607      (t (if (not desc) (format "<%s>" path)
    608 	  (format "[%s](%s)" desc path))))))
    609 
    610 
    611 ;;;; Node Property
    612 
    613 (defun org-md-node-property (node-property _contents _info)
    614   "Transcode a NODE-PROPERTY element into Markdown syntax.
    615 CONTENTS is nil.  INFO is a plist holding contextual
    616 information."
    617   (format "%s:%s"
    618           (org-element-property :key node-property)
    619           (let ((value (org-element-property :value node-property)))
    620             (if value (concat " " value) ""))))
    621 
    622 
    623 ;;;; Paragraph
    624 
    625 (defun org-md-paragraph (paragraph contents _info)
    626   "Transcode PARAGRAPH element into Markdown format.
    627 CONTENTS is the paragraph contents.  INFO is a plist used as
    628 a communication channel."
    629   (let ((first-object (car (org-element-contents paragraph))))
    630     ;; If paragraph starts with a #, protect it.
    631     (if (and (stringp first-object) (string-prefix-p "#" first-object))
    632 	(concat "\\" contents)
    633       contents)))
    634 
    635 
    636 ;;;; Plain List
    637 
    638 (defun org-md-plain-list (_plain-list contents _info)
    639   "Transcode PLAIN-LIST element into Markdown format.
    640 CONTENTS is the plain-list contents.  INFO is a plist used as
    641 a communication channel."
    642   contents)
    643 
    644 
    645 ;;;; Plain Text
    646 
    647 (defun org-md-plain-text (text info)
    648   "Transcode a TEXT string into Markdown format.
    649 TEXT is the string to transcode.  INFO is a plist holding
    650 contextual information."
    651   (when (plist-get info :with-smart-quotes)
    652     (setq text (org-export-activate-smart-quotes text :html info)))
    653   ;; The below series of replacements in `text' is order sensitive.
    654   ;; Protect `, *, _, and \
    655   (setq text (replace-regexp-in-string "[`*_\\]" "\\\\\\&" text))
    656   ;; Protect ambiguous #.  This will protect # at the beginning of
    657   ;; a line, but not at the beginning of a paragraph.  See
    658   ;; `org-md-paragraph'.
    659   (setq text (replace-regexp-in-string "\n#" "\n\\\\#" text))
    660   ;; Protect ambiguous !
    661   (setq text (replace-regexp-in-string "\\(!\\)\\[" "\\\\!" text nil nil 1))
    662   ;; Handle special strings, if required.
    663   (when (plist-get info :with-special-strings)
    664     (setq text (org-html-convert-special-strings text)))
    665   ;; Handle break preservation, if required.
    666   (when (plist-get info :preserve-breaks)
    667     (setq text (replace-regexp-in-string "[ \t]*\n" "  \n" text)))
    668   ;; Return value.
    669   text)
    670 
    671 
    672 ;;;; Property Drawer
    673 
    674 (defun org-md-property-drawer (_property-drawer contents _info)
    675   "Transcode a PROPERTY-DRAWER element into Markdown format.
    676 CONTENTS holds the contents of the drawer.  INFO is a plist
    677 holding contextual information."
    678   (and (org-string-nw-p contents)
    679        (replace-regexp-in-string "^" "    " contents)))
    680 
    681 
    682 ;;;; Quote Block
    683 
    684 (defun org-md-quote-block (_quote-block contents _info)
    685   "Transcode QUOTE-BLOCK element into Markdown format.
    686 CONTENTS is the quote-block contents.  INFO is a plist used as
    687 a communication channel."
    688   (replace-regexp-in-string
    689    "^" "> "
    690    (replace-regexp-in-string "\n\\'" "" contents)))
    691 
    692 
    693 ;;;; Section
    694 
    695 (defun org-md-section (_section contents _info)
    696   "Transcode SECTION element into Markdown format.
    697 CONTENTS is the section contents.  INFO is a plist used as
    698 a communication channel."
    699   contents)
    700 
    701 
    702 ;;;; Template
    703 
    704 (defun org-md-inner-template (contents info)
    705   "Return body of document after converting it to Markdown syntax.
    706 CONTENTS is the transcoded contents string.  INFO is a plist
    707 holding export options."
    708   ;; Make sure CONTENTS is separated from table of contents and
    709   ;; footnotes with at least a blank line.
    710   (concat
    711    ;; Table of contents.
    712    (let ((depth (plist-get info :with-toc)))
    713      (when depth
    714        (concat (org-md--build-toc info (and (wholenump depth) depth)) "\n")))
    715    ;; Document contents.
    716    contents
    717    "\n"
    718    ;; Footnotes section.
    719    (org-md--footnote-section info)))
    720 
    721 (defun org-md-template (contents _info)
    722   "Return complete document string after Markdown conversion.
    723 CONTENTS is the transcoded contents string.  INFO is a plist used
    724 as a communication channel."
    725   contents)
    726 
    727 
    728 
    729 ;;; Interactive function
    730 
    731 ;;;###autoload
    732 (defun org-md-export-as-markdown (&optional async subtreep visible-only)
    733   "Export current buffer to a Markdown buffer.
    734 
    735 If narrowing is active in the current buffer, only export its
    736 narrowed part.
    737 
    738 If a region is active, export that region.
    739 
    740 A non-nil optional argument ASYNC means the process should happen
    741 asynchronously.  The resulting buffer should be accessible
    742 through the `org-export-stack' interface.
    743 
    744 When optional argument SUBTREEP is non-nil, export the sub-tree
    745 at point, extracting information from the headline properties
    746 first.
    747 
    748 When optional argument VISIBLE-ONLY is non-nil, don't export
    749 contents of hidden elements.
    750 
    751 Export is done in a buffer named \"*Org MD Export*\", which will
    752 be displayed when `org-export-show-temporary-export-buffer' is
    753 non-nil."
    754   (interactive)
    755   (org-export-to-buffer 'md "*Org MD Export*"
    756     async subtreep visible-only nil nil (lambda () (text-mode))))
    757 
    758 ;;;###autoload
    759 (defun org-md-convert-region-to-md ()
    760   "Assume the current region has Org syntax, and convert it to Markdown.
    761 This can be used in any buffer.  For example, you can write an
    762 itemized list in Org syntax in a Markdown buffer and use
    763 this command to convert it."
    764   (interactive)
    765   (org-export-replace-region-by 'md))
    766 
    767 
    768 ;;;###autoload
    769 (defun org-md-export-to-markdown (&optional async subtreep visible-only)
    770   "Export current buffer to a Markdown file.
    771 
    772 If narrowing is active in the current buffer, only export its
    773 narrowed part.
    774 
    775 If a region is active, export that region.
    776 
    777 A non-nil optional argument ASYNC means the process should happen
    778 asynchronously.  The resulting file should be accessible through
    779 the `org-export-stack' interface.
    780 
    781 When optional argument SUBTREEP is non-nil, export the sub-tree
    782 at point, extracting information from the headline properties
    783 first.
    784 
    785 When optional argument VISIBLE-ONLY is non-nil, don't export
    786 contents of hidden elements.
    787 
    788 Return output file's name."
    789   (interactive)
    790   (let ((outfile (org-export-output-file-name ".md" subtreep)))
    791     (org-export-to-file 'md outfile async subtreep visible-only)))
    792 
    793 ;;;###autoload
    794 (defun org-md-publish-to-md (plist filename pub-dir)
    795   "Publish an org file to Markdown.
    796 
    797 FILENAME is the filename of the Org file to be published.  PLIST
    798 is the property list for the given project.  PUB-DIR is the
    799 publishing directory.
    800 
    801 Return output file name."
    802   (org-publish-org-to 'md filename ".md" plist pub-dir))
    803 
    804 (provide 'ox-md)
    805 
    806 ;; Local variables:
    807 ;; generated-autoload-file: "org-loaddefs.el"
    808 ;; End:
    809 
    810 ;;; ox-md.el ends here