Skip to content

Commit 121ae1d

Browse files
committed
rustdoc: reduce allocs in FnDecl::inner_full_print
Instead of maintaining parallel buffers for both HTML and non-HTML output, follow the idiom from the rest of format.rs that f.alternate() == true means textual output. Also, add an argument to control line wrapping explicitly. This allows the caller to render once with textual output and no line wrapping, to decide whether line wrapping should be applied in the final HTML output. Also, remove some format! and " ".repeat calls, and remove a dependency on calling `String::replace` to switch from newlines to spaces. This coincidentally fixes some minor bugs where the old code was undercounting the number of characters for a declaration in text mode.
1 parent 6dfaa14 commit 121ae1d

File tree

1 file changed

+71
-65
lines changed

1 file changed

+71
-65
lines changed

src/librustdoc/html/format.rs

+71-65
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
//! HTML formatting module
22
//!
33
//! This module contains a large number of `fmt::Display` implementations for
4-
//! various types in `rustdoc::clean`. These implementations all currently
5-
//! assume that HTML output is desired, although it may be possible to redesign
6-
//! them in the future to instead emit any format desired.
4+
//! various types in `rustdoc::clean`.
5+
//!
6+
//! These implementations all emit HTML. As an internal implementation detail,
7+
//! some of them support an alternate format that emits text, but that should
8+
//! not be used external to this module.
79
810
use std::borrow::Cow;
911
use std::cell::Cell;
10-
use std::fmt;
12+
use std::fmt::{self, Write};
1113
use std::iter::{self, once};
1214

1315
use rustc_ast as ast;
@@ -126,7 +128,6 @@ impl Buffer {
126128
// the fmt::Result return type imposed by fmt::Write (and avoiding the trait
127129
// import).
128130
pub(crate) fn write_fmt(&mut self, v: fmt::Arguments<'_>) {
129-
use fmt::Write;
130131
self.buffer.write_fmt(v).unwrap();
131132
}
132133

@@ -279,8 +280,6 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
279280
indent: usize,
280281
ending: Ending,
281282
) -> impl fmt::Display + 'a + Captures<'tcx> {
282-
use fmt::Write;
283-
284283
display_fn(move |f| {
285284
let mut where_predicates = gens.where_predicates.iter().filter(|pred| {
286285
!matches!(pred, clean::WherePredicate::BoundPredicate { bounds, .. } if bounds.is_empty())
@@ -1306,6 +1305,28 @@ impl clean::BareFunctionDecl {
13061305
}
13071306
}
13081307

1308+
// Implements Write but only counts the bytes "written".
1309+
struct WriteCounter(usize);
1310+
1311+
impl std::fmt::Write for WriteCounter {
1312+
fn write_str(&mut self, s: &str) -> fmt::Result {
1313+
self.0 += s.len();
1314+
Ok(())
1315+
}
1316+
}
1317+
1318+
// Implements Display by emitting the given number of spaces.
1319+
struct Indent(usize);
1320+
1321+
impl fmt::Display for Indent {
1322+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1323+
(0..self.0).for_each(|_| {
1324+
f.write_char(' ').unwrap();
1325+
});
1326+
Ok(())
1327+
}
1328+
}
1329+
13091330
impl clean::FnDecl {
13101331
pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
13111332
&'a self,
@@ -1345,95 +1366,80 @@ impl clean::FnDecl {
13451366
indent: usize,
13461367
cx: &'a Context<'tcx>,
13471368
) -> impl fmt::Display + 'a + Captures<'tcx> {
1348-
display_fn(move |f| self.inner_full_print(header_len, indent, f, cx))
1369+
display_fn(move |f| {
1370+
// First, generate the text form of the declaration, with no line wrapping, and count the bytes.
1371+
let mut counter = WriteCounter(0);
1372+
write!(&mut counter, "{:#}", display_fn(|f| { self.inner_full_print(None, f, cx) }))
1373+
.unwrap();
1374+
// If the text form was over 80 characters wide, we will line-wrap our output.
1375+
let line_wrapping_indent =
1376+
if header_len + counter.0 > 80 { Some(indent) } else { None };
1377+
// Generate the final output. This happens to accept `{:#}` formatting to get textual
1378+
// output but in practice it is only formatted with `{}` to get HTML output.
1379+
self.inner_full_print(line_wrapping_indent, f, cx)
1380+
})
13491381
}
13501382

13511383
fn inner_full_print(
13521384
&self,
1353-
header_len: usize,
1354-
indent: usize,
1385+
// For None, the declaration will not be line-wrapped. For Some(n),
1386+
// the declaration will be line-wrapped, with an indent of n spaces.
1387+
line_wrapping_indent: Option<usize>,
13551388
f: &mut fmt::Formatter<'_>,
13561389
cx: &Context<'_>,
13571390
) -> fmt::Result {
13581391
let amp = if f.alternate() { "&" } else { "&amp;" };
1359-
let mut args = Buffer::html();
1360-
let mut args_plain = Buffer::new();
1392+
1393+
write!(f, "(")?;
1394+
if let Some(n) = line_wrapping_indent {
1395+
write!(f, "\n{}", Indent(n + 4))?;
1396+
}
13611397
for (i, input) in self.inputs.values.iter().enumerate() {
1398+
if i > 0 {
1399+
match line_wrapping_indent {
1400+
None => write!(f, ", ")?,
1401+
Some(n) => write!(f, ",\n{}", Indent(n + 4))?,
1402+
};
1403+
}
13621404
if let Some(selfty) = input.to_self() {
13631405
match selfty {
13641406
clean::SelfValue => {
1365-
args.push_str("self");
1366-
args_plain.push_str("self");
1407+
write!(f, "self")?;
13671408
}
13681409
clean::SelfBorrowed(Some(ref lt), mtbl) => {
1369-
write!(args, "{}{} {}self", amp, lt.print(), mtbl.print_with_space());
1370-
write!(args_plain, "&{} {}self", lt.print(), mtbl.print_with_space());
1410+
write!(f, "{}{} {}self", amp, lt.print(), mtbl.print_with_space())?;
13711411
}
13721412
clean::SelfBorrowed(None, mtbl) => {
1373-
write!(args, "{}{}self", amp, mtbl.print_with_space());
1374-
write!(args_plain, "&{}self", mtbl.print_with_space());
1413+
write!(f, "{}{}self", amp, mtbl.print_with_space())?;
13751414
}
13761415
clean::SelfExplicit(ref typ) => {
1377-
if f.alternate() {
1378-
write!(args, "self: {:#}", typ.print(cx));
1379-
} else {
1380-
write!(args, "self: {}", typ.print(cx));
1381-
}
1382-
write!(args_plain, "self: {:#}", typ.print(cx));
1416+
write!(f, "self: ")?;
1417+
fmt::Display::fmt(&typ.print(cx), f)?;
13831418
}
13841419
}
13851420
} else {
1386-
if i > 0 {
1387-
args.push_str("\n");
1388-
}
13891421
if input.is_const {
1390-
args.push_str("const ");
1391-
args_plain.push_str("const ");
1392-
}
1393-
write!(args, "{}: ", input.name);
1394-
write!(args_plain, "{}: ", input.name);
1395-
1396-
if f.alternate() {
1397-
write!(args, "{:#}", input.type_.print(cx));
1398-
} else {
1399-
write!(args, "{}", input.type_.print(cx));
1422+
write!(f, "const ")?;
14001423
}
1401-
write!(args_plain, "{:#}", input.type_.print(cx));
1402-
}
1403-
if i + 1 < self.inputs.values.len() {
1404-
args.push_str(",");
1405-
args_plain.push_str(",");
1424+
write!(f, "{}: ", input.name)?;
1425+
fmt::Display::fmt(&input.type_.print(cx), f)?;
14061426
}
14071427
}
14081428

1409-
let mut args_plain = format!("({})", args_plain.into_inner());
1410-
let mut args = args.into_inner();
1411-
14121429
if self.c_variadic {
1413-
args.push_str(",\n ...");
1414-
args_plain.push_str(", ...");
1430+
match line_wrapping_indent {
1431+
None => write!(f, ", ...")?,
1432+
Some(n) => write!(f, "\n{}...", Indent(n + 4))?,
1433+
};
14151434
}
14161435

1417-
let arrow_plain = format!("{:#}", self.output.print(cx));
1418-
let arrow =
1419-
if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) };
1420-
1421-
let declaration_len = header_len + args_plain.len() + arrow_plain.len();
1422-
let output = if declaration_len > 80 {
1423-
let full_pad = format!("\n{}", " ".repeat(indent + 4));
1424-
let close_pad = format!("\n{}", " ".repeat(indent));
1425-
format!(
1426-
"({pad}{args}{close}){arrow}",
1427-
pad = if self.inputs.values.is_empty() { "" } else { &full_pad },
1428-
args = args.replace('\n', &full_pad),
1429-
close = close_pad,
1430-
arrow = arrow
1431-
)
1432-
} else {
1433-
format!("({args}){arrow}", args = args.replace('\n', " "), arrow = arrow)
1436+
match line_wrapping_indent {
1437+
None => write!(f, ")")?,
1438+
Some(n) => write!(f, "\n{})", Indent(n))?,
14341439
};
14351440

1436-
write!(f, "{}", output)
1441+
fmt::Display::fmt(&self.output.print(cx), f)?;
1442+
Ok(())
14371443
}
14381444
}
14391445

0 commit comments

Comments
 (0)