summaryrefslogtreecommitdiffstats
path: root/src/librustdoc/html/format.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc/html/format.rs')
-rw-r--r--src/librustdoc/html/format.rs202
1 files changed, 104 insertions, 98 deletions
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 0e4c5ed68..1b445b898 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -1,13 +1,15 @@
//! HTML formatting module
//!
//! This module contains a large number of `fmt::Display` implementations for
-//! various types in `rustdoc::clean`. These implementations all currently
-//! assume that HTML output is desired, although it may be possible to redesign
-//! them in the future to instead emit any format desired.
+//! various types in `rustdoc::clean`.
+//!
+//! These implementations all emit HTML. As an internal implementation detail,
+//! some of them support an alternate format that emits text, but that should
+//! not be used external to this module.
use std::borrow::Cow;
use std::cell::Cell;
-use std::fmt;
+use std::fmt::{self, Write};
use std::iter::{self, once};
use rustc_ast as ast;
@@ -19,7 +21,6 @@ use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_metadata::creader::{CStore, LoadedMacro};
use rustc_middle::ty;
-use rustc_middle::ty::DefIdTree;
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::kw;
use rustc_span::{sym, Symbol};
@@ -127,7 +128,6 @@ impl Buffer {
// the fmt::Result return type imposed by fmt::Write (and avoiding the trait
// import).
pub(crate) fn write_fmt(&mut self, v: fmt::Arguments<'_>) {
- use fmt::Write;
self.buffer.write_fmt(v).unwrap();
}
@@ -136,10 +136,6 @@ impl Buffer {
self.into_inner()
}
- pub(crate) fn is_for_html(&self) -> bool {
- self.for_html
- }
-
pub(crate) fn reserve(&mut self, additional: usize) {
self.buffer.reserve(additional)
}
@@ -280,8 +276,6 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
indent: usize,
ending: Ending,
) -> impl fmt::Display + 'a + Captures<'tcx> {
- use fmt::Write;
-
display_fn(move |f| {
let mut where_predicates = gens.where_predicates.iter().filter(|pred| {
!matches!(pred, clean::WherePredicate::BoundPredicate { bounds, .. } if bounds.is_empty())
@@ -309,13 +303,13 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
write!(
f,
"for<{:#}> {ty_cx:#}: {generic_bounds:#}",
- comma_sep(bound_params.iter().map(|lt| lt.print()), true)
+ comma_sep(bound_params.iter().map(|lt| lt.print(cx)), true)
)
} else {
write!(
f,
"for&lt;{}&gt; {ty_cx}: {generic_bounds}",
- comma_sep(bound_params.iter().map(|lt| lt.print()), true)
+ comma_sep(bound_params.iter().map(|lt| lt.print(cx)), true)
)
}
}
@@ -355,10 +349,10 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
let mut br_with_padding = String::with_capacity(6 * indent + 28);
br_with_padding.push_str("\n");
- let padding_amout =
+ let padding_amount =
if ending == Ending::Newline { indent + 4 } else { indent + "fn where ".len() };
- for _ in 0..padding_amout {
+ for _ in 0..padding_amount {
br_with_padding.push_str(" ");
}
let where_preds = where_preds.to_string().replace('\n', &br_with_padding);
@@ -773,6 +767,12 @@ pub(crate) fn link_tooltip(did: DefId, fragment: &Option<UrlFragment>, cx: &Cont
.or_else(|| cache.external_paths.get(&did))
else { return String::new() };
let mut buf = Buffer::new();
+ let fqp = if *shortty == ItemType::Primitive {
+ // primitives are documented in a crate, but not actually part of it
+ &fqp[fqp.len() - 1..]
+ } else {
+ &fqp
+ };
if let &Some(UrlFragment::Item(id)) = fragment {
write!(buf, "{} ", cx.tcx().def_descr(id));
for component in fqp {
@@ -1138,22 +1138,21 @@ fn fmt_type<'cx>(
// the ugliness comes from inlining across crates where
// everything comes in as a fully resolved QPath (hard to
// look at).
- match href(trait_.def_id(), cx) {
- Ok((ref url, _, ref path)) if !f.alternate() => {
- write!(
- f,
- "<a class=\"associatedtype\" href=\"{url}#{shortty}.{name}\" \
- title=\"type {path}::{name}\">{name}</a>{args}",
- url = url,
- shortty = ItemType::AssocType,
- name = assoc.name,
- path = join_with_double_colon(path),
- args = assoc.args.print(cx),
- )?;
- }
- _ => write!(f, "{}{:#}", assoc.name, assoc.args.print(cx))?,
- }
- Ok(())
+ if !f.alternate() && let Ok((url, _, path)) = href(trait_.def_id(), cx) {
+ write!(
+ f,
+ "<a class=\"associatedtype\" href=\"{url}#{shortty}.{name}\" \
+ title=\"type {path}::{name}\">{name}</a>",
+ shortty = ItemType::AssocType,
+ name = assoc.name,
+ path = join_with_double_colon(&path),
+ )
+ } else {
+ write!(f, "{}", assoc.name)
+ }?;
+
+ // Carry `f.alternate()` into this display w/o branching manually.
+ fmt::Display::fmt(&assoc.args.print(cx), f)
}
}
}
@@ -1307,6 +1306,28 @@ impl clean::BareFunctionDecl {
}
}
+// Implements Write but only counts the bytes "written".
+struct WriteCounter(usize);
+
+impl std::fmt::Write for WriteCounter {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ self.0 += s.len();
+ Ok(())
+ }
+}
+
+// Implements Display by emitting the given number of spaces.
+struct Indent(usize);
+
+impl fmt::Display for Indent {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ (0..self.0).for_each(|_| {
+ f.write_char(' ').unwrap();
+ });
+ Ok(())
+ }
+}
+
impl clean::FnDecl {
pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
&'a self,
@@ -1346,95 +1367,80 @@ impl clean::FnDecl {
indent: usize,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
- display_fn(move |f| self.inner_full_print(header_len, indent, f, cx))
+ display_fn(move |f| {
+ // First, generate the text form of the declaration, with no line wrapping, and count the bytes.
+ let mut counter = WriteCounter(0);
+ write!(&mut counter, "{:#}", display_fn(|f| { self.inner_full_print(None, f, cx) }))
+ .unwrap();
+ // If the text form was over 80 characters wide, we will line-wrap our output.
+ let line_wrapping_indent =
+ if header_len + counter.0 > 80 { Some(indent) } else { None };
+ // Generate the final output. This happens to accept `{:#}` formatting to get textual
+ // output but in practice it is only formatted with `{}` to get HTML output.
+ self.inner_full_print(line_wrapping_indent, f, cx)
+ })
}
fn inner_full_print(
&self,
- header_len: usize,
- indent: usize,
+ // For None, the declaration will not be line-wrapped. For Some(n),
+ // the declaration will be line-wrapped, with an indent of n spaces.
+ line_wrapping_indent: Option<usize>,
f: &mut fmt::Formatter<'_>,
cx: &Context<'_>,
) -> fmt::Result {
let amp = if f.alternate() { "&" } else { "&amp;" };
- let mut args = Buffer::html();
- let mut args_plain = Buffer::new();
+
+ write!(f, "(")?;
+ if let Some(n) = line_wrapping_indent {
+ write!(f, "\n{}", Indent(n + 4))?;
+ }
for (i, input) in self.inputs.values.iter().enumerate() {
+ if i > 0 {
+ match line_wrapping_indent {
+ None => write!(f, ", ")?,
+ Some(n) => write!(f, ",\n{}", Indent(n + 4))?,
+ };
+ }
if let Some(selfty) = input.to_self() {
match selfty {
clean::SelfValue => {
- args.push_str("self");
- args_plain.push_str("self");
+ write!(f, "self")?;
}
clean::SelfBorrowed(Some(ref lt), mtbl) => {
- write!(args, "{}{} {}self", amp, lt.print(), mtbl.print_with_space());
- write!(args_plain, "&{} {}self", lt.print(), mtbl.print_with_space());
+ write!(f, "{}{} {}self", amp, lt.print(), mtbl.print_with_space())?;
}
clean::SelfBorrowed(None, mtbl) => {
- write!(args, "{}{}self", amp, mtbl.print_with_space());
- write!(args_plain, "&{}self", mtbl.print_with_space());
+ write!(f, "{}{}self", amp, mtbl.print_with_space())?;
}
clean::SelfExplicit(ref typ) => {
- if f.alternate() {
- write!(args, "self: {:#}", typ.print(cx));
- } else {
- write!(args, "self: {}", typ.print(cx));
- }
- write!(args_plain, "self: {:#}", typ.print(cx));
+ write!(f, "self: ")?;
+ fmt::Display::fmt(&typ.print(cx), f)?;
}
}
} else {
- if i > 0 {
- args.push_str("\n");
- }
if input.is_const {
- args.push_str("const ");
- args_plain.push_str("const ");
- }
- write!(args, "{}: ", input.name);
- write!(args_plain, "{}: ", input.name);
-
- if f.alternate() {
- write!(args, "{:#}", input.type_.print(cx));
- } else {
- write!(args, "{}", input.type_.print(cx));
+ write!(f, "const ")?;
}
- write!(args_plain, "{:#}", input.type_.print(cx));
- }
- if i + 1 < self.inputs.values.len() {
- args.push_str(",");
- args_plain.push_str(",");
+ write!(f, "{}: ", input.name)?;
+ fmt::Display::fmt(&input.type_.print(cx), f)?;
}
}
- let mut args_plain = format!("({})", args_plain.into_inner());
- let mut args = args.into_inner();
-
if self.c_variadic {
- args.push_str(",\n ...");
- args_plain.push_str(", ...");
+ match line_wrapping_indent {
+ None => write!(f, ", ...")?,
+ Some(n) => write!(f, "\n{}...", Indent(n + 4))?,
+ };
}
- let arrow_plain = format!("{:#}", self.output.print(cx));
- let arrow =
- if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) };
-
- let declaration_len = header_len + args_plain.len() + arrow_plain.len();
- let output = if declaration_len > 80 {
- let full_pad = format!("\n{}", " ".repeat(indent + 4));
- let close_pad = format!("\n{}", " ".repeat(indent));
- format!(
- "({pad}{args}{close}){arrow}",
- pad = if self.inputs.values.is_empty() { "" } else { &full_pad },
- args = args.replace('\n', &full_pad),
- close = close_pad,
- arrow = arrow
- )
- } else {
- format!("({args}){arrow}", args = args.replace('\n', " "), arrow = arrow)
+ match line_wrapping_indent {
+ None => write!(f, ")")?,
+ Some(n) => write!(f, "\n{})", Indent(n))?,
};
- write!(f, "{}", output)
+ fmt::Display::fmt(&self.output.print(cx), f)?;
+ Ok(())
}
}
@@ -1469,7 +1475,7 @@ pub(crate) fn visibility_print_with_space<'a, 'tcx: 'a>(
debug!("path={:?}", path);
// modified from `resolved_path()` to work with `DefPathData`
let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
- let anchor = anchor(vis_did, last_name, cx).to_string();
+ let anchor = anchor(vis_did, last_name, cx);
let mut s = "pub(in ".to_owned();
for seg in &path.data[..path.data.len() - 1] {
@@ -1491,9 +1497,9 @@ pub(crate) fn visibility_to_src_with_space<'a, 'tcx: 'a>(
tcx: TyCtxt<'tcx>,
item_did: DefId,
) -> impl fmt::Display + 'a + Captures<'tcx> {
- let to_print = match visibility {
- None => String::new(),
- Some(ty::Visibility::Public) => "pub ".to_owned(),
+ let to_print: Cow<'static, str> = match visibility {
+ None => "".into(),
+ Some(ty::Visibility::Public) => "pub ".into(),
Some(ty::Visibility::Restricted(vis_did)) => {
// FIXME(camelid): This may not work correctly if `item_did` is a module.
// However, rustdoc currently never displays a module's
@@ -1501,17 +1507,17 @@ pub(crate) fn visibility_to_src_with_space<'a, 'tcx: 'a>(
let parent_module = find_nearest_parent_module(tcx, item_did);
if vis_did.is_crate_root() {
- "pub(crate) ".to_owned()
+ "pub(crate) ".into()
} else if parent_module == Some(vis_did) {
// `pub(in foo)` where `foo` is the parent module
// is the same as no visibility modifier
- String::new()
+ "".into()
} else if parent_module.and_then(|parent| find_nearest_parent_module(tcx, parent))
== Some(vis_did)
{
- "pub(super) ".to_owned()
+ "pub(super) ".into()
} else {
- format!("pub(in {}) ", tcx.def_path_str(vis_did))
+ format!("pub(in {}) ", tcx.def_path_str(vis_did)).into()
}
}
};