]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/format.rs
Rollup merge of #83646 - glittershark:bound-map, r=m-ou-se
[rust.git] / src / librustdoc / html / format.rs
1 //! HTML formatting module
2 //!
3 //! 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.
7
8 use std::cell::Cell;
9 use std::fmt;
10 use std::iter;
11
12 use rustc_data_structures::captures::Captures;
13 use rustc_data_structures::fx::FxHashSet;
14 use rustc_hir as hir;
15 use rustc_hir::def_id::DefId;
16 use rustc_middle::ty::TyCtxt;
17 use rustc_span::def_id::CRATE_DEF_INDEX;
18 use rustc_target::spec::abi::Abi;
19
20 use crate::clean::{
21     self, utils::find_nearest_parent_module, ExternalCrate, FakeDefId, GetDefId, PrimitiveType,
22 };
23 use crate::formats::item_type::ItemType;
24 use crate::html::escape::Escape;
25 use crate::html::render::cache::ExternalLocation;
26 use crate::html::render::Context;
27
28 crate trait Print {
29     fn print(self, buffer: &mut Buffer);
30 }
31
32 impl<F> Print for F
33 where
34     F: FnOnce(&mut Buffer),
35 {
36     fn print(self, buffer: &mut Buffer) {
37         (self)(buffer)
38     }
39 }
40
41 impl Print for String {
42     fn print(self, buffer: &mut Buffer) {
43         buffer.write_str(&self);
44     }
45 }
46
47 impl Print for &'_ str {
48     fn print(self, buffer: &mut Buffer) {
49         buffer.write_str(self);
50     }
51 }
52
53 #[derive(Debug, Clone)]
54 crate struct Buffer {
55     for_html: bool,
56     buffer: String,
57 }
58
59 impl Buffer {
60     crate fn empty_from(v: &Buffer) -> Buffer {
61         Buffer { for_html: v.for_html, buffer: String::new() }
62     }
63
64     crate fn html() -> Buffer {
65         Buffer { for_html: true, buffer: String::new() }
66     }
67
68     crate fn new() -> Buffer {
69         Buffer { for_html: false, buffer: String::new() }
70     }
71
72     crate fn is_empty(&self) -> bool {
73         self.buffer.is_empty()
74     }
75
76     crate fn into_inner(self) -> String {
77         self.buffer
78     }
79
80     crate fn insert_str(&mut self, idx: usize, s: &str) {
81         self.buffer.insert_str(idx, s);
82     }
83
84     crate fn push_str(&mut self, s: &str) {
85         self.buffer.push_str(s);
86     }
87
88     crate fn push_buffer(&mut self, other: Buffer) {
89         self.buffer.push_str(&other.buffer);
90     }
91
92     // Intended for consumption by write! and writeln! (std::fmt) but without
93     // the fmt::Result return type imposed by fmt::Write (and avoiding the trait
94     // import).
95     crate fn write_str(&mut self, s: &str) {
96         self.buffer.push_str(s);
97     }
98
99     // Intended for consumption by write! and writeln! (std::fmt) but without
100     // the fmt::Result return type imposed by fmt::Write (and avoiding the trait
101     // import).
102     crate fn write_fmt(&mut self, v: fmt::Arguments<'_>) {
103         use fmt::Write;
104         self.buffer.write_fmt(v).unwrap();
105     }
106
107     crate fn to_display<T: Print>(mut self, t: T) -> String {
108         t.print(&mut self);
109         self.into_inner()
110     }
111
112     crate fn is_for_html(&self) -> bool {
113         self.for_html
114     }
115
116     crate fn reserve(&mut self, additional: usize) {
117         self.buffer.reserve(additional)
118     }
119 }
120
121 fn comma_sep<T: fmt::Display>(items: impl Iterator<Item = T>) -> impl fmt::Display {
122     display_fn(move |f| {
123         for (i, item) in items.enumerate() {
124             if i != 0 {
125                 write!(f, ", ")?;
126             }
127             fmt::Display::fmt(&item, f)?;
128         }
129         Ok(())
130     })
131 }
132
133 crate fn print_generic_bounds<'a, 'tcx: 'a>(
134     bounds: &'a [clean::GenericBound],
135     cx: &'a Context<'tcx>,
136 ) -> impl fmt::Display + 'a + Captures<'tcx> {
137     display_fn(move |f| {
138         let mut bounds_dup = FxHashSet::default();
139
140         for (i, bound) in
141             bounds.iter().filter(|b| bounds_dup.insert(b.print(cx).to_string())).enumerate()
142         {
143             if i > 0 {
144                 f.write_str(" + ")?;
145             }
146             fmt::Display::fmt(&bound.print(cx), f)?;
147         }
148         Ok(())
149     })
150 }
151
152 impl clean::GenericParamDef {
153     crate fn print<'a, 'tcx: 'a>(
154         &'a self,
155         cx: &'a Context<'tcx>,
156     ) -> impl fmt::Display + 'a + Captures<'tcx> {
157         display_fn(move |f| match self.kind {
158             clean::GenericParamDefKind::Lifetime => write!(f, "{}", self.name),
159             clean::GenericParamDefKind::Type { ref bounds, ref default, .. } => {
160                 f.write_str(&*self.name.as_str())?;
161
162                 if !bounds.is_empty() {
163                     if f.alternate() {
164                         write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
165                     } else {
166                         write!(f, ":&nbsp;{}", print_generic_bounds(bounds, cx))?;
167                     }
168                 }
169
170                 if let Some(ref ty) = default {
171                     if f.alternate() {
172                         write!(f, " = {:#}", ty.print(cx))?;
173                     } else {
174                         write!(f, "&nbsp;=&nbsp;{}", ty.print(cx))?;
175                     }
176                 }
177
178                 Ok(())
179             }
180             clean::GenericParamDefKind::Const { ref ty, .. } => {
181                 if f.alternate() {
182                     write!(f, "const {}: {:#}", self.name, ty.print(cx))
183                 } else {
184                     write!(f, "const {}:&nbsp;{}", self.name, ty.print(cx))
185                 }
186             }
187         })
188     }
189 }
190
191 impl clean::Generics {
192     crate fn print<'a, 'tcx: 'a>(
193         &'a self,
194         cx: &'a Context<'tcx>,
195     ) -> impl fmt::Display + 'a + Captures<'tcx> {
196         display_fn(move |f| {
197             let real_params =
198                 self.params.iter().filter(|p| !p.is_synthetic_type_param()).collect::<Vec<_>>();
199             if real_params.is_empty() {
200                 return Ok(());
201             }
202             if f.alternate() {
203                 write!(f, "<{:#}>", comma_sep(real_params.iter().map(|g| g.print(cx))))
204             } else {
205                 write!(f, "&lt;{}&gt;", comma_sep(real_params.iter().map(|g| g.print(cx))))
206             }
207         })
208     }
209 }
210
211 /// * The Generics from which to emit a where-clause.
212 /// * The number of spaces to indent each line with.
213 /// * Whether the where-clause needs to add a comma and newline after the last bound.
214 crate fn print_where_clause<'a, 'tcx: 'a>(
215     gens: &'a clean::Generics,
216     cx: &'a Context<'tcx>,
217     indent: usize,
218     end_newline: bool,
219 ) -> impl fmt::Display + 'a + Captures<'tcx> {
220     display_fn(move |f| {
221         if gens.where_predicates.is_empty() {
222             return Ok(());
223         }
224         let mut clause = String::new();
225         if f.alternate() {
226             clause.push_str(" where");
227         } else {
228             if end_newline {
229                 clause.push_str(" <span class=\"where fmt-newline\">where");
230             } else {
231                 clause.push_str(" <span class=\"where\">where");
232             }
233         }
234         for (i, pred) in gens.where_predicates.iter().enumerate() {
235             if f.alternate() {
236                 clause.push(' ');
237             } else {
238                 clause.push_str("<br>");
239             }
240
241             match pred {
242                 clean::WherePredicate::BoundPredicate { ty, bounds } => {
243                     let bounds = bounds;
244                     if f.alternate() {
245                         clause.push_str(&format!(
246                             "{:#}: {:#}",
247                             ty.print(cx),
248                             print_generic_bounds(bounds, cx)
249                         ));
250                     } else {
251                         clause.push_str(&format!(
252                             "{}: {}",
253                             ty.print(cx),
254                             print_generic_bounds(bounds, cx)
255                         ));
256                     }
257                 }
258                 clean::WherePredicate::RegionPredicate { lifetime, bounds } => {
259                     clause.push_str(&format!(
260                         "{}: {}",
261                         lifetime.print(),
262                         bounds
263                             .iter()
264                             .map(|b| b.print(cx).to_string())
265                             .collect::<Vec<_>>()
266                             .join(" + ")
267                     ));
268                 }
269                 clean::WherePredicate::EqPredicate { lhs, rhs } => {
270                     if f.alternate() {
271                         clause.push_str(&format!("{:#} == {:#}", lhs.print(cx), rhs.print(cx),));
272                     } else {
273                         clause.push_str(&format!("{} == {}", lhs.print(cx), rhs.print(cx),));
274                     }
275                 }
276             }
277
278             if i < gens.where_predicates.len() - 1 || end_newline {
279                 clause.push(',');
280             }
281         }
282
283         if end_newline {
284             // add a space so stripping <br> tags and breaking spaces still renders properly
285             if f.alternate() {
286                 clause.push(' ');
287             } else {
288                 clause.push_str("&nbsp;");
289             }
290         }
291
292         if !f.alternate() {
293             clause.push_str("</span>");
294             let padding = "&nbsp;".repeat(indent + 4);
295             clause = clause.replace("<br>", &format!("<br>{}", padding));
296             clause.insert_str(0, &"&nbsp;".repeat(indent.saturating_sub(1)));
297             if !end_newline {
298                 clause.insert_str(0, "<br>");
299             }
300         }
301         write!(f, "{}", clause)
302     })
303 }
304
305 impl clean::Lifetime {
306     crate fn print(&self) -> impl fmt::Display + '_ {
307         self.get_ref()
308     }
309 }
310
311 impl clean::Constant {
312     crate fn print(&self, tcx: TyCtxt<'_>) -> impl fmt::Display + '_ {
313         let expr = self.expr(tcx);
314         display_fn(
315             move |f| {
316                 if f.alternate() { f.write_str(&expr) } else { write!(f, "{}", Escape(&expr)) }
317             },
318         )
319     }
320 }
321
322 impl clean::PolyTrait {
323     fn print<'a, 'tcx: 'a>(
324         &'a self,
325         cx: &'a Context<'tcx>,
326     ) -> impl fmt::Display + 'a + Captures<'tcx> {
327         display_fn(move |f| {
328             if !self.generic_params.is_empty() {
329                 if f.alternate() {
330                     write!(
331                         f,
332                         "for<{:#}> ",
333                         comma_sep(self.generic_params.iter().map(|g| g.print(cx)))
334                     )?;
335                 } else {
336                     write!(
337                         f,
338                         "for&lt;{}&gt; ",
339                         comma_sep(self.generic_params.iter().map(|g| g.print(cx)))
340                     )?;
341                 }
342             }
343             if f.alternate() {
344                 write!(f, "{:#}", self.trait_.print(cx))
345             } else {
346                 write!(f, "{}", self.trait_.print(cx))
347             }
348         })
349     }
350 }
351
352 impl clean::GenericBound {
353     crate fn print<'a, 'tcx: 'a>(
354         &'a self,
355         cx: &'a Context<'tcx>,
356     ) -> impl fmt::Display + 'a + Captures<'tcx> {
357         display_fn(move |f| match self {
358             clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()),
359             clean::GenericBound::TraitBound(ty, modifier) => {
360                 let modifier_str = match modifier {
361                     hir::TraitBoundModifier::None => "",
362                     hir::TraitBoundModifier::Maybe => "?",
363                     hir::TraitBoundModifier::MaybeConst => "?const",
364                 };
365                 if f.alternate() {
366                     write!(f, "{}{:#}", modifier_str, ty.print(cx))
367                 } else {
368                     write!(f, "{}{}", modifier_str, ty.print(cx))
369                 }
370             }
371         })
372     }
373 }
374
375 impl clean::GenericArgs {
376     fn print<'a, 'tcx: 'a>(
377         &'a self,
378         cx: &'a Context<'tcx>,
379     ) -> impl fmt::Display + 'a + Captures<'tcx> {
380         display_fn(move |f| {
381             match self {
382                 clean::GenericArgs::AngleBracketed { args, bindings } => {
383                     if !args.is_empty() || !bindings.is_empty() {
384                         if f.alternate() {
385                             f.write_str("<")?;
386                         } else {
387                             f.write_str("&lt;")?;
388                         }
389                         let mut comma = false;
390                         for arg in args {
391                             if comma {
392                                 f.write_str(", ")?;
393                             }
394                             comma = true;
395                             if f.alternate() {
396                                 write!(f, "{:#}", arg.print(cx))?;
397                             } else {
398                                 write!(f, "{}", arg.print(cx))?;
399                             }
400                         }
401                         for binding in bindings {
402                             if comma {
403                                 f.write_str(", ")?;
404                             }
405                             comma = true;
406                             if f.alternate() {
407                                 write!(f, "{:#}", binding.print(cx))?;
408                             } else {
409                                 write!(f, "{}", binding.print(cx))?;
410                             }
411                         }
412                         if f.alternate() {
413                             f.write_str(">")?;
414                         } else {
415                             f.write_str("&gt;")?;
416                         }
417                     }
418                 }
419                 clean::GenericArgs::Parenthesized { inputs, output } => {
420                     f.write_str("(")?;
421                     let mut comma = false;
422                     for ty in inputs {
423                         if comma {
424                             f.write_str(", ")?;
425                         }
426                         comma = true;
427                         if f.alternate() {
428                             write!(f, "{:#}", ty.print(cx))?;
429                         } else {
430                             write!(f, "{}", ty.print(cx))?;
431                         }
432                     }
433                     f.write_str(")")?;
434                     if let Some(ref ty) = *output {
435                         if f.alternate() {
436                             write!(f, " -> {:#}", ty.print(cx))?;
437                         } else {
438                             write!(f, " -&gt; {}", ty.print(cx))?;
439                         }
440                     }
441                 }
442             }
443             Ok(())
444         })
445     }
446 }
447
448 crate fn href(did: DefId, cx: &Context<'_>) -> Option<(String, ItemType, Vec<String>)> {
449     let cache = &cx.cache();
450     let relative_to = &cx.current;
451     fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] {
452         if shortty == ItemType::Module { &fqp[..] } else { &fqp[..fqp.len() - 1] }
453     }
454
455     if !did.is_local() && !cache.access_levels.is_public(did) && !cache.document_private {
456         return None;
457     }
458
459     let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
460         Some(&(ref fqp, shortty)) => (fqp, shortty, {
461             let module_fqp = to_module_fqp(shortty, fqp);
462             href_relative_parts(module_fqp, relative_to)
463         }),
464         None => {
465             let &(ref fqp, shortty) = cache.external_paths.get(&did)?;
466             let module_fqp = to_module_fqp(shortty, fqp);
467             (
468                 fqp,
469                 shortty,
470                 match cache.extern_locations[&did.krate] {
471                     ExternalLocation::Remote(ref s) => {
472                         let s = s.trim_end_matches('/');
473                         let mut s = vec![&s[..]];
474                         s.extend(module_fqp[..].iter().map(String::as_str));
475                         s
476                     }
477                     ExternalLocation::Local => href_relative_parts(module_fqp, relative_to),
478                     ExternalLocation::Unknown => return None,
479                 },
480             )
481         }
482     };
483     let last = &fqp.last().unwrap()[..];
484     let filename;
485     match shortty {
486         ItemType::Module => {
487             url_parts.push("index.html");
488         }
489         _ => {
490             filename = format!("{}.{}.html", shortty.as_str(), last);
491             url_parts.push(&filename);
492         }
493     }
494     Some((url_parts.join("/"), shortty, fqp.to_vec()))
495 }
496
497 /// Both paths should only be modules.
498 /// This is because modules get their own directories; that is, `std::vec` and `std::vec::Vec` will
499 /// both need `../iter/trait.Iterator.html` to get at the iterator trait.
500 crate fn href_relative_parts<'a>(fqp: &'a [String], relative_to_fqp: &'a [String]) -> Vec<&'a str> {
501     for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() {
502         // e.g. linking to std::iter from std::vec (`dissimilar_part_count` will be 1)
503         if f != r {
504             let dissimilar_part_count = relative_to_fqp.len() - i;
505             let fqp_module = fqp[i..fqp.len()].iter().map(String::as_str);
506             return iter::repeat("..").take(dissimilar_part_count).chain(fqp_module).collect();
507         }
508     }
509     // e.g. linking to std::sync::atomic from std::sync
510     if relative_to_fqp.len() < fqp.len() {
511         fqp[relative_to_fqp.len()..fqp.len()].iter().map(String::as_str).collect()
512     // e.g. linking to std::sync from std::sync::atomic
513     } else if fqp.len() < relative_to_fqp.len() {
514         let dissimilar_part_count = relative_to_fqp.len() - fqp.len();
515         iter::repeat("..").take(dissimilar_part_count).collect()
516     // linking to the same module
517     } else {
518         Vec::new()
519     }
520 }
521
522 /// Used when rendering a `ResolvedPath` structure. This invokes the `path`
523 /// rendering function with the necessary arguments for linking to a local path.
524 fn resolved_path<'a, 'cx: 'a>(
525     w: &mut fmt::Formatter<'_>,
526     did: DefId,
527     path: &clean::Path,
528     print_all: bool,
529     use_absolute: bool,
530     cx: &'cx Context<'_>,
531 ) -> fmt::Result {
532     let last = path.segments.last().unwrap();
533
534     if print_all {
535         for seg in &path.segments[..path.segments.len() - 1] {
536             write!(w, "{}::", seg.name)?;
537         }
538     }
539     if w.alternate() {
540         write!(w, "{}{:#}", &last.name, last.args.print(cx))?;
541     } else {
542         let path = if use_absolute {
543             if let Some((_, _, fqp)) = href(did, cx) {
544                 format!(
545                     "{}::{}",
546                     fqp[..fqp.len() - 1].join("::"),
547                     anchor(did, fqp.last().unwrap(), cx)
548                 )
549             } else {
550                 last.name.to_string()
551             }
552         } else {
553             anchor(did, &*last.name.as_str(), cx).to_string()
554         };
555         write!(w, "{}{}", path, last.args.print(cx))?;
556     }
557     Ok(())
558 }
559
560 fn primitive_link(
561     f: &mut fmt::Formatter<'_>,
562     prim: clean::PrimitiveType,
563     name: &str,
564     cx: &Context<'_>,
565 ) -> fmt::Result {
566     let m = &cx.cache();
567     let mut needs_termination = false;
568     if !f.alternate() {
569         match m.primitive_locations.get(&prim) {
570             Some(&def_id) if def_id.is_local() => {
571                 let len = cx.current.len();
572                 let len = if len == 0 { 0 } else { len - 1 };
573                 write!(
574                     f,
575                     "<a class=\"primitive\" href=\"{}primitive.{}.html\">",
576                     "../".repeat(len),
577                     prim.as_sym()
578                 )?;
579                 needs_termination = true;
580             }
581             Some(&def_id) => {
582                 let cname_str;
583                 let loc = match m.extern_locations[&def_id.krate] {
584                     ExternalLocation::Remote(ref s) => {
585                         cname_str =
586                             ExternalCrate { crate_num: def_id.krate }.name(cx.tcx()).as_str();
587                         Some(vec![s.trim_end_matches('/'), &cname_str[..]])
588                     }
589                     ExternalLocation::Local => {
590                         cname_str =
591                             ExternalCrate { crate_num: def_id.krate }.name(cx.tcx()).as_str();
592                         Some(if cx.current.first().map(|x| &x[..]) == Some(&cname_str[..]) {
593                             iter::repeat("..").take(cx.current.len() - 1).collect()
594                         } else {
595                             let cname = iter::once(&cname_str[..]);
596                             iter::repeat("..").take(cx.current.len()).chain(cname).collect()
597                         })
598                     }
599                     ExternalLocation::Unknown => None,
600                 };
601                 if let Some(loc) = loc {
602                     write!(
603                         f,
604                         "<a class=\"primitive\" href=\"{}/primitive.{}.html\">",
605                         loc.join("/"),
606                         prim.as_sym()
607                     )?;
608                     needs_termination = true;
609                 }
610             }
611             None => {}
612         }
613     }
614     write!(f, "{}", name)?;
615     if needs_termination {
616         write!(f, "</a>")?;
617     }
618     Ok(())
619 }
620
621 /// Helper to render type parameters
622 fn tybounds<'a, 'tcx: 'a>(
623     param_names: &'a Option<Vec<clean::GenericBound>>,
624     cx: &'a Context<'tcx>,
625 ) -> impl fmt::Display + 'a + Captures<'tcx> {
626     display_fn(move |f| match *param_names {
627         Some(ref params) => {
628             for param in params {
629                 write!(f, " + ")?;
630                 fmt::Display::fmt(&param.print(cx), f)?;
631             }
632             Ok(())
633         }
634         None => Ok(()),
635     })
636 }
637
638 crate fn anchor<'a, 'cx: 'a>(
639     did: DefId,
640     text: &'a str,
641     cx: &'cx Context<'_>,
642 ) -> impl fmt::Display + 'a {
643     let parts = href(did.into(), cx);
644     display_fn(move |f| {
645         if let Some((url, short_ty, fqp)) = parts {
646             write!(
647                 f,
648                 r#"<a class="{}" href="{}" title="{} {}">{}</a>"#,
649                 short_ty,
650                 url,
651                 short_ty,
652                 fqp.join("::"),
653                 text
654             )
655         } else {
656             write!(f, "{}", text)
657         }
658     })
659 }
660
661 fn fmt_type<'cx>(
662     t: &clean::Type,
663     f: &mut fmt::Formatter<'_>,
664     use_absolute: bool,
665     cx: &'cx Context<'_>,
666 ) -> fmt::Result {
667     debug!("fmt_type(t = {:?})", t);
668
669     match *t {
670         clean::Generic(name) => write!(f, "{}", name),
671         clean::ResolvedPath { did, ref param_names, ref path, is_generic } => {
672             if param_names.is_some() {
673                 f.write_str("dyn ")?;
674             }
675             // Paths like `T::Output` and `Self::Output` should be rendered with all segments.
676             resolved_path(f, did, path, is_generic, use_absolute, cx)?;
677             fmt::Display::fmt(&tybounds(param_names, cx), f)
678         }
679         clean::Infer => write!(f, "_"),
680         clean::Primitive(prim) => primitive_link(f, prim, &*prim.as_sym().as_str(), cx),
681         clean::BareFunction(ref decl) => {
682             if f.alternate() {
683                 write!(
684                     f,
685                     "{:#}{}{:#}fn{:#}",
686                     decl.print_hrtb_with_space(cx),
687                     decl.unsafety.print_with_space(),
688                     print_abi_with_space(decl.abi),
689                     decl.decl.print(cx),
690                 )
691             } else {
692                 write!(
693                     f,
694                     "{}{}{}",
695                     decl.print_hrtb_with_space(cx),
696                     decl.unsafety.print_with_space(),
697                     print_abi_with_space(decl.abi)
698                 )?;
699                 primitive_link(f, PrimitiveType::Fn, "fn", cx)?;
700                 write!(f, "{}", decl.decl.print(cx))
701             }
702         }
703         clean::Tuple(ref typs) => {
704             match &typs[..] {
705                 &[] => primitive_link(f, PrimitiveType::Unit, "()", cx),
706                 &[ref one] => {
707                     primitive_link(f, PrimitiveType::Tuple, "(", cx)?;
708                     // Carry `f.alternate()` into this display w/o branching manually.
709                     fmt::Display::fmt(&one.print(cx), f)?;
710                     primitive_link(f, PrimitiveType::Tuple, ",)", cx)
711                 }
712                 many => {
713                     primitive_link(f, PrimitiveType::Tuple, "(", cx)?;
714                     for (i, item) in many.iter().enumerate() {
715                         if i != 0 {
716                             write!(f, ", ")?;
717                         }
718                         fmt::Display::fmt(&item.print(cx), f)?;
719                     }
720                     primitive_link(f, PrimitiveType::Tuple, ")", cx)
721                 }
722             }
723         }
724         clean::Slice(ref t) => {
725             primitive_link(f, PrimitiveType::Slice, "[", cx)?;
726             fmt::Display::fmt(&t.print(cx), f)?;
727             primitive_link(f, PrimitiveType::Slice, "]", cx)
728         }
729         clean::Array(ref t, ref n) => {
730             primitive_link(f, PrimitiveType::Array, "[", cx)?;
731             fmt::Display::fmt(&t.print(cx), f)?;
732             if f.alternate() {
733                 primitive_link(f, PrimitiveType::Array, &format!("; {}]", n), cx)
734             } else {
735                 primitive_link(f, PrimitiveType::Array, &format!("; {}]", Escape(n)), cx)
736             }
737         }
738         clean::Never => primitive_link(f, PrimitiveType::Never, "!", cx),
739         clean::RawPointer(m, ref t) => {
740             let m = match m {
741                 hir::Mutability::Mut => "mut",
742                 hir::Mutability::Not => "const",
743             };
744             match **t {
745                 clean::Generic(_) | clean::ResolvedPath { is_generic: true, .. } => {
746                     if f.alternate() {
747                         primitive_link(
748                             f,
749                             clean::PrimitiveType::RawPointer,
750                             &format!("*{} {:#}", m, t.print(cx)),
751                             cx,
752                         )
753                     } else {
754                         primitive_link(
755                             f,
756                             clean::PrimitiveType::RawPointer,
757                             &format!("*{} {}", m, t.print(cx)),
758                             cx,
759                         )
760                     }
761                 }
762                 _ => {
763                     primitive_link(f, clean::PrimitiveType::RawPointer, &format!("*{} ", m), cx)?;
764                     fmt::Display::fmt(&t.print(cx), f)
765                 }
766             }
767         }
768         clean::BorrowedRef { lifetime: ref l, mutability, type_: ref ty } => {
769             let lt = match l {
770                 Some(l) => format!("{} ", l.print()),
771                 _ => String::new(),
772             };
773             let m = mutability.print_with_space();
774             let amp = if f.alternate() { "&".to_string() } else { "&amp;".to_string() };
775             match **ty {
776                 clean::Slice(ref bt) => {
777                     // `BorrowedRef{ ... Slice(T) }` is `&[T]`
778                     match **bt {
779                         clean::Generic(_) => {
780                             if f.alternate() {
781                                 primitive_link(
782                                     f,
783                                     PrimitiveType::Slice,
784                                     &format!("{}{}{}[{:#}]", amp, lt, m, bt.print(cx)),
785                                     cx,
786                                 )
787                             } else {
788                                 primitive_link(
789                                     f,
790                                     PrimitiveType::Slice,
791                                     &format!("{}{}{}[{}]", amp, lt, m, bt.print(cx)),
792                                     cx,
793                                 )
794                             }
795                         }
796                         _ => {
797                             primitive_link(
798                                 f,
799                                 PrimitiveType::Slice,
800                                 &format!("{}{}{}[", amp, lt, m),
801                                 cx,
802                             )?;
803                             if f.alternate() {
804                                 write!(f, "{:#}", bt.print(cx))?;
805                             } else {
806                                 write!(f, "{}", bt.print(cx))?;
807                             }
808                             primitive_link(f, PrimitiveType::Slice, "]", cx)
809                         }
810                     }
811                 }
812                 clean::ResolvedPath { param_names: Some(ref v), .. } if !v.is_empty() => {
813                     write!(f, "{}{}{}(", amp, lt, m)?;
814                     fmt_type(&ty, f, use_absolute, cx)?;
815                     write!(f, ")")
816                 }
817                 clean::Generic(..) => {
818                     primitive_link(
819                         f,
820                         PrimitiveType::Reference,
821                         &format!("{}{}{}", amp, lt, m),
822                         cx,
823                     )?;
824                     fmt_type(&ty, f, use_absolute, cx)
825                 }
826                 _ => {
827                     write!(f, "{}{}{}", amp, lt, m)?;
828                     fmt_type(&ty, f, use_absolute, cx)
829                 }
830             }
831         }
832         clean::ImplTrait(ref bounds) => {
833             if f.alternate() {
834                 write!(f, "impl {:#}", print_generic_bounds(bounds, cx))
835             } else {
836                 write!(f, "impl {}", print_generic_bounds(bounds, cx))
837             }
838         }
839         clean::QPath { ref name, ref self_type, ref trait_, ref self_def_id } => {
840             let should_show_cast = match *trait_ {
841                 box clean::ResolvedPath { ref path, .. } => {
842                     !path.segments.is_empty()
843                         && self_def_id
844                             .zip(trait_.def_id())
845                             .map_or(!self_type.is_self_type(), |(id, trait_)| id != trait_)
846                 }
847                 _ => true,
848             };
849             if f.alternate() {
850                 if should_show_cast {
851                     write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))?
852                 } else {
853                     write!(f, "{:#}::", self_type.print(cx))?
854                 }
855             } else {
856                 if should_show_cast {
857                     write!(f, "&lt;{} as {}&gt;::", self_type.print(cx), trait_.print(cx))?
858                 } else {
859                     write!(f, "{}::", self_type.print(cx))?
860                 }
861             };
862             match *trait_ {
863                 // It's pretty unsightly to look at `<A as B>::C` in output, and
864                 // we've got hyperlinking on our side, so try to avoid longer
865                 // notation as much as possible by making `C` a hyperlink to trait
866                 // `B` to disambiguate.
867                 //
868                 // FIXME: this is still a lossy conversion and there should probably
869                 //        be a better way of representing this in general? Most of
870                 //        the ugliness comes from inlining across crates where
871                 //        everything comes in as a fully resolved QPath (hard to
872                 //        look at).
873                 box clean::ResolvedPath { did, ref param_names, .. } => {
874                     match href(did.into(), cx) {
875                         Some((ref url, _, ref path)) if !f.alternate() => {
876                             write!(
877                                 f,
878                                 "<a class=\"type\" href=\"{url}#{shortty}.{name}\" \
879                                     title=\"type {path}::{name}\">{name}</a>",
880                                 url = url,
881                                 shortty = ItemType::AssocType,
882                                 name = name,
883                                 path = path.join("::")
884                             )?;
885                         }
886                         _ => write!(f, "{}", name)?,
887                     }
888
889                     // FIXME: `param_names` are not rendered, and this seems bad?
890                     drop(param_names);
891                     Ok(())
892                 }
893                 _ => write!(f, "{}", name),
894             }
895         }
896     }
897 }
898
899 impl clean::Type {
900     crate fn print<'b, 'a: 'b, 'tcx: 'a>(
901         &'a self,
902         cx: &'a Context<'tcx>,
903     ) -> impl fmt::Display + 'b + Captures<'tcx> {
904         display_fn(move |f| fmt_type(self, f, false, cx))
905     }
906 }
907
908 impl clean::Impl {
909     crate fn print<'a, 'tcx: 'a>(
910         &'a self,
911         use_absolute: bool,
912         cx: &'a Context<'tcx>,
913     ) -> impl fmt::Display + 'a + Captures<'tcx> {
914         display_fn(move |f| {
915             if f.alternate() {
916                 write!(f, "impl{:#} ", self.generics.print(cx))?;
917             } else {
918                 write!(f, "impl{} ", self.generics.print(cx))?;
919             }
920
921             if let Some(ref ty) = self.trait_ {
922                 if self.negative_polarity {
923                     write!(f, "!")?;
924                 }
925                 fmt::Display::fmt(&ty.print(cx), f)?;
926                 write!(f, " for ")?;
927             }
928
929             if let Some(ref ty) = self.blanket_impl {
930                 fmt_type(ty, f, use_absolute, cx)?;
931             } else {
932                 fmt_type(&self.for_, f, use_absolute, cx)?;
933             }
934
935             fmt::Display::fmt(&print_where_clause(&self.generics, cx, 0, true), f)?;
936             Ok(())
937         })
938     }
939 }
940
941 impl clean::Arguments {
942     crate fn print<'a, 'tcx: 'a>(
943         &'a self,
944         cx: &'a Context<'tcx>,
945     ) -> impl fmt::Display + 'a + Captures<'tcx> {
946         display_fn(move |f| {
947             for (i, input) in self.values.iter().enumerate() {
948                 if !input.name.is_empty() {
949                     write!(f, "{}: ", input.name)?;
950                 }
951                 if f.alternate() {
952                     write!(f, "{:#}", input.type_.print(cx))?;
953                 } else {
954                     write!(f, "{}", input.type_.print(cx))?;
955                 }
956                 if i + 1 < self.values.len() {
957                     write!(f, ", ")?;
958                 }
959             }
960             Ok(())
961         })
962     }
963 }
964
965 impl clean::FnRetTy {
966     crate fn print<'a, 'tcx: 'a>(
967         &'a self,
968         cx: &'a Context<'tcx>,
969     ) -> impl fmt::Display + 'a + Captures<'tcx> {
970         display_fn(move |f| match self {
971             clean::Return(clean::Tuple(tys)) if tys.is_empty() => Ok(()),
972             clean::Return(ty) if f.alternate() => {
973                 write!(f, " -> {:#}", ty.print(cx))
974             }
975             clean::Return(ty) => write!(f, " -&gt; {}", ty.print(cx)),
976             clean::DefaultReturn => Ok(()),
977         })
978     }
979 }
980
981 impl clean::BareFunctionDecl {
982     fn print_hrtb_with_space<'a, 'tcx: 'a>(
983         &'a self,
984         cx: &'a Context<'tcx>,
985     ) -> impl fmt::Display + 'a + Captures<'tcx> {
986         display_fn(move |f| {
987             if !self.generic_params.is_empty() {
988                 write!(f, "for<{}> ", comma_sep(self.generic_params.iter().map(|g| g.print(cx))))
989             } else {
990                 Ok(())
991             }
992         })
993     }
994 }
995
996 impl clean::FnDecl {
997     crate fn print<'b, 'a: 'b, 'tcx: 'a>(
998         &'a self,
999         cx: &'a Context<'tcx>,
1000     ) -> impl fmt::Display + 'b + Captures<'tcx> {
1001         display_fn(move |f| {
1002             let ellipsis = if self.c_variadic { ", ..." } else { "" };
1003             if f.alternate() {
1004                 write!(
1005                     f,
1006                     "({args:#}{ellipsis}){arrow:#}",
1007                     args = self.inputs.print(cx),
1008                     ellipsis = ellipsis,
1009                     arrow = self.output.print(cx)
1010                 )
1011             } else {
1012                 write!(
1013                     f,
1014                     "({args}{ellipsis}){arrow}",
1015                     args = self.inputs.print(cx),
1016                     ellipsis = ellipsis,
1017                     arrow = self.output.print(cx)
1018                 )
1019             }
1020         })
1021     }
1022
1023     /// * `header_len`: The length of the function header and name. In other words, the number of
1024     ///   characters in the function declaration up to but not including the parentheses.
1025     ///   <br>Used to determine line-wrapping.
1026     /// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is
1027     ///   necessary.
1028     /// * `asyncness`: Whether the function is async or not.
1029     crate fn full_print<'a, 'tcx: 'a>(
1030         &'a self,
1031         header_len: usize,
1032         indent: usize,
1033         asyncness: hir::IsAsync,
1034         cx: &'a Context<'tcx>,
1035     ) -> impl fmt::Display + 'a + Captures<'tcx> {
1036         display_fn(move |f| self.inner_full_print(header_len, indent, asyncness, f, cx))
1037     }
1038
1039     fn inner_full_print(
1040         &self,
1041         header_len: usize,
1042         indent: usize,
1043         asyncness: hir::IsAsync,
1044         f: &mut fmt::Formatter<'_>,
1045         cx: &Context<'_>,
1046     ) -> fmt::Result {
1047         let amp = if f.alternate() { "&" } else { "&amp;" };
1048         let mut args = String::new();
1049         let mut args_plain = String::new();
1050         for (i, input) in self.inputs.values.iter().enumerate() {
1051             if i == 0 {
1052                 args.push_str("<br>");
1053             }
1054
1055             if let Some(selfty) = input.to_self() {
1056                 match selfty {
1057                     clean::SelfValue => {
1058                         args.push_str("self");
1059                         args_plain.push_str("self");
1060                     }
1061                     clean::SelfBorrowed(Some(ref lt), mtbl) => {
1062                         args.push_str(&format!(
1063                             "{}{} {}self",
1064                             amp,
1065                             lt.print(),
1066                             mtbl.print_with_space()
1067                         ));
1068                         args_plain.push_str(&format!(
1069                             "&{} {}self",
1070                             lt.print(),
1071                             mtbl.print_with_space()
1072                         ));
1073                     }
1074                     clean::SelfBorrowed(None, mtbl) => {
1075                         args.push_str(&format!("{}{}self", amp, mtbl.print_with_space()));
1076                         args_plain.push_str(&format!("&{}self", mtbl.print_with_space()));
1077                     }
1078                     clean::SelfExplicit(ref typ) => {
1079                         if f.alternate() {
1080                             args.push_str(&format!("self: {:#}", typ.print(cx)));
1081                         } else {
1082                             args.push_str(&format!("self: {}", typ.print(cx)));
1083                         }
1084                         args_plain.push_str(&format!("self: {:#}", typ.print(cx)));
1085                     }
1086                 }
1087             } else {
1088                 if i > 0 {
1089                     args.push_str(" <br>");
1090                     args_plain.push(' ');
1091                 }
1092                 if !input.name.is_empty() {
1093                     args.push_str(&format!("{}: ", input.name));
1094                     args_plain.push_str(&format!("{}: ", input.name));
1095                 }
1096
1097                 if f.alternate() {
1098                     args.push_str(&format!("{:#}", input.type_.print(cx)));
1099                 } else {
1100                     args.push_str(&input.type_.print(cx).to_string());
1101                 }
1102                 args_plain.push_str(&format!("{:#}", input.type_.print(cx)));
1103             }
1104             if i + 1 < self.inputs.values.len() {
1105                 args.push(',');
1106                 args_plain.push(',');
1107             }
1108         }
1109
1110         let mut args_plain = format!("({})", args_plain);
1111
1112         if self.c_variadic {
1113             args.push_str(",<br> ...");
1114             args_plain.push_str(", ...");
1115         }
1116
1117         let arrow_plain;
1118         let arrow = if let hir::IsAsync::Async = asyncness {
1119             let output = self.sugared_async_return_type();
1120             arrow_plain = format!("{:#}", output.print(cx));
1121             if f.alternate() { arrow_plain.clone() } else { format!("{}", output.print(cx)) }
1122         } else {
1123             arrow_plain = format!("{:#}", self.output.print(cx));
1124             if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) }
1125         };
1126
1127         let declaration_len = header_len + args_plain.len() + arrow_plain.len();
1128         let output = if declaration_len > 80 {
1129             let full_pad = format!("<br>{}", "&nbsp;".repeat(indent + 4));
1130             let close_pad = format!("<br>{}", "&nbsp;".repeat(indent));
1131             format!(
1132                 "({args}{close}){arrow}",
1133                 args = args.replace("<br>", &full_pad),
1134                 close = close_pad,
1135                 arrow = arrow
1136             )
1137         } else {
1138             format!("({args}){arrow}", args = args.replace("<br>", ""), arrow = arrow)
1139         };
1140
1141         if f.alternate() {
1142             write!(f, "{}", output.replace("<br>", "\n"))
1143         } else {
1144             write!(f, "{}", output)
1145         }
1146     }
1147 }
1148
1149 impl clean::Visibility {
1150     crate fn print_with_space<'a, 'tcx: 'a>(
1151         self,
1152         item_did: FakeDefId,
1153         cx: &'a Context<'tcx>,
1154     ) -> impl fmt::Display + 'a + Captures<'tcx> {
1155         let to_print = match self {
1156             clean::Public => "pub ".to_owned(),
1157             clean::Inherited => String::new(),
1158             clean::Visibility::Restricted(vis_did) => {
1159                 // FIXME(camelid): This may not work correctly if `item_did` is a module.
1160                 //                 However, rustdoc currently never displays a module's
1161                 //                 visibility, so it shouldn't matter.
1162                 let parent_module = find_nearest_parent_module(cx.tcx(), item_did.expect_real());
1163
1164                 if vis_did.index == CRATE_DEF_INDEX {
1165                     "pub(crate) ".to_owned()
1166                 } else if parent_module == Some(vis_did) {
1167                     // `pub(in foo)` where `foo` is the parent module
1168                     // is the same as no visibility modifier
1169                     String::new()
1170                 } else if parent_module
1171                     .map(|parent| find_nearest_parent_module(cx.tcx(), parent))
1172                     .flatten()
1173                     == Some(vis_did)
1174                 {
1175                     "pub(super) ".to_owned()
1176                 } else {
1177                     let path = cx.tcx().def_path(vis_did);
1178                     debug!("path={:?}", path);
1179                     // modified from `resolved_path()` to work with `DefPathData`
1180                     let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
1181                     let anchor = anchor(vis_did, &last_name.as_str(), cx).to_string();
1182
1183                     let mut s = "pub(in ".to_owned();
1184                     for seg in &path.data[..path.data.len() - 1] {
1185                         s.push_str(&format!("{}::", seg.data.get_opt_name().unwrap()));
1186                     }
1187                     s.push_str(&format!("{}) ", anchor));
1188                     s
1189                 }
1190             }
1191         };
1192         display_fn(move |f| f.write_str(&to_print))
1193     }
1194
1195     /// This function is the same as print_with_space, except that it renders no links.
1196     /// It's used for macros' rendered source view, which is syntax highlighted and cannot have
1197     /// any HTML in it.
1198     crate fn to_src_with_space<'a, 'tcx: 'a>(
1199         self,
1200         tcx: TyCtxt<'tcx>,
1201         item_did: DefId,
1202     ) -> impl fmt::Display + 'a + Captures<'tcx> {
1203         let to_print = match self {
1204             clean::Public => "pub ".to_owned(),
1205             clean::Inherited => String::new(),
1206             clean::Visibility::Restricted(vis_did) => {
1207                 // FIXME(camelid): This may not work correctly if `item_did` is a module.
1208                 //                 However, rustdoc currently never displays a module's
1209                 //                 visibility, so it shouldn't matter.
1210                 let parent_module = find_nearest_parent_module(tcx, item_did);
1211
1212                 if vis_did.index == CRATE_DEF_INDEX {
1213                     "pub(crate) ".to_owned()
1214                 } else if parent_module == Some(vis_did) {
1215                     // `pub(in foo)` where `foo` is the parent module
1216                     // is the same as no visibility modifier
1217                     String::new()
1218                 } else if parent_module
1219                     .map(|parent| find_nearest_parent_module(tcx, parent))
1220                     .flatten()
1221                     == Some(vis_did)
1222                 {
1223                     "pub(super) ".to_owned()
1224                 } else {
1225                     format!("pub(in {}) ", tcx.def_path_str(vis_did))
1226                 }
1227             }
1228         };
1229         display_fn(move |f| f.write_str(&to_print))
1230     }
1231 }
1232
1233 crate trait PrintWithSpace {
1234     fn print_with_space(&self) -> &str;
1235 }
1236
1237 impl PrintWithSpace for hir::Unsafety {
1238     fn print_with_space(&self) -> &str {
1239         match self {
1240             hir::Unsafety::Unsafe => "unsafe ",
1241             hir::Unsafety::Normal => "",
1242         }
1243     }
1244 }
1245
1246 impl PrintWithSpace for hir::Constness {
1247     fn print_with_space(&self) -> &str {
1248         match self {
1249             hir::Constness::Const => "const ",
1250             hir::Constness::NotConst => "",
1251         }
1252     }
1253 }
1254
1255 impl PrintWithSpace for hir::IsAsync {
1256     fn print_with_space(&self) -> &str {
1257         match self {
1258             hir::IsAsync::Async => "async ",
1259             hir::IsAsync::NotAsync => "",
1260         }
1261     }
1262 }
1263
1264 impl PrintWithSpace for hir::Mutability {
1265     fn print_with_space(&self) -> &str {
1266         match self {
1267             hir::Mutability::Not => "",
1268             hir::Mutability::Mut => "mut ",
1269         }
1270     }
1271 }
1272
1273 impl clean::Import {
1274     crate fn print<'a, 'tcx: 'a>(
1275         &'a self,
1276         cx: &'a Context<'tcx>,
1277     ) -> impl fmt::Display + 'a + Captures<'tcx> {
1278         display_fn(move |f| match self.kind {
1279             clean::ImportKind::Simple(name) => {
1280                 if name == self.source.path.last() {
1281                     write!(f, "use {};", self.source.print(cx))
1282                 } else {
1283                     write!(f, "use {} as {};", self.source.print(cx), name)
1284                 }
1285             }
1286             clean::ImportKind::Glob => {
1287                 if self.source.path.segments.is_empty() {
1288                     write!(f, "use *;")
1289                 } else {
1290                     write!(f, "use {}::*;", self.source.print(cx))
1291                 }
1292             }
1293         })
1294     }
1295 }
1296
1297 impl clean::ImportSource {
1298     crate fn print<'a, 'tcx: 'a>(
1299         &'a self,
1300         cx: &'a Context<'tcx>,
1301     ) -> impl fmt::Display + 'a + Captures<'tcx> {
1302         display_fn(move |f| match self.did {
1303             Some(did) => resolved_path(f, did, &self.path, true, false, cx),
1304             _ => {
1305                 for seg in &self.path.segments[..self.path.segments.len() - 1] {
1306                     write!(f, "{}::", seg.name)?;
1307                 }
1308                 let name = self.path.last_name();
1309                 if let hir::def::Res::PrimTy(p) = self.path.res {
1310                     primitive_link(f, PrimitiveType::from(p), &*name, cx)?;
1311                 } else {
1312                     write!(f, "{}", name)?;
1313                 }
1314                 Ok(())
1315             }
1316         })
1317     }
1318 }
1319
1320 impl clean::TypeBinding {
1321     crate fn print<'a, 'tcx: 'a>(
1322         &'a self,
1323         cx: &'a Context<'tcx>,
1324     ) -> impl fmt::Display + 'a + Captures<'tcx> {
1325         display_fn(move |f| {
1326             f.write_str(&*self.name.as_str())?;
1327             match self.kind {
1328                 clean::TypeBindingKind::Equality { ref ty } => {
1329                     if f.alternate() {
1330                         write!(f, " = {:#}", ty.print(cx))?;
1331                     } else {
1332                         write!(f, " = {}", ty.print(cx))?;
1333                     }
1334                 }
1335                 clean::TypeBindingKind::Constraint { ref bounds } => {
1336                     if !bounds.is_empty() {
1337                         if f.alternate() {
1338                             write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
1339                         } else {
1340                             write!(f, ":&nbsp;{}", print_generic_bounds(bounds, cx))?;
1341                         }
1342                     }
1343                 }
1344             }
1345             Ok(())
1346         })
1347     }
1348 }
1349
1350 crate fn print_abi_with_space(abi: Abi) -> impl fmt::Display {
1351     display_fn(move |f| {
1352         let quot = if f.alternate() { "\"" } else { "&quot;" };
1353         match abi {
1354             Abi::Rust => Ok(()),
1355             abi => write!(f, "extern {0}{1}{0} ", quot, abi.name()),
1356         }
1357     })
1358 }
1359
1360 crate fn print_default_space<'a>(v: bool) -> &'a str {
1361     if v { "default " } else { "" }
1362 }
1363
1364 impl clean::GenericArg {
1365     crate fn print<'a, 'tcx: 'a>(
1366         &'a self,
1367         cx: &'a Context<'tcx>,
1368     ) -> impl fmt::Display + 'a + Captures<'tcx> {
1369         display_fn(move |f| match self {
1370             clean::GenericArg::Lifetime(lt) => fmt::Display::fmt(&lt.print(), f),
1371             clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cx), f),
1372             clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f),
1373         })
1374     }
1375 }
1376
1377 crate fn display_fn(f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl fmt::Display {
1378     struct WithFormatter<F>(Cell<Option<F>>);
1379
1380     impl<F> fmt::Display for WithFormatter<F>
1381     where
1382         F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
1383     {
1384         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1385             (self.0.take()).unwrap()(f)
1386         }
1387     }
1388
1389     WithFormatter(Cell::new(Some(f)))
1390 }