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 "" 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