damascene_markdown/lib.rs
1//! Markdown → Damascene `El` transformer.
2//!
3//! ```ignore
4//! use damascene_core::prelude::*;
5//! use damascene_markdown::md;
6//!
7//! let tree: El = md("# Hi\n\nHello **world** with [a link](https://damascene.dev).");
8//! ```
9//!
10//! Markdown is defined as a transformation to HTML, and Damascene's widget
11//! kit already echoes most of HTML's shape (`text_runs` ≈ `<p>`,
12//! `hard_break` ≈ `<br>`, span modifiers ≈ inline tags, `bullet_list`
13//! ≈ `<ul>`, `code_block` ≈ `<pre><code>`, …). The transformer walks
14//! `pulldown-cmark`'s streaming `Event` API and assembles an `El` tree
15//! out of those primitives — a column of blocks an author would have
16//! written by hand. The rendered output behaves like any other Damascene
17//! tree: themed surfaces, selection, hit-test, layout, lint.
18//!
19//! Supported today:
20//!
21//! - Headings `#` … `###` (and h4–h6 clamped to h3).
22//! - Paragraphs with inline emphasis, strong, code, link, hard / soft
23//! breaks. Soft breaks render as a space (CommonMark default).
24//! - Bulleted (`-` / `*`), numbered (`1.`), and GFM task lists,
25//! including nested lists and non-1 ordered starts.
26//! - Block quotes.
27//! - Fenced and indented code blocks.
28//! - Horizontal rules.
29//! - GFM tables.
30//! - Optional native math (`$…$` / `$$…$$`) via
31//! `MarkdownOptions::math(true)`. The first renderer slice supports
32//! a focused TeX subset: rows, identifiers / numbers / operators,
33//! `\frac`, `\sqrt`, superscripts, and subscripts.
34//! - Inline + block images render as block-level alt-text placeholders
35//! today. Real image resolution and inline images are Phase 2 follow-ups.
36//!
37//! Syntax highlighting (default-on `highlighting` feature): fenced code
38//! blocks with a recognised language tag (`` ```rust ``, `` ```python ``,
39//! …) are tokenized through `syntect` (regex-fancy, no `onig` C
40//! dependency) and emitted as a styled `text_runs([...])` paragraph
41//! inside the same sunken `code_block` chrome. Tags `syntect` doesn't
42//! recognise (and all fences with the feature off) fall back to the
43//! plain monospace `code_block` — never an error. Each token's colour is
44//! an Damascene palette token (`tokens::SUCCESS` for strings,
45//! `tokens::INFO` for keywords / numbers, `tokens::MUTED_FOREGROUND`
46//! for comments, …) so swapping `Theme::damascene_dark()` for
47//! `Theme::damascene_light()` recolours the syntax run automatically.
48//! `default-features = false` opts out of the highlighter and shrinks
49//! the dependency surface.
50//!
51//! **Streaming / repeated rendering:** parsing is full-document per
52//! call — there is no incremental API. For conversation views that
53//! re-render a backlog every frame (and re-parse a growing reply per
54//! streamed delta), wrap calls in [`MdCache`]: stable messages become
55//! cheap tree clones and only the changing tail re-parses.
56//!
57//! [`md_with_options`] exposes output-changing parser extensions. Today
58//! that includes smart punctuation and GFM alert blockquotes; [`md`]
59//! keeps both off by default.
60//!
61//! With the `html` feature, embedded HTML routes through
62//! [`damascene-html`](damascene_html): block scraps render in place,
63//! and fragmented inline tags (`<b>`, `Text`, `</b>` as pulldown-cmark
64//! emits them) are buffered so their styling — including inline
65//! `style="…"` — carries onto the text between them. `md_with_lints`
66//! surfaces the HTML transformer's findings (dropped declarations,
67//! unsupported tags, sanitized styles), and
68//! `MarkdownOptions::html_options` forwards `HtmlOptions` — set
69//! `sanitize_styles` for untrusted input.
70//!
71//! Deferred:
72//!
73//! - Footnotes, full TeX / MathML import, definition lists,
74//! heading attributes, metadata blocks, superscript/subscript, and
75//! wikilinks. (Their *content* flattens into plain paragraphs or the
76//! surrounding inline flow rather than being dropped.)
77
78#[cfg(feature = "highlighting")]
79mod cache;
80mod highlight;
81
82mod transformer;
83
84pub use cache::MdCache;
85/// Re-exported from [`damascene-html`](damascene_html) so embedders of
86/// the `html` feature can name the lint and option types without a
87/// direct dependency.
88#[cfg(feature = "html")]
89pub use damascene_html::{Finding, FindingKind, HtmlOptions};
90#[cfg(feature = "html")]
91pub use transformer::md_with_lints;
92pub use transformer::{MarkdownOptions, md, md_with_options};