diff --git a/Cargo.lock b/Cargo.lock index 221b5b2..d2ce868 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -540,6 +540,7 @@ dependencies = [ "comline-codegen-rust", "comline-codegen-typescript", "comline-core", + "comline-doc-html", "comline-doc-markdown", "comline-runtime-conformance", "eyre", @@ -563,7 +564,7 @@ dependencies = [ [[package]] name = "comline-codegen" version = "0.1.0" -source = "git+https://github.com/ComlineProject/generation?rev=7b51264b216ef4c5e2afbe36897a5b35dafe7e06#7b51264b216ef4c5e2afbe36897a5b35dafe7e06" +source = "git+https://github.com/ComlineProject/generation?rev=58ff61b4a72a2bcdba6685809e615ffb614e1d8e#58ff61b4a72a2bcdba6685809e615ffb614e1d8e" dependencies = [ "comline-core", "eyre", @@ -577,7 +578,7 @@ dependencies = [ [[package]] name = "comline-codegen-rust" version = "0.1.0" -source = "git+https://github.com/ComlineProject/comline-rust?rev=bef9a74c028d7a44b4dc13ed677d300e061b2147#bef9a74c028d7a44b4dc13ed677d300e061b2147" +source = "git+https://github.com/ComlineProject/comline-rust?rev=52e0eadf1c424f9486ee97d75d74cf59bbc1f191#52e0eadf1c424f9486ee97d75d74cf59bbc1f191" dependencies = [ "comline-codegen", "comline-core", @@ -587,7 +588,7 @@ dependencies = [ [[package]] name = "comline-codegen-typescript" version = "0.1.0" -source = "git+https://github.com/ComlineProject/comline-typescript?rev=47b54cc75b05bc52586d431d26ed4def316cf0c4#47b54cc75b05bc52586d431d26ed4def316cf0c4" +source = "git+https://github.com/ComlineProject/comline-typescript?rev=932edaad1fd0a0dba47f8fff132149e03c0340dc#932edaad1fd0a0dba47f8fff132149e03c0340dc" dependencies = [ "comline-codegen", "comline-core", @@ -628,13 +629,23 @@ name = "comline-core-stdlib" version = "0.1.0" source = "git+https://github.com/ComlineProject/core?rev=076cae0c3a0639b0c083b61a94f9d4913e853b3f#076cae0c3a0639b0c083b61a94f9d4913e853b3f" +[[package]] +name = "comline-doc-html" +version = "0.1.0" +source = "git+https://github.com/ComlineProject/generation?rev=58ff61b4a72a2bcdba6685809e615ffb614e1d8e#58ff61b4a72a2bcdba6685809e615ffb614e1d8e" +dependencies = [ + "comline-codegen", + "eyre", + "pulldown-cmark", + "serde_json", +] + [[package]] name = "comline-doc-markdown" version = "0.1.0" -source = "git+https://github.com/ComlineProject/generation?rev=7b51264b216ef4c5e2afbe36897a5b35dafe7e06#7b51264b216ef4c5e2afbe36897a5b35dafe7e06" +source = "git+https://github.com/ComlineProject/generation?rev=58ff61b4a72a2bcdba6685809e615ffb614e1d8e#58ff61b4a72a2bcdba6685809e615ffb614e1d8e" dependencies = [ "comline-codegen", - "comline-core", "eyre", "serde", "serde_json", @@ -2051,6 +2062,24 @@ dependencies = [ "cc", ] +[[package]] +name = "pulldown-cmark" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e" +dependencies = [ + "bitflags 2.13.1", + "memchr", + "pulldown-cmark-escape", + "unicase", +] + +[[package]] +name = "pulldown-cmark-escape" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" + [[package]] name = "pulley-interpreter" version = "49.0.3" @@ -2972,6 +3001,12 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" +[[package]] +name = "unicase" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357cc3acc6a036009fd6c973ed009037c732d60d0b4f6c673e9041497482a28f" + [[package]] name = "unicode-ident" version = "1.0.27" diff --git a/Cargo.toml b/Cargo.toml index 7c0494c..8ba85bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,12 +38,14 @@ comline-core = { git = "https://github.com/ComlineProject/core", rev = "076cae0c # `comline-codegen`'s types, so the rev below must be byte-identical to the # one *they* pin - Cargo only unifies a git dependency across identical rev # strings, and two copies of `GenRequest` are two incompatible types. -comline-codegen = { git = "https://github.com/ComlineProject/generation", rev = "7b51264b216ef4c5e2afbe36897a5b35dafe7e06" } -# The official Markdown documentation renderer (`comline doc`), linked in -# like the official generators. Same generation rev as `comline-codegen`. -comline-doc-markdown = { git = "https://github.com/ComlineProject/generation", rev = "7b51264b216ef4c5e2afbe36897a5b35dafe7e06" } -comline-codegen-rust = { git = "https://github.com/ComlineProject/comline-rust", rev = "bef9a74c028d7a44b4dc13ed677d300e061b2147", optional = true } -comline-codegen-typescript = { git = "https://github.com/ComlineProject/comline-typescript", rev = "47b54cc75b05bc52586d431d26ed4def316cf0c4", optional = true } +comline-codegen = { git = "https://github.com/ComlineProject/generation", rev = "58ff61b4a72a2bcdba6685809e615ffb614e1d8e" } +# The official Markdown and HTML documentation renderers (`comline doc`), +# linked in like the official generators. Same generation rev as +# `comline-codegen`. +comline-doc-markdown = { git = "https://github.com/ComlineProject/generation", rev = "58ff61b4a72a2bcdba6685809e615ffb614e1d8e" } +comline-doc-html = { git = "https://github.com/ComlineProject/generation", rev = "58ff61b4a72a2bcdba6685809e615ffb614e1d8e" } +comline-codegen-rust = { git = "https://github.com/ComlineProject/comline-rust", rev = "52e0eadf1c424f9486ee97d75d74cf59bbc1f191", optional = true } +comline-codegen-typescript = { git = "https://github.com/ComlineProject/comline-typescript", rev = "932edaad1fd0a0dba47f8fff132149e03c0340dc", optional = true } # `comline conformance`: the conformance runner (corpus loading, judging, # profile proofs) and its case format, a public spec. Pinned by git rev. comline-runtime-conformance = { git = "https://github.com/ComlineProject/runtime", rev = "6f8f73a247f0aa4ac8b7041b2fdc4b02bce0e0d6" } diff --git a/docs/cli.md b/docs/cli.md index 9d3cbaa..81f06d5 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -449,7 +449,7 @@ argument matches no stored version (the error lists the versions that exist). ### `comline doc` ``` -comline doc [--out ] [--version ] [--changelog-from ] [--format markdown|json] +comline doc [--out ] [--version ] [--changelog-from ] [--format markdown|html|json] ``` Write the package's documentation. Nothing is written to `.comline/`. @@ -460,6 +460,14 @@ Write the package's documentation. Nothing is written to `.comline/`. every unit with its `///` docs, fields, arguments and each protocol's wire summary, `changelog.md` with `--changelog-from`, and `nav.yml`, a `nav` for mkdocs or zensical. +- **`html`** writes a self-contained static site, rendered by the official HTML + renderer, also linked in: `index.html`, one page per module + (`types/user.html`), `changelog.html` with `--changelog-from`, `style.css` + and `search.js`. It has the same content as the Markdown pages, plus a + sidebar of modules and units, a search box over the units (the index is + inlined in `search.js`), and light and dark themes that follow the OS + setting. Nothing loads from the network and every link is relative, so + `index.html` opens straight from disk or from any static host. - **`json`** writes the documentation model itself, `model.json`, in the shape of core's `schemas/doc-model.json`. @@ -468,7 +476,7 @@ Write the package's documentation. Nothing is written to `.comline/`. | `--out ` | `[doc] out`, else `docs/api` | where to write, relative to the project | | `--version ` | the working tree | document a built version (a version string, a commit hash, or `HEAD`) from its frozen IR, `//!` headers included | | `--changelog-from ` | `[doc] changelog_from` | add what changed from built version `` to the documented one | -| `--format ` | `[doc] format`, else `markdown` | `markdown`, `json`, or another id the `[doc] renderer` offers | +| `--format ` | `[doc] format`, else `markdown` | `markdown`, `html`, `json`, or another id the `[doc] renderer` offers | A built version keeps no source, so with `--version` its protocols have no wire summary. A version built before headers were stored has none. @@ -497,6 +505,7 @@ changelog_from = "0.1.0" comline doc comline doc --out site/api --changelog-from 0.1.0 comline doc --version 0.2.0 +comline doc --format html --out site comline doc --format json ``` diff --git a/src/cli.rs b/src/cli.rs index 671ad0f..e09b2e1 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -112,8 +112,10 @@ version string, a commit hash or `HEAD`), which is documented from its frozen \ IR, `//!` headers included. A built version keeps no source, so its protocols \ have no wire summary. -`--format json` writes the documentation model itself (`model.json`), for \ -other tools. `comline.toml`'s `[doc]` table sets the defaults: `out` \ +`--format html` writes a self-contained static site instead: `index.html`, \ +one page per module, `changelog.html`, a stylesheet and a search script, all \ +linked relatively, so `index.html` opens straight from disk. `--format json` \ +writes the documentation model itself (`model.json`), for other tools. `comline.toml`'s `[doc]` table sets the defaults: `out` \ (`docs/api`), `format` (`markdown`), `renderer` (an external renderer, run \ like an external generator), `links` (dependency docs URLs) and \ `changelog_from`. @@ -122,6 +124,7 @@ Examples: comline doc comline doc --out site/api --changelog-from 0.1.0 comline doc --version 0.2.0 + comline doc --format html --out site comline doc --format json")] Doc { /// Output directory; default from `[doc] out`, else `docs/api` @@ -136,7 +139,7 @@ Examples: #[arg(long, value_name = "VERSION")] changelog_from: Option, - /// `markdown` (pages) or `json` (the model); default from `[doc] format` + /// `markdown` (pages), `html` (a static site) or `json` (the model); default from `[doc] format` #[arg(long, value_name = "FORMAT")] format: Option, }, diff --git a/src/commands/doc.rs b/src/commands/doc.rs index ae40a6f..d0ca6b8 100644 --- a/src/commands/doc.rs +++ b/src/commands/doc.rs @@ -4,8 +4,8 @@ //! compiled working tree, or from a built version's frozen IR and `//!` //! headers with `--version`. `--changelog-from` adds what changed since an //! earlier built version. `--format json` writes the model itself; any other -//! format is a renderer's id, `markdown` being the official one, linked in -//! like the official generators. `[doc] renderer` in `comline.toml` names an +//! format is a renderer's id: `markdown` and `html` are the official ones, +//! linked in like the official generators. `[doc] renderer` in `comline.toml` names an //! external renderer instead, run exactly like an external generator //! ([`crate::external`]: the same sandbox, limits and path checks). //! @@ -39,6 +39,7 @@ pub struct Flags { pub(crate) fn renderer_registry() -> Registry { let mut registry = Registry::new(); comline_doc_markdown::register(&mut registry); + comline_doc_html::register(&mut registry); registry } diff --git a/tests/cli/doc.rs b/tests/cli/doc.rs index 4f74d8f..3825b36 100644 --- a/tests/cli/doc.rs +++ b/tests/cli/doc.rs @@ -137,10 +137,73 @@ fn an_unknown_format_names_what_is_offered() { comline_cmd() .current_dir(&shop) - .args(["doc", "--format", "html"]) + .args(["doc", "--format", "pdf"]) .assert() .failure() - .stderr(predicate::str::contains( - "doesn't render `html`; it offers `markdown`", - )); + .stderr(predicate::str::contains("doesn't render `pdf`; it offers")) + .stderr(predicate::str::contains("`html`")) + .stderr(predicate::str::contains("`markdown`")); +} + +#[test] +fn html_writes_a_self_contained_site() { + let temp = tempfile::tempdir().unwrap(); + let shop = temp.path().join("shop"); + package(&shop, "//! A shop.\n\n", API); + comline_cmd() + .current_dir(&shop) + .arg("build") + .assert() + .success(); + + comline_cmd() + .current_dir(&shop) + .args([ + "doc", + "--format", + "html", + "--out", + "site", + "--changelog-from", + "0.1.0-dev.1", + ]) + .assert() + .success(); + + let out = shop.join("site"); + let index = read(&out.join("index.html")); + assert!(index.starts_with(""), "{index}"); + assert!(index.contains("

shop

\n

A shop.

"), "{index}"); + assert!(index.contains("api")); + let api = read(&out.join("api.html")); + assert!( + api.contains("totalu64What it costs."), + "{api}" + ); + assert!(api.contains("Order")); + assert!(api.contains("

On the wire

")); + assert!(api.contains("")); + assert!(read(&out.join("search.js")).contains("\"u\":\"api.html#order\"")); + assert!(out.join("style.css").exists()); + assert!(out.join("changelog.html").exists()); + assert!(!out.join("nav.yml").exists()); +} + +#[test] +fn html_is_a_doc_format_in_comline_toml() { + let temp = tempfile::tempdir().unwrap(); + let shop = temp.path().join("shop"); + package(&shop, "", API); + fs::write( + shop.join("comline.toml"), + "[doc]\nout = \"site\"\nformat = \"html\"\n", + ) + .unwrap(); + comline_cmd() + .current_dir(&shop) + .arg("doc") + .assert() + .success(); + assert!(shop.join("site/index.html").exists()); + assert!(shop.join("site/api.html").exists()); } diff --git a/tests/cli/external.rs b/tests/cli/external.rs index ad2739f..48d9a2e 100644 --- a/tests/cli/external.rs +++ b/tests/cli/external.rs @@ -287,20 +287,20 @@ fn a_language_the_generator_does_not_offer_is_refused() { ))); } -/// `[doc] renderer`: the official Markdown renderer run as an external one -/// writes what the built-in writes. -fn renderer_matches_builtin(kind: Kind) { +/// `[doc] renderer`: an official renderer (`bin`, rendering `format`) run as +/// an external one writes what the built-in writes, byte for byte. +fn renderer_matches_builtin(kind: Kind, bin: &str, format: &str) { let temp = tempfile::tempdir().unwrap(); let project = fixture_project(temp.path()); comline_cmd() .current_dir(&project) - .args(["doc", "--out", "builtin"]) + .args(["doc", "--out", "builtin", "--format", format]) .assert() .success(); - let file = install(&project, kind, "renderer", "comline-doc-markdown"); + let file = install(&project, kind, bin, &format!("comline-doc-{format}")); fs::write( project.join("comline.toml"), - format!("[doc]\nrenderer = \"{file}\"\nout = \"external\"\n"), + format!("[doc]\nrenderer = \"{file}\"\nout = \"external\"\nformat = \"{format}\"\n"), ) .unwrap(); comline_cmd() @@ -319,12 +319,23 @@ fn renderer_matches_builtin(kind: Kind) { #[cfg(feature = "gen-wasm")] #[test] fn a_wasm_renderer_writes_what_the_builtin_does() { - renderer_matches_builtin(Kind::Wasm); + renderer_matches_builtin(Kind::Wasm, "renderer", "markdown"); } #[test] fn a_native_renderer_writes_what_the_builtin_does() { - renderer_matches_builtin(Kind::Native); + renderer_matches_builtin(Kind::Native, "renderer", "markdown"); +} + +#[cfg(feature = "gen-wasm")] +#[test] +fn a_wasm_html_renderer_writes_what_the_builtin_does() { + renderer_matches_builtin(Kind::Wasm, "renderer-html", "html"); +} + +#[test] +fn a_native_html_renderer_writes_what_the_builtin_does() { + renderer_matches_builtin(Kind::Native, "renderer-html", "html"); } #[test] diff --git a/tests/fixtures/generators/Cargo.lock b/tests/fixtures/generators/Cargo.lock index 64dfc52..d0c624f 100644 --- a/tests/fixtures/generators/Cargo.lock +++ b/tests/fixtures/generators/Cargo.lock @@ -14,6 +14,12 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" + [[package]] name = "blake3" version = "1.8.7" @@ -73,7 +79,7 @@ dependencies = [ [[package]] name = "comline-codegen" version = "0.1.0" -source = "git+https://github.com/ComlineProject/generation?rev=7b51264b216ef4c5e2afbe36897a5b35dafe7e06#7b51264b216ef4c5e2afbe36897a5b35dafe7e06" +source = "git+https://github.com/ComlineProject/generation?rev=58ff61b4a72a2bcdba6685809e615ffb614e1d8e#58ff61b4a72a2bcdba6685809e615ffb614e1d8e" dependencies = [ "comline-core", "eyre", @@ -87,7 +93,7 @@ dependencies = [ [[package]] name = "comline-codegen-rust" version = "0.1.0" -source = "git+https://github.com/ComlineProject/comline-rust?rev=bef9a74c028d7a44b4dc13ed677d300e061b2147#bef9a74c028d7a44b4dc13ed677d300e061b2147" +source = "git+https://github.com/ComlineProject/comline-rust?rev=52e0eadf1c424f9486ee97d75d74cf59bbc1f191#52e0eadf1c424f9486ee97d75d74cf59bbc1f191" dependencies = [ "comline-codegen", "comline-core", @@ -116,13 +122,23 @@ name = "comline-core-stdlib" version = "0.1.0" source = "git+https://github.com/ComlineProject/core?rev=076cae0c3a0639b0c083b61a94f9d4913e853b3f#076cae0c3a0639b0c083b61a94f9d4913e853b3f" +[[package]] +name = "comline-doc-html" +version = "0.1.0" +source = "git+https://github.com/ComlineProject/generation?rev=58ff61b4a72a2bcdba6685809e615ffb614e1d8e#58ff61b4a72a2bcdba6685809e615ffb614e1d8e" +dependencies = [ + "comline-codegen", + "eyre", + "pulldown-cmark", + "serde_json", +] + [[package]] name = "comline-doc-markdown" version = "0.1.0" -source = "git+https://github.com/ComlineProject/generation?rev=7b51264b216ef4c5e2afbe36897a5b35dafe7e06#7b51264b216ef4c5e2afbe36897a5b35dafe7e06" +source = "git+https://github.com/ComlineProject/generation?rev=58ff61b4a72a2bcdba6685809e615ffb614e1d8e#58ff61b4a72a2bcdba6685809e615ffb614e1d8e" dependencies = [ "comline-codegen", - "comline-core", "eyre", "serde", "serde_json", @@ -134,6 +150,7 @@ version = "0.0.0" dependencies = [ "comline-codegen", "comline-codegen-rust", + "comline-doc-html", "comline-doc-markdown", ] @@ -290,6 +307,24 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pulldown-cmark" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e" +dependencies = [ + "bitflags", + "memchr", + "pulldown-cmark-escape", + "unicase", +] + +[[package]] +name = "pulldown-cmark-escape" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" + [[package]] name = "quote" version = "1.0.47" @@ -519,6 +554,12 @@ version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86a801b3cea342a06d468c8710662aa29e5e05e4f5c0d62f00bbb7f2ad7941c2" +[[package]] +name = "unicase" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357cc3acc6a036009fd6c973ed009037c732d60d0b4f6c673e9041497482a28f" + [[package]] name = "unicode-ident" version = "1.0.27" diff --git a/tests/fixtures/generators/Cargo.toml b/tests/fixtures/generators/Cargo.toml index f92c3de..2ab33d8 100644 --- a/tests/fixtures/generators/Cargo.toml +++ b/tests/fixtures/generators/Cargo.toml @@ -12,6 +12,7 @@ publish = false [dependencies] # The same revs the CLI pins, so the real generator's output is comparable # byte for byte with the CLI's built-in one. -comline-codegen = { git = "https://github.com/ComlineProject/generation", rev = "7b51264b216ef4c5e2afbe36897a5b35dafe7e06" } -comline-doc-markdown = { git = "https://github.com/ComlineProject/generation", rev = "7b51264b216ef4c5e2afbe36897a5b35dafe7e06" } -comline-codegen-rust = { git = "https://github.com/ComlineProject/comline-rust", rev = "bef9a74c028d7a44b4dc13ed677d300e061b2147" } +comline-codegen = { git = "https://github.com/ComlineProject/generation", rev = "58ff61b4a72a2bcdba6685809e615ffb614e1d8e" } +comline-doc-markdown = { git = "https://github.com/ComlineProject/generation", rev = "58ff61b4a72a2bcdba6685809e615ffb614e1d8e" } +comline-doc-html = { git = "https://github.com/ComlineProject/generation", rev = "58ff61b4a72a2bcdba6685809e615ffb614e1d8e" } +comline-codegen-rust = { git = "https://github.com/ComlineProject/comline-rust", rev = "52e0eadf1c424f9486ee97d75d74cf59bbc1f191" } diff --git a/tests/fixtures/generators/src/bin/renderer-html.rs b/tests/fixtures/generators/src/bin/renderer-html.rs new file mode 100644 index 0000000..92377d8 --- /dev/null +++ b/tests/fixtures/generators/src/bin/renderer-html.rs @@ -0,0 +1,10 @@ +//! The official HTML renderer as an external one: what generation's +//! `comline-doc-html` binary is. + +use std::process::ExitCode; + +fn main() -> ExitCode { + let mut registry = comline_codegen::Registry::new(); + comline_doc_html::register(&mut registry); + comline_codegen::protocol::serve_stdio(®istry) +}