From 5c773e4c83d736963ab1d8f751e82d0f2abe56ca Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 4 Jul 2021 15:31:41 +0200 Subject: [PATCH 1/5] Add "copy to clipboard" button for all codeblocks --- src/librustdoc/html/highlight.rs | 86 +++++++++++++++---- src/librustdoc/html/highlight/tests.rs | 6 ++ src/librustdoc/html/markdown.rs | 19 ++-- src/librustdoc/html/render/print_item.rs | 2 +- src/librustdoc/html/sources.rs | 2 +- src/librustdoc/html/static/css/noscript.css | 2 +- src/librustdoc/html/static/css/rustdoc.css | 30 +++++-- src/librustdoc/html/static/css/themes/ayu.css | 9 +- .../html/static/css/themes/dark.css | 7 ++ .../html/static/css/themes/light.css | 7 ++ src/librustdoc/html/static/js/main.js | 84 ++++++++++++++---- src/test/rustdoc/hidden-line.rs | 4 +- src/test/rustdoc/issue-41783.rs | 4 +- 13 files changed, 201 insertions(+), 61 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index ece3ee640e2a6..a592d4e3f873c 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,7 +82,7 @@ crate fn render_with_highlighting( } write_header(out, class, extra_content); - write_code(out, &src, edition, context_info); + write_code(out, src, edition, context_info); write_footer(out, playground_button); } @@ -86,24 +112,50 @@ 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)) - .highlight(&mut |highlight| { - match highlight { - Highlight::Token { text, class } => string(out, Escape(text), class, &context_info), - Highlight::EnterSpan { class } => enter_span(out, class), - Highlight::ExitSpan => exit_span(out), - }; - }); + let mut iter = src.peekable(); + + // 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() { + match line { + Line::Hidden(text) => { + write!( + out, + "{}{}", + Escape(&text.replace("\r\n", "\n")), + if iter.peek().is_some() && !text.ends_with('\n') { "\n" } else { "" }, + ); + } + Line::Shown(text) => { + 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), + Highlight::EnterSpan { class } => enter_span(out, class), + Highlight::ExitSpan => exit_span(out), + }; + }); + if iter.peek().is_some() && !text.ends_with('\n') { + write!(out, "\n"); + } + } + } + } } fn write_footer(out: &mut Buffer, playground_button: Option<&str>) { - writeln!(out, "{}", playground_button.unwrap_or_default()); + writeln!( + out, + "\ +
\ + {}\ +
\ + ", + playground_button.unwrap_or_default() + ); } /// 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..2d97ac5df683a 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#"