Skip to content

Commit 7b6093e

Browse files
committed
Auto merge of #112202 - nicklimmm:item-template-derive-macro, r=GuillaumeGomez
rustdoc: Add `item_template` macro Closes #112021 This change removes the use of `self.borrows()` in Askama templates, removes code duplication from `item_and_mut_cx()`, and improved readability by eliminating the prefix `item_template_` when calling from the template. References: - Discussion issue: #112021 - `ItemTemplate` PR: #111946 r? `@GuillaumeGomez`
2 parents 34d64ab + e240dab commit 7b6093e

File tree

2 files changed

+120
-61
lines changed

2 files changed

+120
-61
lines changed

src/librustdoc/html/render/print_item.rs

+116-57
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use rustc_middle::middle::stability;
99
use rustc_middle::ty::{self, TyCtxt};
1010
use rustc_span::hygiene::MacroKind;
1111
use rustc_span::symbol::{kw, sym, Symbol};
12-
use std::borrow::Borrow;
1312
use std::cell::{RefCell, RefMut};
1413
use std::cmp::Ordering;
1514
use std::fmt;
@@ -40,6 +39,110 @@ use crate::html::{highlight, static_files};
4039
use askama::Template;
4140
use itertools::Itertools;
4241

42+
/// Generates an Askama template struct for rendering items with common methods.
43+
///
44+
/// Usage:
45+
/// ```ignore (illustrative)
46+
/// item_template!(
47+
/// #[template(path = "<template.html>", /* additional values */)]
48+
/// /* additional meta items */
49+
/// struct MyItem<'a, 'cx> {
50+
/// cx: RefCell<&'a mut Context<'cx>>,
51+
/// it: &'a clean::Item,
52+
/// /* additional fields */
53+
/// },
54+
/// methods = [ /* method names (comma separated; refer to macro definition of `item_template_methods!()`) */ ]
55+
/// )
56+
/// ```
57+
///
58+
/// NOTE: ensure that the generic lifetimes (`'a`, `'cx`) and
59+
/// required fields (`cx`, `it`) are identical (in terms of order and definition).
60+
macro_rules! item_template {
61+
(
62+
$(#[$meta:meta])*
63+
struct $name:ident<'a, 'cx> {
64+
cx: RefCell<&'a mut Context<'cx>>,
65+
it: &'a clean::Item,
66+
$($field_name:ident: $field_ty:ty),*,
67+
},
68+
methods = [$($methods:tt),* $(,)?]
69+
) => {
70+
#[derive(Template)]
71+
$(#[$meta])*
72+
struct $name<'a, 'cx> {
73+
cx: RefCell<&'a mut Context<'cx>>,
74+
it: &'a clean::Item,
75+
$($field_name: $field_ty),*
76+
}
77+
78+
impl<'a, 'cx: 'a> ItemTemplate<'a, 'cx> for $name<'a, 'cx> {
79+
fn item_and_mut_cx(&self) -> (&'a clean::Item, RefMut<'_, &'a mut Context<'cx>>) {
80+
(&self.it, self.cx.borrow_mut())
81+
}
82+
}
83+
84+
impl<'a, 'cx: 'a> $name<'a, 'cx> {
85+
item_template_methods!($($methods)*);
86+
}
87+
};
88+
}
89+
90+
/// Implement common methods for item template structs generated by `item_template!()`.
91+
///
92+
/// NOTE: this macro is intended to be used only by `item_template!()`.
93+
macro_rules! item_template_methods {
94+
() => {};
95+
(document $($rest:tt)*) => {
96+
fn document<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
97+
display_fn(move |f| {
98+
let (item, mut cx) = self.item_and_mut_cx();
99+
let v = document(*cx, item, None, HeadingOffset::H2);
100+
write!(f, "{v}")
101+
})
102+
}
103+
item_template_methods!($($rest)*);
104+
};
105+
(document_type_layout $($rest:tt)*) => {
106+
fn document_type_layout<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
107+
display_fn(move |f| {
108+
let (item, cx) = self.item_and_mut_cx();
109+
let def_id = item.item_id.expect_def_id();
110+
let v = document_type_layout(*cx, def_id);
111+
write!(f, "{v}")
112+
})
113+
}
114+
item_template_methods!($($rest)*);
115+
};
116+
(render_attributes_in_pre $($rest:tt)*) => {
117+
fn render_attributes_in_pre<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
118+
display_fn(move |f| {
119+
let (item, cx) = self.item_and_mut_cx();
120+
let tcx = cx.tcx();
121+
let v = render_attributes_in_pre(item, "", tcx);
122+
write!(f, "{v}")
123+
})
124+
}
125+
item_template_methods!($($rest)*);
126+
};
127+
(render_assoc_items $($rest:tt)*) => {
128+
fn render_assoc_items<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
129+
display_fn(move |f| {
130+
let (item, mut cx) = self.item_and_mut_cx();
131+
let def_id = item.item_id.expect_def_id();
132+
let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All, None);
133+
write!(f, "{v}")
134+
})
135+
}
136+
item_template_methods!($($rest)*);
137+
};
138+
($method:ident $($rest:tt)*) => {
139+
compile_error!(concat!("unknown method: ", stringify!($method)));
140+
};
141+
($token:tt $($rest:tt)*) => {
142+
compile_error!(concat!("unexpected token: ", stringify!($token)));
143+
};
144+
}
145+
43146
const ITEM_TABLE_OPEN: &str = "<ul class=\"item-table\">";
44147
const ITEM_TABLE_CLOSE: &str = "</ul>";
45148
const ITEM_TABLE_ROW_OPEN: &str = "<li>";
@@ -222,49 +325,6 @@ trait ItemTemplate<'a, 'cx: 'a>: askama::Template + fmt::Display {
222325
fn item_and_mut_cx(&self) -> (&'a clean::Item, RefMut<'_, &'a mut Context<'cx>>);
223326
}
224327

225-
fn item_template_document<'a: 'b, 'b, 'cx: 'a>(
226-
templ: &'b impl ItemTemplate<'a, 'cx>,
227-
) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
228-
display_fn(move |f| {
229-
let (item, mut cx) = templ.item_and_mut_cx();
230-
let v = document(*cx, item, None, HeadingOffset::H2);
231-
write!(f, "{v}")
232-
})
233-
}
234-
235-
fn item_template_document_type_layout<'a: 'b, 'b, 'cx: 'a>(
236-
templ: &'b impl ItemTemplate<'a, 'cx>,
237-
) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
238-
display_fn(move |f| {
239-
let (item, cx) = templ.item_and_mut_cx();
240-
let def_id = item.item_id.expect_def_id();
241-
let v = document_type_layout(*cx, def_id);
242-
write!(f, "{v}")
243-
})
244-
}
245-
246-
fn item_template_render_attributes_in_pre<'a: 'b, 'b, 'cx: 'a>(
247-
templ: &'b impl ItemTemplate<'a, 'cx>,
248-
) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
249-
display_fn(move |f| {
250-
let (item, cx) = templ.item_and_mut_cx();
251-
let tcx = cx.tcx();
252-
let v = render_attributes_in_pre(item, "", tcx);
253-
write!(f, "{v}")
254-
})
255-
}
256-
257-
fn item_template_render_assoc_items<'a: 'b, 'b, 'cx: 'a>(
258-
templ: &'b impl ItemTemplate<'a, 'cx>,
259-
) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
260-
display_fn(move |f| {
261-
let (item, mut cx) = templ.item_and_mut_cx();
262-
let def_id = item.item_id.expect_def_id();
263-
let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All, None);
264-
write!(f, "{v}")
265-
})
266-
}
267-
268328
fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: &[clean::Item]) {
269329
write!(w, "{}", document(cx, item, None, HeadingOffset::H2));
270330

@@ -1200,19 +1260,15 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea
12001260
}
12011261

12021262
fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) {
1203-
#[derive(Template)]
1204-
#[template(path = "item_union.html")]
1205-
struct ItemUnion<'a, 'cx> {
1206-
cx: RefCell<&'a mut Context<'cx>>,
1207-
it: &'a clean::Item,
1208-
s: &'a clean::Union,
1209-
}
1210-
1211-
impl<'a, 'cx: 'a> ItemTemplate<'a, 'cx> for ItemUnion<'a, 'cx> {
1212-
fn item_and_mut_cx(&self) -> (&'a clean::Item, RefMut<'_, &'a mut Context<'cx>>) {
1213-
(self.it, self.cx.borrow_mut())
1214-
}
1215-
}
1263+
item_template!(
1264+
#[template(path = "item_union.html")]
1265+
struct ItemUnion<'a, 'cx> {
1266+
cx: RefCell<&'a mut Context<'cx>>,
1267+
it: &'a clean::Item,
1268+
s: &'a clean::Union,
1269+
},
1270+
methods = [document, document_type_layout, render_attributes_in_pre, render_assoc_items]
1271+
);
12161272

12171273
impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
12181274
fn render_union<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
@@ -1222,6 +1278,7 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean:
12221278
write!(f, "{v}")
12231279
})
12241280
}
1281+
12251282
fn document_field<'b>(
12261283
&'b self,
12271284
field: &'a clean::Item,
@@ -1232,10 +1289,12 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean:
12321289
write!(f, "{v}")
12331290
})
12341291
}
1292+
12351293
fn stability_field(&self, field: &clean::Item) -> Option<String> {
12361294
let cx = self.cx.borrow();
12371295
field.stability_class(cx.tcx())
12381296
}
1297+
12391298
fn print_ty<'b>(
12401299
&'b self,
12411300
ty: &'a clean::Type,

src/librustdoc/html/templates/item_union.html

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<pre class="rust item-decl"><code>
2-
{{ self::item_template_render_attributes_in_pre(self.borrow()) | safe }}
2+
{{ self.render_attributes_in_pre() | safe }}
33
{{ self.render_union() | safe }}
44
</code></pre>
5-
{{ self::item_template_document(self.borrow()) | safe }}
5+
{{ self.document() | safe }}
66
{% if self.fields_iter().peek().is_some() %}
77
<h2 id="fields" class="fields small-section-header">
88
Fields<a href="#fields" class="anchor">§</a>
@@ -19,5 +19,5 @@ <h2 id="fields" class="fields small-section-header">
1919
{{ self.document_field(field) | safe }}
2020
{% endfor %}
2121
{% endif %}
22-
{{ self::item_template_render_assoc_items(self.borrow()) | safe }}
23-
{{ self::item_template_document_type_layout(self.borrow()) | safe }}
22+
{{ self.render_assoc_items() | safe }}
23+
{{ self.document_type_layout() | safe }}

0 commit comments

Comments
 (0)