Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,23 @@
Please ensure you've read the [Contributing](README.md#contributing) section of
the README before proceeding.
<!-- This file is generated by examples/update-readme.rs. -->

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 <<ashe@kivikakk.ee>>. 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.
8 changes: 1 addition & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<ashe@kivikakk.ee>>
Expand Down
4 changes: 2 additions & 2 deletions examples/headers.rs
Original file line number Diff line number Diff line change
@@ -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"));
Expand All @@ -22,7 +22,7 @@ fn get_document_title(document: &str) -> String {
continue;
}

return collect_text(node);
return node.collect_text();
}

"Untitled Document".to_string()
Expand Down
37 changes: 37 additions & 0 deletions examples/update-readme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,42 @@ fn main() -> Result<(), Box<dyn Error>> {
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,
"<!-- This file is generated by examples/update-readme.rs. -->"
)?;
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(())
}
36 changes: 3 additions & 33 deletions src/html.rs
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,7 @@ fn render_heading<T>(
write!(context, "<h{}", nh.level)?;

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.anchorizer.anchorize(&text_content);

write!(context, r##" id="{prefix}{id}""##)?;
Expand All @@ -620,7 +620,7 @@ fn render_heading<T>(
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()
Expand All @@ -641,7 +641,7 @@ fn render_heading<T>(
}
}
Some(adapter) => {
let text_content = collect_text(node);
let text_content = node.collect_text();
let heading = HeadingMeta {
level: nh.level,
content: text_content,
Expand Down Expand Up @@ -1607,36 +1607,6 @@ fn render_wiki_link<T>(
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<T>(
context: &mut Context<T>,
nfd: &NodeFootnoteDefinition,
Expand Down
28 changes: 28 additions & 0 deletions src/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1170,6 +1170,34 @@ impl<'a> arena_tree::Node<'a, RefCell<Ast>> {
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)
}
Expand Down
Loading