From e240dab0c86f023e06e1fc2249eff10054298b09 Mon Sep 17 00:00:00 2001 From: Nicky Lim Date: Fri, 2 Jun 2023 16:59:10 +0800 Subject: [PATCH] Add `item_template` macro --- src/librustdoc/html/render/print_item.rs | 173 ++++++++++++------ src/librustdoc/html/templates/item_union.html | 8 +- 2 files changed, 120 insertions(+), 61 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 01089ed348b..02cfad6b346 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -9,7 +9,6 @@ use rustc_middle::middle::stability; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Symbol}; -use std::borrow::Borrow; use std::cell::{RefCell, RefMut}; use std::cmp::Ordering; use std::fmt; @@ -40,6 +39,110 @@ use crate::html::{highlight, static_files}; use askama::Template; use itertools::Itertools; +/// Generates an Askama template struct for rendering items with common methods. +/// +/// Usage: +/// ```ignore (illustrative) +/// item_template!( +/// #[template(path = "", /* additional values */)] +/// /* additional meta items */ +/// struct MyItem<'a, 'cx> { +/// cx: RefCell<&'a mut Context<'cx>>, +/// it: &'a clean::Item, +/// /* additional fields */ +/// }, +/// methods = [ /* method names (comma separated; refer to macro definition of `item_template_methods!()`) */ ] +/// ) +/// ``` +/// +/// NOTE: ensure that the generic lifetimes (`'a`, `'cx`) and +/// required fields (`cx`, `it`) are identical (in terms of order and definition). +macro_rules! item_template { + ( + $(#[$meta:meta])* + struct $name:ident<'a, 'cx> { + cx: RefCell<&'a mut Context<'cx>>, + it: &'a clean::Item, + $($field_name:ident: $field_ty:ty),*, + }, + methods = [$($methods:tt),* $(,)?] + ) => { + #[derive(Template)] + $(#[$meta])* + struct $name<'a, 'cx> { + cx: RefCell<&'a mut Context<'cx>>, + it: &'a clean::Item, + $($field_name: $field_ty),* + } + + impl<'a, 'cx: 'a> ItemTemplate<'a, 'cx> for $name<'a, 'cx> { + fn item_and_mut_cx(&self) -> (&'a clean::Item, RefMut<'_, &'a mut Context<'cx>>) { + (&self.it, self.cx.borrow_mut()) + } + } + + impl<'a, 'cx: 'a> $name<'a, 'cx> { + item_template_methods!($($methods)*); + } + }; +} + +/// Implement common methods for item template structs generated by `item_template!()`. +/// +/// NOTE: this macro is intended to be used only by `item_template!()`. +macro_rules! item_template_methods { + () => {}; + (document $($rest:tt)*) => { + fn document<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { + display_fn(move |f| { + let (item, mut cx) = self.item_and_mut_cx(); + let v = document(*cx, item, None, HeadingOffset::H2); + write!(f, "{v}") + }) + } + item_template_methods!($($rest)*); + }; + (document_type_layout $($rest:tt)*) => { + fn document_type_layout<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { + display_fn(move |f| { + let (item, cx) = self.item_and_mut_cx(); + let def_id = item.item_id.expect_def_id(); + let v = document_type_layout(*cx, def_id); + write!(f, "{v}") + }) + } + item_template_methods!($($rest)*); + }; + (render_attributes_in_pre $($rest:tt)*) => { + fn render_attributes_in_pre<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { + display_fn(move |f| { + let (item, cx) = self.item_and_mut_cx(); + let tcx = cx.tcx(); + let v = render_attributes_in_pre(item, "", tcx); + write!(f, "{v}") + }) + } + item_template_methods!($($rest)*); + }; + (render_assoc_items $($rest:tt)*) => { + fn render_assoc_items<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { + display_fn(move |f| { + let (item, mut cx) = self.item_and_mut_cx(); + let def_id = item.item_id.expect_def_id(); + let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All, None); + write!(f, "{v}") + }) + } + item_template_methods!($($rest)*); + }; + ($method:ident $($rest:tt)*) => { + compile_error!(concat!("unknown method: ", stringify!($method))); + }; + ($token:tt $($rest:tt)*) => { + compile_error!(concat!("unexpected token: ", stringify!($token))); + }; +} + const ITEM_TABLE_OPEN: &str = ""; const ITEM_TABLE_ROW_OPEN: &str = "
  • "; @@ -222,49 +325,6 @@ trait ItemTemplate<'a, 'cx: 'a>: askama::Template + fmt::Display { fn item_and_mut_cx(&self) -> (&'a clean::Item, RefMut<'_, &'a mut Context<'cx>>); } -fn item_template_document<'a: 'b, 'b, 'cx: 'a>( - templ: &'b impl ItemTemplate<'a, 'cx>, -) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { - display_fn(move |f| { - let (item, mut cx) = templ.item_and_mut_cx(); - let v = document(*cx, item, None, HeadingOffset::H2); - write!(f, "{v}") - }) -} - -fn item_template_document_type_layout<'a: 'b, 'b, 'cx: 'a>( - templ: &'b impl ItemTemplate<'a, 'cx>, -) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { - display_fn(move |f| { - let (item, cx) = templ.item_and_mut_cx(); - let def_id = item.item_id.expect_def_id(); - let v = document_type_layout(*cx, def_id); - write!(f, "{v}") - }) -} - -fn item_template_render_attributes_in_pre<'a: 'b, 'b, 'cx: 'a>( - templ: &'b impl ItemTemplate<'a, 'cx>, -) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { - display_fn(move |f| { - let (item, cx) = templ.item_and_mut_cx(); - let tcx = cx.tcx(); - let v = render_attributes_in_pre(item, "", tcx); - write!(f, "{v}") - }) -} - -fn item_template_render_assoc_items<'a: 'b, 'b, 'cx: 'a>( - templ: &'b impl ItemTemplate<'a, 'cx>, -) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { - display_fn(move |f| { - let (item, mut cx) = templ.item_and_mut_cx(); - let def_id = item.item_id.expect_def_id(); - let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All, None); - write!(f, "{v}") - }) -} - fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: &[clean::Item]) { write!(w, "{}", document(cx, item, None, HeadingOffset::H2)); @@ -1200,19 +1260,15 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea } fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) { - #[derive(Template)] - #[template(path = "item_union.html")] - struct ItemUnion<'a, 'cx> { - cx: RefCell<&'a mut Context<'cx>>, - it: &'a clean::Item, - s: &'a clean::Union, - } - - impl<'a, 'cx: 'a> ItemTemplate<'a, 'cx> for ItemUnion<'a, 'cx> { - fn item_and_mut_cx(&self) -> (&'a clean::Item, RefMut<'_, &'a mut Context<'cx>>) { - (self.it, self.cx.borrow_mut()) - } - } + item_template!( + #[template(path = "item_union.html")] + struct ItemUnion<'a, 'cx> { + cx: RefCell<&'a mut Context<'cx>>, + it: &'a clean::Item, + s: &'a clean::Union, + }, + methods = [document, document_type_layout, render_attributes_in_pre, render_assoc_items] + ); impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { 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: write!(f, "{v}") }) } + fn document_field<'b>( &'b self, field: &'a clean::Item, @@ -1232,10 +1289,12 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean: write!(f, "{v}") }) } + fn stability_field(&self, field: &clean::Item) -> Option { let cx = self.cx.borrow(); field.stability_class(cx.tcx()) } + fn print_ty<'b>( &'b self, ty: &'a clean::Type, diff --git a/src/librustdoc/html/templates/item_union.html b/src/librustdoc/html/templates/item_union.html index c2196700513..a01457971c1 100644 --- a/src/librustdoc/html/templates/item_union.html +++ b/src/librustdoc/html/templates/item_union.html @@ -1,8 +1,8 @@
    
    -    {{ self::item_template_render_attributes_in_pre(self.borrow()) | safe }}
    +    {{ self.render_attributes_in_pre() | safe }}
         {{ self.render_union() | safe }}
     
    -{{ self::item_template_document(self.borrow()) | safe }} +{{ self.document() | safe }} {% if self.fields_iter().peek().is_some() %}

    Fieldsยง @@ -19,5 +19,5 @@ {{ self.document_field(field) | safe }} {% endfor %} {% endif %} -{{ self::item_template_render_assoc_items(self.borrow()) | safe }} -{{ self::item_template_document_type_layout(self.borrow()) | safe }} +{{ self.render_assoc_items() | safe }} +{{ self.document_type_layout() | safe }}