Skip to content

Commit 07b2721

Browse files
authored
Rollup merge of rust-lang#109187 - clubby789:askama-source, r=GuillaumeGomez
Render source page layout with Askama ~~I was looking at making `code_html` render into the buffer instead of in advance, but it turned out to need a pretty big refactor, so starting with rearranging the high-level layout.~~ Found another approach which required much less changes cc rust-lang#108868
2 parents a9cb27d + 4212c1b commit 07b2721

File tree

3 files changed

+67
-69
lines changed

3 files changed

+67
-69
lines changed

src/librustdoc/html/highlight.rs

+21-38
Original file line numberDiff line numberDiff line change
@@ -65,23 +65,6 @@ pub(crate) fn render_item_decl_with_highlighting(src: &str, out: &mut Buffer) {
6565
write!(out, "</pre>");
6666
}
6767

68-
/// Highlights `src` as a source code page, returning the HTML output.
69-
pub(crate) fn render_source_with_highlighting(
70-
src: &str,
71-
out: &mut Buffer,
72-
line_numbers: Buffer,
73-
href_context: HrefContext<'_, '_>,
74-
decoration_info: DecorationInfo,
75-
extra: Option<&str>,
76-
) {
77-
write_header(out, "", Some(line_numbers), Tooltip::None);
78-
if let Some(extra) = extra {
79-
out.push_str(extra);
80-
}
81-
write_code(out, src, Some(href_context), Some(decoration_info));
82-
write_footer(out, None);
83-
}
84-
8568
fn write_header(out: &mut Buffer, class: &str, extra_content: Option<Buffer>, tooltip: Tooltip) {
8669
write!(
8770
out,
@@ -143,8 +126,8 @@ fn can_merge(class1: Option<Class>, class2: Option<Class>, text: &str) -> bool {
143126

144127
/// This type is used as a conveniency to prevent having to pass all its fields as arguments into
145128
/// the various functions (which became its methods).
146-
struct TokenHandler<'a, 'tcx> {
147-
out: &'a mut Buffer,
129+
struct TokenHandler<'a, 'tcx, F: Write> {
130+
out: &'a mut F,
148131
/// It contains the closing tag and the associated `Class`.
149132
closing_tags: Vec<(&'static str, Class)>,
150133
/// This is used because we don't automatically generate the closing tag on `ExitSpan` in
@@ -159,7 +142,7 @@ struct TokenHandler<'a, 'tcx> {
159142
href_context: Option<HrefContext<'a, 'tcx>>,
160143
}
161144

162-
impl<'a, 'tcx> TokenHandler<'a, 'tcx> {
145+
impl<'a, 'tcx, F: Write> TokenHandler<'a, 'tcx, F> {
163146
fn handle_exit_span(&mut self) {
164147
// We can't get the last `closing_tags` element using `pop()` because `closing_tags` is
165148
// being used in `write_pending_elems`.
@@ -211,7 +194,7 @@ impl<'a, 'tcx> TokenHandler<'a, 'tcx> {
211194
}
212195
}
213196

214-
impl<'a, 'tcx> Drop for TokenHandler<'a, 'tcx> {
197+
impl<'a, 'tcx, F: Write> Drop for TokenHandler<'a, 'tcx, F> {
215198
/// When leaving, we need to flush all pending data to not have missing content.
216199
fn drop(&mut self) {
217200
if self.pending_exit_span.is_some() {
@@ -233,8 +216,8 @@ impl<'a, 'tcx> Drop for TokenHandler<'a, 'tcx> {
233216
/// item definition.
234217
///
235218
/// More explanations about spans and how we use them here are provided in the
236-
fn write_code(
237-
out: &mut Buffer,
219+
pub(super) fn write_code(
220+
out: &mut impl Write,
238221
src: &str,
239222
href_context: Option<HrefContext<'_, '_>>,
240223
decoration_info: Option<DecorationInfo>,
@@ -883,7 +866,7 @@ impl<'src> Classifier<'src> {
883866
/// Called when we start processing a span of text that should be highlighted.
884867
/// The `Class` argument specifies how it should be highlighted.
885868
fn enter_span(
886-
out: &mut Buffer,
869+
out: &mut impl Write,
887870
klass: Class,
888871
href_context: &Option<HrefContext<'_, '_>>,
889872
) -> &'static str {
@@ -894,8 +877,8 @@ fn enter_span(
894877
}
895878

896879
/// Called at the end of a span of highlighted text.
897-
fn exit_span(out: &mut Buffer, closing_tag: &str) {
898-
out.write_str(closing_tag);
880+
fn exit_span(out: &mut impl Write, closing_tag: &str) {
881+
out.write_str(closing_tag).unwrap();
899882
}
900883

901884
/// Called for a span of text. If the text should be highlighted differently
@@ -915,15 +898,15 @@ fn exit_span(out: &mut Buffer, closing_tag: &str) {
915898
/// will then try to find this `span` in the `span_correspondance_map`. If found, it'll then
916899
/// generate a link for this element (which corresponds to where its definition is located).
917900
fn string<T: Display>(
918-
out: &mut Buffer,
901+
out: &mut impl Write,
919902
text: T,
920903
klass: Option<Class>,
921904
href_context: &Option<HrefContext<'_, '_>>,
922905
open_tag: bool,
923906
) {
924907
if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context, open_tag)
925908
{
926-
out.write_str(closing_tag);
909+
out.write_str(closing_tag).unwrap();
927910
}
928911
}
929912

@@ -937,24 +920,24 @@ fn string<T: Display>(
937920
/// in `span_map.rs::collect_spans_and_sources`. If it cannot retrieve the information, then it's
938921
/// the same as the second point (`klass` is `Some` but doesn't have a [`rustc_span::Span`]).
939922
fn string_without_closing_tag<T: Display>(
940-
out: &mut Buffer,
923+
out: &mut impl Write,
941924
text: T,
942925
klass: Option<Class>,
943926
href_context: &Option<HrefContext<'_, '_>>,
944927
open_tag: bool,
945928
) -> Option<&'static str> {
946929
let Some(klass) = klass
947930
else {
948-
write!(out, "{}", text);
931+
write!(out, "{}", text).unwrap();
949932
return None;
950933
};
951934
let Some(def_span) = klass.get_span()
952935
else {
953936
if !open_tag {
954-
write!(out, "{}", text);
937+
write!(out, "{}", text).unwrap();
955938
return None;
956939
}
957-
write!(out, "<span class=\"{}\">{}", klass.as_html(), text);
940+
write!(out, "<span class=\"{}\">{}", klass.as_html(), text).unwrap();
958941
return Some("</span>");
959942
};
960943

@@ -1009,28 +992,28 @@ fn string_without_closing_tag<T: Display>(
1009992
if !open_tag {
1010993
// We're already inside an element which has the same klass, no need to give it
1011994
// again.
1012-
write!(out, "<a href=\"{}\">{}", href, text_s);
995+
write!(out, "<a href=\"{}\">{}", href, text_s).unwrap();
1013996
} else {
1014997
let klass_s = klass.as_html();
1015998
if klass_s.is_empty() {
1016-
write!(out, "<a href=\"{}\">{}", href, text_s);
999+
write!(out, "<a href=\"{}\">{}", href, text_s).unwrap();
10171000
} else {
1018-
write!(out, "<a class=\"{}\" href=\"{}\">{}", klass_s, href, text_s);
1001+
write!(out, "<a class=\"{}\" href=\"{}\">{}", klass_s, href, text_s).unwrap();
10191002
}
10201003
}
10211004
return Some("</a>");
10221005
}
10231006
}
10241007
if !open_tag {
1025-
write!(out, "{}", text_s);
1008+
write!(out, "{}", text_s).unwrap();
10261009
return None;
10271010
}
10281011
let klass_s = klass.as_html();
10291012
if klass_s.is_empty() {
1030-
write!(out, "{}", text_s);
1013+
out.write_str(&text_s).unwrap();
10311014
Some("")
10321015
} else {
1033-
write!(out, "<span class=\"{}\">{}", klass_s, text_s);
1016+
write!(out, "<span class=\"{}\">{}", klass_s, text_s).unwrap();
10341017
Some("</span>")
10351018
}
10361019
}

src/librustdoc/html/sources.rs

+27-31
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
use crate::clean;
22
use crate::docfs::PathError;
33
use crate::error::Error;
4+
use crate::html::format;
45
use crate::html::format::Buffer;
56
use crate::html::highlight;
67
use crate::html::layout;
78
use crate::html::render::Context;
89
use crate::visit::DocVisitor;
910

11+
use askama::Template;
1012
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1113
use rustc_hir::def_id::LOCAL_CRATE;
1214
use rustc_middle::ty::TyCtxt;
@@ -16,6 +18,7 @@ use rustc_span::source_map::FileName;
1618
use std::cell::RefCell;
1719
use std::ffi::OsStr;
1820
use std::fs;
21+
use std::ops::RangeInclusive;
1922
use std::path::{Component, Path, PathBuf};
2023
use std::rc::Rc;
2124

@@ -299,39 +302,32 @@ pub(crate) fn print_src(
299302
decoration_info: highlight::DecorationInfo,
300303
source_context: SourceContext,
301304
) {
305+
#[derive(Template)]
306+
#[template(path = "source.html")]
307+
struct Source<Code: std::fmt::Display> {
308+
embedded: bool,
309+
needs_expansion: bool,
310+
lines: RangeInclusive<usize>,
311+
code_html: Code,
312+
}
302313
let lines = s.lines().count();
303-
let mut line_numbers = Buffer::empty_from(buf);
304-
let extra;
305-
line_numbers.write_str("<pre class=\"src-line-numbers\">");
314+
let (embedded, needs_expansion, lines) = match source_context {
315+
SourceContext::Standalone => (false, false, 1..=lines),
316+
SourceContext::Embedded { offset, needs_expansion } => {
317+
(true, needs_expansion, (1 + offset)..=(lines + offset))
318+
}
319+
};
306320
let current_href = context
307321
.href_from_span(clean::Span::new(file_span), false)
308322
.expect("only local crates should have sources emitted");
309-
match source_context {
310-
SourceContext::Standalone => {
311-
extra = None;
312-
for line in 1..=lines {
313-
writeln!(line_numbers, "<a href=\"#{line}\" id=\"{line}\">{line}</a>")
314-
}
315-
}
316-
SourceContext::Embedded { offset, needs_expansion } => {
317-
extra = if needs_expansion {
318-
Some(r#"<button class="expand">&varr;</button>"#)
319-
} else {
320-
None
321-
};
322-
for line_number in 1..=lines {
323-
let line = line_number + offset;
324-
writeln!(line_numbers, "<span>{line}</span>")
325-
}
326-
}
327-
}
328-
line_numbers.write_str("</pre>");
329-
highlight::render_source_with_highlighting(
330-
s,
331-
buf,
332-
line_numbers,
333-
highlight::HrefContext { context, file_span, root_path, current_href },
334-
decoration_info,
335-
extra,
336-
);
323+
let code = format::display_fn(move |fmt| {
324+
highlight::write_code(
325+
fmt,
326+
s,
327+
Some(highlight::HrefContext { context, file_span, root_path, current_href }),
328+
Some(decoration_info),
329+
);
330+
Ok(())
331+
});
332+
Source { embedded, needs_expansion, lines, code_html: code }.render_into(buf).unwrap();
337333
}
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<div class="example-wrap"> {# #}
2+
<pre class="src-line-numbers">
3+
{% for line in lines.clone() %}
4+
{% if embedded %}
5+
<span>{{line|safe}}</span>
6+
{%~ else %}
7+
<a href="#{{line|safe}}" id="{{line|safe}}">{{line|safe}}</a>
8+
{%~ endif %}
9+
{% endfor %}
10+
</pre> {# #}
11+
<pre class="rust"> {# #}
12+
<code>
13+
{% if needs_expansion %}
14+
<button class="expand">&varr;</button>
15+
{% endif %}
16+
{{code_html|safe}}
17+
</code> {# #}
18+
</pre> {# #}
19+
</div>

0 commit comments

Comments
 (0)