mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 06:11:35 +00:00
Improve support for code block attributes
This commit is contained in:
parent
d58a3a277a
commit
63caef372a
2 changed files with 170 additions and 19 deletions
|
@ -211,12 +211,29 @@ fn has_test_related_attribute(fn_def: &ast::Fn) -> bool {
|
||||||
.any(|attribute_text| attribute_text.contains("test"))
|
.any(|attribute_text| attribute_text.contains("test"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RUSTDOC_FENCE: &str = "```";
|
||||||
|
const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
|
||||||
|
&["", "rust", "should_panic", "edition2015", "edition2018"];
|
||||||
|
|
||||||
fn has_runnable_doc_test(fn_def: &ast::Fn) -> bool {
|
fn has_runnable_doc_test(fn_def: &ast::Fn) -> bool {
|
||||||
fn_def.doc_comment_text().map_or(false, |comments_text| {
|
fn_def.doc_comment_text().map_or(false, |comments_text| {
|
||||||
comments_text.contains("```")
|
let mut in_code_block = false;
|
||||||
&& !comments_text.contains("```ignore")
|
|
||||||
&& !comments_text.contains("```no_run")
|
for line in comments_text.lines() {
|
||||||
&& !comments_text.contains("```compile_fail")
|
if let Some(header) = line.strip_prefix(RUSTDOC_FENCE) {
|
||||||
|
in_code_block = !in_code_block;
|
||||||
|
|
||||||
|
if in_code_block
|
||||||
|
&& header
|
||||||
|
.split(',')
|
||||||
|
.all(|sub| RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE.contains(&sub.trim()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,7 +438,21 @@ fn main() {}
|
||||||
/// ```
|
/// ```
|
||||||
/// let x = 5;
|
/// let x = 5;
|
||||||
/// ```
|
/// ```
|
||||||
fn foo() {}
|
fn should_have_runnable() {}
|
||||||
|
|
||||||
|
/// ```edition2018
|
||||||
|
/// let x = 5;
|
||||||
|
/// ```
|
||||||
|
fn should_have_runnable_1() {}
|
||||||
|
|
||||||
|
/// ```
|
||||||
|
/// let z = 55;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// let z = 56;
|
||||||
|
/// ```
|
||||||
|
fn should_have_runnable_2() {}
|
||||||
|
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// let z = 55;
|
/// let z = 55;
|
||||||
|
@ -437,8 +468,27 @@ fn should_have_no_runnable_2() {}
|
||||||
/// let z = 55;
|
/// let z = 55;
|
||||||
/// ```
|
/// ```
|
||||||
fn should_have_no_runnable_3() {}
|
fn should_have_no_runnable_3() {}
|
||||||
|
|
||||||
|
/// ```text
|
||||||
|
/// arbitrary plain text
|
||||||
|
/// ```
|
||||||
|
fn should_have_no_runnable_4() {}
|
||||||
|
|
||||||
|
/// ```text
|
||||||
|
/// arbitrary plain text
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```sh
|
||||||
|
/// $ shell code
|
||||||
|
/// ```
|
||||||
|
fn should_have_no_runnable_5() {}
|
||||||
|
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// let z = 55;
|
||||||
|
/// ```
|
||||||
|
fn should_have_no_runnable_6() {}
|
||||||
"#,
|
"#,
|
||||||
&[&BIN, &DOCTEST],
|
&[&BIN, &DOCTEST, &DOCTEST, &DOCTEST],
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
[
|
[
|
||||||
Runnable {
|
Runnable {
|
||||||
|
@ -464,9 +514,9 @@ fn should_have_no_runnable_3() {}
|
||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
full_range: 15..57,
|
full_range: 15..74,
|
||||||
focus_range: None,
|
focus_range: None,
|
||||||
name: "foo",
|
name: "should_have_runnable",
|
||||||
kind: FN,
|
kind: FN,
|
||||||
container_name: None,
|
container_name: None,
|
||||||
description: None,
|
description: None,
|
||||||
|
@ -474,7 +524,47 @@ fn should_have_no_runnable_3() {}
|
||||||
},
|
},
|
||||||
kind: DocTest {
|
kind: DocTest {
|
||||||
test_id: Path(
|
test_id: Path(
|
||||||
"foo",
|
"should_have_runnable",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
cfg_exprs: [],
|
||||||
|
},
|
||||||
|
Runnable {
|
||||||
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
full_range: 76..148,
|
||||||
|
focus_range: None,
|
||||||
|
name: "should_have_runnable_1",
|
||||||
|
kind: FN,
|
||||||
|
container_name: None,
|
||||||
|
description: None,
|
||||||
|
docs: None,
|
||||||
|
},
|
||||||
|
kind: DocTest {
|
||||||
|
test_id: Path(
|
||||||
|
"should_have_runnable_1",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
cfg_exprs: [],
|
||||||
|
},
|
||||||
|
Runnable {
|
||||||
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
full_range: 150..254,
|
||||||
|
focus_range: None,
|
||||||
|
name: "should_have_runnable_2",
|
||||||
|
kind: FN,
|
||||||
|
container_name: None,
|
||||||
|
description: None,
|
||||||
|
docs: None,
|
||||||
|
},
|
||||||
|
kind: DocTest {
|
||||||
|
test_id: Path(
|
||||||
|
"should_have_runnable_2",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
cfg_exprs: [],
|
cfg_exprs: [],
|
||||||
|
|
|
@ -1,22 +1,32 @@
|
||||||
//! Transforms markdown
|
//! Transforms markdown
|
||||||
|
|
||||||
|
const RUSTDOC_FENCE: &str = "```";
|
||||||
|
const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC: &[&str] =
|
||||||
|
&["", "rust", "should_panic", "ignore", "no_run", "compile_fail", "edition2015", "edition2018"];
|
||||||
|
|
||||||
pub(crate) fn format_docs(src: &str) -> String {
|
pub(crate) fn format_docs(src: &str) -> String {
|
||||||
let mut processed_lines = Vec::new();
|
let mut processed_lines = Vec::new();
|
||||||
let mut in_code_block = false;
|
let mut in_code_block = false;
|
||||||
for line in src.lines() {
|
let mut is_rust = false;
|
||||||
if in_code_block && code_line_ignored_by_rustdoc(line) {
|
|
||||||
|
for mut line in src.lines() {
|
||||||
|
if in_code_block && is_rust && code_line_ignored_by_rustdoc(line) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if line.starts_with("```") {
|
if let Some(header) = line.strip_prefix(RUSTDOC_FENCE) {
|
||||||
in_code_block ^= true
|
in_code_block ^= true;
|
||||||
}
|
|
||||||
|
|
||||||
let line = if in_code_block && line.starts_with("```") && !line.contains("rust") {
|
if in_code_block {
|
||||||
"```rust"
|
is_rust = header
|
||||||
} else {
|
.split(',')
|
||||||
line
|
.all(|sub| RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC.contains(&sub.trim()));
|
||||||
};
|
|
||||||
|
if is_rust {
|
||||||
|
line = "```rust";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
processed_lines.push(line);
|
processed_lines.push(line);
|
||||||
}
|
}
|
||||||
|
@ -38,6 +48,30 @@ mod tests {
|
||||||
assert_eq!(format_docs(comment), "```rust\nfn some_rust() {}\n```");
|
assert_eq!(format_docs(comment), "```rust\nfn some_rust() {}\n```");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_format_docs_handles_plain_text() {
|
||||||
|
let comment = "```text\nthis is plain text\n```";
|
||||||
|
assert_eq!(format_docs(comment), "```text\nthis is plain text\n```");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_format_docs_handles_non_rust() {
|
||||||
|
let comment = "```sh\nsupposedly shell code\n```";
|
||||||
|
assert_eq!(format_docs(comment), "```sh\nsupposedly shell code\n```");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_format_docs_handles_rust_alias() {
|
||||||
|
let comment = "```ignore\nlet z = 55;\n```";
|
||||||
|
assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_format_docs_handles_complex_code_block_attrs() {
|
||||||
|
let comment = "```rust,no_run\nlet z = 55;\n```";
|
||||||
|
assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_format_docs_skips_comments_in_rust_block() {
|
fn test_format_docs_skips_comments_in_rust_block() {
|
||||||
let comment =
|
let comment =
|
||||||
|
@ -45,6 +79,16 @@ mod tests {
|
||||||
assert_eq!(format_docs(comment), "```rust\n#stay1\nstay2\n```");
|
assert_eq!(format_docs(comment), "```rust\n#stay1\nstay2\n```");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_format_docs_does_not_skip_lines_if_plain_text() {
|
||||||
|
let comment =
|
||||||
|
"```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```";
|
||||||
|
assert_eq!(
|
||||||
|
format_docs(comment),
|
||||||
|
"```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_format_docs_keeps_comments_outside_of_rust_block() {
|
fn test_format_docs_keeps_comments_outside_of_rust_block() {
|
||||||
let comment = " # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t";
|
let comment = " # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t";
|
||||||
|
@ -72,4 +116,21 @@ let a = 1;
|
||||||
"```rust\nfn main(){}\n```\nSome comment.\n```rust\nlet a = 1;\n```"
|
"```rust\nfn main(){}\n```\nSome comment.\n```rust\nlet a = 1;\n```"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_blocks_in_comments_marked_as_text() {
|
||||||
|
let comment = r#"```text
|
||||||
|
filler
|
||||||
|
text
|
||||||
|
```
|
||||||
|
Some comment.
|
||||||
|
```
|
||||||
|
let a = 1;
|
||||||
|
```"#;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
format_docs(comment),
|
||||||
|
"```text\nfiller\ntext\n```\nSome comment.\n```rust\nlet a = 1;\n```"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue