mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-17 01:13:11 +00:00
Add support for double quotes in markdown codeblock attributes
This commit is contained in:
parent
d829fee6b5
commit
4ce17fa30e
@ -654,3 +654,14 @@ pub struct Bar;
|
|||||||
|
|
||||||
To be noted, `rust` and `.rust`/`class=rust` have different effects: `rust` indicates that this is
|
To be noted, `rust` and `.rust`/`class=rust` have different effects: `rust` indicates that this is
|
||||||
a Rust code block whereas the two others add a "rust" CSS class on the code block.
|
a Rust code block whereas the two others add a "rust" CSS class on the code block.
|
||||||
|
|
||||||
|
You can also use double quotes:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#![feature(custom_code_classes_in_docs)]
|
||||||
|
|
||||||
|
/// ```"not rust" {."hello everyone"}
|
||||||
|
/// int main(void) { return 0; }
|
||||||
|
/// ```
|
||||||
|
pub struct Bar;
|
||||||
|
```
|
||||||
|
@ -892,6 +892,75 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> {
|
|||||||
extra.error_invalid_codeblock_attr(err);
|
extra.error_invalid_codeblock_attr(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns false if the string is unfinished.
|
||||||
|
fn skip_string(&mut self) -> bool {
|
||||||
|
while let Some((_, c)) = self.inner.next() {
|
||||||
|
if c == '"' {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.emit_error("unclosed quote string: missing `\"` at the end");
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_in_attribute_block(&mut self, start: usize) -> Option<TokenKind<'a>> {
|
||||||
|
while let Some((pos, c)) = self.inner.next() {
|
||||||
|
if is_separator(c) {
|
||||||
|
return Some(TokenKind::Attribute(&self.data[start..pos]));
|
||||||
|
} else if c == '{' {
|
||||||
|
// There shouldn't be a nested block!
|
||||||
|
self.emit_error("unexpected `{` inside attribute block (`{}`)");
|
||||||
|
let attr = &self.data[start..pos];
|
||||||
|
if attr.is_empty() {
|
||||||
|
return self.next();
|
||||||
|
}
|
||||||
|
self.inner.next();
|
||||||
|
return Some(TokenKind::Attribute(attr));
|
||||||
|
} else if c == '}' {
|
||||||
|
self.is_in_attribute_block = false;
|
||||||
|
let attr = &self.data[start..pos];
|
||||||
|
if attr.is_empty() {
|
||||||
|
return self.next();
|
||||||
|
}
|
||||||
|
return Some(TokenKind::Attribute(attr));
|
||||||
|
} else if c == '"' && !self.skip_string() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Unclosed attribute block!
|
||||||
|
self.emit_error("unclosed attribute block (`{}`): missing `}` at the end");
|
||||||
|
let token = &self.data[start..];
|
||||||
|
if token.is_empty() { None } else { Some(TokenKind::Attribute(token)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_outside_attribute_block(&mut self, start: usize) -> Option<TokenKind<'a>> {
|
||||||
|
while let Some((pos, c)) = self.inner.next() {
|
||||||
|
if is_separator(c) {
|
||||||
|
return Some(TokenKind::Token(&self.data[start..pos]));
|
||||||
|
} else if c == '{' {
|
||||||
|
self.is_in_attribute_block = true;
|
||||||
|
let token = &self.data[start..pos];
|
||||||
|
if token.is_empty() {
|
||||||
|
return self.next();
|
||||||
|
}
|
||||||
|
return Some(TokenKind::Token(token));
|
||||||
|
} else if c == '}' {
|
||||||
|
// We're not in a block so it shouldn't be there!
|
||||||
|
self.emit_error("unexpected `}` outside attribute block (`{}`)");
|
||||||
|
let token = &self.data[start..pos];
|
||||||
|
if token.is_empty() {
|
||||||
|
return self.next();
|
||||||
|
}
|
||||||
|
self.inner.next();
|
||||||
|
return Some(TokenKind::Attribute(token));
|
||||||
|
} else if c == '"' && !self.skip_string() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let token = &self.data[start..];
|
||||||
|
if token.is_empty() { None } else { Some(TokenKind::Token(token)) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> Iterator for TagIterator<'a, 'tcx> {
|
impl<'a, 'tcx> Iterator for TagIterator<'a, 'tcx> {
|
||||||
@ -905,55 +974,9 @@ impl<'a, 'tcx> Iterator for TagIterator<'a, 'tcx> {
|
|||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
if self.is_in_attribute_block {
|
if self.is_in_attribute_block {
|
||||||
while let Some((pos, c)) = self.inner.next() {
|
self.parse_in_attribute_block(start)
|
||||||
if is_separator(c) {
|
|
||||||
return Some(TokenKind::Attribute(&self.data[start..pos]));
|
|
||||||
} else if c == '{' {
|
|
||||||
// There shouldn't be a nested block!
|
|
||||||
self.emit_error("unexpected `{` inside attribute block (`{}`)");
|
|
||||||
let attr = &self.data[start..pos];
|
|
||||||
if attr.is_empty() {
|
|
||||||
return self.next();
|
|
||||||
}
|
|
||||||
self.inner.next();
|
|
||||||
return Some(TokenKind::Attribute(attr));
|
|
||||||
} else if c == '}' {
|
|
||||||
self.is_in_attribute_block = false;
|
|
||||||
let attr = &self.data[start..pos];
|
|
||||||
if attr.is_empty() {
|
|
||||||
return self.next();
|
|
||||||
}
|
|
||||||
return Some(TokenKind::Attribute(attr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Unclosed attribute block!
|
|
||||||
self.emit_error("unclosed attribute block (`{}`): missing `}` at the end");
|
|
||||||
let token = &self.data[start..];
|
|
||||||
if token.is_empty() { None } else { Some(TokenKind::Attribute(token)) }
|
|
||||||
} else {
|
} else {
|
||||||
while let Some((pos, c)) = self.inner.next() {
|
self.parse_outside_attribute_block(start)
|
||||||
if is_separator(c) {
|
|
||||||
return Some(TokenKind::Token(&self.data[start..pos]));
|
|
||||||
} else if c == '{' {
|
|
||||||
self.is_in_attribute_block = true;
|
|
||||||
let token = &self.data[start..pos];
|
|
||||||
if token.is_empty() {
|
|
||||||
return self.next();
|
|
||||||
}
|
|
||||||
return Some(TokenKind::Token(token));
|
|
||||||
} else if c == '}' {
|
|
||||||
// We're not in a block so it shouldn't be there!
|
|
||||||
self.emit_error("unexpected `}` outside attribute block (`{}`)");
|
|
||||||
let token = &self.data[start..pos];
|
|
||||||
if token.is_empty() {
|
|
||||||
return self.next();
|
|
||||||
}
|
|
||||||
self.inner.next();
|
|
||||||
return Some(TokenKind::Attribute(token));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let token = &self.data[start..];
|
|
||||||
if token.is_empty() { None } else { Some(TokenKind::Token(token)) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -982,7 +1005,7 @@ fn handle_class(class: &str, after: &str, data: &mut LangString, extra: Option<&
|
|||||||
extra.error_invalid_codeblock_attr(&format!("missing class name after `{after}`"));
|
extra.error_invalid_codeblock_attr(&format!("missing class name after `{after}`"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
data.added_classes.push(class.to_owned());
|
data.added_classes.push(class.replace('"', ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,6 +218,18 @@ fn test_lang_string_parse() {
|
|||||||
rust: false,
|
rust: false,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
t(LangString {
|
||||||
|
original: r#"{class="first"}"#.into(),
|
||||||
|
added_classes: vec!["first".into()],
|
||||||
|
rust: false,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
t(LangString {
|
||||||
|
original: r#"{class=f"irst"}"#.into(),
|
||||||
|
added_classes: vec!["first".into()],
|
||||||
|
rust: false,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
17
tests/rustdoc-ui/custom_code_classes_in_docs-warning3.rs
Normal file
17
tests/rustdoc-ui/custom_code_classes_in_docs-warning3.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// This test ensures that warnings are working as expected for "custom_code_classes_in_docs"
|
||||||
|
// feature.
|
||||||
|
|
||||||
|
#![feature(custom_code_classes_in_docs)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![feature(no_core)]
|
||||||
|
#![no_core]
|
||||||
|
|
||||||
|
/// ```{class="}
|
||||||
|
/// main;
|
||||||
|
/// ```
|
||||||
|
//~^^^ ERROR unclosed quote string
|
||||||
|
//~| ERROR unclosed quote string
|
||||||
|
/// ```"
|
||||||
|
/// main;
|
||||||
|
/// ```
|
||||||
|
pub fn foo() {}
|
33
tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr
Normal file
33
tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
error: unclosed quote string: missing `"` at the end
|
||||||
|
--> $DIR/custom_code_classes_in_docs-warning3.rs:9:1
|
||||||
|
|
|
||||||
|
LL | / /// ```{class="}
|
||||||
|
LL | | /// main;
|
||||||
|
LL | | /// ```
|
||||||
|
LL | |
|
||||||
|
... |
|
||||||
|
LL | | /// main;
|
||||||
|
LL | | /// ```
|
||||||
|
| |_______^
|
||||||
|
|
|
||||||
|
note: the lint level is defined here
|
||||||
|
--> $DIR/custom_code_classes_in_docs-warning3.rs:5:9
|
||||||
|
|
|
||||||
|
LL | #![deny(warnings)]
|
||||||
|
| ^^^^^^^^
|
||||||
|
= note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(warnings)]`
|
||||||
|
|
||||||
|
error: unclosed quote string: missing `"` at the end
|
||||||
|
--> $DIR/custom_code_classes_in_docs-warning3.rs:9:1
|
||||||
|
|
|
||||||
|
LL | / /// ```{class="}
|
||||||
|
LL | | /// main;
|
||||||
|
LL | | /// ```
|
||||||
|
LL | |
|
||||||
|
... |
|
||||||
|
LL | | /// main;
|
||||||
|
LL | | /// ```
|
||||||
|
| |_______^
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
Loading…
Reference in New Issue
Block a user