Documents to Markdown, and back.
Coelho.Markdown.to_markdown(document)
#=> "## Release notes\n\nFixed **three** bugs."For a README, an export, a commit message, a language model's prompt —
anywhere Markdown is the format and HTML is not. The output is CommonMark
with two GitHub extensions, ~~strikethrough~~ and pipe tables, and every
character a writer typed comes back as that character: what would be
syntax is escaped, so a paragraph that starts with # stays a paragraph.
A mark is written with its Markdown delimiters — **, *, ~~ — where
CommonMark is certain to read them as such, and as the equivalent HTML
element where it would not: a bold *b right after a letter, a span that
begins with a space. Both are Markdown, and both come back. Underline,
highlight, subscript and superscript have no delimiters at all, so they are
always their element: <u>, <mark>, <sub>, <sup>.
What Markdown cannot say
It is dropped, or written another way, rather than approximated:
- an empty paragraph or heading, and a line break at the edge of one
- a paragraph's alignment, and a table cell's span; a table's first row
is its header, whatever it held. A cell is one line: a paragraph in it
writes a line break as
<br>, a|as|and code as<code>, since GitHub splits the row on any other|, and anything else in it — two paragraphs, a list, a code block — is written as Coelho's HTML - an ordered list numbered past nine digits, which no Markdown marker holds, is written as Coelho's HTML
- a heading holding a line break is written as Coelho's own HTML for it
- an attachment is a link to it — resolved through
:context, asCoelho.Renderresolves it — or its name when it has no URL, and its caption a paragraph after it - an image without alt text comes back with an empty one, and a URL comes
back percent-encoded where CommonMark encodes it (
[,], a space) - a code block's language is written, and lost again on import, as it is
by
Coelho.HTML.from_html/3
from_markdown/3 goes the other way, through Coelho.HTML.from_html/3, so
the schema decides what is kept exactly as it does for imported HTML. It
needs the optional :mdex (a precompiled binding to comrak, a CommonMark
parser) and :floki dependencies; to_markdown/3 needs neither.
Your own nodes and marks
A node of the application's own renders as its children — block content as
blocks, inline content as text — or, for a void node, as the text its
:to_text gives. :nodes and :marks say otherwise, by name:
Coelho.Markdown.to_markdown(document, schema,
nodes: %{mention: fn node, _children -> "@" <> node["attrs"]["handle"] end},
marks: %{spoiler: {"||", "||"}}
)A node override returns the Markdown itself, escaped as it needs; a mark
override is the pair of strings it is wrapped in. It applies to a shipped
mark as well — highlight: {"==", "=="} for a renderer that reads it.
Summary
Functions
Converts Markdown into a validated document, and says what it left behind.
Renders a validated document as Markdown. nil is "".
Types
Functions
@spec from_markdown(String.t(), Coelho.Schema.t(), keyword()) :: {:ok, map(), [Coelho.HTML.warning()]} | {:error, term()}
Converts Markdown into a validated document, and says what it left behind.
Through HTML: MDEx renders the Markdown — CommonMark, with GitHub's tables
and strikethrough — and Coelho.HTML.from_html/3 imports that under the
schema, so HTML written inside the Markdown is held to the same rules as
any other: see its documentation for the warnings.
@spec to_markdown(map() | nil, Coelho.Schema.t(), opts()) :: String.t()
Renders a validated document as Markdown. nil is "".