Render source page layout with Askama
Co-authored-by: Michael Howell <michael@notriddle.com>
This commit is contained in:
parent
24c0b81c1f
commit
102c8fa290
@ -65,23 +65,6 @@ pub(crate) fn render_item_decl_with_highlighting(src: &str, out: &mut Buffer) {
|
|||||||
write!(out, "</pre>");
|
write!(out, "</pre>");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Highlights `src` as a source code page, returning the HTML output.
|
|
||||||
pub(crate) fn render_source_with_highlighting(
|
|
||||||
src: &str,
|
|
||||||
out: &mut Buffer,
|
|
||||||
line_numbers: Buffer,
|
|
||||||
href_context: HrefContext<'_, '_>,
|
|
||||||
decoration_info: DecorationInfo,
|
|
||||||
extra: Option<&str>,
|
|
||||||
) {
|
|
||||||
write_header(out, "", Some(line_numbers), Tooltip::None);
|
|
||||||
if let Some(extra) = extra {
|
|
||||||
out.push_str(extra);
|
|
||||||
}
|
|
||||||
write_code(out, src, Some(href_context), Some(decoration_info));
|
|
||||||
write_footer(out, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_header(out: &mut Buffer, class: &str, extra_content: Option<Buffer>, tooltip: Tooltip) {
|
fn write_header(out: &mut Buffer, class: &str, extra_content: Option<Buffer>, tooltip: Tooltip) {
|
||||||
write!(
|
write!(
|
||||||
out,
|
out,
|
||||||
@ -143,8 +126,8 @@ fn can_merge(class1: Option<Class>, class2: Option<Class>, text: &str) -> bool {
|
|||||||
|
|
||||||
/// This type is used as a conveniency to prevent having to pass all its fields as arguments into
|
/// This type is used as a conveniency to prevent having to pass all its fields as arguments into
|
||||||
/// the various functions (which became its methods).
|
/// the various functions (which became its methods).
|
||||||
struct TokenHandler<'a, 'tcx> {
|
struct TokenHandler<'a, 'tcx, F: Write> {
|
||||||
out: &'a mut Buffer,
|
out: &'a mut F,
|
||||||
/// It contains the closing tag and the associated `Class`.
|
/// It contains the closing tag and the associated `Class`.
|
||||||
closing_tags: Vec<(&'static str, Class)>,
|
closing_tags: Vec<(&'static str, Class)>,
|
||||||
/// This is used because we don't automatically generate the closing tag on `ExitSpan` in
|
/// This is used because we don't automatically generate the closing tag on `ExitSpan` in
|
||||||
@ -159,7 +142,7 @@ struct TokenHandler<'a, 'tcx> {
|
|||||||
href_context: Option<HrefContext<'a, 'tcx>>,
|
href_context: Option<HrefContext<'a, 'tcx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> TokenHandler<'a, 'tcx> {
|
impl<'a, 'tcx, F: Write> TokenHandler<'a, 'tcx, F> {
|
||||||
fn handle_exit_span(&mut self) {
|
fn handle_exit_span(&mut self) {
|
||||||
// We can't get the last `closing_tags` element using `pop()` because `closing_tags` is
|
// We can't get the last `closing_tags` element using `pop()` because `closing_tags` is
|
||||||
// being used in `write_pending_elems`.
|
// being used in `write_pending_elems`.
|
||||||
@ -211,7 +194,7 @@ fn write_pending_elems(&mut self, current_class: Option<Class>) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> Drop for TokenHandler<'a, 'tcx> {
|
impl<'a, 'tcx, F: Write> Drop for TokenHandler<'a, 'tcx, F> {
|
||||||
/// When leaving, we need to flush all pending data to not have missing content.
|
/// When leaving, we need to flush all pending data to not have missing content.
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if self.pending_exit_span.is_some() {
|
if self.pending_exit_span.is_some() {
|
||||||
@ -233,8 +216,8 @@ fn drop(&mut self) {
|
|||||||
/// item definition.
|
/// item definition.
|
||||||
///
|
///
|
||||||
/// More explanations about spans and how we use them here are provided in the
|
/// More explanations about spans and how we use them here are provided in the
|
||||||
fn write_code(
|
pub(super) fn write_code(
|
||||||
out: &mut Buffer,
|
out: &mut impl Write,
|
||||||
src: &str,
|
src: &str,
|
||||||
href_context: Option<HrefContext<'_, '_>>,
|
href_context: Option<HrefContext<'_, '_>>,
|
||||||
decoration_info: Option<DecorationInfo>,
|
decoration_info: Option<DecorationInfo>,
|
||||||
@ -883,7 +866,7 @@ fn check_if_is_union_keyword(&mut self) -> bool {
|
|||||||
/// Called when we start processing a span of text that should be highlighted.
|
/// Called when we start processing a span of text that should be highlighted.
|
||||||
/// The `Class` argument specifies how it should be highlighted.
|
/// The `Class` argument specifies how it should be highlighted.
|
||||||
fn enter_span(
|
fn enter_span(
|
||||||
out: &mut Buffer,
|
out: &mut impl Write,
|
||||||
klass: Class,
|
klass: Class,
|
||||||
href_context: &Option<HrefContext<'_, '_>>,
|
href_context: &Option<HrefContext<'_, '_>>,
|
||||||
) -> &'static str {
|
) -> &'static str {
|
||||||
@ -894,8 +877,8 @@ fn enter_span(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Called at the end of a span of highlighted text.
|
/// Called at the end of a span of highlighted text.
|
||||||
fn exit_span(out: &mut Buffer, closing_tag: &str) {
|
fn exit_span(out: &mut impl Write, closing_tag: &str) {
|
||||||
out.write_str(closing_tag);
|
out.write_str(closing_tag).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called for a span of text. If the text should be highlighted differently
|
/// Called for a span of text. If the text should be highlighted differently
|
||||||
@ -915,7 +898,7 @@ fn exit_span(out: &mut Buffer, closing_tag: &str) {
|
|||||||
/// will then try to find this `span` in the `span_correspondance_map`. If found, it'll then
|
/// will then try to find this `span` in the `span_correspondance_map`. If found, it'll then
|
||||||
/// generate a link for this element (which corresponds to where its definition is located).
|
/// generate a link for this element (which corresponds to where its definition is located).
|
||||||
fn string<T: Display>(
|
fn string<T: Display>(
|
||||||
out: &mut Buffer,
|
out: &mut impl Write,
|
||||||
text: T,
|
text: T,
|
||||||
klass: Option<Class>,
|
klass: Option<Class>,
|
||||||
href_context: &Option<HrefContext<'_, '_>>,
|
href_context: &Option<HrefContext<'_, '_>>,
|
||||||
@ -923,7 +906,7 @@ fn string<T: Display>(
|
|||||||
) {
|
) {
|
||||||
if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context, open_tag)
|
if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context, open_tag)
|
||||||
{
|
{
|
||||||
out.write_str(closing_tag);
|
out.write_str(closing_tag).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -937,7 +920,7 @@ fn string<T: Display>(
|
|||||||
/// in `span_map.rs::collect_spans_and_sources`. If it cannot retrieve the information, then it's
|
/// in `span_map.rs::collect_spans_and_sources`. If it cannot retrieve the information, then it's
|
||||||
/// the same as the second point (`klass` is `Some` but doesn't have a [`rustc_span::Span`]).
|
/// the same as the second point (`klass` is `Some` but doesn't have a [`rustc_span::Span`]).
|
||||||
fn string_without_closing_tag<T: Display>(
|
fn string_without_closing_tag<T: Display>(
|
||||||
out: &mut Buffer,
|
out: &mut impl Write,
|
||||||
text: T,
|
text: T,
|
||||||
klass: Option<Class>,
|
klass: Option<Class>,
|
||||||
href_context: &Option<HrefContext<'_, '_>>,
|
href_context: &Option<HrefContext<'_, '_>>,
|
||||||
@ -945,16 +928,16 @@ fn string_without_closing_tag<T: Display>(
|
|||||||
) -> Option<&'static str> {
|
) -> Option<&'static str> {
|
||||||
let Some(klass) = klass
|
let Some(klass) = klass
|
||||||
else {
|
else {
|
||||||
write!(out, "{}", text);
|
write!(out, "{}", text).unwrap();
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let Some(def_span) = klass.get_span()
|
let Some(def_span) = klass.get_span()
|
||||||
else {
|
else {
|
||||||
if !open_tag {
|
if !open_tag {
|
||||||
write!(out, "{}", text);
|
write!(out, "{}", text).unwrap();
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
write!(out, "<span class=\"{}\">{}", klass.as_html(), text);
|
write!(out, "<span class=\"{}\">{}", klass.as_html(), text).unwrap();
|
||||||
return Some("</span>");
|
return Some("</span>");
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1009,28 +992,28 @@ fn string_without_closing_tag<T: Display>(
|
|||||||
if !open_tag {
|
if !open_tag {
|
||||||
// We're already inside an element which has the same klass, no need to give it
|
// We're already inside an element which has the same klass, no need to give it
|
||||||
// again.
|
// again.
|
||||||
write!(out, "<a href=\"{}\">{}", href, text_s);
|
write!(out, "<a href=\"{}\">{}", href, text_s).unwrap();
|
||||||
} else {
|
} else {
|
||||||
let klass_s = klass.as_html();
|
let klass_s = klass.as_html();
|
||||||
if klass_s.is_empty() {
|
if klass_s.is_empty() {
|
||||||
write!(out, "<a href=\"{}\">{}", href, text_s);
|
write!(out, "<a href=\"{}\">{}", href, text_s).unwrap();
|
||||||
} else {
|
} else {
|
||||||
write!(out, "<a class=\"{}\" href=\"{}\">{}", klass_s, href, text_s);
|
write!(out, "<a class=\"{}\" href=\"{}\">{}", klass_s, href, text_s).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Some("</a>");
|
return Some("</a>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !open_tag {
|
if !open_tag {
|
||||||
write!(out, "{}", text_s);
|
write!(out, "{}", text_s).unwrap();
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let klass_s = klass.as_html();
|
let klass_s = klass.as_html();
|
||||||
if klass_s.is_empty() {
|
if klass_s.is_empty() {
|
||||||
write!(out, "{}", text_s);
|
out.write_str(&text_s).unwrap();
|
||||||
Some("")
|
Some("")
|
||||||
} else {
|
} else {
|
||||||
write!(out, "<span class=\"{}\">{}", klass_s, text_s);
|
write!(out, "<span class=\"{}\">{}", klass_s, text_s).unwrap();
|
||||||
Some("</span>")
|
Some("</span>")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
use crate::clean;
|
use crate::clean;
|
||||||
use crate::docfs::PathError;
|
use crate::docfs::PathError;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use crate::html::format;
|
||||||
use crate::html::format::Buffer;
|
use crate::html::format::Buffer;
|
||||||
use crate::html::highlight;
|
use crate::html::highlight;
|
||||||
use crate::html::layout;
|
use crate::html::layout;
|
||||||
use crate::html::render::Context;
|
use crate::html::render::Context;
|
||||||
use crate::visit::DocVisitor;
|
use crate::visit::DocVisitor;
|
||||||
|
|
||||||
|
use askama::Template;
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||||
use rustc_hir::def_id::LOCAL_CRATE;
|
use rustc_hir::def_id::LOCAL_CRATE;
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
@ -16,6 +18,7 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::ops::RangeInclusive;
|
||||||
use std::path::{Component, Path, PathBuf};
|
use std::path::{Component, Path, PathBuf};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
@ -299,39 +302,32 @@ pub(crate) fn print_src(
|
|||||||
decoration_info: highlight::DecorationInfo,
|
decoration_info: highlight::DecorationInfo,
|
||||||
source_context: SourceContext,
|
source_context: SourceContext,
|
||||||
) {
|
) {
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "source.html")]
|
||||||
|
struct Source<Code: std::fmt::Display> {
|
||||||
|
embedded: bool,
|
||||||
|
needs_expansion: bool,
|
||||||
|
lines: RangeInclusive<usize>,
|
||||||
|
code_html: Code,
|
||||||
|
}
|
||||||
let lines = s.lines().count();
|
let lines = s.lines().count();
|
||||||
let mut line_numbers = Buffer::empty_from(buf);
|
let (embedded, needs_expansion, lines) = match source_context {
|
||||||
let extra;
|
SourceContext::Standalone => (false, false, 1..=lines),
|
||||||
line_numbers.write_str("<pre class=\"src-line-numbers\">");
|
SourceContext::Embedded { offset, needs_expansion } => {
|
||||||
|
(true, needs_expansion, (1 + offset)..=(lines + offset))
|
||||||
|
}
|
||||||
|
};
|
||||||
let current_href = context
|
let current_href = context
|
||||||
.href_from_span(clean::Span::new(file_span), false)
|
.href_from_span(clean::Span::new(file_span), false)
|
||||||
.expect("only local crates should have sources emitted");
|
.expect("only local crates should have sources emitted");
|
||||||
match source_context {
|
let code = format::display_fn(move |fmt| {
|
||||||
SourceContext::Standalone => {
|
highlight::write_code(
|
||||||
extra = None;
|
fmt,
|
||||||
for line in 1..=lines {
|
s,
|
||||||
writeln!(line_numbers, "<a href=\"#{line}\" id=\"{line}\">{line}</a>")
|
Some(highlight::HrefContext { context, file_span, root_path, current_href }),
|
||||||
}
|
Some(decoration_info),
|
||||||
}
|
);
|
||||||
SourceContext::Embedded { offset, needs_expansion } => {
|
Ok(())
|
||||||
extra = if needs_expansion {
|
});
|
||||||
Some(r#"<button class="expand">↕</button>"#)
|
Source { embedded, needs_expansion, lines, code_html: code }.render_into(buf).unwrap();
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
for line_number in 1..=lines {
|
|
||||||
let line = line_number + offset;
|
|
||||||
writeln!(line_numbers, "<span>{line}</span>")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
line_numbers.write_str("</pre>");
|
|
||||||
highlight::render_source_with_highlighting(
|
|
||||||
s,
|
|
||||||
buf,
|
|
||||||
line_numbers,
|
|
||||||
highlight::HrefContext { context, file_span, root_path, current_href },
|
|
||||||
decoration_info,
|
|
||||||
extra,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
19
src/librustdoc/html/templates/source.html
Normal file
19
src/librustdoc/html/templates/source.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<div class="example-wrap"> {# #}
|
||||||
|
<pre class="src-line-numbers">
|
||||||
|
{% for line in lines.clone() %}
|
||||||
|
{% if embedded %}
|
||||||
|
<span>{{line}}</span>
|
||||||
|
{%~ else %}
|
||||||
|
<a href="#{{line}}" id="{{line}}">{{line}}</a>
|
||||||
|
{%~ endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</pre> {# #}
|
||||||
|
<pre class="rust"> {# #}
|
||||||
|
<code>
|
||||||
|
{% if needs_expansion %}
|
||||||
|
<button class="expand">↕</button>
|
||||||
|
{% endif %}
|
||||||
|
{{code_html|safe}}
|
||||||
|
</code> {# #}
|
||||||
|
</pre> {# #}
|
||||||
|
</div>
|
Loading…
Reference in New Issue
Block a user