dtolnay / request-for-implementation

Crates that don't exist, but should
610 stars 6 forks source link

Cargo subcommand to perform pluggable expansions against rustdoc #20

Open dtolnay opened 5 years ago

dtolnay commented 5 years ago

In the GitBook that backs https://serde.rs, I use several GitBook plugins, a few of which I wrote myself.

For example I have a plugin that lets me insert playground links into code blocks. Here is an invocation right from the homepage: markdown (shown below) and rendered here with the "Run" link.

!PLAYGROUND a58fc361e02c4c0a08fd99cacd9567d1
```rust
/*
 * rust code
 */

Similarly a file name plugin -- [markdown here](https://raw.githubusercontent.com/serde-rs/serde-rs.github.io/master/_src/derive.md) and [rendered here](https://serde.rs/derive.html).

````markdown
!FILENAME Cargo.toml
```toml
[dependencies]
serde = "1.0"
serde_derive = "1.0"

Here is the documentation on GitBook plugins: https://toolchain.gitbook.com/plugins/create.html
The way this one of mine is implemented is by running some regex replacements against the pre-rendered and post-rendered markdown comment. The whole implementation is just a few lines of JavaScript.

```javascript
"page:before": function(page) {
    page.content = page.content.replace(/(!FILENAME .*)/g, '$1\n');
    page.content = page.content.replace(/(!PLAYGROUND .*)/g, '$1\n');
    return page;
},

"page": function(page) {
    var pattern = /<p>!FILENAME (.+)<\/p>/g;
    var tag = '<div><p class="code-filename">$1</p></div>';
    page.content = page.content.replace(pattern, tag);

    var pattern = /<p>!PLAYGROUND (.+)<\/p>\n<pre>/g;
    var tag = '<pre><a class="playground-link" target="_blank" href="https://play.rust-lang.org/?gist=$1"></a>';
    page.content = page.content.replace(pattern, tag);

    return page;
}

I would be interested in having the same capability to run snippets of logic against my pre-rendered and post-rendered rustdoc. I would like to have a Cargo subcommand:

cargo plugin-doc

which forwards to cargo doc but executes plugins before (on the Markdown) and after (on the HTML).

Plugins would be listed as metadata in Cargo.toml:

[package.metadata.rustdoc]
plugins = ["playground-links", "..."]

Plugins would be installed as ordinary binaries like cargo install rustdoc-plugin-playground-links. This project also involves developing the library for writing plugins. Mirroring the GitBook system, plugin code might look something like:

use rustdoc_plugin::{Page, Plugin};

struct PlaygroundLinksPlugin;

// Both functions are optional and do nothing by default, so you can
// implement just one or the other.
impl Plugin for PlaygroundLinksPlugin {
    fn page_before(page: &mut Page) {
        /* ... */
    }
    fn page_after(page: &mut Page) {
        /* ... */
    }
}

fn main() {
    rustdoc_plugin::main(PlaygroundLinksPlugin);
}
pickfire commented 4 years ago

Would it be good to add hooks in rustdoc instead of adding these to another crate? If possible, I would like to help on this.

dtolnay commented 4 years ago

I think it would be substantially easier to experiment with this on its own outside of the main rustdoc codebase.

After there is a working product and it's something people like, we could start making a case for including it in rustdoc.

astaninger commented 4 years ago

I would like to give this a shot, if it hasn't already been done