Skip to content

rustdoc: Unify where clause styling #113043

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 10 commits into from
147 changes: 49 additions & 98 deletions src/librustdoc/html/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

use std::borrow::Cow;
use std::cell::Cell;
use std::fmt::{self, Write};
use std::fmt::{self, Display, Write};
use std::iter::{self, once};

use rustc_ast as ast;
Expand Down Expand Up @@ -276,115 +276,66 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
indent: usize,
ending: Ending,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
let mut where_predicates = gens.where_predicates.iter().filter(|pred| {
!matches!(pred, clean::WherePredicate::BoundPredicate { bounds, .. } if bounds.is_empty())
}).map(|pred| {
display_fn(move |f| {
if f.alternate() {
f.write_str(" ")?;
} else {
f.write_str("\n")?;
}

match pred {
clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
let ty_cx = ty.print(cx);
let generic_bounds = print_generic_bounds(bounds, cx);
let padding_amount = indent + 4;

if bound_params.is_empty() {
if f.alternate() {
write!(f, "{ty_cx:#}: {generic_bounds:#}")
} else {
write!(f, "{ty_cx}: {generic_bounds}")
}
} else {
if f.alternate() {
write!(
f,
"for<{:#}> {ty_cx:#}: {generic_bounds:#}",
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(cx)), true)
)
}
}
}
clean::WherePredicate::RegionPredicate { lifetime, bounds } => {
let mut bounds_display = String::new();
for bound in bounds.iter().map(|b| b.print(cx)) {
write!(bounds_display, "{bound} + ")?;
}
bounds_display.truncate(bounds_display.len() - " + ".len());
write!(f, "{}: {bounds_display}", lifetime.print())
}
// FIXME(fmease): Render bound params.
clean::WherePredicate::EqPredicate { lhs, rhs, bound_params: _ } => {
if f.alternate() {
write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx))
} else {
write!(f, "{} == {}", lhs.print(cx), rhs.print(cx))
}
}
}
display_fn(move |f| {
let where_preds: String = gens
.where_predicates
.iter()
.filter(|pred| {
!matches!(pred, clean::WherePredicate::BoundPredicate { bounds, .. } if bounds.is_empty())
})
}).peekable();
.map(|pred| {
print_where_pred_helper(cx, pred, padding_amount)
})
.join(",");

if where_predicates.peek().is_none() {
if where_preds.is_empty() {
return Ok(());
}

let where_preds = comma_sep(where_predicates, false);
let clause = if f.alternate() {
if ending == Ending::Newline {
format!(" where{where_preds},")
} else {
format!(" where{where_preds}")
}
if ending == Ending::Newline {
write!(f, "<span class=\"where fmt-newline\">where{where_preds},</span>")
} else {
let mut br_with_padding = String::with_capacity(6 * indent + 28);
br_with_padding.push('\n');

let where_indent = 3;
let padding_amount = if ending == Ending::Newline {
indent + 4
} else if indent == 0 {
4
} else {
indent + where_indent + "where ".len()
};

for _ in 0..padding_amount {
br_with_padding.push(' ');
}
let where_preds = where_preds.to_string().replace('\n', &br_with_padding);
let indent_str = " ".repeat(indent);
write!(f, "\n{indent_str}<span class=\"where\">where{where_preds}</span>")
}
})
}

if ending == Ending::Newline {
let mut clause = " ".repeat(indent.saturating_sub(1));
write!(clause, "<span class=\"where fmt-newline\">where{where_preds},</span>")?;
clause
} else {
// insert a newline after a single space but before multiple spaces at the start
if indent == 0 {
format!("\n<span class=\"where\">where{where_preds}</span>")
} else {
// put the first one on the same line as the 'where' keyword
let where_preds = where_preds.replacen(&br_with_padding, " ", 1);
fn print_where_pred_helper<'a, 'tcx: 'a>(
cx: &'a Context<'tcx>,
pred: &'a clean::WherePredicate,
indent_len: usize,
) -> impl Display + Captures<'a> + Captures<'tcx> {
display_fn(move |f| {
f.write_str("\n")?;
f.write_str(&" ".repeat(indent_len))?;

let mut clause = br_with_padding;
// +1 is for `\n`.
clause.truncate(indent + 1 + where_indent);
match pred {
clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
let ty_cx = ty.print(cx);
let generic_bounds = print_generic_bounds(bounds, cx);

write!(clause, "<span class=\"where\">where{where_preds}</span>")?;
clause
if bound_params.is_empty() {
write!(f, "{ty_cx}: {generic_bounds}")
} else {
write!(
f,
"for&lt;{}&gt; {ty_cx}: {generic_bounds}",
comma_sep(bound_params.iter().map(|lt| lt.print(cx)), true)
)
}
}
};
write!(f, "{clause}")
clean::WherePredicate::RegionPredicate { lifetime, bounds } => {
let bounds_display = bounds.iter().map(|b| b.print(cx)).join(" + ");
write!(f, "{}: {bounds_display}", lifetime.print())
}
// FIXME(fmease): Render bound params.
clean::WherePredicate::EqPredicate { lhs, rhs, bound_params: _ } => {
write!(f, "{} == {}", lhs.print(cx), rhs.print(cx))
}
}
})
}

Expand Down
5 changes: 4 additions & 1 deletion src/librustdoc/html/static/css/rustdoc.css
Original file line number Diff line number Diff line change
Expand Up @@ -640,13 +640,16 @@ pre, .rustdoc.source .example-wrap {
background: var(--table-alt-row-background-color);
}

.where {
font-size: 0.875rem;
}

/* Shift "where ..." part of method or fn definition down a line */
.method .where,
.fn .where,
.where.fmt-newline {
display: block;
white-space: pre-wrap;
font-size: 0.875rem;
}

.item-info {
Expand Down
3 changes: 3 additions & 0 deletions tests/rustdoc/issue-112901-where-clause.assoc-S-impl-F.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<section id="associatedtype.F" class="associatedtype trait-impl"><a href="#associatedtype.F" class="anchor">&#167;</a><h4 class="code-header">type <a href="trait.Tr.html#associatedtype.F" class="associatedtype">F</a>&lt;T&gt; = T
<span class="where">where
T: <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a></span></h4></section>
3 changes: 3 additions & 0 deletions tests/rustdoc/issue-112901-where-clause.assoc-Tr-F.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<section id="associatedtype.F" class="method"><a class="srclink rightside" href="../../src/foo/issue-112901-where-clause.rs.html#28-30">source</a><h4 class="code-header">type <a href="#associatedtype.F" class="associatedtype">F</a>&lt;T&gt;
<span class="where">where
T: <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a></span></h4></section>
5 changes: 5 additions & 0 deletions tests/rustdoc/issue-112901-where-clause.assoc-Tr-decl.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<pre class="rust item-decl"><code>pub trait Tr {
type <a href="#associatedtype.F" class="associatedtype">F</a>&lt;T&gt;
<span class="where">where
T: <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a></span>;
}</code></pre>
3 changes: 3 additions & 0 deletions tests/rustdoc/issue-112901-where-clause.assoc-Tr-impl-F.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<section id="associatedtype.F-1" class="associatedtype trait-impl"><a href="#associatedtype.F-1" class="anchor">&#167;</a><h4 class="code-header">type <a href="#associatedtype.F" class="associatedtype">F</a>&lt;T&gt; = T
<span class="where">where
T: <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a></span></h4></section>
36 changes: 36 additions & 0 deletions tests/rustdoc/issue-112901-where-clause.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#![crate_name = "foo"]
#![feature(trivial_bounds)]

pub mod structs {
// @has foo/structs/struct.A.html
// @snapshot structs-A - '//pre[@class="rust item-decl"]'
pub struct A<T>(T)
where
T: Copy;

// @has foo/structs/struct.S.html
// @snapshot structs-S - '//pre[@class="rust item-decl"]'
pub struct S
where
String: Clone;
}

pub mod assoc {
// @has foo/assoc/struct.S.html
// @snapshot assoc-S-impl-F - '//section[@id="associatedtype.F"]'
pub struct S;

// @has foo/assoc/trait.Tr.html
// @snapshot assoc-Tr-decl - '//pre[@class="rust item-decl"]'
// @snapshot assoc-Tr-F - '//section[@id="associatedtype.F"]'
// @snapshot assoc-Tr-impl-F - '//section[@id="associatedtype.F-1"]'
pub trait Tr {
type F<T>
where
T: Clone;
}

impl Tr for S {
type F<T> = T where T: Clone;
}
}
3 changes: 3 additions & 0 deletions tests/rustdoc/issue-112901-where-clause.structs-A.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<pre class="rust item-decl"><code>pub struct A&lt;T&gt;(_)
<span class="where">where
T: <a class="trait" href="{{channel}}/core/marker/trait.Copy.html" title="trait core::marker::Copy">Copy</a></span>;</code></pre>
3 changes: 3 additions & 0 deletions tests/rustdoc/issue-112901-where-clause.structs-S.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<pre class="rust item-decl"><code>pub struct S
<span class="where">where
<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>: <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a></span>;</code></pre>
2 changes: 1 addition & 1 deletion tests/rustdoc/where.SWhere_Simd_item-decl.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<pre class="rust item-decl"><code>pub struct Simd&lt;T&gt;(_)
<span class="where">where
T: <a class="trait" href="trait.MyTrait.html" title="trait foo::MyTrait">MyTrait</a></span>;</code></pre>
T: <a class="trait" href="trait.MyTrait.html" title="trait foo::MyTrait">MyTrait</a></span>;</code></pre>
14 changes: 9 additions & 5 deletions tests/rustdoc/where.SWhere_TraitWhere_item-decl.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
<pre class="rust item-decl"><code>pub trait TraitWhere {
type <a href="#associatedtype.Item" class="associatedtype">Item</a>&lt;'a&gt;
<span class="where">where Self: 'a</span>;
<span class="where">where
Self: 'a</span>;

// Provided methods
fn <a href="#method.func" class="fn">func</a>(self)
<span class="where">where Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span> { ... }
<span class="where">where
Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span> { ... }
<span class="item-spacer" /> fn <a href="#method.lines" class="fn">lines</a>(self) -&gt; <a class="struct" href="{{channel}}/std/io/struct.Lines.html" title="struct std::io::Lines">Lines</a>&lt;Self&gt;
<span class="where">where Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span> { ... }
<span class="where">where
Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span> { ... }
<span class="item-spacer" /> fn <a href="#method.merge" class="fn">merge</a>&lt;T&gt;(self, a: T)
<span class="where">where Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a>,
T: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span> { ... }
<span class="where">where
Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a>,
T: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span> { ... }
}</code></pre>