Skip to content

Commit 4a8ccdb

Browse files
committed
Auto merge of #64044 - Mark-Simulacrum:rustdoc-clean-2, r=GuillaumeGomez
Rustdoc: formatting to buffers This should be reviewed commit-by-commit. I've not attempted to fully flesh out what the end state of this PR could look like yet as I wanted to get it up for some early feedback (I already think this has some wins, too, so we could land it as-is). The primary idea with `Buffer` is that it internally tracks whether we're printing to HTML or text, and the goal is that eventually instead of branch on `fmt.alternate()` anywhere, we'd call a helper like `buf.nbsp()` which would either return ` ` or ` ` depending on the target we're printing to. Obviously, that's not included in this PR, in part because it was already getting quite big.
2 parents f7b05af + 02c5c5c commit 4a8ccdb

File tree

4 files changed

+864
-916
lines changed

4 files changed

+864
-916
lines changed

src/librustdoc/html/format.rs

+102-8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,100 @@ use crate::clean::{self, PrimitiveType};
1818
use crate::html::item_type::ItemType;
1919
use crate::html::render::{self, cache, CURRENT_DEPTH};
2020

21+
pub trait Print {
22+
fn print(self, buffer: &mut Buffer);
23+
}
24+
25+
impl<F> Print for F
26+
where F: FnOnce(&mut Buffer),
27+
{
28+
fn print(self, buffer: &mut Buffer) {
29+
(self)(buffer)
30+
}
31+
}
32+
33+
impl Print for String {
34+
fn print(self, buffer: &mut Buffer) {
35+
buffer.write_str(&self);
36+
}
37+
}
38+
39+
impl Print for &'_ str {
40+
fn print(self, buffer: &mut Buffer) {
41+
buffer.write_str(self);
42+
}
43+
}
44+
45+
#[derive(Debug, Clone)]
46+
pub struct Buffer {
47+
for_html: bool,
48+
buffer: String,
49+
}
50+
51+
impl Buffer {
52+
crate fn empty_from(v: &Buffer) -> Buffer {
53+
Buffer {
54+
for_html: v.for_html,
55+
buffer: String::new(),
56+
}
57+
}
58+
59+
crate fn html() -> Buffer {
60+
Buffer {
61+
for_html: true,
62+
buffer: String::new(),
63+
}
64+
}
65+
66+
crate fn is_empty(&self) -> bool {
67+
self.buffer.is_empty()
68+
}
69+
70+
crate fn into_inner(self) -> String {
71+
self.buffer
72+
}
73+
74+
crate fn insert_str(&mut self, idx: usize, s: &str) {
75+
self.buffer.insert_str(idx, s);
76+
}
77+
78+
crate fn push_str(&mut self, s: &str) {
79+
self.buffer.push_str(s);
80+
}
81+
82+
// Intended for consumption by write! and writeln! (std::fmt) but without
83+
// the fmt::Result return type imposed by fmt::Write (and avoiding the trait
84+
// import).
85+
crate fn write_str(&mut self, s: &str) {
86+
self.buffer.push_str(s);
87+
}
88+
89+
// Intended for consumption by write! and writeln! (std::fmt) but without
90+
// the fmt::Result return type imposed by fmt::Write (and avoiding the trait
91+
// import).
92+
crate fn write_fmt(&mut self, v: fmt::Arguments<'_>) {
93+
use fmt::Write;
94+
self.buffer.write_fmt(v).unwrap();
95+
}
96+
97+
crate fn to_display<T: Print>(mut self, t: T) -> String {
98+
t.print(&mut self);
99+
self.into_inner()
100+
}
101+
102+
crate fn with_formatter<T: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result>(&mut self, t: T) {
103+
self.from_display(display_fn(move |f| (t)(f)));
104+
}
105+
106+
crate fn from_display<T: std::fmt::Display>(&mut self, t: T) {
107+
if self.for_html {
108+
write!(self, "{}", t);
109+
} else {
110+
write!(self, "{:#}", t);
111+
}
112+
}
113+
}
114+
21115
/// Helper to render an optional visibility with a space after it (if the
22116
/// visibility is preset)
23117
#[derive(Copy, Clone)]
@@ -200,11 +294,11 @@ impl<'a> fmt::Display for WhereClause<'a> {
200294
}
201295
&clean::WherePredicate::RegionPredicate { ref lifetime, ref bounds } => {
202296
clause.push_str(&format!("{}: {}",
203-
lifetime,
204-
bounds.iter()
205-
.map(|b| b.to_string())
206-
.collect::<Vec<_>>()
207-
.join(" + ")));
297+
lifetime,
298+
bounds.iter()
299+
.map(|b| b.to_string())
300+
.collect::<Vec<_>>()
301+
.join(" + ")));
208302
}
209303
&clean::WherePredicate::EqPredicate { ref lhs, ref rhs } => {
210304
if f.alternate() {
@@ -778,9 +872,9 @@ impl fmt::Display for clean::Impl {
778872

779873
// The difference from above is that trait is not hyperlinked.
780874
pub fn fmt_impl_for_trait_page(i: &clean::Impl,
781-
f: &mut fmt::Formatter<'_>,
782-
use_absolute: bool) -> fmt::Result {
783-
fmt_impl(i, f, false, use_absolute)
875+
f: &mut Buffer,
876+
use_absolute: bool) {
877+
f.with_formatter(|f| fmt_impl(i, f, false, use_absolute))
784878
}
785879

786880
impl fmt::Display for clean::Arguments {

src/librustdoc/html/layout.rs

+18-17
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
1-
use std::fmt;
2-
use std::io;
31
use std::path::PathBuf;
42

53
use crate::externalfiles::ExternalHtml;
64
use crate::html::render::SlashChecker;
5+
use crate::html::format::{Buffer, Print};
76

87
#[derive(Clone)]
98
pub struct Layout {
109
pub logo: String,
1110
pub favicon: String,
1211
pub external_html: ExternalHtml,
1312
pub krate: String,
13+
/// The given user css file which allow to customize the generated
14+
/// documentation theme.
15+
pub css_file_extension: Option<PathBuf>,
16+
/// If false, the `select` element to have search filtering by crates on rendered docs
17+
/// won't be generated.
18+
pub generate_search_filter: bool,
1419
}
1520

1621
pub struct Page<'a> {
@@ -25,19 +30,15 @@ pub struct Page<'a> {
2530
pub static_extra_scripts: &'a [&'a str],
2631
}
2732

28-
pub fn render<T: fmt::Display, S: fmt::Display>(
29-
dst: &mut dyn io::Write,
33+
pub fn render<T: Print, S: Print>(
3034
layout: &Layout,
3135
page: &Page<'_>,
32-
sidebar: &S,
33-
t: &T,
34-
css_file_extension: bool,
36+
sidebar: S,
37+
t: T,
3538
themes: &[PathBuf],
36-
generate_search_filter: bool,
37-
) -> io::Result<()> {
39+
) -> String {
3840
let static_root_path = page.static_root_path.unwrap_or(page.root_path);
39-
write!(dst,
40-
"<!DOCTYPE html>\
41+
format!("<!DOCTYPE html>\
4142
<html lang=\"en\">\
4243
<head>\
4344
<meta charset=\"utf-8\">\
@@ -164,7 +165,7 @@ pub fn render<T: fmt::Display, S: fmt::Display>(
164165
<script defer src=\"{root_path}search-index{suffix}.js\"></script>\
165166
</body>\
166167
</html>",
167-
css_extension = if css_file_extension {
168+
css_extension = if layout.css_file_extension.is_some() {
168169
format!("<link rel=\"stylesheet\" \
169170
type=\"text/css\" \
170171
href=\"{static_root_path}theme{suffix}.css\">",
@@ -173,7 +174,7 @@ pub fn render<T: fmt::Display, S: fmt::Display>(
173174
} else {
174175
String::new()
175176
},
176-
content = *t,
177+
content = Buffer::html().to_display(t),
177178
static_root_path = static_root_path,
178179
root_path = page.root_path,
179180
css_class = page.css_class,
@@ -207,7 +208,7 @@ pub fn render<T: fmt::Display, S: fmt::Display>(
207208
in_header = layout.external_html.in_header,
208209
before_content = layout.external_html.before_content,
209210
after_content = layout.external_html.after_content,
210-
sidebar = *sidebar,
211+
sidebar = Buffer::html().to_display(sidebar),
211212
krate = layout.krate,
212213
themes = themes.iter()
213214
.filter_map(|t| t.file_stem())
@@ -228,7 +229,7 @@ pub fn render<T: fmt::Display, S: fmt::Display>(
228229
root_path=page.root_path,
229230
extra_script=e)
230231
}).collect::<String>(),
231-
filter_crates=if generate_search_filter {
232+
filter_crates=if layout.generate_search_filter {
232233
"<select id=\"crate-search\">\
233234
<option value=\"All crates\">All crates</option>\
234235
</select>"
@@ -238,9 +239,9 @@ pub fn render<T: fmt::Display, S: fmt::Display>(
238239
)
239240
}
240241

241-
pub fn redirect(dst: &mut dyn io::Write, url: &str) -> io::Result<()> {
242+
pub fn redirect(url: &str) -> String {
242243
// <script> triggers a redirect before refresh, so this is fine.
243-
write!(dst,
244+
format!(
244245
r##"<!DOCTYPE html>
245246
<html lang="en">
246247
<head>

0 commit comments

Comments
 (0)