|
8 | 8 | use std::borrow::Cow; |
9 | 9 | use std::collections::VecDeque; |
10 | 10 | use std::fmt::{self, Display, Write}; |
| 11 | +use std::iter; |
11 | 12 |
|
12 | 13 | use rustc_data_structures::fx::FxIndexMap; |
13 | 14 | use rustc_lexer::{Cursor, FrontmatterAllowed, LiteralKind, TokenKind}; |
14 | 15 | use rustc_span::edition::Edition; |
15 | 16 | use rustc_span::symbol::Symbol; |
16 | 17 | use rustc_span::{BytePos, DUMMY_SP, Span}; |
17 | 18 |
|
18 | | -use super::format::{self, write_str}; |
| 19 | +use super::format; |
19 | 20 | use crate::clean::PrimitiveType; |
| 21 | +use crate::display::Joined as _; |
20 | 22 | use crate::html::escape::EscapeBodyText; |
21 | 23 | use crate::html::macro_expansion::ExpandedCode; |
22 | 24 | use crate::html::render::{Context, LinkFromSrc}; |
@@ -45,92 +47,72 @@ pub(crate) enum Tooltip { |
45 | 47 | CompileFail, |
46 | 48 | ShouldPanic, |
47 | 49 | Edition(Edition), |
48 | | - None, |
49 | 50 | } |
50 | 51 |
|
51 | 52 | /// Highlights `src` as an inline example, returning the HTML output. |
52 | 53 | pub(crate) fn render_example_with_highlighting( |
53 | 54 | src: &str, |
54 | | - out: &mut String, |
55 | | - tooltip: Tooltip, |
| 55 | + tooltip: Option<&Tooltip>, |
56 | 56 | playground_button: Option<&str>, |
57 | 57 | extra_classes: &[String], |
58 | | -) { |
59 | | - write_header(out, "rust-example-rendered", None, tooltip, extra_classes); |
60 | | - write_code(out, src, None, None, None); |
61 | | - write_footer(out, playground_button); |
| 58 | +) -> impl Display { |
| 59 | + fmt::from_fn(move |f| { |
| 60 | + write_header("rust-example-rendered", tooltip, extra_classes).fmt(f)?; |
| 61 | + write_code(f, src, None, None, None); |
| 62 | + write_footer(playground_button).fmt(f) |
| 63 | + }) |
62 | 64 | } |
63 | 65 |
|
64 | | -fn write_header( |
65 | | - out: &mut String, |
66 | | - class: &str, |
67 | | - extra_content: Option<&str>, |
68 | | - tooltip: Tooltip, |
69 | | - extra_classes: &[String], |
70 | | -) { |
71 | | - write_str( |
72 | | - out, |
73 | | - format_args!( |
| 66 | +fn write_header(class: &str, tooltip: Option<&Tooltip>, extra_classes: &[String]) -> impl Display { |
| 67 | + fmt::from_fn(move |f| { |
| 68 | + write!( |
| 69 | + f, |
74 | 70 | "<div class=\"example-wrap{}\">", |
75 | | - match tooltip { |
76 | | - Tooltip::IgnoreAll | Tooltip::IgnoreSome(_) => " ignore", |
77 | | - Tooltip::CompileFail => " compile_fail", |
78 | | - Tooltip::ShouldPanic => " should_panic", |
79 | | - Tooltip::Edition(_) => " edition", |
80 | | - Tooltip::None => "", |
81 | | - } |
82 | | - ), |
83 | | - ); |
84 | | - |
85 | | - if tooltip != Tooltip::None { |
86 | | - let tooltip = fmt::from_fn(|f| match &tooltip { |
87 | | - Tooltip::IgnoreAll => f.write_str("This example is not tested"), |
88 | | - Tooltip::IgnoreSome(platforms) => { |
89 | | - f.write_str("This example is not tested on ")?; |
90 | | - match &platforms[..] { |
91 | | - [] => unreachable!(), |
92 | | - [platform] => f.write_str(platform)?, |
93 | | - [first, second] => write!(f, "{first} or {second}")?, |
94 | | - [platforms @ .., last] => { |
95 | | - for platform in platforms { |
96 | | - write!(f, "{platform}, ")?; |
| 71 | + tooltip |
| 72 | + .map(|tooltip| match tooltip { |
| 73 | + Tooltip::IgnoreAll | Tooltip::IgnoreSome(_) => " ignore", |
| 74 | + Tooltip::CompileFail => " compile_fail", |
| 75 | + Tooltip::ShouldPanic => " should_panic", |
| 76 | + Tooltip::Edition(_) => " edition", |
| 77 | + }) |
| 78 | + .unwrap_or_default() |
| 79 | + )?; |
| 80 | + |
| 81 | + if let Some(tooltip) = tooltip { |
| 82 | + let tooltip = fmt::from_fn(|f| match tooltip { |
| 83 | + Tooltip::IgnoreAll => f.write_str("This example is not tested"), |
| 84 | + Tooltip::IgnoreSome(platforms) => { |
| 85 | + f.write_str("This example is not tested on ")?; |
| 86 | + match &platforms[..] { |
| 87 | + [] => unreachable!(), |
| 88 | + [platform] => f.write_str(platform)?, |
| 89 | + [first, second] => write!(f, "{first} or {second}")?, |
| 90 | + [platforms @ .., last] => { |
| 91 | + for platform in platforms { |
| 92 | + write!(f, "{platform}, ")?; |
| 93 | + } |
| 94 | + write!(f, "or {last}")?; |
97 | 95 | } |
98 | | - write!(f, "or {last}")?; |
99 | 96 | } |
| 97 | + Ok(()) |
100 | 98 | } |
101 | | - Ok(()) |
102 | | - } |
103 | | - Tooltip::CompileFail => f.write_str("This example deliberately fails to compile"), |
104 | | - Tooltip::ShouldPanic => f.write_str("This example panics"), |
105 | | - Tooltip::Edition(edition) => write!(f, "This example runs with edition {edition}"), |
106 | | - Tooltip::None => unreachable!(), |
| 99 | + Tooltip::CompileFail => f.write_str("This example deliberately fails to compile"), |
| 100 | + Tooltip::ShouldPanic => f.write_str("This example panics"), |
| 101 | + Tooltip::Edition(edition) => write!(f, "This example runs with edition {edition}"), |
| 102 | + }); |
| 103 | + |
| 104 | + write!(f, "<a href=\"#\" class=\"tooltip\" title=\"{tooltip}\">ⓘ</a>")?; |
| 105 | + } |
| 106 | + |
| 107 | + let classes = fmt::from_fn(|f| { |
| 108 | + iter::once("rust") |
| 109 | + .chain(Some(class).filter(|class| !class.is_empty())) |
| 110 | + .chain(extra_classes.iter().map(String::as_str)) |
| 111 | + .joined(" ", f) |
107 | 112 | }); |
108 | | - write_str(out, format_args!("<a href=\"#\" class=\"tooltip\" title=\"{tooltip}\">ⓘ</a>")); |
109 | | - } |
110 | 113 |
|
111 | | - if let Some(extra) = extra_content { |
112 | | - out.push_str(extra); |
113 | | - } |
114 | | - if class.is_empty() { |
115 | | - write_str( |
116 | | - out, |
117 | | - format_args!( |
118 | | - "<pre class=\"rust{}{}\">", |
119 | | - if extra_classes.is_empty() { "" } else { " " }, |
120 | | - extra_classes.join(" ") |
121 | | - ), |
122 | | - ); |
123 | | - } else { |
124 | | - write_str( |
125 | | - out, |
126 | | - format_args!( |
127 | | - "<pre class=\"rust {class}{}{}\">", |
128 | | - if extra_classes.is_empty() { "" } else { " " }, |
129 | | - extra_classes.join(" ") |
130 | | - ), |
131 | | - ); |
132 | | - } |
133 | | - write_str(out, format_args!("<code>")); |
| 114 | + write!(f, "<pre class=\"{classes}\"><code>") |
| 115 | + }) |
134 | 116 | } |
135 | 117 |
|
136 | 118 | /// Check if two `Class` can be merged together. In the following rules, "unclassified" means `None` |
@@ -577,8 +559,8 @@ pub(super) fn write_code( |
577 | 559 | }); |
578 | 560 | } |
579 | 561 |
|
580 | | -fn write_footer(out: &mut String, playground_button: Option<&str>) { |
581 | | - write_str(out, format_args!("</code></pre>{}</div>", playground_button.unwrap_or_default())); |
| 562 | +fn write_footer(playground_button: Option<&str>) -> impl Display { |
| 563 | + fmt::from_fn(move |f| write!(f, "</code></pre>{}</div>", playground_button.unwrap_or_default())) |
582 | 564 | } |
583 | 565 |
|
584 | 566 | /// How a span of text is classified. Mostly corresponds to token kinds. |
|
0 commit comments