diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index ece3ee640e2a6..15a4f4b10ad6f 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -9,8 +9,9 @@ use crate::clean::PrimitiveType; use crate::html::escape::Escape; use crate::html::render::Context; -use std::fmt::{Display, Write}; -use std::iter::Peekable; +use std::borrow::Cow; +use std::fmt::{Debug, Display, Write}; +use std::iter::{once, Peekable}; use rustc_lexer::{LiteralKind, TokenKind}; use rustc_span::edition::Edition; @@ -18,6 +19,7 @@ use rustc_span::symbol::Symbol; use rustc_span::{BytePos, Span, DUMMY_SP}; use super::format::{self, Buffer}; +use super::markdown::Line; use super::render::LinkFromSrc; /// This type is needed in case we want to render links on items to allow to go to their definition. @@ -31,7 +33,7 @@ crate struct ContextInfo<'a, 'b, 'c> { } /// Highlights `src`, returning the HTML output. -crate fn render_with_highlighting( +crate fn render_source_with_highlighting( src: &str, out: &mut Buffer, class: Option<&str>, @@ -41,7 +43,31 @@ crate fn render_with_highlighting( extra_content: Option, context_info: Option>, ) { - debug!("highlighting: ================\n{}\n==============", src); + render_with_highlighting( + once(Line::Shown(Cow::Borrowed(src))), + out, + class, + playground_button, + tooltip, + edition, + extra_content, + context_info, + ) +} + +/// Highlights `src` containing potential hidden lines, returning the HTML output. If you don't have +/// hidden lines, use [`render_source_with_highlighting`] instead. +crate fn render_with_highlighting<'a>( + src: impl Iterator> + Debug, + out: &mut Buffer, + class: Option<&str>, + playground_button: Option<&str>, + tooltip: Option<(Option, &str)>, + edition: Edition, + extra_content: Option, + context_info: Option>, +) { + debug!("highlighting: ================\n{:?}\n==============", src); if let Some((edition_info, class)) = tooltip { write!( out, @@ -56,8 +82,8 @@ crate fn render_with_highlighting( } write_header(out, class, extra_content); - write_code(out, &src, edition, context_info); - write_footer(out, playground_button); + let expand = write_code(out, src, edition, context_info); + write_footer(out, playground_button, expand); } fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option) { @@ -86,13 +112,31 @@ fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option>, edition: Edition, context_info: Option>, -) { - // This replace allows to fix how the code source with DOS backline characters is displayed. - let src = src.replace("\r\n", "\n"); - Classifier::new(&src, edition, context_info.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP)) +) -> bool { + let mut iter = src.peekable(); + let mut expand = false; + + // For each `Line`, we replace DOS backlines with '\n'. This replace allows to fix how the code + // source with DOS backline characters is displayed. + while let Some(line) = iter.next() { + let (before, text, after) = match line { + Line::Hidden(text) => { + expand = true; + ("", text.replace("\r\n", "\n"), "") + } + Line::Shown(text) => ("", text.replace("\r\n", "\n"), ""), + }; + if !before.is_empty() { + out.push_str(before); + } + Classifier::new( + &text.replace("\r\n", "\n"), + edition, + context_info.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP), + ) .highlight(&mut |highlight| { match highlight { Highlight::Token { text, class } => string(out, Escape(text), class, &context_info), @@ -100,10 +144,27 @@ fn write_code( Highlight::ExitSpan => exit_span(out), }; }); + if iter.peek().is_some() && !text.ends_with('\n') { + out.push_str("\n"); + } + if !after.is_empty() { + out.push_str(after); + } + } + expand } -fn write_footer(out: &mut Buffer, playground_button: Option<&str>) { - writeln!(out, "{}", playground_button.unwrap_or_default()); +fn write_footer(out: &mut Buffer, playground_button: Option<&str>, expand: bool) { + writeln!( + out, + "\ +
\ + {}{}\ +
\ + ", + playground_button.unwrap_or_default(), + if expand { "" } else { "" }, + ); } /// How a span of text is classified. Mostly corresponds to token kinds. diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs index 68592ae96c187..7e09a60083e7b 100644 --- a/src/librustdoc/html/highlight/tests.rs +++ b/src/librustdoc/html/highlight/tests.rs @@ -1,9 +1,13 @@ use super::write_code; use crate::html::format::Buffer; +use crate::html::markdown::Line; use expect_test::expect_file; use rustc_span::create_default_session_globals_then; use rustc_span::edition::Edition; +use std::borrow::Cow; +use std::iter::once; + const STYLE: &str = r#"