From 97841d57602ded7f719aa25fd79a0a1a212ccdc7 Mon Sep 17 00:00:00 2001 From: Asherah Connor Date: Mon, 6 Jul 2026 21:02:43 +1000 Subject: [PATCH 1/2] move collect_text* helpers out of html. --- examples/headers.rs | 4 ++-- src/html.rs | 36 +++--------------------------------- src/nodes.rs | 28 ++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 35 deletions(-) diff --git a/examples/headers.rs b/examples/headers.rs index 9e9f1886..6e55ada0 100644 --- a/examples/headers.rs +++ b/examples/headers.rs @@ -1,6 +1,6 @@ // Extract the document title by srching for a level-one header at the root level. -use comrak::{Arena, Options, html::collect_text, nodes::NodeValue, parse_document}; +use comrak::{Arena, Options, nodes::NodeValue, parse_document}; fn main() { println!("{:?}", get_document_title("# Hello\n")); @@ -22,7 +22,7 @@ fn get_document_title(document: &str) -> String { continue; } - return collect_text(node); + return node.collect_text(); } "Untitled Document".to_string() diff --git a/src/html.rs b/src/html.rs index e634fb0c..3953efb7 100644 --- a/src/html.rs +++ b/src/html.rs @@ -609,7 +609,7 @@ fn render_heading( write!(context, "( context.write_str(">")?; } else { if let Some(prefix) = context.options.extension.effective_header_id_prefix() { - let text_content = collect_text(node); + let text_content = node.collect_text(); let id = context.current_anchorized_id.take().unwrap(); let href_prefix = if context.options.extension.header_id_prefix_in_href { prefix.as_str() @@ -641,7 +641,7 @@ fn render_heading( } } Some(adapter) => { - let text_content = collect_text(node); + let text_content = node.collect_text(); let heading = HeadingMeta { level: nh.level, content: text_content, @@ -1607,36 +1607,6 @@ fn render_wiki_link( Ok(ChildRendering::HTML) } -// Helpers - -/// Recurses through a node and all of its children in depth-first (document) -/// order, returning the concatenated literal contents of text, code and math -/// blocks. Line breaks and soft breaks are represented as a single whitespace -/// character. -pub fn collect_text(node: Node<'_>) -> String { - let mut text = String::with_capacity(20); - collect_text_append(node, &mut text); - text -} - -/// Recurses through a node and all of its children in depth-first (document) -/// order, appending the literal contents of text, code and math blocks to -/// an output buffer. Line breaks and soft breaks are represented as a single -/// whitespace character. -pub fn collect_text_append(node: Node<'_>, output: &mut String) { - match node.data().value { - NodeValue::Text(ref literal) => output.push_str(literal), - NodeValue::Code(NodeCode { ref literal, .. }) => output.push_str(literal), - NodeValue::LineBreak | NodeValue::SoftBreak => output.push(' '), - NodeValue::Math(NodeMath { ref literal, .. }) => output.push_str(literal), - _ => { - for n in node.children() { - collect_text_append(n, output); - } - } - } -} - fn put_footnote_backref( context: &mut Context, nfd: &NodeFootnoteDefinition, diff --git a/src/nodes.rs b/src/nodes.rs index e2782509..78ac3810 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -1170,6 +1170,34 @@ impl<'a> arena_tree::Node<'a, RefCell> { Ok(()) } + /// Recurses through a node and all of its children in depth-first + /// (document) order, returning the concatenated literal contents of text, + /// code and math blocks. Line breaks and soft breaks are represented as a + /// single whitespace character. + pub fn collect_text(&'a self) -> String { + let mut text = String::with_capacity(20); + self.collect_text_append(&mut text); + text + } + + /// Recurses through a node and all of its children in depth-first + /// (document) order, appending the literal contents of text, code and math + /// blocks to an output buffer. Line breaks and soft breaks are represented + /// as a single whitespace character. + pub fn collect_text_append(&'a self, output: &mut String) { + match self.data().value { + NodeValue::Text(ref literal) => output.push_str(literal), + NodeValue::Code(NodeCode { ref literal, .. }) => output.push_str(literal), + NodeValue::LineBreak | NodeValue::SoftBreak => output.push(' '), + NodeValue::Math(NodeMath { ref literal, .. }) => output.push_str(literal), + _ => { + for n in self.children() { + n.collect_text_append(output); + } + } + } + } + pub(crate) fn last_child_is_open(&self) -> bool { self.last_child().is_some_and(|n| n.data().open) } From 6abbc3270f4b13dc8a8e51a32e1ef75de245f688 Mon Sep 17 00:00:00 2001 From: Asherah Connor Date: Mon, 6 Jul 2026 21:02:43 +1000 Subject: [PATCH 2/2] create CONTRIBUTING.md from README.md. --- CONTRIBUTING.md | 25 ++++++++++++++++++++++--- README.md | 8 +------- examples/update-readme.rs | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e4e7a60e..df837b4a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,23 @@ -Please ensure you've read the [Contributing](README.md#contributing) section of -the README before proceeding. + -Thank you! +The following is included verbatim from [README.md](README.md). + +--- + +Contributions are **highly encouraged**; if you'd like to assist, consider checking out the [`good first issue` label](https://github.com/kivikakk/comrak/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)! I'm happy to help provide direction and guidance throughout, even if (especially if!) you're new to Rust or open source. + +Where possible I practice [Optimistic Merging](http://hintjens.com/blog:106) as described by Peter Hintjens. Please keep the [code of conduct](CODE_OF_CONDUCT.md) in mind too. + +Thank you to Comrak's many contributors for PRs and issues opened! + +### Vulnerabilities + +Please report security issues to the author at <>. If you +have a GitHub account, you are welcome to use its interface to [privately report +security issues](https://github.com/kivikakk/comrak/security/advisories/new). + +### Policy on LLM contributions + +This policy is based on and inspired by [Servo's AI contributions policy](https://book.servo.org/contributing/getting-started.html#ai-contributions) ([permalink](https://archive.is/7LLb2#ai-contributions)) and [QEMU's Use of AI-generated content policy](https://github.com/qemu/qemu/blob/667e1fff878326c35c7f5146072e60a63a9a41c8/docs/devel/code-provenance.rst#use-of-ai-generated-content). Please see those links for a more detailed analysis. + +Contributions must not include content generated by large language models or other probabilistic tools, including but not limited to Copilot or ChatGPT. This policy covers code, documentation, pull requests, issues, comments, and any other contributions to Comrak. diff --git a/README.md b/README.md index 5a206fc4..bb30392e 100644 --- a/README.md +++ b/README.md @@ -419,16 +419,10 @@ This policy is based on and inspired by [Servo's AI contributions policy](https: Contributions must not include content generated by large language models or other probabilistic tools, including but not limited to Copilot or ChatGPT. This policy covers code, documentation, pull requests, issues, comments, and any other contributions to Comrak. -### Code Contributors +## Contributors [![Small chart showing Comrak contributors.](https://opencollective.com/comrak/contributors.svg?width=890&button=false)](https://github.com/kivikakk/comrak/graphs/contributors) -### Financial Contributors - -Since September 2025, the scope of my [day job](https://about.gitlab.com/company/team/#kivikakk) includes Comrak, meaning I can spend some time on it as part of my paid work! That's so nice, and so donations from the community are better directed elsewhere, in my opinion. - -If you feel like you would like to do so anyway, however, [GitHub Sponsors](https://github.com/sponsors/kivikakk) is the best way. - ## Contact Asherah Connor <> diff --git a/examples/update-readme.rs b/examples/update-readme.rs index 83a87630..31279754 100644 --- a/examples/update-readme.rs +++ b/examples/update-readme.rs @@ -89,5 +89,42 @@ fn main() -> Result<(), Box> { format_commonmark(doc, &options, &mut out)?; std::fs::write("README.md", &out)?; + // Strip everything except the "Contributing" section. + + let mut in_contributing = false; + + for node in doc.children() { + if let NodeValue::Heading(ref nh) = node.data().value { + if !in_contributing { + in_contributing = nh.level == 2 && node.collect_text() == "Contributing"; + node.detach(); + continue; + } else if nh.level <= 2 { + in_contributing = false; + } + } + + if !in_contributing { + node.detach(); + } + } + + out.clear(); + writeln!( + out, + "" + )?; + writeln!(out)?; + writeln!( + out, + "The following is included verbatim from [README.md](README.md)." + )?; + writeln!(out)?; + writeln!(out, "---")?; + writeln!(out)?; + format_commonmark(doc, &options, &mut out)?; + + std::fs::write("CONTRIBUTING.md", &out)?; + Ok(()) }