|
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