Skip to content

Commit 00ae7ad

Browse files
committed
Auto merge of rust-lang#110800 - GuillaumeGomez:custom_code_classes_in_docs, r=t-rustdoc
Accept additional user-defined syntax classes in fenced code blocks Part of rust-lang#79483. This is a re-opening of rust-lang#79454 after a big update/cleanup. I also converted the syntax to pandoc as suggested by `@notriddle:` the idea is to be as compatible as possible with the existing instead of having our own syntax. ## Motivation From the original issue: rust-lang#78917 > The technique used by `inline-c-rs` can be ported to other languages. It's just super fun to see C code inside Rust documentation that is also tested by `cargo doc`. I'm sure this technique can be used by other languages in the future. Having custom CSS classes for syntax highlighting will allow tools like `highlight.js` to be used in order to provide highlighting for languages other than Rust while not increasing technical burden on rustdoc. ## What is the feature about? In short, this PR changes two things, both related to codeblocks in doc comments in Rust documentation: * Allow to disable generation of `language-*` CSS classes with the `custom` attribute. * Add your own CSS classes to a code block so that you can use other tools to highlight them. #### The `custom` attribute Let's start with the new `custom` attribute: it will disable the generation of the `language-*` CSS class on the generated HTML code block. For example: ```rust /// ```custom,c /// int main(void) { /// return 0; /// } /// ``` ``` The generated HTML code block will not have `class="language-c"` because the `custom` attribute has been set. The `custom` attribute becomes especially useful with the other thing added by this feature: adding your own CSS classes. #### Adding your own CSS classes The second part of this feature is to allow users to add CSS classes themselves so that they can then add a JS library which will do it (like `highlight.js` or `prism.js`), allowing to support highlighting for other languages than Rust without increasing burden on rustdoc. To disable the automatic `language-*` CSS class generation, you need to use the `custom` attribute as well. This allow users to write the following: ```rust /// Some code block with `{class=language-c}` as the language string. /// /// ```custom,{class=language-c} /// int main(void) { /// return 0; /// } /// ``` fn main() {} ``` This will notably produce the following HTML: ```html <pre class="language-c"> int main(void) { return 0; }</pre> ``` Instead of: ```html <pre class="rust rust-example-rendered"> <span class="ident">int</span> <span class="ident">main</span>(<span class="ident">void</span>) { <span class="kw">return</span> <span class="number">0</span>; } </pre> ``` To be noted, we could have written `{.language-c}` to achieve the same result. `.` and `class=` have the same effect. One last syntax point: content between parens (`(like this)`) is now considered as comment and is not taken into account at all. In addition to this, I added an `unknown` field into `LangString` (the parsed code block "attribute") because of cases like this: ```rust /// ```custom,class:language-c /// main; /// ``` pub fn foo() {} ``` Without this `unknown` field, it would generate in the DOM: `<pre class="language-class:language-c language-c">`, which is quite bad. So instead, it now stores all unknown tags into the `unknown` field and use the first one as "language". So in this case, since there is no unknown tag, it'll simply generate `<pre class="language-c">`. I added tests to cover this. Finally, I added a parser for the codeblock attributes to make it much easier to maintain. It'll be pretty easy to extend. As to why this syntax for adding attributes was picked: it's [Pandoc's syntax](https://pandoc.org/MANUAL.html#extension-fenced_code_attributes). Even if it seems clunkier in some cases, it's extensible, and most third-party Markdown renderers are smart enough to ignore Pandoc's brace-delimited attributes (from [this comment](rust-lang#110800 (comment))). ## Raised concerns #### It's not obvious when the `language-*` attribute generation will be added or not. It is added by default. If you want to disable it, you will need to use the `custom` attribute. #### Why not using HTML in markdown directly then? Code examples in most languages are likely to contain `<`, `>`, `&` and `"` characters. These characters [require escaping](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre) when written inside the `<pre>` element. Using the \`\`\` code blocks allows rustdoc to take care of escaping, which means doc authors can paste code samples directly without manually converting them to HTML. cc `@poliorcetics` r? `@notriddle`
2 parents 20999de + 7c72edf commit 00ae7ad

17 files changed

+965
-74
lines changed

compiler/rustc_feature/src/active.rs

+2
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,8 @@ declare_features! (
401401
/// Allows function attribute `#[coverage(on/off)]`, to control coverage
402402
/// instrumentation of that function.
403403
(active, coverage_attribute, "CURRENT_RUSTC_VERSION", Some(84605), None),
404+
/// Allows users to provide classes for fenced code block using `class:classname`.
405+
(active, custom_code_classes_in_docs, "CURRENT_RUSTC_VERSION", Some(79483), None),
404406
/// Allows non-builtin attributes in inner attribute position.
405407
(active, custom_inner_attributes, "1.30.0", Some(54726), None),
406408
/// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`.

compiler/rustc_middle/src/ty/typeck_results.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -165,15 +165,15 @@ pub struct TypeckResults<'tcx> {
165165
/// reading places that are mentioned in a closure (because of _ patterns). However,
166166
/// to ensure the places are initialized, we introduce fake reads.
167167
/// Consider these two examples:
168-
/// ``` (discriminant matching with only wildcard arm)
168+
/// ```ignore (discriminant matching with only wildcard arm)
169169
/// let x: u8;
170170
/// let c = || match x { _ => () };
171171
/// ```
172172
/// In this example, we don't need to actually read/borrow `x` in `c`, and so we don't
173173
/// want to capture it. However, we do still want an error here, because `x` should have
174174
/// to be initialized at the point where c is created. Therefore, we add a "fake read"
175175
/// instead.
176-
/// ``` (destructured assignments)
176+
/// ```ignore (destructured assignments)
177177
/// let c = || {
178178
/// let (t1, t2) = t;
179179
/// }

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,7 @@ symbols! {
592592
cttz,
593593
cttz_nonzero,
594594
custom_attribute,
595+
custom_code_classes_in_docs,
595596
custom_derive,
596597
custom_inner_attributes,
597598
custom_mir,

src/doc/rustdoc/src/unstable-features.md

+44
Original file line numberDiff line numberDiff line change
@@ -625,3 +625,47 @@ and check the values of `feature`: `foo` and `bar`.
625625

626626
This flag enables the generation of links in the source code pages which allow the reader
627627
to jump to a type definition.
628+
629+
### Custom CSS classes for code blocks
630+
631+
```rust
632+
#![feature(custom_code_classes_in_docs)]
633+
634+
/// ```custom,{class=language-c}
635+
/// int main(void) { return 0; }
636+
/// ```
637+
pub struct Bar;
638+
```
639+
640+
The text `int main(void) { return 0; }` is rendered without highlighting in a code block
641+
with the class `language-c`. This can be used to highlight other languages through JavaScript
642+
libraries for example.
643+
644+
Without the `custom` attribute, it would be generated as a Rust code example with an additional
645+
`language-C` CSS class. Therefore, if you specifically don't want it to be a Rust code example,
646+
don't forget to add the `custom` attribute.
647+
648+
To be noted that you can replace `class=` with `.` to achieve the same result:
649+
650+
```rust
651+
#![feature(custom_code_classes_in_docs)]
652+
653+
/// ```custom,{.language-c}
654+
/// int main(void) { return 0; }
655+
/// ```
656+
pub struct Bar;
657+
```
658+
659+
To be noted, `rust` and `.rust`/`class=rust` have different effects: `rust` indicates that this is
660+
a Rust code block whereas the two others add a "rust" CSS class on the code block.
661+
662+
You can also use double quotes:
663+
664+
```rust
665+
#![feature(custom_code_classes_in_docs)]
666+
667+
/// ```"not rust" {."hello everyone"}
668+
/// int main(void) { return 0; }
669+
/// ```
670+
pub struct Bar;
671+
```

src/librustdoc/html/highlight.rs

+21-4
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,9 @@ pub(crate) fn render_example_with_highlighting(
5252
out: &mut Buffer,
5353
tooltip: Tooltip,
5454
playground_button: Option<&str>,
55+
extra_classes: &[String],
5556
) {
56-
write_header(out, "rust-example-rendered", None, tooltip);
57+
write_header(out, "rust-example-rendered", None, tooltip, extra_classes);
5758
write_code(out, src, None, None);
5859
write_footer(out, playground_button);
5960
}
@@ -65,7 +66,13 @@ pub(crate) fn render_item_decl_with_highlighting(src: &str, out: &mut Buffer) {
6566
write!(out, "</pre>");
6667
}
6768

68-
fn write_header(out: &mut Buffer, class: &str, extra_content: Option<Buffer>, tooltip: Tooltip) {
69+
fn write_header(
70+
out: &mut Buffer,
71+
class: &str,
72+
extra_content: Option<Buffer>,
73+
tooltip: Tooltip,
74+
extra_classes: &[String],
75+
) {
6976
write!(
7077
out,
7178
"<div class=\"example-wrap{}\">",
@@ -100,9 +107,19 @@ fn write_header(out: &mut Buffer, class: &str, extra_content: Option<Buffer>, to
100107
out.push_buffer(extra);
101108
}
102109
if class.is_empty() {
103-
write!(out, "<pre class=\"rust\">");
110+
write!(
111+
out,
112+
"<pre class=\"rust{}{}\">",
113+
if extra_classes.is_empty() { "" } else { " " },
114+
extra_classes.join(" "),
115+
);
104116
} else {
105-
write!(out, "<pre class=\"rust {class}\">");
117+
write!(
118+
out,
119+
"<pre class=\"rust {class}{}{}\">",
120+
if extra_classes.is_empty() { "" } else { " " },
121+
extra_classes.join(" "),
122+
);
106123
}
107124
write!(out, "<code>");
108125
}

0 commit comments

Comments
 (0)