You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: text/3123-rustdoc-scrape-examples.md
+24-26Lines changed: 24 additions & 26 deletions
Original file line number
Diff line number
Diff line change
@@ -53,18 +53,18 @@ The goal of this RFC is to bridge the gap between automatically generated docume
53
53
54
54
The `scrape-examples` feature of Rustdoc finds examples of code where a particular function is called. For example, if we are documenting [`Filter::and`](https://willcrichton.net/example-analyzer/warp/trait.Filter.html#method.and), and a file [`examples/returning.rs`](https://github.com/seanmonstar/warp/tree/bf8bfc4134035dbff882f9b26cb9d1aa57f2c338/examples/returning.rs) contains a call to `and`, then the corresponding Rustdoc documentation looks like this:
55
55
56
+
56
57
<kbd>
57
-
<imgwidth="982"alt="Screen Shot 2021-05-11 at 11 31 12 AM"src="https://user-images.githubusercontent.com/663326/117852001-81cebb80-b24c-11eb-88ef-8532a5f012c4.png">
58
+
<imgwidth="982"alt="Screen Shot 2021-05-11 at 11 31 12 AM"src="https://user-images.githubusercontent.com/663326/120575286-a3e3d580-c3d5-11eb-9183-c65aa89f5250.png">
58
59
</kbd>
59
60
<br /><br />
60
61
61
-
After the user-provided documentation in the doc-comment, `scrape-examples` inserts a code example (if one exists). The code example shows a window into the source file with the function call highlighted in yellow. The icons in the top-right of the code viewer allow the user to expand the code sample to the full file, or to navigate through other calls in the same file. The link above the example goes to the example in the crate's repository.
62
+
After the user-provided documentation in the doc-comment, `scrape-examples` inserts a code example (if one exists). The code example shows a window into the source file with the function call highlighted in yellow. The icons in the top-right of the code viewer allow the user to expand the code sample to the full file, or to navigate through other calls in the same file. The link above the example goes to the full listing in Rustdoc's generated `src/` directory, similar to other `[src]` links.
62
63
63
64
Additionally, the user can click "More examples" to see every example from the `examples/` directory, like this:
64
65
65
66
<kbd>
66
-
<imgwidth="956"alt="Screen Shot 2021-05-11 at 11 31 36 AM"src="https://user-images.githubusercontent.com/663326/117852026-8abf8d00-b24c-11eb-819d-51627798e005.png">
67
-
67
+
<imgwidth="956"alt="Screen Shot 2021-05-11 at 11 31 36 AM"src="https://user-images.githubusercontent.com/663326/120575318-ae05d400-c3d5-11eb-9a25-990591c1a075.png">
68
68
</kbd>
69
69
<br /><br />
70
70
@@ -77,24 +77,24 @@ cargo doc --scrape-examples
77
77
78
78
# Reference-level explanation
79
79
80
-
I have implemented a prototype of the `scrape-examples` feature as modifications to rustdoc and cargo. You can check out the diffs:
The feature uses the following high-level flow, with some added technical details as necessary.
85
85
86
86
1. The user gives `--scrape-examples` as an argument to `cargo doc`.
87
-
2. Cargo runs the equivalent of `cargo build --examples` ([source](https://github.com/willcrichton/cargo/blob/fd25a0301314a9eba6beb5239891fc5902a9a9a9/src/cargo/ops/cargo_compile.rs#L618-L631)).
88
-
* Specifically, for each unit being documented, it copies the Config and CliFeatures from the input CompileOpts. Then it sets the CompileFilter to only match examples.
89
-
4. Cargo generates build flags for each example. ([source](https://github.com/willcrichton/cargo/blob/fd25a0301314a9eba6beb5239891fc5902a9a9a9/src/cargo/ops/cargo_compile.rs#L633-L646)).
90
-
* This is implemented by repurposing the `Doctest` target, which also is used to generate build flags to pass to rustdoc.
91
-
6. Cargo identifies a remote repository URL for linking to the examples ([source](https://github.com/willcrichton/cargo/blob/fd25a0301314a9eba6beb5239891fc5902a9a9a9/src/cargo/ops/cargo_compile.rs#L594-L608)).
92
-
*Currently this is done by retrieving `package.repository` from the manifest and casing on the domain name. If examples were packaged with rustdoc like other source files, then this could instead link to the generated `src` directory.
9. Rustdoc iterates through each example and uses a visitor to identify spans of calls to functions in the crate being documented ([source](https://github.com/willcrichton/rust/blob/2653c671a4ae89070fdf00f9e149486146e7fc18/src/librustdoc/scrape_examples.rs)).
95
-
* This means that rustc is invoked multiple times within a single process before the core of rustdoc is actually executed. Care will be needed to avoid issues with global state like the string interner.
96
-
11. Rustdoc adds the scraped examples to the documentation for each function ([source](https://github.com/willcrichton/rust/blob/2653c671a4ae89070fdf00f9e149486146e7fc18/src/librustdoc/html/render/mod.rs#L2394-L2471)).
97
-
12. Rustdoc's Javascript adds interactivity to the examples when loaded ([source](https://github.com/willcrichton/rust/blob/2653c671a4ae89070fdf00f9e149486146e7fc18/src/librustdoc/html/static/main.js#L1415-L1599)).
87
+
2. Cargo runs the equivalent of `cargo rustdoc --examples` ([source](https://github.com/willcrichton/cargo/blob/9c9f86772cbcf49f77119b7471021989e72c9936/src/cargo/ops/cargo_compile.rs#L596-L655)).
88
+
* Specifically, when constructing the `BuildContext`, Cargo will now recursively invoke `rustdoc` on all files matching the `--examples` filter.
89
+
* Each invocation includes a flag `--scrape-examples <output path>` which directs rustdoc to output to a file at the specific location.
90
+
3. An instance of rustdoc runs for each example, finding all call-sites and exporting them to a JSON file ([source](https://github.com/willcrichton/rust/blob/20044cd72dc220e787b081ae2139df49c2320471/src/librustdoc/scrape_examples.rs)).
91
+
* A visitor runs over the HIR to find call sites that resolve to a specific linkable function.
92
+
*As a part of this pass, rustdoc also generates source files for the examples, e.g. `target/doc/src/example/foo.rs`. These are then linked to during rendering.
93
+
* The format of the generated JSON is `{function: {file: {locations: [list of spans], other metadata}}}`. See the [`AllCallLocations`](https://github.com/willcrichton/rust/blob/20044cd72dc220e787b081ae2139df49c2320471/src/librustdoc/scrape_examples.rs#L24-L32) type.
94
+
4. Rustdoc is then invoked as normal for the package being documented, except with the added flags `--with-examples <path/to/json>` for each generated JSON file. Rustdoc reads the JSON data from disk and stores them in `RenderOptions`.
95
+
5. Rustdoc renders the call locations into the HTML ([source](https://github.com/willcrichton/rust/blob/20044cd72dc220e787b081ae2139df49c2320471/src/librustdoc/html/render/mod.rs#L2433-L2508)).
96
+
* This involves reading the source file from disk to embed the example into the page.
97
+
6. Rustdoc's Javascript adds interactivity to the examples when loaded ([source](https://github.com/willcrichton/rust/blob/20044cd72dc220e787b081ae2139df49c2320471/src/librustdoc/html/static/main.js#L965-L1135)).
98
98
* Most of the logic here is to extend the code viewer with additional features like toggling between snippet / full file, navigating between call sites, and highlighting code in-situ.
99
99
100
100
The primary use case for this will be on docs.rs. My expectation is that docs.rs would use the `--scrape-examples` flag, and all docs hosted there would have the scraped examples.
@@ -122,14 +122,12 @@ I have never seen a documentation generator with this exact feature before. Ther
122
122
# Unresolved questions
123
123
[unresolved-questions]: #unresolved-questions
124
124
125
-
1.**UI design:** What is the best UI to show the examples inline? My prototype represents my best effort at a draft, but I'm open to suggestions. For example:
126
-
* Is showing 1 example by default the best idea? Or should scraped examples be hidden by default?
127
-
* Is the ability to see the full file context worth the increase in page size?
128
-
* How should the examples be ordered? Is there a way to determine the "best" examples to show first?
129
-
2.**Tooling integration:** Are there better ways to accomplish the tooling sub-tasks? Specifically:
130
-
* Is there a robust way of generating links to examples based on the Cargo.toml `package.repository` field, especially that generalizes across choice of VCS? Is there a way to reliably get the current commit so as to generate stable links?
131
-
* Is invoking rustc on each example within rustdoc the best way to analyze the examples for call sites? In my [original prototype](https://github.com/willcrichton/example-analyzer), I wrote a standalone tool that output JSON which was then read in by Rustdoc. One benefit of this approach is that Rustdoc could then integrate with any tool that analyzes call sites. But the downside is requiring yet another tool to be in-tree.
132
-
* What is the best way to handle Cargo workspaces? For example, some workspaces like [wasmtime](https://github.com/bytecodealliance/wasmtime) have a single examples directory at the root with many crates in a `crates/` subfolder. However, under my current strategy for finding examples, they would only be scraped during documentation of the root crate, not the other crates in the workspace.
125
+
The main unresolved questions are about the UI: what is the best UI to show the examples inline? My prototype represents my best effort at a draft, but I'm open to suggestions. For example:
126
+
127
+
1. Is showing 1 example by default the best idea? Or should scraped examples be hidden by default?
128
+
2. Is the ability to see the full file context worth the increase in page size?
129
+
3. How should the examples be ordered? Is there a way to determine the "best" examples to show first?
0 commit comments