diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ide/src/runnables.rs | 108 | ||||
-rw-r--r-- | crates/rust-analyzer/src/markdown.rs | 81 |
2 files changed, 170 insertions, 19 deletions
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index dd59d9e70..989a63c09 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -211,12 +211,29 @@ fn has_test_related_attribute(fn_def: &ast::Fn) -> bool { | |||
211 | .any(|attribute_text| attribute_text.contains("test")) | 211 | .any(|attribute_text| attribute_text.contains("test")) |
212 | } | 212 | } |
213 | 213 | ||
214 | const RUSTDOC_FENCE: &str = "```"; | ||
215 | const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = | ||
216 | &["", "rust", "should_panic", "edition2015", "edition2018"]; | ||
217 | |||
214 | fn has_runnable_doc_test(fn_def: &ast::Fn) -> bool { | 218 | fn has_runnable_doc_test(fn_def: &ast::Fn) -> bool { |
215 | fn_def.doc_comment_text().map_or(false, |comments_text| { | 219 | fn_def.doc_comment_text().map_or(false, |comments_text| { |
216 | comments_text.contains("```") | 220 | let mut in_code_block = false; |
217 | && !comments_text.contains("```ignore") | 221 | |
218 | && !comments_text.contains("```no_run") | 222 | for line in comments_text.lines() { |
219 | && !comments_text.contains("```compile_fail") | 223 | if let Some(header) = line.strip_prefix(RUSTDOC_FENCE) { |
224 | in_code_block = !in_code_block; | ||
225 | |||
226 | if in_code_block | ||
227 | && header | ||
228 | .split(',') | ||
229 | .all(|sub| RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE.contains(&sub.trim())) | ||
230 | { | ||
231 | return true; | ||
232 | } | ||
233 | } | ||
234 | } | ||
235 | |||
236 | false | ||
220 | }) | 237 | }) |
221 | } | 238 | } |
222 | 239 | ||
@@ -421,7 +438,21 @@ fn main() {} | |||
421 | /// ``` | 438 | /// ``` |
422 | /// let x = 5; | 439 | /// let x = 5; |
423 | /// ``` | 440 | /// ``` |
424 | fn foo() {} | 441 | fn should_have_runnable() {} |
442 | |||
443 | /// ```edition2018 | ||
444 | /// let x = 5; | ||
445 | /// ``` | ||
446 | fn should_have_runnable_1() {} | ||
447 | |||
448 | /// ``` | ||
449 | /// let z = 55; | ||
450 | /// ``` | ||
451 | /// | ||
452 | /// ```ignore | ||
453 | /// let z = 56; | ||
454 | /// ``` | ||
455 | fn should_have_runnable_2() {} | ||
425 | 456 | ||
426 | /// ```no_run | 457 | /// ```no_run |
427 | /// let z = 55; | 458 | /// let z = 55; |
@@ -437,8 +468,27 @@ fn should_have_no_runnable_2() {} | |||
437 | /// let z = 55; | 468 | /// let z = 55; |
438 | /// ``` | 469 | /// ``` |
439 | fn should_have_no_runnable_3() {} | 470 | fn should_have_no_runnable_3() {} |
471 | |||
472 | /// ```text | ||
473 | /// arbitrary plain text | ||
474 | /// ``` | ||
475 | fn should_have_no_runnable_4() {} | ||
476 | |||
477 | /// ```text | ||
478 | /// arbitrary plain text | ||
479 | /// ``` | ||
480 | /// | ||
481 | /// ```sh | ||
482 | /// $ shell code | ||
483 | /// ``` | ||
484 | fn should_have_no_runnable_5() {} | ||
485 | |||
486 | /// ```rust,no_run | ||
487 | /// let z = 55; | ||
488 | /// ``` | ||
489 | fn should_have_no_runnable_6() {} | ||
440 | "#, | 490 | "#, |
441 | &[&BIN, &DOCTEST], | 491 | &[&BIN, &DOCTEST, &DOCTEST, &DOCTEST], |
442 | expect![[r#" | 492 | expect![[r#" |
443 | [ | 493 | [ |
444 | Runnable { | 494 | Runnable { |
@@ -464,9 +514,49 @@ fn should_have_no_runnable_3() {} | |||
464 | file_id: FileId( | 514 | file_id: FileId( |
465 | 1, | 515 | 1, |
466 | ), | 516 | ), |
467 | full_range: 15..57, | 517 | full_range: 15..74, |
468 | focus_range: None, | 518 | focus_range: None, |
469 | name: "foo", | 519 | name: "should_have_runnable", |
520 | kind: FN, | ||
521 | container_name: None, | ||
522 | description: None, | ||
523 | docs: None, | ||
524 | }, | ||
525 | kind: DocTest { | ||
526 | test_id: Path( | ||
527 | "should_have_runnable", | ||
528 | ), | ||
529 | }, | ||
530 | cfg_exprs: [], | ||
531 | }, | ||
532 | Runnable { | ||
533 | nav: NavigationTarget { | ||
534 | file_id: FileId( | ||
535 | 1, | ||
536 | ), | ||
537 | full_range: 76..148, | ||
538 | focus_range: None, | ||
539 | name: "should_have_runnable_1", | ||
540 | kind: FN, | ||
541 | container_name: None, | ||
542 | description: None, | ||
543 | docs: None, | ||
544 | }, | ||
545 | kind: DocTest { | ||
546 | test_id: Path( | ||
547 | "should_have_runnable_1", | ||
548 | ), | ||
549 | }, | ||
550 | cfg_exprs: [], | ||
551 | }, | ||
552 | Runnable { | ||
553 | nav: NavigationTarget { | ||
554 | file_id: FileId( | ||
555 | 1, | ||
556 | ), | ||
557 | full_range: 150..254, | ||
558 | focus_range: None, | ||
559 | name: "should_have_runnable_2", | ||
470 | kind: FN, | 560 | kind: FN, |
471 | container_name: None, | 561 | container_name: None, |
472 | description: None, | 562 | description: None, |
@@ -474,7 +564,7 @@ fn should_have_no_runnable_3() {} | |||
474 | }, | 564 | }, |
475 | kind: DocTest { | 565 | kind: DocTest { |
476 | test_id: Path( | 566 | test_id: Path( |
477 | "foo", | 567 | "should_have_runnable_2", |
478 | ), | 568 | ), |
479 | }, | 569 | }, |
480 | cfg_exprs: [], | 570 | cfg_exprs: [], |
diff --git a/crates/rust-analyzer/src/markdown.rs b/crates/rust-analyzer/src/markdown.rs index 76bef45cc..968ea55f0 100644 --- a/crates/rust-analyzer/src/markdown.rs +++ b/crates/rust-analyzer/src/markdown.rs | |||
@@ -1,22 +1,32 @@ | |||
1 | //! Transforms markdown | 1 | //! Transforms markdown |
2 | 2 | ||
3 | const RUSTDOC_FENCE: &str = "```"; | ||
4 | const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC: &[&str] = | ||
5 | &["", "rust", "should_panic", "ignore", "no_run", "compile_fail", "edition2015", "edition2018"]; | ||
6 | |||
3 | pub(crate) fn format_docs(src: &str) -> String { | 7 | pub(crate) fn format_docs(src: &str) -> String { |
4 | let mut processed_lines = Vec::new(); | 8 | let mut processed_lines = Vec::new(); |
5 | let mut in_code_block = false; | 9 | let mut in_code_block = false; |
6 | for line in src.lines() { | 10 | let mut is_rust = false; |
7 | if in_code_block && code_line_ignored_by_rustdoc(line) { | 11 | |
12 | for mut line in src.lines() { | ||
13 | if in_code_block && is_rust && code_line_ignored_by_rustdoc(line) { | ||
8 | continue; | 14 | continue; |
9 | } | 15 | } |
10 | 16 | ||
11 | if line.starts_with("```") { | 17 | if let Some(header) = line.strip_prefix(RUSTDOC_FENCE) { |
12 | in_code_block ^= true | 18 | in_code_block ^= true; |
13 | } | ||
14 | 19 | ||
15 | let line = if in_code_block && line.starts_with("```") && !line.contains("rust") { | 20 | if in_code_block { |
16 | "```rust" | 21 | is_rust = header |
17 | } else { | 22 | .split(',') |
18 | line | 23 | .all(|sub| RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC.contains(&sub.trim())); |
19 | }; | 24 | |
25 | if is_rust { | ||
26 | line = "```rust"; | ||
27 | } | ||
28 | } | ||
29 | } | ||
20 | 30 | ||
21 | processed_lines.push(line); | 31 | processed_lines.push(line); |
22 | } | 32 | } |
@@ -39,6 +49,30 @@ mod tests { | |||
39 | } | 49 | } |
40 | 50 | ||
41 | #[test] | 51 | #[test] |
52 | fn test_format_docs_handles_plain_text() { | ||
53 | let comment = "```text\nthis is plain text\n```"; | ||
54 | assert_eq!(format_docs(comment), "```text\nthis is plain text\n```"); | ||
55 | } | ||
56 | |||
57 | #[test] | ||
58 | fn test_format_docs_handles_non_rust() { | ||
59 | let comment = "```sh\nsupposedly shell code\n```"; | ||
60 | assert_eq!(format_docs(comment), "```sh\nsupposedly shell code\n```"); | ||
61 | } | ||
62 | |||
63 | #[test] | ||
64 | fn test_format_docs_handles_rust_alias() { | ||
65 | let comment = "```ignore\nlet z = 55;\n```"; | ||
66 | assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```"); | ||
67 | } | ||
68 | |||
69 | #[test] | ||
70 | fn test_format_docs_handles_complex_code_block_attrs() { | ||
71 | let comment = "```rust,no_run\nlet z = 55;\n```"; | ||
72 | assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```"); | ||
73 | } | ||
74 | |||
75 | #[test] | ||
42 | fn test_format_docs_skips_comments_in_rust_block() { | 76 | fn test_format_docs_skips_comments_in_rust_block() { |
43 | let comment = | 77 | let comment = |
44 | "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```"; | 78 | "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```"; |
@@ -46,6 +80,16 @@ mod tests { | |||
46 | } | 80 | } |
47 | 81 | ||
48 | #[test] | 82 | #[test] |
83 | fn test_format_docs_does_not_skip_lines_if_plain_text() { | ||
84 | let comment = | ||
85 | "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```"; | ||
86 | assert_eq!( | ||
87 | format_docs(comment), | ||
88 | "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```", | ||
89 | ); | ||
90 | } | ||
91 | |||
92 | #[test] | ||
49 | fn test_format_docs_keeps_comments_outside_of_rust_block() { | 93 | fn test_format_docs_keeps_comments_outside_of_rust_block() { |
50 | let comment = " # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t"; | 94 | let comment = " # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t"; |
51 | assert_eq!(format_docs(comment), comment); | 95 | assert_eq!(format_docs(comment), comment); |
@@ -72,4 +116,21 @@ let a = 1; | |||
72 | "```rust\nfn main(){}\n```\nSome comment.\n```rust\nlet a = 1;\n```" | 116 | "```rust\nfn main(){}\n```\nSome comment.\n```rust\nlet a = 1;\n```" |
73 | ); | 117 | ); |
74 | } | 118 | } |
119 | |||
120 | #[test] | ||
121 | fn test_code_blocks_in_comments_marked_as_text() { | ||
122 | let comment = r#"```text | ||
123 | filler | ||
124 | text | ||
125 | ``` | ||
126 | Some comment. | ||
127 | ``` | ||
128 | let a = 1; | ||
129 | ```"#; | ||
130 | |||
131 | assert_eq!( | ||
132 | format_docs(comment), | ||
133 | "```text\nfiller\ntext\n```\nSome comment.\n```rust\nlet a = 1;\n```" | ||
134 | ); | ||
135 | } | ||
75 | } | 136 | } |