documents: exports read through MarkdownReader, one fresh stack per area; reading helpers close again
CI / platforms (windows-latest) (push) Failing after 13s
CI / site (push) Successful in 22s
CI / crate (push) Failing after 28s
CI / platforms (ubuntu-24.04) (push) Canceled after 0s

The writer in ElyMark kept its own copy of the inline reading, which drifted
from the drawing. Reader goes public as MarkdownReader with block, inline,
note and label, and its blocks as MarkdownBlock. Shortcodes, InlineTag,
inline_tag and html_block_text close back to the tree; headings(text) goes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-14 01:36:21 -04:00
1 parent 97fa047d18
commit 12512217bd
6 files changed
+32 -29

No files matched your search

+1 -2
View File
@@ -58,8 +58,7 @@ pub use thumbs::PageThumbnailList;
pub use todo::{Checklist, TodoItem};
pub use toolbar::FixedFormatToolbar;
pub use tree::{
AlertKind, Inline, InlineTag, Shortcodes, Style, alert, headings, html_block_text, inline_tag,
is_toc,
AlertKind, Inline, Node as MarkdownBlock, Reader as MarkdownReader, Style, alert, is_toc,
};
pub(crate) use viewer::step as zoom_step;
pub use viewer::{DocPage, DocumentViewer, PageHit, PageNote};
+2 -7
View File
@@ -1,7 +1,7 @@
use markdown::mdast;
use super::{Inline, Node, Reader, Run, html};
use crate::documents::markdown::{file_tree, span};
use crate::documents::markdown::span;
/// A heading anywhere in the file.
#[derive(Clone, Debug, PartialEq)]
@@ -77,7 +77,7 @@ impl Reader<'_> {
}
/// An HTML block's text, as a browser shows it.
pub fn html_block_text(written: &str) -> Inline {
pub(super) fn html_block_text(written: &str) -> Inline {
let mut run = Run::default();
let mut rest = written;
while let Some(at) = rest.find('<') {
@@ -232,11 +232,6 @@ pub fn is_toc(node: &mdast::Node, source: &str) -> bool {
&& source[span(node)].trim().eq_ignore_ascii_case("[toc]")
}
/// Every heading in `text`, as `[toc]` lists them.
pub fn headings(text: &str) -> Vec<DocumentHeading> {
Reader::new(text, &file_tree(text)).headings
}
/// A plain first line naming a kind, and what follows.
fn marker(children: &[mdast::Node]) -> Option<(AlertKind, Vec<mdast::Node>)> {
let mut line = String::new();
+3 -3
View File
@@ -49,7 +49,7 @@ fn candidate(text: &str) -> Option<usize> {
/// One DOM text node's shortcodes, as GitHub reads them.
#[derive(Default)]
pub struct Shortcodes {
pub(super) struct Shortcodes {
/// Settled by the node's first `:name:`.
reads: Option<bool>,
/// Text before it held more than white space.
@@ -59,12 +59,12 @@ pub struct Shortcodes {
impl Shortcodes {
/// A new DOM text node starts.
pub fn split(&mut self) {
pub(super) fn split(&mut self) {
*self = Self::default();
}
/// Blank or spaced text before the first reads them all.
pub fn read(&mut self, text: &str) -> String {
pub(super) fn read(&mut self, text: &str) -> String {
if self.reads.is_none() {
let at = candidate(text);
let lead = &text[..at.unwrap_or(text.len())];
+2 -2
View File
@@ -98,7 +98,7 @@ pub(crate) fn breaks(name: &str) -> bool {
/// What one inline HTML tag does, as GitHub shows it.
#[derive(Clone, Debug, PartialEq)]
pub enum InlineTag {
pub(super) enum InlineTag {
/// Opens `name`; an anchor styles only with an address.
Open {
name: String,
@@ -114,7 +114,7 @@ pub enum InlineTag {
}
/// What the inline HTML `written` does.
pub fn inline_tag(written: &str) -> InlineTag {
pub(super) fn inline_tag(written: &str) -> InlineTag {
let Some(tag) = tag(written) else {
return InlineTag::Gone;
};
+23 -14
View File
@@ -8,9 +8,9 @@ mod blocks;
mod emoji;
mod html;
pub use blocks::{AlertKind, DocumentHeading, alert, headings, html_block_text, is_toc};
pub use emoji::Shortcodes;
pub use html::{InlineTag, inline_tag};
use blocks::html_block_text;
pub use blocks::{AlertKind, DocumentHeading, alert, is_toc};
use html::InlineTag;
/// How a stretch of inline text prints.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -80,11 +80,12 @@ pub enum Node {
}
/// A tree read against its source and its link definitions.
pub(crate) struct Reader<'a> {
pub struct Reader<'a> {
source: &'a str,
urls: HashMap<String, String>,
/// Footnotes numbered by their first reference.
notes: HashMap<String, usize>,
/// Every heading, as `[toc]` lists them.
pub headings: Vec<DocumentHeading>,
}
@@ -243,7 +244,7 @@ pub fn parse(markdown: &str) -> Vec<Node> {
impl<'a> Reader<'a> {
/// A reader of `root`, parsed from `source`.
pub(crate) fn new(source: &'a str, root: &mdast::Node) -> Self {
pub fn new(source: &'a str, root: &mdast::Node) -> Self {
let mut nodes = Vec::new();
walk(root, &mut nodes);
let (mut urls, mut notes, mut headings) = (HashMap::new(), HashMap::new(), Vec::new());
@@ -298,7 +299,8 @@ impl<'a> Reader<'a> {
.expect("a reference names a definition")
}
pub(crate) fn block(&self, node: &mdast::Node, out: &mut Vec<Node>) {
/// `node`'s blocks, as the drawing reads them.
pub fn block(&self, node: &mdast::Node, out: &mut Vec<Node>) {
match node {
mdast::Node::Paragraph(paragraph) => self.paragraph(node, &paragraph.children, out),
mdast::Node::Heading(heading) => {
@@ -350,13 +352,7 @@ impl<'a> Reader<'a> {
}
mdast::Node::ThematicBreak(_) => out.push(Node::Rule),
mdast::Node::FootnoteDefinition(note) => out.push(Node::Footnote {
label: match self.notes.get(&note.identifier) {
Some(number) => number.to_string(),
None => note
.label
.clone()
.unwrap_or_else(|| note.identifier.clone()),
},
label: self.label(note),
body: self.nested(&note.children),
}),
mdast::Node::Html(html) => text_block(html_block_text(&html.value), out),
@@ -381,8 +377,21 @@ impl<'a> Reader<'a> {
text_block(run.cut(), out);
}
/// A footnote's number, by its first reference.
pub fn note(&self, identifier: &str) -> Option<usize> {
self.notes.get(identifier).copied()
}
/// A note's label: its number, once referenced.
pub fn label(&self, note: &mdast::FootnoteDefinition) -> String {
match self.note(&note.identifier) {
Some(number) => number.to_string(),
None => note.label.clone().unwrap_or_else(|| note.identifier.clone()),
}
}
/// Inline text where images draw as their alt text.
pub(crate) fn inline(&self, nodes: &[mdast::Node]) -> Inline {
pub fn inline(&self, nodes: &[mdast::Node]) -> Inline {
let mut run = Run::default();
for node in nodes {
self.inline_into(node, &mut run, &mut Images::Alt);