config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

ox-md.el (29091B)


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