-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Accept additional user-defined classes in fenced code blocks #79454
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
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 |
---|---|---|
|
@@ -207,6 +207,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> { | |
let should_panic; | ||
let ignore; | ||
let edition; | ||
let added_classes; | ||
if let Some(Event::Start(Tag::CodeBlock(kind))) = event { | ||
let parse_result = match kind { | ||
CodeBlockKind::Fenced(ref lang) => { | ||
|
@@ -221,6 +222,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> { | |
should_panic = parse_result.should_panic; | ||
ignore = parse_result.ignore; | ||
edition = parse_result.edition; | ||
added_classes = parse_result.added_classes; | ||
} else { | ||
return event; | ||
} | ||
|
@@ -289,33 +291,42 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> { | |
)) | ||
}); | ||
|
||
let tooltip = if ignore != Ignore::None { | ||
Some((None, "ignore")) | ||
let (tooltip, special_class) = if ignore != Ignore::None { | ||
(Some((None, "ignore")), " ignore") | ||
} else if compile_fail { | ||
Some((None, "compile_fail")) | ||
(Some((None, "compile_fail")), " compile_fail") | ||
} else if should_panic { | ||
Some((None, "should_panic")) | ||
(Some((None, "should_panic")), " should_panic") | ||
} else if explicit_edition { | ||
Some((Some(edition), "edition")) | ||
(Some((Some(edition), "edition")), " edition") | ||
} else { | ||
None | ||
(None, "") | ||
}; | ||
|
||
// insert newline to clearly separate it from the | ||
// previous block so we can shorten the html output | ||
let mut s = Buffer::new(); | ||
s.push_str("\n"); | ||
highlight::render_with_highlighting( | ||
&text, | ||
&mut s, | ||
Some(&format!( | ||
"rust-example-rendered{}", | ||
if let Some((_, class)) = tooltip { format!(" {}", class) } else { String::new() } | ||
)), | ||
playground_button.as_deref(), | ||
tooltip, | ||
edition, | ||
); | ||
|
||
if added_classes.is_empty() { | ||
highlight::render_with_highlighting( | ||
&text, | ||
&mut s, | ||
Some(&format!("rust-example-rendered{}", special_class)), | ||
playground_button.as_deref(), | ||
tooltip, | ||
edition, | ||
); | ||
} else { | ||
let classes = if special_class.is_empty() { | ||
added_classes.join(" ") | ||
} else { | ||
format!("{} {}", special_class.trim(), added_classes.join(" ")) | ||
}; | ||
|
||
highlight::render_with_added_classes(&text, &mut s, classes, tooltip); | ||
} | ||
|
||
Some(Event::Html(s.into_inner().into())) | ||
} | ||
} | ||
|
@@ -744,6 +755,7 @@ crate struct LangString { | |
crate error_codes: Vec<String>, | ||
crate allow_fail: bool, | ||
crate edition: Option<Edition>, | ||
crate added_classes: Vec<String>, | ||
} | ||
|
||
#[derive(Eq, PartialEq, Clone, Debug)] | ||
|
@@ -766,6 +778,7 @@ impl Default for LangString { | |
error_codes: Vec::new(), | ||
allow_fail: false, | ||
edition: None, | ||
added_classes: Vec::new(), | ||
} | ||
} | ||
} | ||
|
@@ -775,7 +788,7 @@ impl LangString { | |
string: &str, | ||
allow_error_code_check: ErrorCodes, | ||
enable_per_target_ignores: bool, | ||
) -> LangString { | ||
) -> Self { | ||
Self::parse(string, allow_error_code_check, enable_per_target_ignores, None) | ||
} | ||
|
||
|
@@ -809,7 +822,7 @@ impl LangString { | |
allow_error_code_check: ErrorCodes, | ||
enable_per_target_ignores: bool, | ||
extra: Option<&ExtraInfo<'_>>, | ||
) -> LangString { | ||
) -> Self { | ||
let allow_error_code_check = allow_error_code_check.as_bool(); | ||
let mut seen_rust_tags = false; | ||
let mut seen_other_tags = false; | ||
|
@@ -868,6 +881,14 @@ impl LangString { | |
seen_other_tags = true; | ||
} | ||
} | ||
x if x.starts_with("class:") => { | ||
let class = x.trim_start_matches("class:"); | ||
if class.is_empty() { | ||
seen_other_tags = true; | ||
Comment on lines
+886
to
+887
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. Hmm, what does this do? I would expect |
||
} else { | ||
data.added_classes.push(class.to_owned()); | ||
} | ||
} | ||
x if extra.is_some() => { | ||
let s = x.to_lowercase(); | ||
match if s == "compile-fail" || s == "compile_fail" || s == "compilefail" { | ||
|
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,83 @@ | ||||||||||
//! NIGHTLY & UNSTABLE CHECK: custom_code_classes_in_docs | ||||||||||
//! | ||||||||||
//! This pass will produce errors when finding custom classes outside of | ||||||||||
//! nightly + relevant feature active. | ||||||||||
|
||||||||||
use super::{span_of_attrs, Pass}; | ||||||||||
use crate::clean::{Crate, Item}; | ||||||||||
use crate::core::DocContext; | ||||||||||
use crate::fold::DocFolder; | ||||||||||
use crate::html::markdown::{find_testable_code, ErrorCodes, LangString}; | ||||||||||
|
||||||||||
use rustc_session::parse::feature_err; | ||||||||||
use rustc_span::symbol::sym; | ||||||||||
|
||||||||||
crate const CHECK_CUSTOM_CODE_CLASSES: Pass = Pass { | ||||||||||
name: "check-custom-code-classes", | ||||||||||
run: check_custom_code_classes, | ||||||||||
description: "check for custom code classes while not in nightly", | ||||||||||
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.
Suggested change
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. Or, since it has to be a specific feature gate:
Suggested change
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. I'm suddenly wondering if the negative here is correct. Maybe it should be 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 doesn't pop up anywhere except 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. @poliorcetics this is still waiting on you to accept the change I think. |
||||||||||
}; | ||||||||||
|
||||||||||
crate fn check_custom_code_classes(krate: Crate, cx: &mut DocContext<'_>) -> Crate { | ||||||||||
let mut coll = CustomCodeClassLinter { cx }; | ||||||||||
|
||||||||||
coll.fold_crate(krate) | ||||||||||
} | ||||||||||
|
||||||||||
struct CustomCodeClassLinter<'a, 'tcx> { | ||||||||||
cx: &'a DocContext<'tcx>, | ||||||||||
} | ||||||||||
|
||||||||||
impl<'a, 'tcx> DocFolder for CustomCodeClassLinter<'a, 'tcx> { | ||||||||||
fn fold_item(&mut self, item: Item) -> Option<Item> { | ||||||||||
let dox = item.attrs.collapsed_doc_value().unwrap_or_default(); | ||||||||||
|
||||||||||
look_for_custom_classes(&self.cx, &dox, &item); | ||||||||||
|
||||||||||
Some(self.fold_item_recur(item)) | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
struct TestsWithCustomClasses { | ||||||||||
custom_classes_found: Vec<String>, | ||||||||||
} | ||||||||||
|
||||||||||
impl crate::doctest::Tester for TestsWithCustomClasses { | ||||||||||
fn add_test(&mut self, _: String, config: LangString, _: usize) { | ||||||||||
self.custom_classes_found.extend(config.added_classes.into_iter()); | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
crate fn look_for_custom_classes<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) { | ||||||||||
let hir_id = match DocContext::as_local_hir_id(cx.tcx, item.def_id) { | ||||||||||
Some(hir_id) => hir_id, | ||||||||||
None => { | ||||||||||
// If non-local, no need to check anything. | ||||||||||
return; | ||||||||||
} | ||||||||||
}; | ||||||||||
|
||||||||||
let mut tests = TestsWithCustomClasses { custom_classes_found: vec![] }; | ||||||||||
|
||||||||||
find_testable_code(&dox, &mut tests, ErrorCodes::No, false, None); | ||||||||||
|
||||||||||
if !tests.custom_classes_found.is_empty() && !cx.tcx.features().custom_code_classes_in_docs { | ||||||||||
debug!("reporting error for {:?} (hid_id={:?})", item, hir_id); | ||||||||||
let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span()); | ||||||||||
feature_err( | ||||||||||
&cx.tcx.sess.parse_sess, | ||||||||||
sym::custom_code_classes_in_docs, | ||||||||||
sp, | ||||||||||
"custom classes in code blocks are unstable", | ||||||||||
) | ||||||||||
poliorcetics marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
.note( | ||||||||||
// This will list the wrong items to make them more easily searchable. | ||||||||||
// To ensure the most correct hits, it adds back the 'class:' that was stripped. | ||||||||||
&format!( | ||||||||||
"found these custom classes: class:{}", | ||||||||||
tests.custom_classes_found.join(", class:") | ||||||||||
), | ||||||||||
) | ||||||||||
.emit(); | ||||||||||
} | ||||||||||
} |
Uh oh!
There was an error while loading. Please reload this page.