-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
rustdoc: simplify highlight.rs #99337
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,48 +33,80 @@ pub(crate) struct HrefContext<'a, 'b, 'c> { | |
|
||
/// Decorations are represented as a map from CSS class to vector of character ranges. | ||
/// Each range will be wrapped in a span with that class. | ||
#[derive(Default)] | ||
pub(crate) struct DecorationInfo(pub(crate) FxHashMap<&'static str, Vec<(u32, u32)>>); | ||
|
||
/// Highlights `src`, returning the HTML output. | ||
pub(crate) fn render_with_highlighting( | ||
#[derive(Eq, PartialEq, Clone, Copy)] | ||
pub(crate) enum Tooltip { | ||
Ignore, | ||
CompileFail, | ||
ShouldPanic, | ||
Edition(Edition), | ||
None, | ||
} | ||
|
||
/// Highlights `src` as an inline example, returning the HTML output. | ||
pub(crate) fn render_example_with_highlighting( | ||
src: &str, | ||
out: &mut Buffer, | ||
class: Option<&str>, | ||
tooltip: Tooltip, | ||
playground_button: Option<&str>, | ||
tooltip: Option<(Option<Edition>, &str)>, | ||
edition: Edition, | ||
extra_content: Option<Buffer>, | ||
href_context: Option<HrefContext<'_, '_, '_>>, | ||
decoration_info: Option<DecorationInfo>, | ||
) { | ||
debug!("highlighting: ================\n{}\n==============", src); | ||
if let Some((edition_info, class)) = tooltip { | ||
let class = match tooltip { | ||
Tooltip::Ignore => " ignore", | ||
Tooltip::CompileFail => " compile_fail", | ||
Tooltip::ShouldPanic => " should_panic", | ||
Tooltip::Edition(_) => " edition", | ||
Tooltip::None => "", | ||
}; | ||
|
||
if tooltip != Tooltip::None { | ||
write!( | ||
out, | ||
"<div class='information'><div class='tooltip {}'{}>ⓘ</div></div>", | ||
"<div class='information'><div class='tooltip{}'{}>ⓘ</div></div>", | ||
class, | ||
if let Some(edition_info) = edition_info { | ||
if let Tooltip::Edition(edition_info) = tooltip { | ||
format!(" data-edition=\"{}\"", edition_info) | ||
} else { | ||
String::new() | ||
}, | ||
); | ||
} | ||
|
||
write_header(out, class, extra_content); | ||
write_code(out, src, edition, href_context, decoration_info); | ||
write_header(out, &format!("rust-example-rendered{}", class), None); | ||
write_code(out, src, None, None); | ||
write_footer(out, playground_button); | ||
} | ||
|
||
fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option<Buffer>) { | ||
/// Highlights `src` as a macro, returning the HTML output. | ||
pub(crate) fn render_macro_with_highlighting(src: &str, out: &mut Buffer) { | ||
write_header(out, "macro", None); | ||
write_code(out, src, None, None); | ||
write_footer(out, None); | ||
} | ||
|
||
/// 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, | ||
) { | ||
write_header(out, "", Some(line_numbers)); | ||
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>) { | ||
write!(out, "<div class=\"example-wrap\">"); | ||
if let Some(extra) = extra_content { | ||
out.push_buffer(extra); | ||
} | ||
if let Some(class) = class { | ||
write!(out, "<pre class=\"rust {}\">", class); | ||
} else { | ||
if class.is_empty() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems less good. Having to check if a string is empty is easy to forget whereas you cannot make the mistake with |
||
write!(out, "<pre class=\"rust\">"); | ||
} else { | ||
write!(out, "<pre class=\"rust {}\">", class); | ||
} | ||
write!(out, "<code>"); | ||
} | ||
|
@@ -93,7 +125,6 @@ fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option<Buf | |
fn write_code( | ||
out: &mut Buffer, | ||
src: &str, | ||
edition: Edition, | ||
href_context: Option<HrefContext<'_, '_, '_>>, | ||
decoration_info: Option<DecorationInfo>, | ||
) { | ||
|
@@ -102,7 +133,6 @@ fn write_code( | |
let mut closing_tags: Vec<&'static str> = Vec::new(); | ||
Classifier::new( | ||
&src, | ||
edition, | ||
href_context.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP), | ||
decoration_info, | ||
) | ||
|
@@ -220,7 +250,7 @@ impl<'a> Iterator for TokenIter<'a> { | |
} | ||
|
||
/// Classifies into identifier class; returns `None` if this is a non-keyword identifier. | ||
fn get_real_ident_class(text: &str, edition: Edition, allow_path_keywords: bool) -> Option<Class> { | ||
fn get_real_ident_class(text: &str, allow_path_keywords: bool) -> Option<Class> { | ||
let ignore: &[&str] = | ||
if allow_path_keywords { &["self", "Self", "super", "crate"] } else { &["self", "Self"] }; | ||
if ignore.iter().any(|k| *k == text) { | ||
|
@@ -229,7 +259,7 @@ fn get_real_ident_class(text: &str, edition: Edition, allow_path_keywords: bool) | |
Some(match text { | ||
"ref" | "mut" => Class::RefKeyWord, | ||
"false" | "true" => Class::Bool, | ||
_ if Symbol::intern(text).is_reserved(|| edition) => Class::KeyWord, | ||
_ if Symbol::intern(text).is_reserved(|| Edition::Edition2021) => Class::KeyWord, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is quite a big change. Until now the highlight depended on the edition provided to rustdoc. Even though it is a welcomed simplification, I'm not too sure if it's a good idea... Another problem I have with this approach: we will need to not forget to update it whenever the edition is updated. To go around that limitation, you could add a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, it seems it's not taking into account the |
||
_ => return None, | ||
}) | ||
} | ||
|
@@ -311,7 +341,6 @@ struct Classifier<'a> { | |
in_attribute: bool, | ||
in_macro: bool, | ||
in_macro_nonterminal: bool, | ||
edition: Edition, | ||
byte_pos: u32, | ||
file_span: Span, | ||
src: &'a str, | ||
|
@@ -321,20 +350,14 @@ struct Classifier<'a> { | |
impl<'a> Classifier<'a> { | ||
/// Takes as argument the source code to HTML-ify, the rust edition to use and the source code | ||
/// file span which will be used later on by the `span_correspondance_map`. | ||
fn new( | ||
src: &str, | ||
edition: Edition, | ||
file_span: Span, | ||
decoration_info: Option<DecorationInfo>, | ||
) -> Classifier<'_> { | ||
fn new(src: &str, file_span: Span, decoration_info: Option<DecorationInfo>) -> Classifier<'_> { | ||
let tokens = PeekIter::new(TokenIter { src }); | ||
let decorations = decoration_info.map(Decorations::new); | ||
Classifier { | ||
tokens, | ||
in_attribute: false, | ||
in_macro: false, | ||
in_macro_nonterminal: false, | ||
edition, | ||
byte_pos: 0, | ||
file_span, | ||
src, | ||
|
@@ -354,7 +377,6 @@ impl<'a> Classifier<'a> { | |
let start = self.byte_pos as usize; | ||
let mut pos = start; | ||
let mut has_ident = false; | ||
let edition = self.edition; | ||
|
||
loop { | ||
let mut nb = 0; | ||
|
@@ -376,7 +398,7 @@ impl<'a> Classifier<'a> { | |
|
||
if let Some((None, text)) = self.tokens.peek().map(|(token, text)| { | ||
if *token == TokenKind::Ident { | ||
let class = get_real_ident_class(text, edition, true); | ||
let class = get_real_ident_class(text, true); | ||
(class, text) | ||
} else { | ||
// Doesn't matter which Class we put in here... | ||
|
@@ -634,7 +656,7 @@ impl<'a> Classifier<'a> { | |
sink(Highlight::Token { text, class: None }); | ||
return; | ||
} | ||
TokenKind::Ident => match get_real_ident_class(text, self.edition, false) { | ||
TokenKind::Ident => match get_real_ident_class(text, false) { | ||
None => match text { | ||
"Option" | "Result" => Class::PreludeTy, | ||
"Some" | "None" | "Ok" | "Err" => Class::PreludeVal, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,7 +11,6 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; | |
use rustc_hir::def_id::LOCAL_CRATE; | ||
use rustc_middle::ty::TyCtxt; | ||
use rustc_session::Session; | ||
use rustc_span::edition::Edition; | ||
use rustc_span::source_map::FileName; | ||
|
||
use std::ffi::OsStr; | ||
|
@@ -213,11 +212,10 @@ impl SourceCollector<'_, '_> { | |
print_src( | ||
buf, | ||
contents, | ||
cx.shared.edition(), | ||
file_span, | ||
cx, | ||
&root_path, | ||
None, | ||
highlight::DecorationInfo::default(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even though it could be simplified into |
||
SourceContext::Standalone, | ||
) | ||
}, | ||
|
@@ -266,11 +264,10 @@ pub(crate) enum SourceContext { | |
pub(crate) fn print_src( | ||
buf: &mut Buffer, | ||
s: &str, | ||
edition: Edition, | ||
file_span: rustc_span::Span, | ||
context: &Context<'_>, | ||
root_path: &str, | ||
decoration_info: Option<highlight::DecorationInfo>, | ||
decoration_info: highlight::DecorationInfo, | ||
source_context: SourceContext, | ||
) { | ||
let lines = s.lines().count(); | ||
|
@@ -289,15 +286,11 @@ pub(crate) fn print_src( | |
} | ||
} | ||
line_numbers.write_str("</pre>"); | ||
highlight::render_with_highlighting( | ||
highlight::render_source_with_highlighting( | ||
s, | ||
buf, | ||
None, | ||
None, | ||
None, | ||
edition, | ||
Some(line_numbers), | ||
Some(highlight::HrefContext { context, file_span, root_path }), | ||
line_numbers, | ||
highlight::HrefContext { context, file_span, root_path }, | ||
decoration_info, | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This new distinction is really nice!