# `Coelho.Markdown`
[🔗](https://github.com/nseaSeb/coelho/blob/main/lib/coelho/markdown.ex#L1)

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 `&#124;` 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`, as
    `Coelho.Render` resolves 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.

# `opts`

```elixir
@type opts() :: [
  nodes: %{optional(atom()) =&gt; (map(), String.t() -&gt; iodata())},
  marks: %{optional(atom()) =&gt; {String.t(), String.t()}},
  context: Coelho.Attachments.context()
]
```

# `from_markdown`

```elixir
@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.

# `to_markdown`

```elixir
@spec to_markdown(map() | nil, Coelho.Schema.t(), opts()) :: String.t()
```

Renders a validated document as Markdown. `nil` is `""`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
