Skip to content

Commit 6da2b7d

Browse files
committed
rustdoc: do not escape quotes in body text
Escaping quote marks is only needed in attributes, not text. ```console $ du -hs doc-old/ doc-new/ 670M doc-old/ 669M doc-new/ ```
1 parent 7230f6c commit 6da2b7d

File tree

4 files changed

+50
-8
lines changed

4 files changed

+50
-8
lines changed

src/librustdoc/html/escape.rs

+36
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,39 @@ impl<'a> fmt::Display for Escape<'a> {
3838
Ok(())
3939
}
4040
}
41+
42+
/// Wrapper struct which will emit the HTML-escaped version of the contained
43+
/// string when passed to a format string.
44+
///
45+
/// This is only safe to use for text nodes. If you need your output to be
46+
/// safely contained in an attribute, use [`Escape`]. If you don't know the
47+
/// difference, use [`Escape`].
48+
pub(crate) struct EscapeBodyText<'a>(pub &'a str);
49+
50+
impl<'a> fmt::Display for EscapeBodyText<'a> {
51+
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
52+
// Because the internet is always right, turns out there's not that many
53+
// characters to escape: http://stackoverflow.com/questions/7381974
54+
let EscapeBodyText(s) = *self;
55+
let pile_o_bits = s;
56+
let mut last = 0;
57+
for (i, ch) in s.char_indices() {
58+
let s = match ch {
59+
'>' => "&gt;",
60+
'<' => "&lt;",
61+
'&' => "&amp;",
62+
_ => continue,
63+
};
64+
fmt.write_str(&pile_o_bits[last..i])?;
65+
fmt.write_str(s)?;
66+
// NOTE: we only expect single byte characters here - which is fine as long as we
67+
// only match single byte characters
68+
last = i + 1;
69+
}
70+
71+
if last < s.len() {
72+
fmt.write_str(&pile_o_bits[last..])?;
73+
}
74+
Ok(())
75+
}
76+
}

src/librustdoc/html/highlight.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
//! Use the `render_with_highlighting` to highlight some rust code.
77
88
use crate::clean::PrimitiveType;
9-
use crate::html::escape::Escape;
9+
use crate::html::escape::EscapeBodyText;
1010
use crate::html::render::{Context, LinkFromSrc};
1111

1212
use std::collections::VecDeque;
@@ -189,7 +189,7 @@ impl<'a, 'tcx, F: Write> TokenHandler<'a, 'tcx, F> {
189189
&& can_merge(current_class, Some(*parent_class), "")
190190
{
191191
for (text, class) in self.pending_elems.iter() {
192-
string(self.out, Escape(text), *class, &self.href_context, false);
192+
string(self.out, EscapeBodyText(text), *class, &self.href_context, false);
193193
}
194194
} else {
195195
// We only want to "open" the tag ourselves if we have more than one pending and if the
@@ -202,7 +202,13 @@ impl<'a, 'tcx, F: Write> TokenHandler<'a, 'tcx, F> {
202202
None
203203
};
204204
for (text, class) in self.pending_elems.iter() {
205-
string(self.out, Escape(text), *class, &self.href_context, close_tag.is_none());
205+
string(
206+
self.out,
207+
EscapeBodyText(text),
208+
*class,
209+
&self.href_context,
210+
close_tag.is_none(),
211+
);
206212
}
207213
if let Some(close_tag) = close_tag {
208214
exit_span(self.out, close_tag);
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
<span class="kw">pub fn </span>foo() {
2-
<span class="macro">println!</span>(<span class="string">&quot;foo&quot;</span>);
2+
<span class="macro">println!</span>(<span class="string">"foo"</span>);
33
}

src/librustdoc/html/highlight/fixtures/sample.html

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
.lifetime { color: #B76514; }
99
.question-mark { color: #ff9011; }
1010
</style>
11-
<pre><code><span class="attr">#![crate_type = <span class="string">&quot;lib&quot;</span>]
11+
<pre><code><span class="attr">#![crate_type = <span class="string">"lib"</span>]
1212

1313
</span><span class="kw">use </span>std::path::{Path, PathBuf};
1414

15-
<span class="attr">#[cfg(target_os = <span class="string">&quot;linux&quot;</span>)]
16-
#[cfg(target_os = <span class="string">&quot;windows&quot;</span>)]
15+
<span class="attr">#[cfg(target_os = <span class="string">"linux"</span>)]
16+
#[cfg(target_os = <span class="string">"windows"</span>)]
1717
</span><span class="kw">fn </span>main() -&gt; () {
1818
<span class="kw">let </span>foo = <span class="bool-val">true </span>&amp;&amp; <span class="bool-val">false </span>|| <span class="bool-val">true</span>;
1919
<span class="kw">let _</span>: <span class="kw-2">*const </span>() = <span class="number">0</span>;
@@ -22,7 +22,7 @@
2222
<span class="kw">let _ </span>= <span class="kw-2">*</span>foo;
2323
<span class="macro">mac!</span>(foo, <span class="kw-2">&amp;mut </span>bar);
2424
<span class="macro">assert!</span>(<span class="self">self</span>.length &lt; N &amp;&amp; index &lt;= <span class="self">self</span>.length);
25-
::std::env::var(<span class="string">&quot;gateau&quot;</span>).is_ok();
25+
::std::env::var(<span class="string">"gateau"</span>).is_ok();
2626
<span class="attr">#[rustfmt::skip]
2727
</span><span class="kw">let </span>s:std::path::PathBuf = std::path::PathBuf::new();
2828
<span class="kw">let </span><span class="kw-2">mut </span>s = String::new();

0 commit comments

Comments
 (0)