{"id":13439783,"url":"https://github.com/pulldown-cmark/pulldown-cmark","last_synced_at":"2025-12-12T14:20:10.410Z","repository":{"id":33174150,"uuid":"36815297","full_name":"pulldown-cmark/pulldown-cmark","owner":"pulldown-cmark","description":"An efficient, reliable parser for CommonMark, a standard dialect of Markdown","archived":false,"fork":false,"pushed_at":"2025-03-31T07:10:38.000Z","size":4603,"stargazers_count":2225,"open_issues_count":58,"forks_count":254,"subscribers_count":25,"default_branch":"master","last_synced_at":"2025-05-12T22:03:24.548Z","etag":null,"topics":["commonmark","markdown","parser","rust"],"latest_commit_sha":null,"homepage":"https://pulldown-cmark.github.io/pulldown-cmark","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pulldown-cmark.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2015-06-03T16:12:34.000Z","updated_at":"2025-05-12T16:39:11.000Z","dependencies_parsed_at":"2023-10-14T13:51:29.337Z","dependency_job_id":"82e5352c-5e50-4dc1-b60c-34c3ae025e95","html_url":"https://github.com/pulldown-cmark/pulldown-cmark","commit_stats":{"total_commits":1188,"total_committers":97,"mean_commits":12.24742268041237,"dds":0.6397306397306397,"last_synced_commit":"81b23e8d24de2ac5bbd193991881ac28cb671458"},"previous_names":["pulldown-cmark/pulldown-cmark","raphlinus/pulldown-cmark"],"tags_count":45,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pulldown-cmark%2Fpulldown-cmark","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pulldown-cmark%2Fpulldown-cmark/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pulldown-cmark%2Fpulldown-cmark/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pulldown-cmark%2Fpulldown-cmark/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pulldown-cmark","download_url":"https://codeload.github.com/pulldown-cmark/pulldown-cmark/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254052692,"owners_count":22006716,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["commonmark","markdown","parser","rust"],"created_at":"2024-07-31T03:01:17.053Z","updated_at":"2025-12-12T14:20:05.325Z","avatar_url":"https://github.com/pulldown-cmark.png","language":"Rust","funding_links":[],"categories":["Rust","Libraries"],"sub_categories":["Markup language"],"readme":"# pulldown-cmark\n\n[![Tests](https://github.com/pulldown-cmark/pulldown-cmark/actions/workflows/rust.yml/badge.svg)](https://github.com/pulldown-cmark/pulldown-cmark/actions/workflows/rust.yml)\n[![Docs](https://docs.rs/pulldown-cmark/badge.svg)](https://docs.rs/pulldown-cmark)\n[![Crates.io](https://img.shields.io/crates/v/pulldown-cmark.svg?maxAge=2592000)](https://crates.io/crates/pulldown-cmark)\n\n[Documentation](https://docs.rs/pulldown-cmark/)\n\nThis library is a pull parser for [CommonMark](http://commonmark.org/), written\nin [Rust](http://www.rust-lang.org/). It comes with a simple command-line tool,\nuseful for rendering to HTML, and is also designed to be easy to use from as\na library.\n\nIt is designed to be:\n\n- Fast; a bare minimum of allocation and copying\n- Safe; written in pure Rust with no unsafe blocks (except in the opt-in SIMD feature)\n- Versatile; in particular source-maps are supported\n- Correct; the goal is 100% compliance with the [CommonMark spec](http://spec.commonmark.org/)\n\nFurther, it optionally supports parsing footnotes,\n[Github flavored tables](https://github.github.com/gfm/#tables-extension-),\n[Github flavored task lists](https://github.github.com/gfm/#task-list-items-extension-) and\n[strikethrough](https://github.github.com/gfm/#strikethrough-extension-).\n\nRustc 1.71.1 or newer is required to build the crate.\n\n## Example\n\nExample usage:\n\n```rust\n// Create parser with example Markdown text.\nlet markdown_input = \"hello world\";\nlet parser = pulldown_cmark::Parser::new(markdown_input);\n\n// Write to a new String buffer.\nlet mut html_output = String::new();\npulldown_cmark::html::push_html(\u0026mut html_output, parser);\nassert_eq!(\u0026html_output, \"\u003cp\u003ehello world\u003c/p\u003e\\n\");\n```\n\n## Why a pull parser?\n\nThere are many parsers for Markdown and its variants, but to my knowledge none\nuse pull parsing. Pull parsing has become popular for XML, especially for\nmemory-conscious applications, because it uses dramatically less memory than\nconstructing a document tree, but is much easier to use than push parsers. Push\nparsers are notoriously difficult to use, and also often error-prone because of\nthe need for user to delicately juggle state in a series of callbacks.\n\nIn a clean design, the parsing and rendering stages are neatly separated, but\nthis is often sacrificed in the name of performance and expedience. Many Markdown\nimplementations mix parsing and rendering together, and even designs that try\nto separate them (such as the popular [hoedown](https://github.com/hoedown/hoedown)),\nmake the assumption that the rendering process can be fully represented as a\nserialized string.\n\nPull parsing is in some sense the most versatile architecture. It's possible to\ndrive a push interface, also with minimal memory, and quite straightforward to\nconstruct an AST. Another advantage is that source-map information (the mapping\nbetween parsed blocks and offsets within the source text) is readily available;\nyou can call `into_offset_iter()` to create an iterator that yields `(Event, Range)`\npairs, where the second element is the event's corresponding range in the source\ndocument.\n\nWhile manipulating ASTs is the most flexible way to transform documents,\noperating on iterators is surprisingly easy, and quite efficient. Here, for\nexample, is the code to transform soft line breaks into hard breaks:\n\n```rust\nlet parser = parser.map(|event| match event {\n\tEvent::SoftBreak =\u003e Event::HardBreak,\n\t_ =\u003e event\n});\n```\n\nOr expanding an abbreviation in text:\n\n```rust\nlet parser = parser.map(|event| match event {\n\tEvent::Text(text) =\u003e Event::Text(text.replace(\"abbr\", \"abbreviation\").into()),\n\t_ =\u003e event\n});\n```\n\nAnother simple example is code to determine the max nesting level:\n\n```rust\nlet mut max_nesting = 0;\nlet mut level = 0;\nfor event in parser {\n\tmatch event {\n\t\tEvent::Start(_) =\u003e {\n\t\t\tlevel += 1;\n\t\t\tmax_nesting = std::cmp::max(max_nesting, level);\n\t\t}\n\t\tEvent::End(_) =\u003e level -= 1,\n\t\t_ =\u003e ()\n\t}\n}\n```\n\nNote that consecutive text events can happen due to the manner in which the\nparser evaluates the source. A utility `TextMergeStream` exists to improve\nthe comfort of iterating the events:\n\n```rust\nuse pulldown_cmark::{Event, Parser, Options};\n\nlet markdown_input = \"Hello world, this is a ~~complicated~~ *very simple* example.\";\n\nlet iterator = TextMergeStream::new(Parser::new(markdown_input));\n\nfor event in iterator {\n    match event {\n        Event::Text(text) =\u003e println!(\"{}\", text),\n        _ =\u003e {}\n    }\n}\n```\n\nThere are some basic but fully functional examples of the usage of the crate in the\n`examples` directory of this repository.\n\n## Using Rust idiomatically\n\nA lot of the internal scanning code is written at a pretty low level (it\npretty much scans byte patterns for the bits of syntax), but the external\ninterface is designed to be idiomatic Rust.\n\nPull parsers are at heart an iterator of events (start and end tags, text,\nand other bits and pieces). The parser data structure implements the\nRust Iterator trait directly, and Event is an enum. Thus, you can use the\nfull power and expressivity of Rust's iterator infrastructure, including\nfor loops and `map` (as in the examples above), collecting the events into\na vector (for recording, playback, and manipulation), and more.\n\nFurther, the `Text` event (representing text) is a small copy-on-write string.\nThe vast majority of text fragments are just\nslices of the source document. For these, copy-on-write gives a convenient\nrepresentation that requires no allocation or copying, but allocated\nstrings are available when they're needed. Thus, when rendering text to\nHTML, most text is copied just once, from the source document to the\nHTML buffer.\n\nWhen using the pulldown-cmark's own HTML renderer, make sure to write to a buffered\ntarget like a `Vec\u003cu8\u003e` or `String`. Since it performs many (very) small writes, writing\ndirectly to stdout, files, or sockets is detrimental to performance. Such writers can\nbe wrapped in a [`BufWriter`](https://doc.rust-lang.org/std/io/struct.BufWriter.html).\n\n## Build options\n\nBy default, the binary is built as well. If you don't want/need it, then build like this:\n\n```bash\n\u003e cargo build --no-default-features\n```\n\nOr add this package as dependency of your project using `cargo add`:\n\n```bash\n\u003e cargo add pulldown-cmark --no-default-features\n```\n\nSIMD accelerated scanners are available for the x64 platform from version 0.5 onwards. To\nenable them, build with simd feature:\n\n```bash\n\u003e cargo build --release --features simd\n```\n\nOr add this package as dependency of your project with the feature using `cargo add`:\n\n```bash\n\u003e cargo add pulldown-cmark --no-default-features --features=simd\n```\n\nFor a higher release performance you may want this configuration in your profile release:\n\n```\nlto = true\ncodegen-units = 1\npanic = \"abort\"\n```\n\n### `no_std` support\n\n`no_std` support can be enabled by compiling with `--no-default-features` to\ndisable `std` support and `--features hashbrown` for `Hash` collections that are only\ndefined in `std` for internal usages in crate. For example:\n\n```toml\n[dependencies]\npulldown-cmark = { version = \"*\", default-features = false, features = [\"hashbrown\", \"other features\"] }\n```\n\nTo support both `std` and `no_std` builds in project, you can use the following\nin your `Cargo.toml`:\n\n```toml\n[features]\ndefault = [\"std\", \"other features\"]\n\nstd = [\"pulldown-cmark/std\"]\nhashbrown = [\"pulldown-cmark/hashbrown\"]\nother_features = []\n[dependencies]\npulldown-cmark = { version = \"*\", default-features = false }\n```\n\n## Authors\n\nThe main author is Raph Levien. The implementation of the new design (v0.3+) was\ncompleted by Marcus Klaas de Vries. Since 2023, the development has been driven\nby Martín Pozo, Michael Howell, Roope Salmi and Martin Geisler.\n\n## License\n\nThis software is under the MIT license. See details in [license file](./LICENSE).\n\n## Contributions\n\nWe gladly accept contributions via GitHub pull requests. Please see\n[CONTRIBUTING.md](CONTRIBUTING.md) for more details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpulldown-cmark%2Fpulldown-cmark","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpulldown-cmark%2Fpulldown-cmark","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpulldown-cmark%2Fpulldown-cmark/lists"}