Skip to content

Commit

Permalink
Implement README sections generation (#104)
Browse files Browse the repository at this point in the history
* Implement `do doc --readme` for generating doc sections.

* Add README for each missing crate.

* Review feature docs.

* Link to cargo features in main docs.
  • Loading branch information
SamRodri authored Apr 15, 2024
1 parent 333f354 commit 0487d85
Show file tree
Hide file tree
Showing 202 changed files with 1,487 additions and 295 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Unpublished

* Add Cargo feature documentation in each crate `README.md` and `lib.rs` docs.

# 0.3.3

Expand Down
151 changes: 120 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,40 +47,129 @@ fn main() {

See the [`API docs`] for more details.

## Crates

The `zng` crate is the only dependency you need to create apps, it re-exports the primary API of the other
crates in well organized and documented modules.

The other crates provide the full API that you might need to implement more advanced features, for example, a
custom property that modifies the behavior of an widget might need to reference the widget's internal state,
this *internal* API will only be available in the widget's crate.

<!--do doc --readme features-->
## Cargo Features

Zng provides the following features which can be enabled in your `Cargo.toml` file:

- **`view`** — Include the default view-process implementation.
- **`view_prebuilt`** — Include the default view-process implementation as an embedded precompiled binary.
- **`inspector`** — Instrument each property and widget instance with inspector nodes and extend windows to be inspected on Ctrl+Shift+I.
- **`trace_widget`** — Instrument every widget outer-most node to trace UI methods.
- **`trace_wgt_item`** — Instrument every property and intrinsic node to trace UI methods.
- **`deadlock_detection`** — Spawns a thread on app creation that checks and prints `parking_lot` deadlocks.
- **`http`** — Enables HTTP tasks, images download.
- **`test_util`** — Test utilities.
- **`multi_app`** — Allows multiple app instances per-process, one app per thread at a time. The `LocalContext` tracks
This crate provides 25 feature flags, 3 enabled by default.

#### `"view"`
Include the default view-process implementation.

#### `"view_prebuilt"`
Include the default view-process implementation as an embedded precompiled binary.

#### `"http"`
Enables HTTP tasks and web features of widgets and services.

#### `"debug_default"`
Enable the `"dyn_*"`, `"inspector"` features in debug builds.

*Enabled by default.*

#### `"dyn_node"`
Use more dynamic dispatch at the node level by enabling `UiNode::cfg_boxed` to box.

This speeds-up compilation time at the cost of runtime.

#### `"inspector"`
Instrument each property and widget instance with "Inspector" nodes and
extend windows to be inspected on Ctrl+Shift+I.

#### `"dyn_app_extension"`
Use dynamic dispatch at the app-extension level.

This speeds-up compilation time at the cost of runtime.

#### `"dyn_closure"`
Box closures at opportune places, such as `Var::map`, reducing the number of monomorphised types.

This speeds-up compilation time at the cost of runtime.

#### `"test_util"`
Test utilities.

#### `"multi_app"`
Allows multiple app instances per-process.

This feature allows multiple apps, one app per thread at a time. The `LocalContext` tracks
what app is currently running in each thread and `app_local!` statics switch to the value of each app
depending on the current thread.
- **`hyphenation_embed_all`** — Embed hyphenation dictionaries for all supported languages. If enabled some 2.8MB of data is embedded, you can provide an alternative dictionary source using the `Hyphenation::dictionary_source` method.
- **`dyn_node`** — Use more dynamic dispatch at the node level by enabling `UiNode::cfg_boxed` to box.
- **`dyn_app_extension`** — Use dynamic dispatch at the app-extension level.
- **`dyn_closure`** — Box closures at opportune places, such as `Var::map`, reducing the number of monomorphised types.
- **`toml`** — Enable TOML configs.
- **`ron`** — Enable RON configs.
- **`yaml`** — Enable YAML configs.
- **`material_icons`** — Include all *Material Icons* icon sets, each icon set embeds some 300KB of data.
- **`material_icons_outlined`** Include *Material Icons Outlined* icon set. If enabled some icons of this set are used for some of the commands.
- **`material_icons_filled`** Include *Material Icons Filled* icon set.
- **`material_icons_rounded`** Include *Material Icons Rounded* icon set.
- **`material_icons_sharp`** Include *Material Icons Sharp* icon set.
- **`view_bundle_licenses`** Collects and bundles third-party licenses used by the `zng-view` crate.

These features are enabled by default:

- **`debug_default`** — Enable the `"dyn_*"` and `"inspector"` features for debug builds only.
- **`ipc`** — Enables pre-build views and connecting to views running in another process.
- **`view_software`** — Enables software renderer fallback in the default view-process (`"view"`).

Not enabled by default, but enabled by `feature="test_util"`.

#### `"trace_widget"`
Instrument every widget outer-most node to trace UI methods.

#### `"trace_wgt_item"`
Instrument every property and intrinsic node to trace UI methods.

Note that this can cause very large trace files and bad performance.

#### `"deadlock_detection"`
Spawns a thread on app creation that checks and prints `parking_lot` deadlocks.

#### `"hyphenation_embed_all"`
Embed hyphenation dictionaries for all supported languages.

If enabled some 2.8MB of data is embedded, you can provide an alternative dictionary source using the
`Hyphenation::dictionary_source` method.

#### `"material_icons"`
Include all Material Icons icon sets in the default app.

#### `"material_icons_outlined"`
Material Icons Outlined icon set.

If enabled some icons of this set are used for some of the commands.

#### `"material_icons_filled"`
Material Icons Filled icon set.

#### `"material_icons_rounded"`
Material Icons Rounded icon set.

#### `"material_icons_sharp"`
Material Icons Sharp icon set.

#### `"toml"`
Enable TOML configs.

#### `"ron"`
Enable RON configs.

#### `"yaml"`
Enable YAML configs.

#### `"view_software"`
Enables software renderer fallback in the default view-process.

If enabled and a native OpenGL 3.2 driver is not available the `swgl` software renderer is used.

*Enabled by default.*

#### `"view_bundle_licenses"`
Collects and bundles third-party licenses used by the `zng-view` crate.

Needs `cargo-about` and Internet connection during build.

Not enabled by default. Note that `"view_prebuilt"` always bundles licenses.

#### `"ipc"`
Enables pre-build views and connecting to views running in another process.

*Enabled by default.*

<!--do doc --readme #SECTION-END-->


## Requirements

Expand Down Expand Up @@ -156,4 +245,4 @@ at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
dual licensed as above, without any additional terms or conditions.
12 changes: 12 additions & 0 deletions tools/do-tasks/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod readme_gen;
mod util;
mod version_doc_sync;
use std::format_args as f;
Expand Down Expand Up @@ -66,6 +67,7 @@ fn install(mut args: Vec<&str>) {

// do doc [-o, --open] [<cargo-doc-args>]
// [-s, --serve]
// [--readme <crate>..]
//
// Generate documentation for zng crates.
//
Expand All @@ -78,7 +80,17 @@ fn install(mut args: Vec<&str>) {
//
// Note: `basic-http-server` can be installed with cargo,
// it is not installed by `do install`.
// doc --readme
// Update READMEs tagged with `<!-- do doc --readme $tag -->` in all publish crates.
// Tags:
// header: Replaces the next paragraph with the shared header.
// features: Replaces or insert the next section with the `## Cargo Features`.
fn doc(mut args: Vec<&str>) {
if take_flag(&mut args, &["--readme"]) {
readme_gen::generate(args);
return;
}

let custom_open = if args.contains(&"--manifest-path") {
if let Some(open) = args.iter_mut().find(|a| **a == "-o") {
*open = "--open";
Expand Down
179 changes: 179 additions & 0 deletions tools/do-tasks/src/readme_gen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
use crate::{println, util};
use std::{borrow::Cow, collections::HashSet, path::PathBuf};

pub fn generate(args: Vec<&str>) {
for member in &util::publish_members() {
if !args.is_empty() && !args.contains(&member.name.as_str()) {
continue;
}

let readme = if member.name == "zng" {
PathBuf::from("zng/../README.md")
} else {
PathBuf::from(format!("{}/README.md", member.name))
};

println(&format!("{}/Cargo.toml", member.name));

let previous = if readme.exists() {
Cow::from(std::fs::read_to_string(&readme).unwrap())
} else {
Cow::from(README_TEMPLATE.to_owned())
};

let mut s = String::new();
let mut lines = previous.lines().peekable();
while let Some(line) = lines.next() {
use std::fmt::*;

writeln!(&mut s, "{line}").unwrap();
match line {
"<!--do doc --readme header-->" => {
writeln!(&mut s, "{HEADER}").unwrap();
while let Some(l) = lines.next() {
if l.trim().is_empty() {
break;
}
}
}
"<!--do doc --readme features-->" => {
if let Some(l) = lines.peek() {
if l.trim() == FEATURES_HEADER {
while let Some(l) = lines.next() {
if l == SECTION_END {
break;
}
}
}
}

let (features, defaults) = read_features(&format!("{}/Cargo.toml", member.name));
if !features.is_empty() {
writeln!(&mut s, "{FEATURES_HEADER}").unwrap();

if features.len() == 1 {
if defaults.contains(&features[0].name) {
writeln!(&mut s, "\n This crate provides 1 feature flag, enabled by default.",).unwrap();
} else {
writeln!(&mut s, "\n This crate provides 1 feature flag, not enabled by default.",).unwrap();
}
} else {
writeln!(
&mut s,
"\nThis crate provides {} feature flags, {} enabled by default.\n",
features.len(),
defaults.len(),
)
.unwrap();
}

for f in features {
if f.docs.is_empty() {
crate::error(format_args!("missing docs for `{}` feature", f.name));
}
writeln!(&mut s, "#### `\"{}\"`\n{}", f.name, f.docs).unwrap();
if defaults.contains(&f.name) {
writeln!(&mut s, "*Enabled by default.*\n").unwrap();
}
}

writeln!(&mut s, "{SECTION_END}").unwrap();
}
}
_ => {}
}
}

if s != previous {
std::fs::write(&readme, s.as_bytes()).unwrap();

if previous == README_TEMPLATE {
println(" generated");
} else {
println(" updated");
}
}
}
}

struct Feature {
name: String,
docs: String,
}

fn read_features(cargo: &str) -> (Vec<Feature>, HashSet<String>) {
let cargo = std::fs::read_to_string(cargo).unwrap();
let mut r = vec![];
let mut rd = HashSet::new();
let mut in_features = false;

let mut next_docs = String::new();

let rgx = regex::Regex::new(r#"(\w+)\s*=\s*\[.*"#).unwrap();

let mut lines = cargo.lines();
while let Some(line) = lines.next() {
let line = line.trim();
if line == "[features]" {
in_features = true;
} else if in_features {
use std::fmt::*;

if line.starts_with('[') && line.ends_with(']') {
break;
}

if line.starts_with('#') {
let docs = line.trim_start_matches(&['#', ' ']);
writeln!(&mut next_docs, "{docs}").unwrap();
} else {
if let Some(caps) = rgx.captures(&line) {
let name = caps.get(1).unwrap().as_str();
if name == "default" {
let s = line.find('[').unwrap();
let mut defaults = String::new();
if let Some(e) = line.find(']') {
defaults.push_str(&line[s + 1..e]);
} else {
defaults.push_str(&line[s + 1..]);
while let Some(line) = lines.next() {
if let Some(e) = line.find(']') {
defaults.push_str(&line[..e]);
break;
}
defaults.push_str(line);
}
}
for dft in defaults.split(',') {
rd.insert(dft.trim_matches(&['"', ' ']).to_owned());
}
} else {
r.push(Feature {
name: name.to_owned(),
docs: std::mem::take(&mut next_docs),
})
};
} else {
next_docs.clear();
}
}
}
}
(r, rd)
}

const HEADER: &str = "This crate is part of the [`zng`](https://github.com/zng-ui/zng?tab=readme-ov-file#crates) project.\n";

const README_TEMPLATE: &str = "\
<!--do doc --readme header-->
.
<!--do doc --readme features-->
";

const FEATURES_HEADER: &str = "## Cargo Features";

const SECTION_END: &str = "<!--do doc --readme #SECTION-END-->";
Loading

0 comments on commit 0487d85

Please sign in to comment.