Compare commits

..

7 Commits
v1.0 ... master

Author SHA1 Message Date
Jonas Bernoulli
4f774f13d3 org-gfm-footnote-section: Cosmetics 2023-12-15 20:01:18 +01:00
Jonas Bernoulli
a971997f8f org-gfm-table: Cosmetics
- It is redundant to use `function' around `lambda'.
- It is semantically better to use `and' if the returned value
  matters.  `when' should be used if we only care about the
  side-effects.
2023-12-15 20:00:13 +01:00
Jonas Bernoulli
4aabaa51b1 Use lexical-binding and fix uncovered bugs
Also silence false-positives.
2023-12-15 19:58:43 +01:00
Jonas Bernoulli
1bfc320f94
End first line of each docstring with a period
The convention calls for the first line to form a complete sentence.
Here we can achieve that in most cases simply by not beginning the
second sentence on the first line.
2023-06-26 14:27:56 +02:00
Jonas Bernoulli
4eaf2adf29
Improve consistency in the usage of empty lines 2023-06-26 14:07:35 +02:00
46faa67dbb
Merge pull request #39 from benley/example-block-src
Use triple-backtick block syntax for example blocks too
2022-09-10 15:21:35 +02:00
Benjamin Staffin
bd85f6a56f Use triple-backtick block syntax for example blocks too
org-mode `#+BEGIN_EXAMPLE` blocks can (optionally) be tagged with a
language, so this will give them proper syntax highlighting on GitHub.
It still works when the language is unset, of course.
2019-05-02 15:38:12 -04:00

110
ox-gfm.el
View File

@ -1,4 +1,4 @@
;;; ox-gfm.el --- Github Flavored Markdown Back-End for Org Export Engine
;;; ox-gfm.el --- Github Flavored Markdown Back-End for Org Export Engine -*- lexical-binding: t; -*-
;; Copyright (C) 2014-2017 Lars Tveito
@ -29,6 +29,7 @@
(require 'ox-md)
(require 'ox-publish)
;;; User-Configurable Variables
@ -56,12 +57,12 @@
:translate-alist '((inner-template . org-gfm-inner-template)
(paragraph . org-gfm-paragraph)
(strike-through . org-gfm-strike-through)
(example-block . org-gfm-example-block)
(src-block . org-gfm-src-block)
(table-cell . org-gfm-table-cell)
(table-row . org-gfm-table-row)
(table . org-gfm-table)))
;;; Transcode Functions
@ -79,12 +80,11 @@ communication channel."
(replace-regexp-in-string "\\`#" "\\#" contents nil t)
contents)))
;;;; Src Block
(defun org-gfm-src-block (src-block contents info)
"Transcode SRC-BLOCK element into Github Flavored Markdown
format. CONTENTS is nil. INFO is a plist used as a communication
(defun org-gfm-src-block (src-block _contents info)
"Transcode SRC-BLOCK element into Github Flavored Markdown format.
_CONTENTS is nil. INFO is a plist used as a communication
channel."
(let* ((lang (org-element-property :language src-block))
(code (org-export-format-code-default src-block info))
@ -92,16 +92,18 @@ channel."
(suffix "```"))
(concat prefix code suffix)))
;;;; Example Block
(defalias 'org-gfm-example-block #'org-gfm-src-block)
;;;; Strike-Through
(defun org-gfm-strike-through (strike-through contents info)
"Transcode STRIKE-THROUGH from Org to Markdown (GFM).
CONTENTS is the text with strike-through markup. INFO is a plist
(defun org-gfm-strike-through (_strike-through contents _info)
"Transcode _STRIKE-THROUGH from Org to Markdown (GFM).
CONTENTS is the text with strike-through markup. _INFO is a plist
holding contextual information."
(format "~~%s~~" contents))
;;;; Table-Common
(defvar width-cookies nil)
@ -112,10 +114,10 @@ holding contextual information."
(defconst gfm-table-separator " |")
(defun org-gfm-table-col-width (table column info)
"Return width of TABLE at given COLUMN. INFO is a plist used as
communication channel. Width of a column is determined either by
inquerying `width-cookies' in the column, or by the maximum cell with in
the column."
"Return width of TABLE at given COLUMN.
INFO is a plist used as communication channel. Width of a column
is determined either by inquerying `width-cookies' in the column,
or by the maximum cell with in the column."
(let ((cookie (when (hash-table-p width-cookies)
(gethash column width-cookies))))
(if (and (eq table width-cookies-table)
@ -144,22 +146,21 @@ the column."
info)
(puthash column max-width width-cookies))))))
(defun org-gfm-make-hline-builder (table info char)
"Return a function to build horizontal line in TABLE with given
CHAR. INFO is a plist used as a communication channel."
`(lambda (col)
"Return a function to build horizontal line in TABLE with given CHAR.
INFO is a plist used as a communication channel."
(lambda (col)
(let ((max-width (max 3 (org-gfm-table-col-width table col info))))
(when (< max-width 1)
(setq max-width 1))
(make-string max-width ,char))))
(make-string max-width char))))
;;;; Table-Cell
(defun org-gfm-table-cell (table-cell contents info)
"Transcode TABLE-CELL element from Org into GFM. CONTENTS is content
of the cell. INFO is a plist used as a communication channel."
"Transcode TABLE-CELL element from Org into GFM.
CONTENTS is content of the cell. INFO is a plist used as a
communication channel."
(let* ((table (org-export-get-parent-table table-cell))
(column (cdr (org-export-table-cell-address table-cell info)))
(width (org-gfm-table-col-width table column info))
@ -172,13 +173,12 @@ of the cell. INFO is a plist used as a communication channel."
?\s)))
(concat left-border contents right-border)))
;;;; Table-Row
(defun org-gfm-table-row (table-row contents info)
"Transcode TABLE-ROW element from Org into GFM. CONTENTS is cell
contents of TABLE-ROW. INFO is a plist used as a communication
channel."
"Transcode TABLE-ROW element from Org into GFM.
CONTENTS is cell contents of TABLE-ROW. INFO is a plist used as a
communication channel."
(let ((table (org-export-get-parent-table table-row)))
(when (and (eq 'rule (org-element-property :type table-row))
;; In GFM, rule is valid only at second row.
@ -186,7 +186,6 @@ channel."
table-row
(org-element-map table 'table-row 'identity info))))
(let* ((table (org-export-get-parent-table table-row))
(header-p (org-export-table-row-starts-header-p table-row info))
(build-rule (org-gfm-make-hline-builder table info ?-))
(cols (cdr (org-export-table-dimensions table info))))
(setq contents
@ -197,8 +196,6 @@ channel."
gfm-table-right-border))))
contents))
;;;; Table
(defun org-gfm-table (table contents info)
@ -209,7 +206,6 @@ contextual information."
(no-header (or (<= (length rows) 1)))
(cols (cdr (org-export-table-dimensions table info)))
(build-dummy-header
(function
(lambda ()
(let ((build-empty-cell (org-gfm-make-hline-builder table info ?\s))
(build-rule (org-gfm-make-hline-builder table info ?-))
@ -222,16 +218,14 @@ contextual information."
(mapconcat (lambda (col) (funcall build-rule col))
columns
gfm-table-separator)
gfm-table-right-border "\n"))))))
(concat (when no-header (funcall build-dummy-header))
gfm-table-right-border "\n")))))
(concat (and no-header (funcall build-dummy-header))
(replace-regexp-in-string "\n\n" "\n" contents))))
;;;; Table of contents
(defun org-gfm-format-toc (headline)
"Return an appropriate table of contents entry for HEADLINE. INFO is a
plist used as a communication channel."
(defun org-gfm-format-toc (headline info)
"Return an appropriate table of contents entry for HEADLINE."
(let* ((title (org-export-data
(org-export-get-alt-title headline info) info))
(level (1- (org-element-property :level headline)))
@ -240,38 +234,25 @@ plist used as a communication channel."
(org-export-get-reference headline info))))
(concat indent "- [" title "]" "(#" anchor ")")))
;;;; Footnote section
(defun org-gfm-footnote-section (info)
"Format the footnote section.
INFO is a plist used as a communication channel."
(let* ((fn-alist (org-export-collect-footnote-definitions info))
(fn-alist
(cl-loop for (n type raw) in fn-alist collect
(cons n (org-trim (org-export-data raw info))))))
(when fn-alist
(and-let* ((fn-alist (org-export-collect-footnote-definitions info)))
(format
"## %s\n%s"
"Footnotes"
(format
"\n%s\n"
(mapconcat
(lambda (fn)
(let ((n (car fn)) (def (cdr fn)))
"## Footnotes\n\n%s\n"
(mapconcat (pcase-lambda (`(,n ,_type ,def))
(format
"%s %s\n"
(format
(plist-get info :html-footnote-format)
(format (plist-get info :html-footnote-format)
(org-html--anchor
(format "fn.%d" n)
n
(format " class=\"footnum\" href=\"#fnr.%d\"" n)
info))
def)))
fn-alist
"\n"))))))
(org-trim (org-export-data def info))))
fn-alist "\n"))))
;;;; Template
@ -281,13 +262,13 @@ CONTENTS is the transcoded contents string. INFO is a plist
holding export options."
(let* ((depth (plist-get info :with-toc))
(headlines (and depth (org-export-collect-headlines info depth)))
(toc-string (or (mapconcat 'org-gfm-format-toc headlines "\n") ""))
(toc-string (or (mapconcat (lambda (headline)
(org-gfm-format-toc headline info))
headlines "\n")
""))
(toc-tail (if headlines "\n\n" "")))
(org-trim (concat toc-string toc-tail contents "\n" (org-gfm-footnote-section info)))))
;;; Interactive function
@ -318,17 +299,16 @@ non-nil."
(org-export-to-buffer 'gfm "*Org GFM Export*"
async subtreep visible-only nil nil (lambda () (text-mode))))
;;;###autoload
(defun org-gfm-convert-region-to-md ()
"Assume the current region has org-mode syntax, and convert it
to Github Flavored Markdown. This can be used in any buffer.
For example, you can write an itemized list in org-mode syntax in
a Markdown buffer and use this command to convert it."
"Convert the region to Github Flavored Markdown.
This can be used in any buffer, this function assume that the
current region has org-mode syntax. For example, you can write
an itemized list in org-mode syntax in a Markdown buffer and use
this command to convert it."
(interactive)
(org-export-replace-region-by 'gfm))
;;;###autoload
(defun org-gfm-export-to-markdown (&optional async subtreep visible-only)
"Export current buffer to a Github Flavored Markdown file.